Various surfaces

Ray-Plane Intersection

Plane is often represented in implicit form :

Equivalent:

where $N = [A B C]^T$ and $P = [x y z]^T$

To find ray-plane intersection, substitute ray equation $P(t)$ into plane equation:

  1. We get $N \cdot P + D = 0$.
  2. Sovle for $t$ to get $t_0$.
  3. if $t_0$ is infinity, no intersection (ray is parallel to plane).
  4. Intersection point is $P(t_0)$.
  5. Verify that intersection is not behind ray origin.

The normal at the intersection is $N$ (or $-N$)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Plane::hit( const Ray &r, double tmin, double tmax, SurfaceHitRecord &rec ) const 
{
Vector3d N( A, B, C );
double NRd = dot( N, r.direction() );
double NRo = dot( N, r.origin() );
double t = (-D - NRo) / NRd;
if ( t < tmin || t > tmax )
{
return false;
}
rec.t = t;
rec.p = r.pointAtParam(t);
rec.normal = N;
rec.mat_ptr = matp;
return true;
}

Ray-Sphere Intersection

Sphere (centered at origin) is often represented in implicit form:

Equivalent:

To find ray-plane intersection, substitute ray equation P(t) into plane equation:

We get $P \cdot P - r^2 = 0$:

$R_o$ is ray origin, $R_d$ is ray direction.

It is a quadratic equation in the form $at^2 + bt + c = 0$

  • $a = R_o \cdot R_o = 1$ (Since $|R_d| = 1$)
  • $b = 2R_d \cdot R_o$
  • $c = R_o \cdot R_o - r^2$

Discriminant: $d = b^2 + 4ac$

Solution: $t_\pm = \frac{-b\pm\sqrt{b^2 + 4ac}}{2a}$

Choose $t_0$ as the closest positive $t$ value ($t_+$ + or $t_-$)

The normal at the intersection point is $P(t_0)/|P(t_0)|$

Very easy to compute, that is why most ray tracing images have spheres.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool Sphere::hit( const Ray &r, double tmin, double tmax, SurfaceHitRecord &rec ) const 
{
Vector3d Rd = r.direction();
Vector3d Ro = r.origin() - center;
double a = dot(Rd,Rd);
double b = 2.0 * dot(Rd, Ro);
double c = dot(Ro,Ro) - pow(radius, 2);
double d = pow(b, 2) - 4.0 * a * c;
if(d < 0)
{
return false;
}
double t = (-b - sqrt(d)) / (2.0f * a);
if ( t >= tmin && t <= tmax )
{
rec.t = t ;
rec.p = r.pointAtParam(t);
rec.normal = (Ro + t * Rd) / radius;
rec.mat_ptr = matp;
return true;
}
return false;
}

Ray-Box Intersection

To find ray-box intersection:

  • For each pair of parallel plane, find the distance to the first plane ($t_{near}$) and to the second plane ($t_{far}$).
  • Keep the largest $t_{near}$ so far, and smallest $t_{far}$ so far.
  • If largest $t_{near}$ > smallest $t_{far}$ , no intersection.
  • Otherwise, the intersection is at P(largest $t_{near}$ )

Ray-Triangle Intersection

Finding intersection between a ray and a general polygon is difficult.

  • Compute ray-plane intersection
  • Determine whether intersection is within polygon
    • Tedious for non-convex polygon
  • Interpolation of attributes at the vertices are not well-
    defined

Much easier to find ray-triangle intersection

  • Can use the barycentric coordinates method.
  • Interpolation of attributes at the vertices are well-defined using the barycentric coordinates.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
bool Triangle::hit( const Ray &r, double tmin, double tmax, SurfaceHitRecord &rec ) const 
{
double A = v0.x() - v1.x();
double B = v0.y() - v1.y();
double C = v0.z() - v1.z();

double D = v0.x() - v2.x();
double E = v0.y() - v2.y();
double F = v0.z() - v2.z();

double G = r.direction().x();
double H = r.direction().y();
double I = r.direction().z();

double J = v0.x() - r.origin().x();
double K = v0.y() - r.origin().y();
double L = v0.z() - r.origin().z();

double EIHF = E*I - H*F;
double GFDI = G*F - D*I;
double DHEG = D*H - E*G;

double denom = (A*EIHF + B*GFDI + C*DHEG);

double beta = (J*EIHF + K*GFDI + L*DHEG) / denom;

if ( beta < 0.0 || beta > 1.0 ) return false;

double AKJB = A*K - J*B;
double JCAL = J*C - A*L;
double BLKC = B*L - K*C;

double gamma = (I*AKJB + H*JCAL + G*BLKC) / denom;

if ( gamma < 0.0 || beta + gamma > 1.0 ) return false;

double t = -(F*AKJB + E*JCAL + D*BLKC) / denom;

if ( t >= tmin && t <= tmax )
{
// We have a hit -- populat hit record.
rec.t = t;
rec.p = r.pointAtParam(t);
double alpha = 1.0 - beta - gamma;
rec.normal = alpha * n0 + beta * n1 + gamma * n2;
rec.mat_ptr = matp;
return true;
}
return false;
}

Barycentric Coordinates

The barycentric coordinates of a point P on a triangle $ABC$ is ($ \alpha $, $ \beta $, $ \gamma $) such that:

where

and

We can rewrite it as:

To find ray-triangle intersection, we let:

Solve for $t$, $\beta$ and $\gamma$

Intersection if $\beta + \gamma < 1$ and $\beta,\gamma > 0$ and $t > 0$

Expand &R_o + tR_d = A + \beta(B-A) + \gamma(C-A)&

we have 3 equations and 3 unknowns here.

Regroup and write in matrix form

Use Cramer’s Rule to solve for $t$, $\beta$ and $\gamma$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool Triangle::hit( const Ray &r, double tmin, double tmax, SurfaceHitRecord &rec ) const 
{
Vector3d e1 = v1 - v0;
Vector3d e2 = v2 - v0;
Vector3d p = cross( r.direction(), e2 );
double a = dot( e1, p );
double f = 1.0 / a;
Vector3d s = r.origin() - v0;
double beta = f * dot( s, p );
if ( beta < 0.0 || beta > 1.0 )
{
return false;
}

Vector3d q = cross( s, e1 );
double gamma = f * dot( r.direction(), q );
if ( gamma < 0.0 || beta + gamma > 1.0 )
{
return false;
}

double t = f * dot( e2, q );

if ( t >= tmin && t <= tmax )
{
rec.t = t;
rec.p = r.pointAtParam(t);
double alpha = 1.0 - beta - gamma;
rec.normal = alpha * n0 + beta * n1 + gamma * n2;
rec.mat_ptr = matp;
return true;
}
return false;
}

Advantages of Barycentric Intersection

  • Efficient
  • No need to store plane equation
  • Barycentric coordinates are useful for linear interpolation of normal vectors, texture coordinates, and other attributes at the vertices

For example, the interpolated normal at $P$ is:

and all vector should do a normalization.

The “Epsilon” Problem

Should not accept intersection for very small positive $t$

  • May falsely intersect the surface at the ray origin
  • Method 1: Use an epsilon value $\varepsilon$ > 0, and accept an intersection only if its $t > \varepsilon$.
  • Method 2: When a new ray is spawned, advanced the ray origin by an epsilon distance $\varepsilon$ in the ray direction


Reference

Learn OpenGL

Share