跳转至

Ray Tracing

Whitted-Style Ray Tracing

Why Ray Tracing?

  • Rasterization couldn't handle global effects well
  • Rasterization is fast, but quality is relatively low
  • Ray tracing is accurate, but is very slow

Basic Ray-Tracing Algorithm

Ray-Surface Intersection

Ray Equation

r=o+td0t< \mathbf{r} = \mathbf{o} + t\mathbf{d}\quad 0\leq t < \infty
  • r\mathbf{r} : point along ray
  • t : "time"
  • o\mathbf{o} : origin
  • d\mathbf{d} : (normalized) direction

Plane Equation

p:(pp)N=0 \mathbf{p}: (\mathbf{p} - \mathbf{p}')\cdot \mathbf{N}=0
  • p\mathbf{p} : all points on plane
  • p\mathbf{p}' : one point on plane
  • N\mathbf{N} : normal vector

Ray Intersection With Plane

p=r(t)t=(po)NdN \mathbf{p}=\mathbf{r}(t)\\ t = \frac{(\mathbf{p}'-\mathbf{o})\cdot \mathbf{N}}{\mathbf{d}\cdot\mathbf{N}}

Check: 0t<0\leq t < \infty

Möller–Trumbore Algorithm

A faster approach, giving barycentric coordinate directly O+tD=(1b1b2)P0+b1P1+b2P2 \mathbf{O} + t\mathbf{D} = (1-b_1-b_2)\mathbf{P}_0 + b_1\mathbf{P}_1 + b_2\mathbf{P}_2

[tb1b2]=1S1E1[S2E2S1SS2D] \begin{bmatrix} t\\b_1\\b_2 \end{bmatrix} = \frac{1}{\mathbf{S}_1\cdot\mathbf{E}_1} \begin{bmatrix} \mathbf{S}_2\cdot \mathbf{E}_2\\ \mathbf{S}_1\cdot \mathbf{S}\\ \mathbf{S}_2\cdot \mathbf{D} \end{bmatrix}

where

  • E1=P1P0\mathbf{E}_1 = \mathbf{P}_1 - \mathbf{P}_0 , E2=P2P0\mathbf{E}_2 = \mathbf{P}_2 - \mathbf{P}_0
  • S=OP0\mathbf{S} = \mathbf{O} - \mathbf{P}_0
  • S1=D×E2\mathbf{S}_1 = \mathbf{D}\times \mathbf{E}_2 , S2=S×E1\mathbf{S}_2 = \mathbf{S}\times \mathbf{E}_1

Cost = (1 div, 27 mul, 17 add)

Bounding Volumes

Axis-Aligned Bounding Box(AABB)

tenter=max{tmin}t_{\text{enter}}=\max\{t_{\text{min}}\}, texit=min{tmax}t_{\text{exit}}=\min\{t_{\text{max}}\} t=pxoxdx t=\frac{\mathbf{p}_x' - \mathbf{o}_x}{\mathbf{d}_x}

Using AABBs to accelerate ray tracing

Preprocess - Build Acceleration Grid

  1. Find bounding box
  2. Create grid
  3. Store each object in overlapping cells

Step through grid in ray traversal order

For each grid cell, test intersection with all objects stored at that cell

Grid Resolution

Heuristic:

  • #cells = C * #objs
  • C27C\approx 27 in 3D

Spatial Partitions

KD-Tree Pre-Processing

Internal nodes store

  • split axis: x-, y-, or z-axis
  • split position: coordinate of split plane along axis
  • children: pointers to child nodes
  • No objects are stored in internal nodes

Leaf nodes store

  • list of objects

Object Partitions & Bounding Volume Hierarchy (BVH)

Building BVH

  • Find bounding box
  • Recursively split set of objects in two subsets
  • Recompute the bounding box of the subsets
  • Stop when necessary
  • Store objects in each leaf node

bvh

Internal nodes store

  • Bounding box

  • Children: pointers to child nodes

Leaf nodes store

  • Bounding box

  • List of objects

Nodes represent subset of primitives in scene

  • All objects in subtree

BVH Traversal

C++
Intersect(Ray ray, BVH node) {
  if (ray misses node.bbox) return;

  if (node is a leaf node) {
    test intersection with all objs;
    return closest iontersection;
  }

  hit1 = Intersect(ray, node.child1);
  hit2 = Intersect(ray, node.child2);

  return the closest of hit1, hit2;
}

Spatial vs Object Partitions

Spatial partition (e.g. KD-tree)

  • Partition space into non-overlapping regions
  • An object can be contained in multiple regions

Object partition (e.g. BVH)

  • Partition set of objects into disjoint subsets
  • Bounding boxes for each set may overlap in space

Basic Radiometry

Measurement system and units for illumination

Accurately measure the spatial properties of light

  • Radiant flux, intensity, irradiance, radiance

Perform lighting calculations in a physically correct manner

Radiant Energy and Flux (Power)

Q [J=Joule] Q\ [\text{J}=\text{Joule}]
ΦdQdt [W=Watt] [lm=lumen] \Phi \equiv \frac{\text{d}Q}{\text{d}t}\ [\text{W}=\text{Watt}]\ [\text{lm}=\text{lumen}]

Flux - #photons flowing through a sensor in unit time

phy

Solid Angle

Definition: Ratio of subtended area on sphere to radius squared. Ω=Ar2 \Omega = \frac{A}{r^2}

dA=(rdθ)(rsindϕ)=r2sinθdθdϕdω=dAr2=sinθdθdϕ \text{d}A = (r\text{d}\theta)(r\sin\text{d}\phi) = r^2\sin\theta\text{d}\theta\text{d}\phi\\ \text{d}\omega = \frac{\text{d}A}{r^2} = \sin\theta \text{d}\theta\text{d}\phi

Radiant Intensity

Definition: The radiant (luminous) intensity is the power per unit solid angle emitted by a point light source. I(ω)dΦdω [Wsr][lmsr=cd=candela] I(\omega)\equiv \frac{\text{d}\Phi}{\text{d}\omega}\ \left[ \frac{\text{W}}{\text{sr}} \right] \left[ \frac{\text{lm}}{\text{sr}} = \text{cd} = \text{candela} \right]

Irradiance

Definition: The irradiance is the power per projected unit area incident on a surface point. E(x)dΦ(x)dA[Wm2][lmm2=lux] E(\mathbf{x}) \equiv \frac{\text{d}\Phi(\mathbf{x})}{\text{d}A}\\ \left[ \frac{\text{W}}{\text{m}^2} \right] \left[ \frac{\text{lm}}{\text{m}^2} = \text{lux}\right]

Lambert's Cosine Law

Radiance

Definition: The radiance (luminance) is the power per unit solid angle per projected unit area. L(p,ω)d2Φ(p,ω)dωdAcosθ L(\text{p},\omega) \equiv \frac{\text{d}^2\Phi(\text{p},\omega)}{\text{d}\omega\text{d}A\cos\theta} Recall

  • Irradiance: power per projected unit area
  • Intensity: power per solid angle

So

  • Radiance: Irradiance per solid angle
  • Radiance: Intensity per projected unit area

Irradiance vs. Radiance

Irradiance: total power received by area dA\text{d}A

Radiance: power received by area dA\text{d}A from "direction" dω\text{d}\omega

Bidirectional Reflectance Distribution Function(BRDF)

BRDF

The BRDF represents how much light is reflected into each outgoing direction ωr\omega_r from each incoming direction fr(ωiωr)=dLr(ωr)dEi(ωi)=dLr(ωr)Li(ωi)cosθidωi[1sr] f_r(\omega_i\to \omega_r) = \frac{\text{d}L_r(\omega_r)}{\text{d}E_i(\omega_i)} = \frac{\text{d}L_r(\omega_r)}{L_i(\omega_i)\cos\theta_i\text{d}\omega_i} \begin{bmatrix} 1\newline \text{sr} \end{bmatrix}

The Reflection Equation

Lr(p,ωr)=H2fr(p,ωiωr)Li(p,ωi)cosθidωi L_r(\text{p},\omega_r) = \int_{H^2} f_r(\text{p},\omega_i\to\omega_r)L_i(\text{p},\omega_i)\cos\theta_i\text{d}\omega_i Adding an Emission term to make it general: Lo(p,ωo)=Le(p,ωo)+Ω+Li(p,ωi)fr(p,ωi,ωo)(nωi)dωi L_o(\text{p},\omega_o) = L_e(\text{p},\omega_o) + \int_{\Omega^+}L_i(\text{p},\omega_i) f_r(\text{p},\omega_i, \omega_o)(n\cdot \omega_i)\text{d}\omega_i Note: now, we assume that all directions are pointing outwards!

Understanding the Rendering Equation

Linear Operator Equation

l(u)=e(u)+l(v)K(u,v)dvKernel of equationL=E+KL l(u) = e(u) + \int l(v)\underbrace{K(u,v)\text{d}v}_{\text{Kernel of equation}}\\ L=E+KL
L=E+KE+K2E+K3E+ L=E+KE+K^2E+K^3E+\cdots

Path Tracing

Problem

  • Where should the ray be reflected for glossy materials?

  • No reflections between diffuse materials?

A Simple Monte Carlo Solution

Compute the radiance at pp towards the camera

Lo(p,ωo)=Ω+Li(p,ωi)fr(p,ωi,ωo)(nωi)dωi L_o(\text{p},\omega_o) = \int_{\Omega^+}L_i(\text{p},\omega_i) f_r(\text{p},\omega_i, \omega_o)(n\cdot \omega_i)\text{d}\omega_i

Monte Carlo Integration: f(x)dx=1Ni=1Nf(Xi)p(Xi)Xip(x)\displaystyle \int f(x)\text{d}x = \frac{1}{N} \sum_{i=1}^N\frac{f(X_i)}{p(X_i)}\quad X_i\sim p(x)

"f(x)f(x)": Li(p,ωi)fr(p,ωi,ωo)(nωi)L_i(\text{p},\omega_i) f_r(\text{p},\omega_i, \omega_o)(n\cdot \omega_i)

PDF: p(ωi)=1/2πp(\omega_i)=1/2\pi (assume uniformly sampling the hemisphere)

Lo(p,ωo)=Ω+Li(p,ωi)fr(p,ωi,ωo)(nωi)dωi1Ni=1NLi(p,ωi)fr(p,ωi,ωo)(nωi)p(ωi) \begin{aligned} L_o(\text{p},\omega_o) &= \int_{\Omega^+}L_i(\text{p},\omega_i) f_r(\text{p},\omega_i, \omega_o)(n\cdot \omega_i)\text{d}\omega_i\\ &\approx \frac{1}{N}\sum_{i=1}^N\frac{L_i(\text{p},\omega_i) f_r(\text{p},\omega_i, \omega_o)(n\cdot \omega_i)}{p(\omega_i)} \end{aligned}

Introducing Global Illumination

What if a ray hits an object? The direct illumination at the object!

Sampling the Light

dω=dcosθxx2\displaystyle \text{d}\omega = \frac{\text{d}\cos \theta'}{\|x'-x\|^2}

Lo(x,ωo)=Ω+Li(x,ωi)fr(x,ωi,ωo)(nωi)dωi=ALi(x,ωi)fr(x,ωi,ωo)cosθcosθxx2dA \begin{aligned} L_o(x,\omega_o) &= \int_{\Omega^+}L_i(x,\omega_i) f_r(x,\omega_i, \omega_o)(n\cdot \omega_i)\text{d}\omega_i\\ &= \int_AL_i(x,\omega_i) f_r(x,\omega_i, \omega_o)\frac{\cos \theta\cos \theta'}{\|x'-x\|^2}\text{d}A \end{aligned}

Now we consider the radiance coming from two parts:

  1. light source (direct, no need to have RR)

  2. other reflectors (indirect, RR)

C
shade(p, wo)
  // Contribution from the light source.
  Uniformly sample the light at x (pdf_light = 1 / A)
  Shoot a ray from p to x'
  if the ray is not blocked in the middle
    L_dir = L_i * f_r * cos θ * cos θ' / |x' - p| ^2 / pdf_light

  // Contribution from other reflectors.
  L_indir = 0.0
  Test Russian Roulette with probability P_RR
  Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
  Trace a ray r(p, wi)
  if ray r hit a non-emitting object at q
    L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR

  return L_dir + L_indir

Core Code

C++
Vector3f Scene::castRay(const Ray &ray, int depth) const {
  if (depth > maxDepth) {
    return Vector3f(0.0f);
  }

  Intersection inter = intersect(ray);
  if (!inter.happened) {
    return Vector3f(0.0f);
  }

  if (inter.m->hasEmission()) {
    return inter.m->getEmission();
  }

  Vector3f hitPoint = inter.coords;
  Vector3f N = inter.normal;

  Vector3f L_dir(0.0f);

  Intersection lightInter;
  float pdf_light = 0.0f;
  sampleLight(lightInter, pdf_light);

  Vector3f x = lightInter.coords;
  Vector3f ws = (x - hitPoint).normalized();
  Vector3f NN = lightInter.normal;

  Ray shadowRay(hitPoint, ws);

  Intersection shadowInter = intersect(shadowRay);
  if (shadowInter.distance - (x - hitPoint).norm() > -0.005f) {
    L_dir = lightInter.emit * inter.m->eval(ray.direction, ws, N) * dotProduct(ws, N) * dotProduct(-ws, NN) / dotProduct(x - hitPoint, x - hitPoint) / pdf_light;
  }

  Vector3f L_indir(0.0f);

  if (get_random_float() < RussianRoulette) {
    Vector3f wi = inter.m->sample(ray.direction, N).normalized();
    Ray reflectionRay(hitPoint, wi);
    Intersection reflectionInter = intersect(reflectionRay);

    if (reflectionInter.happened && !reflectionInter.m->hasEmission()) {
      float pdf_bsdf = inter.m->pdf(ray.direction, wi, N);
      if (pdf_bsdf > 0) {
        L_indir = castRay(reflectionRay, depth + 1) * inter.m->eval(ray.direction, wi, N) * dotProduct(wi, N) / pdf_bsdf / RussianRoulette;
      }
    }
  }

  return L_dir + L_indir;
}

评论