Rama physically-based lighting
Shadows

1 Introduction

For a linear light source $\bs(x)$ in Rama, the direct ligthing integral at a point $\bp$ with normal $\bn$ has the following form (see the diffuse and specular models): \begin{equation} L=\int_{-l}^{+l} \ldots V(\bs(x),\bp)\ldots\mathrm{max}\left(\frac{\bs(x)-\bp}{\Vert\bs(x)-\bp\Vert}\cdot\bn,0\right)\ldots\mathrm{d} x \end{equation} where $V(\bx,\by)$ is $1$ if there is no occluder between $\bx$ and $\by$, or $0$ otherwise.

The $V$ term and the $\mathrm{max}$ function are problematic when one wants to find analytic approximations of this integral. In theory we can always get rid of them, by restricting the integral to the subset of the integration interval where $V$ is equal to $1$ and the dot product is positive. However, in general, this subset is hard to compute. We show here that this subset is a simple sub interval that can be analytically computed, if we restict ourselves to simple occluders like the cylindrical walls enclosing the cylindrical sea, or the horns of the south pole (neglecting the Manhattan buildings, much smaller in comparison, and the mountains).

2 Removing the $\mathrm{max}$ function

Let us suppose, without loss of generality, that the light source is oriented along the $x$-axis. With the change of variable $u=s_x-p_x+x$, we get \begin{equation} L=\int_{s_x-p_x-l}^{s_x-p_x+l} \ldots V(\bs(u),\bp)\ldots\mathrm{max}\left(\frac{au+b}{\Vert\bs(u)-\bp\Vert},0\right)\ldots\mathrm{d} u \end{equation} where \begin{align} a&=n_x\\ b&=(s_y-p_y)n_y + (s_z-p_z)n_z \end{align} In order to remove the $\mathrm{max}$ function we need to restrict the integration interval to the values of $u$ such that $au+b \ge 0$. This is a simple interval which, after intersection with the $[s_x-p_x-l,s_x-p_x+l]$ interval gives yet another interval, noted $[u_0,u_1]$: \begin{equation} L=\int_{u_0}^{u_1} \ldots V(\bs(u),\bp)\ldots\frac{au+b}{\Vert\bs(u)-\bp\Vert}\ldots\mathrm{d} u \end{equation} which can be computed as follows: \begin{align} u_0&=\begin{cases} \mathrm{max}(-\frac{b}{a},s_x-p_x-l)&\text{if}\ a>0\\ s_x-p_x-l&\text{otherwise} \end{cases}\\ u_1&=\begin{cases} \mathrm{min}(-\frac{b}{a},s_x-p_x+l)&\text{if}\ a<0\\ s_x-p_x+l&\text{otherwise} \end{cases} \end{align}

3 Removing the visibility term

The visibility term $V$ can be decomposed into a product of visibility terms $V=\Pi_i V_i$, one per occluder (the product acts as a logical AND, i.e. it means that the source is visible from $\bp$ if and only if it is not masked by any occluder). Each $V_i$ can be removed from the integral by restricting the integration interval to its intersection with the set $S_i$ where $V_i$ is equal to 1. Then the $V$ term can be removed by restricting the integration interval to its intersection with all the $S_i$. For simple occluders like the cylindrical walls or the conical "horns" in the south pole, each $S_i$ is a simple interval, which can be computed analytically, yielding a restricted integration interval which is still an interval. In the following we consider the case of the cylindrical walls. The conical horns can be handled in a similar way.

Let us consider the south cylindrical wall, with a point $\bp$ on one side and a linear light source on the other side (see Fig. 1).

Case 1 Case 2


Figure 1: The two cases where the south cylindrical wall can occlude a part of a linear light source.

The visibility term $V(\bs(x),\bp)$ will be 0 for all $x$ such that the intersection $\bw(x)$ of the line from $\bp$ to $\bs(x)$ with the plane containing the cylindrical wall is not above the wall, but on the wall. We show below that this is the case for all $x \le x_0$ for some value $x_0$.

In the following we suppose that

The intersection $\bw(x)$ of the line from $\bp$ to $\bs(x)$ with the plane $x=W_x$ is given by \begin{equation} \bw(x)=\bp+\frac{W_x-p_x}{s_x+x-p_x}\left(\bs+\left[\begin{array}{c}x\\0\\0\end{array}\right]-\bp\right) \end{equation} This point is on the wall, i.e. the light is occluded, iif $w_y^2+w_z^2>W_r^2$. Posing \begin{align} \bl&=(W_x-p_x)(\bs-\bp)\\ \alpha&=p_y^2+p_z^2-W_r^2\\ \beta&=p_y l_y + p_z l_z\\ \gamma&=l_y^2+l_z^2 \end{align} this condition reads: \begin{equation} \alpha u^2+2\beta u+\gamma>0 \end{equation} whose roots are: \begin{equation} u'_0=\frac{-\beta-\sqrt{\beta^2-\alpha\gamma}}{\alpha}, \quad u'_1=\frac{-\beta+\sqrt{\beta^2-\alpha\gamma}}{\alpha} \end{equation}

We can now distinguish two cases (see Fig. 1):

Thus, in both cases, we can restrict the integral to all $u \gt u'_0$ and remove the visibility term in the integrand: \begin{equation} L=\int_{\mathrm{max}(u_0,u'_0)}^{u_1} \ldots \frac{au+b}{\Vert\bs(u)-\bp\Vert}\ldots\mathrm{d} u \end{equation}

3 Implementation

The implementation of the above equations is straightforward. It is convenient to split it in two functions, one for the cylindrical wall:
// Compute the part of a linear light centered at s which is occluded
// by the cylindrical wall for an observer at p (s and p must be given
// in Rama coordinates). The result is given in coordinates relative
// to p. All the points s + vec3(x,0.0,0.0) on the light source with
// s.x - p.x + x smaller than the returned value are not visible from p.
float wallIntersection(vec3 s, vec3 p) {
  float sSide = sign(s.x - Wx);
  float pSide = sign(p.x - Wx);
  float alpha = dot(p.yz, p.yz) - Wr * Wr;
  if (pSide * sSide < 0.0 && pSide * alpha > 0.0) {
    vec2 l = (Wx - p.x) * (s - p).yz;
    float beta = dot(p.yz, l);
    float gamma = dot(l, l);
    return (-beta - sqrt(beta * beta - alpha * gamma)) / alpha;
  }
  return -1e9;
}

and one combining it with the sub interval computation to remove the $\mathrm{max}$ function:

// Compute the part of a linear light centered at s, of length 2l and
// aligned with the x-axis which is visible from a surface patch
// centered at p, of normal n. The result is given in coordinates
// relative to p, i.e. it is a sub-interval of [s.x-p.x-l,s.x-p.x+l].
vec2 visibleLightSegment(vec3 s, float l, vec3 p, vec3 n) {
  vec3 ps = s - p;
  float a = n.x;
  float b = dot(ps.yz, n.yz);
  float u0 = a > 0.0 ? max(-b / a, ps.x - l) : ps.x - l;
  float u1 = a < 0.0 ? min(-b / a, ps.x + l) : ps.x + l;
  u0 = max(u0, wallIntersection(s, p));
  return u0 < u1 ? vec2(u0, u1) : vec2(0.0);
}

4 Results

Here are our results for the specular highlight on the cylindrical sea, with shadows computed as described above, and without shadows for comparison (i.e. by removing the visibility term and the $\mathrm{max}$ function without changing the integration interval).


Figure 2: Direct lighting on specular surfaces, without shadows. Direct lighting on specular surfaces, with shadows computed as above.