Image Synthesis: Theory and Practice
CSC_52084_EP · Year 1 · Period 2
Contents
- Introduction and overview
- 0. Background: probability, integrals, linear ODEs
- 1. Radiometry: radiance
- 2. Cameras: the pinhole model
- 3. Rasterization versus ray tracing
- 4. The rendering equation
- 5. BRDFs and material models
- 6. Spatial structures and shadows: intersection, BVHs, visibility queries
- 7. Monte Carlo path tracing
- 8. Noise, denoising, tone mapping
- 9. Real time: GPU, hybrid, upscaling
- 10. Neural rendering: NeRF and 3D Gaussians
- 11. Research of the instructor
- Exercises
- Projects and assessment
- References
- A complete path tracer in Python
Introduction and overview
| Module | Image Synthesis (official title, SynapseS 2026-2027 catalog) |
| Code | CSC_52084_EP |
| Team and workload | Instructor: Tamy Boubekeur (École Polytechnique) · 36 h in person, split into 18 h of lectures and 18 h of small-class sessions · 4.5 ECTS (MScT degrees; 5 ECTS engineering curriculum) · official teaching methods: lectures + computer-lab sessions + project (“Cours + travaux dirigés sur machine + projet”) · realistic image synthesis: radiometry, the rendering equation, BRDFs and textures, cameras, rasterization versus ray tracing, shadows and spatial structures, Monte Carlo path tracing, denoising, GPU programming and real-time rendering, global illumination approximations, neural rendering (NeRF, 3D Gaussians) — with a complete mini ray tracer in Python. Official course page: Image Synthesis on Moodle. |
| Period | Fall semester — period 2 (X-3A-P2): from 04/01/2027 to 19/03/2027 · X3A ME1 slot |
| Language | English & French |
| Assessment | Final project (implementation of a research paper with report), carried out outside class time; resit by a similar project; no written or oral exam; course unit earned if final grade ≥ 10 (modalities indicated for the engineering curriculum; LLGA-specific: not communicated). |
| Status in the program | P2 optionNot preselected by a route Course unit listed in the official LLGA 1 – S2 2026-2027 track. |
Audience: an image or GPU engineer who wants to understand a path tracer from end to end before meeting NeRF. Empirical first: each topic starts from a failure — a black image because radiance was ill-defined, noise that never decreases because the estimator is biased — and only then names the concept that explains it: the rendering equation, BRDFs, Monte Carlo estimators, BVHs.
The module goes from the physics of light to neural rendering, with a companion path tracer in Python.
What you will be able to do
- Handle radiometric quantities, first and foremost radiance.
- State the rendering equation and explain why solving it exactly is out of reach.
- Choose a material model (BRDF) according to the intended effect, and drive its parameters with filtered, perspective-correct textures.
- Model a camera with the pinhole model and connect pixels and rays.
- Compare rasterization and ray tracing, their trade-offs and their hybrids.
- Compute hard and soft shadows by shadow rays and shadow maps, and accelerate ray queries with BVHs.
- Write a Monte Carlo path tracer, and know the classical approximations of global illumination (ambient occlusion, radiosity, irradiance caching, photon mapping).
- Handle noise, denoising and tone mapping to go from physical values to a displayable image.
- Program the GPU pipeline with vertex and fragment shaders, and situate NeRF and 3D Gaussians in the rendering landscape and the challenges of real time.
Official objectives and syllabus (2026-2027 catalog)
The official objective of the course unit is to make students able to develop complete interactive 3D rendering systems, and to give a detailed understanding of the simulation of light transport and of the formation of digital images from a 3D scene.
The official description covers the digital models of shape, appearance, lighting and sensors of a scene, the rendering equation and the standard models of illumination, shading and reflectance, the rendering algorithms (projective rasterization, ray tracing), real time, GPU programming and hierarchical spatial structures, with an opening towards global illumination.
The course has a strong practical dimension: the models and algorithms are implemented throughout the term (officially in C++ with the OpenGL API, technical notions reviewed in class; this material uses a Python path tracer as its guiding thread).
Official prerequisites (stated for the engineering degree and the exchange programmes): algorithms and data structures, calculus, programming (C or C++), notions of linear algebra. Official teaching methods: lectures, computer-lab sessions and a project; keywords: 3D computer graphics, image synthesis, rendering.
The sheet’s detailed programme lists ten themes, in the order of the table below, which maps each of them, and each element of the description, to the section that teaches it.
| Element of the official description / syllabus (in the sheet’s order) | Section of this module |
|---|---|
| 1. Introduction | Introduction (overview) and ch. 0 (background); ch. 1 (radiometry, radiance). |
| 2. Rendering Algorithms & Visibility | ch. 3 — rasterization versus ray tracing (definition 3.1), edge functions (theorem 3.2), z-buffer and visibility (definition 3.3). |
| 3. Appearance | ch. 5 — BRDF, reciprocity and energy (definition 5.1), Lambert, Phong, Blinn-Phong, microfacets, Fresnel (5.2–5.4). |
| 4. Textures | ch. 5 — textures: UV mapping and bilinear lookup (definition 5.5), perspective-correct interpolation (proposition 5.6), mipmapping, trilinear and anisotropic filtering (example 5.7). |
| 5. Shadows | ch. 6 — shadows: shadow rays, hard and soft shadows (definition 6.5), penumbra (example 6.6), shadow mapping, bias, PCF, cascades; real-time use in ch. 9. |
| 6. Spatial Structures | ch. 6 — ray-triangle intersection (definition 6.1), BVHs (definition 6.2), SAH (theorem 6.3, example 6.4). |
| 7. Global Illumination | ch. 4 — the rendering equation and the bounce series; ch. 7 — Monte Carlo path tracing, importance sampling, next event estimation, Russian roulette. |
| 8. GPU – Graphics Processing Unit | ch. 9 — the hardware pipeline (definition 9.1), the model-view-projection chain (definition 9.3), shaders in GLSL, hardware ray tracing and upscaling (definition 9.2). |
| 9. Global Illumination Approximation | ch. 9 — approximations: ambient occlusion (definition 9.5, example 9.6), radiosity, irradiance caching and probes, photon mapping, virtual point lights; ch. 8 (denoising). |
| 10. Radiance Fields & Neural Rendering | ch. 10 — volume rendering, NeRF, 3D Gaussians, differentiable rendering. |
| Description: models of shape | triangle meshes (ch. 3, definition 6.1), implicit and neural representations (ch. 10); mesh data structures belong to the P1 Shapes module. |
| Description: models of lighting and of sensors | ch. 1 (radiometry), point and area lights (definition 7.4); ch. 2 (pinhole camera, thin lens, ray generation). |
| Description: illumination, shading and reflectance models | ch. 5 (BRDF models), ch. 9 (shading in the fragment shader). |
| Description: real time, GPU programming, hierarchical spatial structures | ch. 9 (pipeline, shaders, hybrid ray tracing) and ch. 6 (BVHs). |
| Practice: implement the models and algorithms throughout the term | the companion Python path tracer (code), the GLSL shaders of ch. 9, the labs of the session plan. |
Complement to the official syllabus Ch. 8 (noise, denoising, tone mapping), ch. 11 (research of the instructor) and the companion Python path tracer go beyond the published detailed programme. The official practical work is in C++ with OpenGL; these notes use Python for the path tracer and GLSL for the GPU side, so the C++/OpenGL engineering itself (build, buffers, API calls) is not covered here — it is taught in the course’s lab sessions.
Where this course fits in the program
An option of period 2 of M1 that no route of the home page preselects, in the P2 “Choose 1” group: a student takes this course or one of its five siblings, among them Multimodal Generative AI and Deep Learning (P2 variant).
The Monte Carlo background — law of large numbers, central limit theorem, variance of a mean — comes from Refresher in Statistics, ch. 4; ch. 0 recalls it and ch. 7 builds the estimators. Probability Theory for ML: Monte Carlo and Generative Models goes further but is not in the 2026–2027 LLGA list, so it is optional reading.
Two P1 options help: Digital Representation and Analysis of Shapes for 3D surfaces, Signal Processing for filtering. Since Deep Learning is taken once, in P1 or P2, a student of this course took it in P1. The learned denoisers of our ch. 8 and the neural fields of our ch. 10 rely on its backpropagation (Deep Learning, ch. 2), CNNs (ch. 4) and recurrent networks (ch. 5).
Official prerequisites: algorithmics and data structures, calculus, programming in C or C++, notions of linear algebra — the module’s Python path tracer is their application ground, the official implementation of the course being in C++/OpenGL.
Multimodal Generative AI is an alternative to this course, not a sequel. Its §8.4 only sketches NeRF: a student who chose Multimodal instead can read our neural-rendering chapter as a complement. The camera and light models of ch. 1–2 meet the vision side in Image Analysis and Computer Vision, ch. 1. Downstream, the M2 course Analysis and Deep Learning on Geometric Data processes triangle meshes like ours rather than rendering them, and rendered views of 3D products feed the Y2.3 transverse project.
Course outline
- Background: probability, integrals, linear ODEs — the mathematics of rendering, put back in hand.
- Radiometry: radiance — the physical quantities that describe light, and the one that will carry everything.
- Cameras: the pinhole model — forming an image: projection, pixels and rays.
- Rasterization versus ray tracing — the two great families of rendering algorithms and their trade-offs.
- The rendering equation — the integral formulation that grounds the whole rest of the module.
- BRDFs and material models — how a surface responds to light, from diffuse to specular, and textures that vary it over the surface.
- Spatial structures and shadows — making ray-scene intersection computations affordable, and answering the visibility queries that make shadows.
- Monte Carlo path tracing — solving the rendering equation by random sampling.
- Noise, denoising, tone mapping — going from noisy physical values to a displayable final image.
- Real time: GPU, hybrid, upscaling — shaders, the strategies that bring realism closer to interactive budgets, and the approximations of global illumination.
- Neural rendering: NeRF and 3D Gaussians — when networks learn the scene and its rendering.
- Research of the instructor — the research papers of Tamy Boubekeur, read with the tools of the module.
- A complete path tracer in Python — the companion code of the course, to read, run and extend.
- Exercises — radiometric computations and guided extensions of the path tracer.
- Projects and assessment — solo and group subjects (paper studies included), a written-exam proposal and a rubric.
- References — the reference books and papers of rendering.
Session plan
36 h in class = 12 sessions of 3 h. The volume is the one of the SynapseS 2026–2027 sheet (36 h in person: 18 h of lectures and 18 h of small-class sessions, i.e. about 1 h 30 of each per session).
The sheet’s detailed programme lists ten themes without dates; the plan follows their order — introduction (session 1), rendering algorithms and visibility (2), appearance and textures (4), shadows and spatial structures (5), global illumination (6–7), GPU (9), global illumination approximation (10), radiance fields and neural rendering (11–12).
Two placements are ours: the rendering equation (session 3), which opens the global-illumination theme, comes before appearance because the BRDF is defined through it; denoising and tone mapping (session 8), which the sheet does not list, come right after path tracing, which produces the noise. To follow this order, the spatial-structures chapter (ch. 6) now precedes the path-tracing chapter (ch. 7). The split into sessions is our proposal.
Level 1 = foundations (radiometry, cameras, visibility), 2 = core rendering methods, 3 = recent research (neural rendering); the level never goes down from one session to the next.
| Session | Hours | Level | Content | Practice |
|---|---|---|---|---|
| 1 | 3 h | 1 | ch. 0 Background; ch. 1 Radiometry and the invariance of radiance | Ex. 1 |
| 2 | 3 h | 1 | ch. 2 Pinhole camera, projection, ray generation, thin lens; ch. 3 Rasterization versus ray tracing: edge functions, z-buffer | Ex. 4, 7 |
| 3 | 3 h | 2 | ch. 4 The rendering equation and the bounce series | Ex. 2 |
| 4 | 3 h | 2 | ch. 5 BRDFs: Lambert, Phong, microfacets (GGX, Smith), Fresnel; textures, perspective-correct interpolation, mipmaps | Ex. 3, 6 |
| 5 | 3 h | 2 | ch. 6 Ray-triangle intersection, BVHs and the SAH; shadows: shadow rays, penumbrae, shadow maps | Ex. 8 |
| 6 | 3 h | 2 | ch. 7 The Monte Carlo estimator and importance sampling | Ex. 5 |
| 7 | 3 h | 2 | ch. 7 Next event estimation, multiple importance sampling (balance heuristic), Russian roulette, light sampling; the companion path tracer | lab: run and read the companion path tracer |
| 8 | 3 h | 2 | ch. 8 Noise, denoising, tone mapping | lab: bilateral denoising with auxiliary buffers |
| 9 | 3 h | 2 | ch. 9 The real-time pipeline, the model-view-projection chain, shaders, hybrid ray tracing, upscaling | lab: the GLSL shaders of ch. 9; rasterized versus path-traced frame budget |
| 10 | 3 h | 2 | ch. 9 Global illumination approximations: ambient occlusion, radiosity, irradiance caching, photon mapping | lab: ambient occlusion by cosine-sampled rays in the companion path tracer |
| 11 | 3 h | 3 | ch. 10 Volume rendering, transmittance, NeRF and its photometric loss | Ex. 9 |
| 12 | 3 h | 3 | ch. 10 3D Gaussians and differentiable rendering | Ex. 10 (project kick-off) |
Self-study, outside the 36 h (about 2 h in total): ch. 11 Research of the instructor (≈ 2 h). The final project (Ex. 10 or one of the project subjects, implementation of a research paper) is carried out outside class time, as the official assessment states.
How to study this module
- Run the Python path tracer from the first chapters on: every concept of the course has its line of code there.
- Recode the building blocks in small steps — ray, sphere, then direct lighting — rather than attacking everything at once.
- Draw the integrands: the rendering equation is understood on a sketch of light bouncing around.
- Connect the noise in your images to the limit theorems of Refresher in Statistics, ch. 4: variance, sampling, convergence.
- Test each idea on minimal scenes — one sphere, two lights — before scaling up in complexity.
- Keep the full pipeline in mind: radiometry, rendering, denoising, tone mapping — an upstream error is paid downstream.
Independent note Rendering is the master’s generative physics: it provides generative models with a quantitative foundation — light, cameras, integrals — which reappears in neural rendering (ch. 10) and in the rendered views that 3D and multimodal pipelines are trained and evaluated on. The targeted skills open onto careers in computer-generated imagery, real time and computer graphics research.
0. Background: probability, integrals, linear ODEs
L3 background mobilized
- Expectation and variance: linearity of expectation, \mathrm{Var}(X) = \mathbb{E}[X^2] - \mathbb{E}[X]^2, variance of a mean of N i.i.d. variables divided by N — all the noise of Monte Carlo rendering (chapter 7) and its denoising (chapter 8) are in these three lines;
- Law of large numbers: \bar{X}_N \to \mathbb{E}X almost surely — the convergence of path tracing; central limit theorem: the typical error decreases as \sigma/\sqrt{N};
- Multiple integrals, spherical coordinates, solid angle d\omega = \sin\theta\, d\theta\, d\varphi, area of the unit hemisphere 2\pi — chapter 1 lives on the sphere;
- Change of variables for a density: if X \sim p_X and Y = g(X), then p_Y(y) = p_X(g^{-1}(y))\, |\det Dg^{-1}(y)| — the sampling of light (chapter 7);
- First-order linear ODE: y' + a(t)\,y = b(t) is solved with the integrating factor y(t) = e^{-\int a}\big(y_0 + \int b e^{\int a}\big) — the transmittance of chapter 10;
- Algebra: dot and cross products, orthonormal bases, change-of-basis matrices — the camera (chapter 2).
Proposition 0.1 — The formula to remember from chapter 7 To estimate I = \int_D f(x)\,dx, sample X_1, \dots, X_N i.i.d. with density p on D (with p > 0 where f \neq 0) and set
Key result — the Monte Carlo estimator \hat{I}_N = \frac{1}{N}\sum_{k=1}^N \frac{f(X_k)}{p(X_k)}.
Then \mathbb{E}[\hat{I}_N] = I \quad \text{(unbiased estimator)} \qquad \text{and} \qquad \mathrm{Var}(\hat{I}_N) = \mathrm{Var}(f(X)/p(X))/N. Monte Carlo rendering = choosing D (the light paths), f (the radiative contribution) and p (the sampling strategy) — and all the art lies in p.
1. Radiometry: radiance
A render that looks pretty but is not physical breaks as soon as the lighting changes. This chapter fixes the physical quantities of light, and singles out the one that a renderer must compute: radiance.
What breaks Three mistakes recur: confusing irradiance (power per unit area) with radiance (power per unit area and per unit solid angle), selling a biased estimator as unbiased, and using material models that violate reciprocity.
Definition 1.1 — Flux, irradiance, exitance, radiance
- Flux (power): \Phi, in watts — the light energy per second;
- Irradiance E(x) in W/m²: flux received per unit area at x; exitance M: flux emitted per unit area;
- Solid angle: \omega = A/r^2 (surface cut out on the unit sphere), in steradians; the hemisphere: 2\pi sr, the sphere: 4\pi sr;
- Radiance L(x, \omega) in W/(m²·sr): the flux per unit area perpendicular to the direction and per unit solid angle, d^2\Phi = L(x, \omega)\,\cos\theta\, dA\, d\omega, where \theta is the angle between \omega and the normal at x. This is the quantity of the module: sensors (pixels, the eye) respond to radiance.
Theorem 1.2 — Invariance of radiance along a ray In a vacuum (no absorption, no emission), radiance is constant along a ray: for two points x_1, x_2 that “see” each other, the radiance leaving x_1 towards x_2 equals the radiance arriving at x_2 from x_1, L_o(x_1 \to x_2) = L_i(x_2 \leftarrow x_1). The theorem says nothing about the light travelling the other way: a lamp facing a dark wall sends far more radiance to the wall than the wall sends back.
Proof. Consider the beam delimited by two small surfaces dA_1 (around x_1) and dA_2 (around x_2), at distance r.
Let L_1 = L_o(x_1 \to x_2) be the radiance of the beam as it leaves dA_1, and L_2 = L_i(x_2 \leftarrow x_1) its radiance as it arrives at dA_2. The flux crossing this beam can be written on both sides: d^2\Phi = L_1 \cos\theta_1\, dA_1\, d\omega_1 = L_2 \cos\theta_2\, dA_2\, d\omega_2 , and the solid angle under which dA_2 is seen from x_1 equals d\omega_1 = \cos\theta_2\, dA_2 / r^2 (and symmetrically d\omega_2 = \cos\theta_1\, dA_1 / r^2).
Substituting: L_1 \cos\theta_1 dA_1 \frac{\cos\theta_2 dA_2}{r^2} = L_2 \cos\theta_2 dA_2 \frac{\cos\theta_1 dA_1}{r^2} \;\Longrightarrow\; L_1 = L_2. Conservation of flux in the beam forces the equality — all the geometric factors cancel out exactly. ∎
Example 1.3 — A light bulb, round numbers An isotropic “bulb” of flux \Phi = 800 lm (≈ 1.2 W radiometric at 555 nm) at 2 m from a wall.
- Flux in a cone of aperture d\omega = 0.01 sr directed at the wall: d\Phi = \Phi\, d\omega/(4\pi) \approx 0.93 mW.
- Illuminated patch: dA = r^2\, d\omega/\cos\theta = 0.04 m² (at normal incidence), hence irradiance E = d\Phi/dA \approx 23 mW/m².
- At 4 m: same computation, dA multiplied by 4, E divided by 4 (\approx 5.8 mW/m²).
The famous 1/r^2 law is not a law of radiance (constant!) but of beam geometry: the pixels see the same radiance, the projected surface of the source decays as 1/r^2.
Why so many units? Photometry (lumens, candelas, lux) repeats radiometry (watts, W/sr, W/m²) weighted by the eye’s sensitivity (the function V(\lambda), peak at 555 nm where 1 W = 683 lm). Physical rendering computes in spectral radiance W/(m²·sr·nm) then converts to RGB by integration against the response functions of the three sensors — the “white” of an image is only a convention of these integrals (chapter 8: tone mapping).
2. Cameras: the pinhole model
An image is a set of radiance measurements, one per pixel. This chapter models the device that makes these measurements — the camera — and gives the two maps a renderer needs: from a 3D point to a pixel (projection), and from a pixel to a ray (ray generation).
Definition 2.1 — The pinhole An ideal camera is a point O (optical center) and an image plane at distance f (focal length). Every ray through O contributes: the pixel corresponding to direction \omega receives the radiance L(\omega).
In camera coordinates (z forward), the projection reads \pi(x, y, z) = \Big(\, f\,\frac{x}{z},\ \ f\,\frac{y}{z} \,\Big), \qquad K = \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix}, K gathers focal lengths in pixels f_x = f/\delta_x (pixel size \delta) and optical center (c_x, c_y) (principal point); the camera pose (extrinsics) is the world \to camera change-of-basis matrix [R \mid t].
This K assumes that the image v axis and the camera y axis point the same way (both down, as in OpenCV). With the camera’s vertical axis up and image rows counted downwards — the convention of example 2.3 and of OpenGL (definition 9.3) — the entry f_y of K becomes -f_y.
Ray generation, the inverse loop of the projection: \mathbf{d} = \mathrm{norm}\big(R^\top K^{-1} (u, v, 1)^\top\big), \qquad \text{origin } O.
Theorem 2.2 — A central projection maps lines to lines The image under a pinhole of any 3D line not passing through O is a line (or a point if the line passes through O).
Proof. Let \ell be a 3D line. The plane \Pi = \mathrm{span}(O, \ell) (plane containing O and \ell) exists as soon as \ell does not pass through O.
Every ray from a point of \ell is included in \Pi; the image of \ell — the set of intersections of these rays with the image plane — is therefore included in \Pi \cap \text{image plane}, the intersection of two distinct planes: a line. ∎
Example 2.3 — Projecting then pixelizing 640×480 sensor, square pixels of 50 µm ⟹ f_x = f_y = f/\delta; for f = 50 mm: f_x = 1000 px, c = (320, 240).
Camera point (x, y, z) = (1, 2, 5) m: projection (f x/z,\ f y/z) = (10,\,20) mm, then pixels, in image convention (v downward, so K carries -f_y, definition 2.1): u = f_x\, x/z + c_x = 200 + 320 = 520, \qquad v = c_y - f_y\, y/z = 240 - 400 = -160 . The point is out of the vertical field: the vertical field of view equals 2\arctan(H/(2 f_x)) = 2 \arctan(480/2000) \approx 27° (half-field 13.5°), whereas the direction of the point rises \arctan(2/5) \approx 22° above the axis — well beyond the top edge.
Remember: short focal = wide field = strong perspective; long focal = narrow field = compressed “telephoto” perspectives.
Definition 2.4 — Beyond the pinhole
- Thin lens: aperture of radius r and focus at distance D; out-of-focus points form on the sensor a disk (circle of confusion) of size \propto r\,|D - z|/z — the bokeh; in rendering: sample the aperture (lens) and the focus, then average;
- Distortions: the pinhole + lens model is linear in homogeneous coordinates; real lenses add radial distortion (r \mapsto r + k_1 r^3 + \dots) — the “fish-eye” model of the vision module;
- Exposure: the sensor integrates radiance for a duration \Delta t at a given aperture; the (aperture, time) pair does not change the scene’s radiance, only the measured signal — hence rendering in high dynamic range values (HDR, chapter 8).
3. Rasterization versus ray tracing
Once the camera is fixed, a renderer must decide which surface each pixel sees. This chapter compares the two ways of doing it — looping over the objects (rasterization) or over the pixels (ray tracing) — and the arithmetic that makes the first one so fast.
Definition 3.1 — The two orders of traversal
- Rasterization (object order): for each triangle, determine the covered pixels and write the framebuffer; complexity O(\sum_i A_i) (area of the triangles), independent of lighting complexity; native GPU hardware;
- Ray tracing (image order): for each pixel, generate a ray (chapter 2) and find the first intersection (chapter 6); complexity O(N_{pixels} \cdot \log N_{triangles}) with an acceleration structure; secondary rays (shadows, reflections) come naturally — the decisive argument for global illumination.
Theorem 3.2 — Edge functions and barycentric coordinates In the image plane, define the edge function from a to b at p: E_{ab}(p) = (b_x - a_x)(p_y - a_y) - (b_y - a_y) (p_x - a_x) \qquad \text{(2D cross product)}. Then p is inside triangle (a, b, c) iff E_{ab}, E_{bc}, E_{ca} have the same sign at p, and the barycentric coordinates are the area ratios \lambda_a = \frac{E_{bc}(p)}{E_{bc}(a)},\qquad \lambda_b = \frac{E_{ca}(p)}{E_{ca}(b)},\qquad \lambda_c = \frac{E_{ab}(p)}{E_{ab}(c)},\qquad \lambda_a + \lambda_b + \lambda_c = 1. Moreover E_{ab} is affine in p ⟹ incremental update, two additions per pixel: E_{ab}(p + (1, 0)) = E_{ab}(p) - (b_y - a_y).
Proof. E_{ab}(p) is (up to sign) the area of the parallelogram spanned by (b - a) and (p - a), hence the oriented area of triangle (a, b, p).
The point p is on the same side of (ab) as c iff E_{ab}(p) and E_{ab}(c) have the same sign; the three conditions together characterize the interior of the triangle (convexity).
For the barycentrics: p \mapsto E_{bc}(p)/E_{bc}(a) is affine, equals 1 at a and 0 at b and c (bases of (a,b,c)); it is therefore \lambda_a, and the sum equals 1 because it equals 1 at the three vertices (uniqueness of barycentric interpolation).
Affinity gives the increment by differentiation: the gradient of E_{ab} is (-(b_y - a_y),\ b_x - a_x). ∎
Definition 3.3 — Z-buffer and visibility Rasterization resolves visibility with the z-buffer: a depth buffer initialized to +\infty; for each fragment of depth z_p (interpolated), write the color only if z_p < z_{buffer}.
Memory cost: one float per pixel; independent of the drawing order (unlike painter’s sorting) — but ambiguous at equal depths (z-fighting: add a polygonal bias).
In ray tracing, visibility is resolved by the nearest intersection — the z-buffer is useless there: the two families also differ on where visibility is computed (per fragment versus per ray).
Example 3.4 — A triangle over pixels, by hand Projected vertices a = (1, 1), b = (5, 1), c = (1, 4) (pixel coordinates).
- Edge functions: \begin{gathered} E_{ab}(p) = (b - a) \times (p - a) = 4(p_y - 1); \\ E_{bc}(p) = (c - b) \times (p - b) = -4(p_y - 1) - 3(p_x - 5); \\ E_{ca}(p) = (a - c) \times (p - c) = 3(p_x - 1). \end{gathered}
- Test of the pixel with center (1, 2): E_{ab} = 4, E_{bc} = 8, E_{ca} = 0 — on edge [a, c], inside by the “top-left” rule.
- Barycentrics: \lambda_a = E_{bc}(p)/E_{bc}(a) = 8/12 = \tfrac23, \quad \lambda_b = E_{ca}(p)/E_{ca}(b) = 0, \quad \lambda_c = E_{ab}(p)/E_{ab}(c) = 4/12 = \tfrac13 , consistent: (1, 2) is one third of the way along edge [a, c].
- The pixel (2, 2): E_{ab} = 4, E_{bc} = 5, E_{ca} = 3 — all positive, inside (the hypotenuse [b, c] crosses this row at x \approx 3.67), with barycentrics (\lambda_a, \lambda_b, \lambda_c) = \big(\tfrac{5}{12},\ \tfrac{3}{12},\ \tfrac{4}{12}\big).
The same computation, repeated and made incremental (the gradient of E_{ab} is constant), renders tens of millions of triangles per second: rasterization is the arithmetic of finite differences.
Whitted and the synthesis of the two worlds Whitted’s (1980) “ray tracing” follows mirror and refraction rays and then stops: perfect reflections, hard shadows — already out of reach for pure rasterization. Current engines are hybrid: rasterization for primary visibility (fast, memory-coherent), rays on demand for soft shadows, reflections, approximate global illumination (chapter 9); path tracing (chapter 7) remains the reference for architectural rendering and animated film — when render time is counted in hours per frame.
4. The rendering equation
Radiance is the quantity to compute; this chapter writes the equation it satisfies at every surface point. The equation is recursive — the light leaving a point depends on the light leaving all the others — and its solution is a series of bounces.
Definition 4.1 — The light transport equation (LTE) At a surface point x with normal \mathbf{n}, the outgoing radiance L_o(x, \omega_o) in a direction \omega_o of the hemisphere equals (Kajiya, 1986):
Key result — the rendering equation L_o(x, \omega_o) = L_e(x, \omega_o) + \int_{\Omega^+} L_i(x, \omega_i)\, f_r(x, \omega_i \to \omega_o)\, \cos\theta_i\, d\omega_i,
with L_e the self-emission, L_i the incident radiance, f_r the BRDF (chapter 5) and \cos\theta_i = \omega_i \cdot \mathbf{n} the projection factor.
Writing L_i(x, \omega) = L_o(r(x, -\omega), \omega) (the ray traced backwards), the equation becomes an integral over surfaces: it is a Fredholm equation of the second kind, L = L_e + T\,L, \qquad T\ \text{the transport operator (one bounce)}.
Theorem 4.2 — Bounce series (Neumann) and convergence If the materials have bounded albedo \rho = \sup \int_{\Omega^+} f_r \cos\theta_i\, d\omega_i < 1, then T is a contraction in the sup norm and the unique solution of the LTE is the series L = \sum_{k=0}^{\infty} T^k L_e = \underbrace{L_e}_{\text{emission}} + \underbrace{T L_e}_{1^{\text{st}}\ \text{bounce}} + \underbrace{T^2 L_e}_{2^{\text{nd}}\ \text{bounce}} + \dots with truncation error \|T^{k+1} L\|_\infty \leq \rho^{k+1} \|L\|_\infty — geometric decay of the weight of the bounces.
Proof. For every bounded function f on rays, \|T f\|_\infty \leq \sup_x \int_{\Omega^+} f_r \cos\theta_i d\omega_i \cdot \|f\|_\infty \leq \rho \|f\|_\infty. By induction \|T^k L_e\|_\infty \leq \rho^k \|L_e\|_\infty: the series converges in norm (geometric series of ratio \rho < 1), and L = \sum T^k L_e satisfies L = L_e + T L by summing.
Uniqueness follows from contractivity: if L, L' are solutions, \|L - L'\|_\infty = \|T(L - L')\|_\infty \leq \rho \|L - L'\|_\infty forces L = L'. ∎
Example 4.3 — Two surfaces face to face: the series in action Two large parallel diffuse planes of albedo \rho = 0.5 face each other; the first one also emits L_e = 1. Each plane sees only the other, so a perfectly diffuse plane of albedo \rho receiving a uniform radiance L sends back the radiance \rho L (this is what albedo means; ch. 5 derives the corresponding BRDF \rho/\pi).
The radiance leaving the receiver is \rho times what the emitter sends, i.e. L_r = \rho\,(L_e + \rho L_r), whence L_r = \frac{\rho L_e}{1 - \rho^2}. Expanded, this is the bounce series: L_r = \rho + \rho^3 + \rho^5 + \cdots, \quad \text{i.e.} \quad 0.5\ \text{(first bounce)} + 0.125\ \text{(one more round trip)} + 0.031 + \cdots \approx 0.667 .
With \rho = 0.9 (white paint) the same formula gives L_r = 0.9/0.19 \approx 4.7: more than five times the direct contribution 0.9, and the tenth round trip still carries 0.81^{10} \approx 12\% of the first one.
Interreflections dominate direct lighting in a white room (a white alcove looks brighter than a single wall — and the colour of one surface tints its neighbours: the “colour bleeding” of global illumination).
Alternative forms Depending on the context the LTE is rewritten: as a surface integral (integrate L_i over all visible surfaces, with the visibility factor V(x \leftrightarrow y) and the geometry G = \cos\theta_x \cos\theta_y / \|x - y\|^2) — useful for sampling emissive surfaces (next event estimation, chapter 7); or as a flight equation (volume rendering, chapter 10: the emission-scattering term \sigma_s \int p\, L replaces the BRDF). Changing the formulation = changing the sampling strategy.
5. BRDFs and material models
The rendering equation contains one function that describes the material: the BRDF. This chapter defines it, states the two physical constraints it must satisfy, and presents the models used in practice, from the matte Lambertian surface to microfacets; it ends with textures, which make the parameters of these models vary over the surface.
Definition 5.1 — BRDF The bidirectional reflectance distribution function f_r(x,\ \omega_i \to \omega_o) relates incident and outgoing radiance: L_o(x, \omega_o) = \int_{\Omega^+} L_i(\omega_i)\, f_r(\omega_i \to \omega_o)\, \cos\theta_i\, d\omega_i \quad (\text{units } \mathrm{sr}^{-1}). Two physical properties:
- Helmholtz reciprocity: f_r(\omega_i \to \omega_o) = f_r(\omega_o \to \omega_i);
- energy conservation: \int f_r(\omega_i \to \omega_o) \cos\theta_o\, d\omega_o \leq 1 for every \omega_i (the albedo of chapter 4), and f_r \geq 0.
Theorem 5.2 — The Lambertian model is f_r = \rho/\pi A perfectly diffuse surface (identical outgoing radiance in all directions) with albedo \rho \in [0, 1] has the constant BRDF f_r = \frac{\rho}{\pi}.
Proof. By assumption L_o is constant; the exitance equals M = \int_{\Omega^+} L_o \cos\theta_o\, d\omega_o = L_o \int_0^{2\pi} \int_0^{\pi/2} \cos\theta \sin\theta\, d\theta\, d\varphi = L_o \pi. The received irradiance is E = \int L_i \cos\theta_i d\omega_i, and L_o = f_r E. The definition of albedo (M = \rho E) gives \pi f_r E = \rho E, hence f_r = \rho/\pi.
The factor \pi disappears: integrating a constant against \cos\theta over the hemisphere produces \pi — it will return in the cosine sampling of chapter 7. ∎
Definition 5.3 — Phong, Blinn-Phong, microfacets
Phong (1975): f_r = \tfrac{\rho_d}{\pi} + k_s\, \dfrac{n+2}{2\pi}\, \cos^n\alpha with \alpha the angle to the mirror direction — the empirical specular. The factor \frac{n+2}{2\pi} is a normalization: at normal incidence the mirror direction is the normal, \alpha = \theta_o, and \int_{\Omega^+} \cos^n\theta\,\cos\theta\,d\omega = 2\pi\int_0^{\pi/2}\cos^{n+1}\theta\,\sin\theta\,d\theta = \frac{2\pi}{n+2}, so the lobe reflects exactly the fraction k_s of the incident energy whatever its sharpness n; Blinn-Phong replaces \alpha by the angle to the half vector \mathbf{h} = \mathrm{norm}(\omega_i + \omega_o);
Microfacets (Cook–Torrance, 1982): the surface is a field of micro-mirrors; f_r = \frac{D(\mathbf{h})\, G(\omega_i, \omega_o)\, F(\omega_i)} {4\, \cos\theta_i\, \cos\theta_o}, with D the distribution of normals, G a masking-shadowing factor and F the Fresnel coefficient (example 5.4).
F is taken at the angle to the half vector, which is the same for \omega_i and \omega_o, so that f_r stays reciprocal.
The standard choices are the GGX (Trowbridge–Reitz) distribution with roughness \alpha_g \in (0, 1] and the separable Smith factor (Walter et al. 2007): D(\mathbf{h}) = \frac{\alpha_g^2}{\pi\big((\mathbf{n}\cdot\mathbf{h})^2(\alpha_g^2 - 1) + 1\big)^2}, \qquad G(\omega_i, \omega_o) = G_1(\omega_i)\, G_1(\omega_o), G_1(\omega) = \frac{2\,(\mathbf{n}\cdot\omega)}{\mathbf{n}\cdot\omega + \sqrt{\alpha_g^2 + (1 - \alpha_g^2)(\mathbf{n}\cdot\omega)^2}}. D is normalized so that the microfacets project onto exactly the macro-surface: \int_{\Omega^+} D(\mathbf{h})\,(\mathbf{n}\cdot\mathbf{h})\,d\omega_h = 1 . For \alpha_g = 1, D = 1/\pi is constant and this is the integral \int \cos\theta\, d\omega = \pi of theorem 5.2. As \alpha_g decreases, the peak D(\mathbf{n}) = 1/(\pi\alpha_g^2) sharpens, while the tails stay polynomial — the realistic “blurry” halo of GGX reflections.
G_1 equals 1 at normal incidence and falls to 0 at grazing angles, where microfacets hide each other. Engines and glTF set \alpha_g = \text{roughness}^2.
This is the family of standard PBR materials (film and games); layered models (Disney/Burley 2012) add a “sheen” component and a retroreflection lobe to it.
Example 5.4 — Schlick’s Fresnel, in numbers Schlick’s approximation: F(\theta) = F_0 + (1 - F_0)(1 - \cos\theta)^5, with F_0 the reflectance at normal incidence. Typical dielectric (F_0 = 0.04): \begin{gathered} F(0°) = 0.04, \qquad F(45°) = 0.04 + 0.96 \times 0.293^5 \approx 0.042, \\ F(60°) = 0.04 + 0.96 \times 0.5^5 \approx 0.07, \qquad F(85°) = 0.04 + 0.96 \times (1 - 0.087)^5 \approx 0.65. \end{gathered} Physical reading: all surfaces become mirrors at grazing incidence — hence the shiny edges of water or varnish. For gold (F_0 \approx (1.00,\ 0.71,\ 0.29)), even at normal incidence the reflected light is already tinted: the metal “colors” its reflection, a dielectric does not.
| Model | Parameters | Reciprocity | Energy | Use |
|---|---|---|---|---|
| Lambert | \rho | yes | exact (\rho \leq 1) | matte, spectral |
| Phong / Blinn-Phong | \rho_d, k_s, n | Phong: no* | violated if miscalibrated | historical, mobile |
| Cook–Torrance (GGX) | F_0, roughness, metal. | yes | built for it | PBR, film and games |
| Measured (MERL 1000) | 4D table | approx. | data | validation, standards |
*A subtlety that became a quality criterion The “classical” Phong specular lobe (around the mirror direction of \omega_o) is not symmetric in (\omega_i, \omega_o); the variant around the half vector \mathbf{h} is. Modern engines require reciprocity and conservation: they guarantee that swapping the light/camera positions does not change the image — indispensable for path tracing (paths are traced backwards, from the eye toward the light!). A non-reciprocal BRDF makes inverse rendering physically inconsistent.
Textures: mapping, perspective-correct interpolation, filtering
A BRDF with constant parameters gives plastic-looking objects. Real surfaces vary from point to point — the albedo of wood grain, the roughness of a scratched metal, the small-scale relief of a brick wall — and this variation is stored in textures: images whose texels are read during shading to set the parameters of the material model (albedo, roughness, metalness, normal perturbation).
Definition 5.5 — Texture mapping Each vertex of a mesh carries texture coordinates (u, v) \in [0, 1]^2 (a UV parameterization of the surface, made by an artist or computed by unwrapping).
At a shading point inside a triangle, (u, v) is interpolated from the three vertices with the barycentric coordinates of theorem 3.2 (in ray tracing they come from the intersection test, definition 6.1), and the texture is read at (u, v): by nearest texel, or by bilinear interpolation of the four surrounding texels, T(u, v) \approx (1-a)(1-b)\,T_{00} + a(1-b)\,T_{10} + (1-a)b\,T_{01} + ab\,T_{11}, where (a, b) \in [0,1)^2 is the fractional position between texel centres.
Normal maps store a perturbation of the shading normal, displacement maps an actual offset of the geometry.
Proposition 5.6 — Perspective-correct interpolation (proved) On a triangle seen in perspective, an attribute u that is affine on the 3D triangle is not affine in screen coordinates; but 1/Z and u/Z are, where Z is the camera-space depth.
A rasterizer therefore interpolates 1/Z and u/Z linearly with the screen-space barycentrics and recovers at each pixel u = (u/Z)/(1/Z).
Proof. Take the camera at the origin and the pinhole projection x_s = X/Z, y_s = Y/Z (ch. 2). The triangle lies in a plane aX + bY + cZ = d with d \neq 0 (the plane does not contain the camera).
Dividing by Z: a x_s + b y_s + c = d/Z, \qquad \text{so} \qquad 1/Z = (a x_s + b y_s + c)/d is affine in (x_s, y_s).
An affine attribute u = \alpha X + \beta Y + \gamma Z + \delta can be rewritten on the plane, using 1 = (aX + bY + cZ)/d, as u = \alpha' X + \beta' Y + \gamma' Z \quad \text{with} \quad \alpha' = \alpha + \delta a/d, \ \ \beta' = \beta + \delta b/d, \ \ \gamma' = \gamma + \delta c/d; dividing by Z gives u/Z = \alpha' x_s + \beta' y_s + \gamma', affine in (x_s, y_s). ∎
Example 5.7 — The texture that swims, and the mipmap level Interpolation. A segment goes from depth Z = 1 (where u = 0) to Z = 3 (where u = 1).
At its midpoint on screen, 1/Z interpolates to \tfrac12(1 + \tfrac13) = \tfrac23, i.e. Z = 1.5, and u/Z to \tfrac12(0 + \tfrac13) = \tfrac16, so u = \tfrac16 / \tfrac23 = \tfrac14 ; naive screen-space interpolation would give u = \tfrac12, and the texture would slide over the surface as the camera moves.
Filtering. When a 1\,024 \times 1\,024 texture covers a quad only 128 pixels wide, each pixel spans \rho = 8 texels and a single bilinear lookup aliases (moiré, shimmering).
Mipmapping (Williams 1983) precomputes the pyramid of textures downsampled by 2, 4, 8, \dots — an extra memory of \sum_{k \geq 1}4^{-k} = \tfrac13 — and reads level \lambda = \log_2\rho = 3, the 128 \times 128 version, where one texel matches one pixel.
Trilinear filtering blends the two nearest levels when \lambda is not an integer, and anisotropic filtering takes several samples along the elongated footprint of oblique surfaces.
Reading a texture is again the estimation of a pixel integral (ch. 8): mipmaps are a prefiltered answer to it.
6. Spatial structures and shadows: intersection, BVHs, visibility queries
A ray tracer (ch. 3) — and the path tracer of ch. 7 even more — spends most of its time asking which triangle a ray hits first, or whether anything blocks the segment towards a light. This chapter makes these queries cheap: a fast ray-triangle test, then a hierarchy of boxes that discards most of the scene without testing it; it ends with shadows, the visibility queries between a point and a light, in ray tracing and in rasterization.
Definition 6.1 — Ray-triangle intersection (Möller–Trumbore) For a ray o + t\mathbf{d} and a triangle (v_0, v_1, v_2), one solves o + t\mathbf{d} = v_0 + u\,(v_1 - v_0) + v\,(v_2 - v_0) in (t, u, v): a 3 \times 3 linear system solved by Cramer’s rule (2 cross products and a few divisions), the triangle being hit iff t > 0, u, v \geq 0, u + v \leq 1 — and the test returns the barycentrics of Theorem 3.2 for free (interpolation of normals and UVs).
Cost: a few tens of floating-point operations, without branching until the final tests; together with the fact that any surface can be approximated by triangles, this explains the universal conversion of scenes into triangle meshes.
Definition 6.2 — Bounding volume hierarchies (BVHs) A BVH is a binary tree whose every node carries the bounding box (AABB) of its triangles; a ray traversal descends a node iff its box is hit (slab test: intersection of 3 intervals, ~12 comparisons); the leaves test their triangles.
Average cost: O(\log n) boxes for n triangles (well-distributed scenes), versus O(n) brute force; construction O(n \log^2 n) (median), in practice almost O(n \log n).
The alternatives: kd-trees (splitting planes, optimal for static scenes, expensive construction) and uniform grids (uniform scenes, “teapot in a stadium” destroys them). The BVH wins in animation: refitting the tree when the boxes move is nearly free.
Theorem 6.3 — SAH heuristic: area governs probability For a ray of uniform random direction, the probability of hitting an AABB is proportional to its surface area; P[\text{hit } B] = \frac{A(B)}{A(B_{parent})}. From this follows the surface area heuristic (SAH) for choosing a split: \mathrm{cost}(x) = C_{trav} + P_L(x)\, C_L(x) + P_R(x)\, C_R(x), \qquad P_{L,R} \propto A(B_{L,R}), and one minimizes \mathrm{cost} over the candidate positions — the optimal kd/BVH partition to first order.
Proof. (Argument, for random lines.) Model the rays as random lines, isotropic in direction and uniform in position, conditioned on meeting B_{parent}.
For a fixed direction \omega, the lines of direction \omega that meet a convex body K are those crossing the orthogonal projection of K on a plane perpendicular to \omega: their measure is the projected area of K.
By Cauchy’s formula, the projected area of a convex body averaged over all directions equals a quarter of its surface area, A(K)/4. The measure of the lines meeting K is therefore proportional to A(K), and since every line meeting B \subset B_{parent} also meets B_{parent}, P = A(B)/A(B_{parent}). The cost formula then adds the expectations (linearity): traversal paid once, plus the intersection cost of the children weighted by their probabilities. ∎
Example 6.4 — Choosing a split with SAH, round numbers The parent box is a cube of edge 2 (area A = 24) containing 8 triangles:
- 5 clustered in the slab x \in [-1, -0.5] and 3 in the slab x \in [0.5, 1], nothing in between;
- each cluster spans the full 2 \times 2 extent in y and z, and c_i is the cost of one ray-triangle test.
Median split by triangle count (4 and 4, sorted along x): the left child holds 4 triangles of the first cluster, in a box 0.5 \times 2 \times 2 of area 2(1 + 1 + 4) = 12; the right child holds the fifth triangle of that cluster and the 3 others, so its box is the whole cube, of area 24. Hence \mathrm{cost} = C_{trav} + \tfrac{12}{24}\cdot 4\,c_i + \tfrac{24}{24}\cdot 4\,c_i = C_{trav} + 6\,c_i .
SAH split between the clusters: two boxes of area 12 holding 5 and 3 triangles, \mathrm{cost} = C_{trav} + \tfrac{12}{24}\cdot 5\,c_i + \tfrac{12}{24}\cdot 3\,c_i = C_{trav} + 4\,c_i .
The median balances the triangle counts; SAH weighs each child by the probability that a ray enters it, and here leaves the empty slab out of both children — it “cuts through the void” and saves a third of the intersection cost. On real scenes, with triangles concentrated on surfaces and large empty volumes, this is the typical situation.
In practice: coherence and SIMD The primary rays of an image are coherent (neighboring, close directions): traversing them in packets of 4–64 (SIMD) maximizes the rejection of entire branches of the tree. Diffuse rays (chapter 7) are incoherent: they are processed one by one, or by dynamic regrouping. These implementation details make a 10× in performance — this is where the “real time” of chapter 9 is played out.
Shadows: visibility between a point and a light
A shadow is a visibility question: is the light source visible from the shading point? In the rendering equation it appears as the visibility factor V(x \leftrightarrow y) \in \{0, 1\} of the surface form (ch. 4). Ray tracing answers it with one more ray; rasterization needs a second rendering pass from the light. Both are queries against the scene, answered by the same structures as primary visibility: a shadow ray traverses the BVH of definition 6.2 like any other ray.
Definition 6.5 — Shadow rays, hard and soft shadows A shadow ray from the shading point x to a point y of a light returns V(x \leftrightarrow y); it is an any-hit query — the traversal can stop at the first intersection found before y, which makes it cheaper than the closest-hit query of primary rays.
A point light gives hard shadows (V is 0 or 1). An area light gives soft shadows: the irradiance from the light involves \int_{\text{light}} V(x \leftrightarrow y)\,G(x, y)\,L_e\,dA_y , the penumbra being the region where only a fraction of the light is visible; it is estimated by shooting several shadow rays to random points of the light — next event estimation (ch. 7, definition 7.4).
Example 6.6 — Penumbra width, by similar triangles A light of width w_L = 0.5 m, an occluder edge at distance d_o = 2 m from the light and a receiving floor at distance d_r = 3 m from the light. Lines from the two ends of the light through the occluder edge reach the floor at points w_L(d_r - d_o)/d_o = 0.5 \times 1/2 = 0.25 \ \text{m} apart: the penumbra is 25 cm wide.
It shrinks to 0 when the occluder touches the receiver (d_r = d_o) — contact shadows are sharp — and grows with the size of the light, which is why a point light, and hard shadows, are an approximation.
Shadow mapping (Williams 1978), the rasterization answer:
- render the scene from the light and store, per texel, the depth of the closest surface — the shadow map;
- while shading from the camera, transform each point into the light’s view and compare its depth z with the stored z_{\text{map}}: the point is in shadow if z > z_{\text{map}} + b.
The bias b prevents shadow acne (a surface shadowing itself through rounding and the finite resolution of the map), but too large a bias detaches shadows from their casters (peter-panning).
The finite resolution produces jagged edges, reduced by percentage-closer filtering (PCF: average the binary test over neighbouring texels, which also fakes a penumbra) and by cascaded shadow maps (several maps covering increasing distances from the camera); ch. 9 places these techniques in the real-time budget.
7. Monte Carlo path tracing
The bounce series of ch. 4 is an integral over light paths of unbounded dimension, out of reach of deterministic quadrature. This chapter estimates it by random sampling, proves that the estimator is unbiased, and shows how the choice of the sampling density controls the noise.
Definition 7.1 — The rendering estimator Path tracing estimates the LTE (chapter 4) by sampling paths \mathbf{x} = (x_0, \dots, x_k) from the eye toward the sources, with density p(\mathbf{x}), via \hat{L} = \frac{L_e\, f_r\, \cos\theta\, f_r \cos\theta \cdots}{p(\mathbf{x})} \quad \text{(product of transport factors / probability of the path)}. A path is built bounce after bounce: at each bounce, draw an outgoing direction according to a density p(\omega) (hemispherical uniform, or proportional to \cos\theta/\pi — cosine sampling), accumulate the contributions, terminate with Russian roulette (Definition 7.4).
It is an unbiased estimator of the complete bounce series — in contrast with radiosity (deterministic) and photon mapping (stochastic, but biased and only consistent, ch. 9), which trade bias for less noise.
Theorem 7.2 — Unbiased, and variance in \sigma/\sqrt{N} (i) \mathbb{E}\big[f(X)/p(X)\big] = \int_D f as soon as p > 0 on the support of f. (ii) For N i.i.d. samples, the mean estimator \hat{I}_N satisfies \mathrm{Var}(\hat{I}_N) = \mathrm{Var}(f(X)/p(X))/N and \hat{I}_N \to I almost surely.
To divide the typical error by 2, you need 4 times more samples — the curse of Monte Carlo (and its salvation: the -1/2 exponent is independent of the dimension, hence the triumph of MC in rendering, with its infinite dimension of paths).
Proof. (i) \mathbb{E}[f/p] = \int (f/p)\, p = \int f. (ii) the variance of an independent sum adding up, \mathrm{Var}(\hat I_N) = \frac{1}{N^2} \sum \mathrm{Var} = \mathrm{Var}/N, and the strong law of large numbers gives the convergence. For the typical error, the CLT gives \hat I_N - I \approx \mathcal{N}(0, \mathrm{Var}/N): standard deviation in \sqrt{\mathrm{Var}/N}. ∎
Theorem 7.3 — Importance sampling: the zero-variance optimum Among the densities p, the variance of f(X)/p(X) is minimal for p^\ast \propto |f|, and it is zero when f \geq 0; in practice one chooses p close to f where f is large.
The module’s perfect example: for I = \int_{\Omega^+} \cos\theta\, d\omega = \pi (point-to-point form factor), uniform sampling (p = 1/2\pi) gives the estimator 2\pi\cos\theta, of variance \pi^2/3; cosine sampling (p = \cos\theta/\pi) gives the constant estimator \pi — exactly zero variance.
Proof. For f \geq 0, p^\ast = f/\int f gives f/p^\ast = \int f for every draw: a constant, hence zero variance. (For a signed f, p^\ast = |f|/\int|f| gives f/p^\ast = \pm\int|f|, which is the minimal variance but not zero.)
For the uniform sampling computation: \mathbb{E}[X^2] = \int (2\pi\cos\theta)^2 \frac{d\omega}{2\pi} = 2\pi \int_{\Omega^+}\cos^2\theta\, d\omega = 2\pi \cdot \frac{2\pi}{3} = \frac{4\pi^2}{3}, and \mathbb{E}[X]^2 = \pi^2, hence \mathrm{Var} = \frac{4\pi^2}{3} - \pi^2 = \frac{\pi^2}{3}, using \int_{\Omega^+}\cos^2\theta\,d\omega = \int_0^{2\pi}\!\!\int_0^{\pi/2} \cos^2\theta \sin\theta\, d\theta\, d\varphi = \frac{2\pi}{3}.
For the cosine: f/p = \cos\theta / (\cos\theta/\pi) = \pi for every draw. ∎
Definition 7.4 — The three ingredients of a real path tracer
- Next Event Estimation (NEE): at each bounce, connect the point directly to the emissive sources (shadow ray) instead of waiting for a path to hit them by chance — multiple importance sampling (MIS, Veach): combine the “BRDF” strategy and the “light” strategy with balance weights (proposition 7.5);
- Russian roulette: at each bounce, continue the path with probability q (for instance the albedo of the point) and stop it otherwise, dividing the contribution of the surviving paths by q. The estimator stays unbiased, since q \cdot \frac{X}{q} + (1 - q)\cdot 0 = X in expectation, and the number of bounces is geometric with mean \frac{1}{1-q} — the geometric series of Theorem 4.2, sampled. With q = 0.8, paths have 5 bounces on average, and only 0.8^{20} \approx 1\% of them exceed 20 bounces;
- Sampling of extended lights: a 1\,\mathrm{m}^2 panel seen from 3 m subtends a solid angle of at most about 1/9 sr, about 2\% of the hemisphere, and less at oblique incidence — a direction drawn at random over the hemisphere rarely hits it. Drawing points on the source (weighted by its area, the cosines and the squared distance) instead makes every shadow ray count; this often makes the difference between a render still noisy after minutes and one that is clean in seconds.
Proposition 7.5 — Multiple importance sampling with the balance heuristic Let m strategies draw n_i samples X_{i,j} from densities p_i.
Let weights w_i(x) \geq 0 satisfy \sum_i w_i(x) = 1 wherever f(x) \neq 0, with w_i = 0 where p_i = 0. Then F = \sum_{i=1}^{m} \frac{1}{n_i} \sum_{j=1}^{n_i} w_i(X_{i,j})\, \frac{f(X_{i,j})}{p_i(X_{i,j})} is an unbiased estimator of \int f. The balance heuristic w_i(x) = \frac{n_i\, p_i(x)}{\sum_k n_k\, p_k(x)} satisfies these conditions, and with it every sample contributes f(X)/\sum_k n_k p_k(X), whichever strategy drew it.
Proof. By linearity, strategy i contributes n_i \cdot \frac{1}{n_i}\int w_i\, \frac{f}{p_i}\, p_i = \int w_i f ; summing over i gives \int \sum_i w_i f = \int f.
For the balance heuristic, w_i \geq 0, the weights sum to 1, and w_i = 0 where p_i = 0. Substituting, \frac{1}{n_i}\, w_i\, \frac{f}{p_i} = \frac{f}{\sum_k n_k p_k} . ∎
Veach and Guibas (1995) also prove that no other weighting reduces the variance by more than \Big(\frac{1}{\min_i n_i} - \frac{1}{\sum_i n_i}\Big)\, I^2, \qquad I = \int f : the balance heuristic is close to optimal without knowing f.
Example 7.6 — MIS weights for a small and a large light One BRDF sample and one light sample per shading point (n_1 = n_2 = 1).
- Small light, rough surface. At a direction towards the light, the light density is p_L = 50 sr⁻¹ (a light of 0.02 sr sampled uniformly) and the BRDF density p_B = 2 sr⁻¹. The light sample gets the weight 50/52 \approx 0.96; a BRDF sample that happens to hit the light gets 2/52 \approx 0.04.
- Large light, sharp glossy lobe. Now p_L = 0.5 and p_B = 40 in the mirror direction: the BRDF sample gets 40/40.5 \approx 0.99, the light sample 0.01.
Each strategy takes over where its density is high, which is exactly where its estimate has low variance; the weights also settle the “counting the light twice” mistake below.
Common mistakes in a first path tracer
- Forgetting a factor of the estimator: the BRDF, the cosine or the density p — with cosine sampling and a Lambertian BRDF, \frac{f_r \cos\theta}{p} = \frac{(\rho/\pi)\cos\theta}{\cos\theta/\pi} = \rho, a useful check.
- Self-intersection (“shadow acne”): a secondary ray starting exactly on the surface hits it again because of rounding; offset its origin by a small \varepsilon along the normal.
- Counting the light twice: with next event estimation, the emission met by a bounce ray must not be added again for the same light, unless the two strategies are combined with MIS weights (proposition 7.5).
- Averaging in the display space after tone mapping instead of in linear radiance (ch. 8).
Connections with the rest of the curriculum Importance sampling (theorem 7.3) and MIS (proposition 7.5) are general variance-reduction tools of Monte Carlo integration, and Russian roulette is a general way to truncate an infinite sum without bias. Rendering uses Monte Carlo because the rate \sigma/\sqrt{N} of theorem 7.2 does not depend on the dimension of the paths; with the rate fixed, only \sigma can be reduced. The same estimators appear in learning: a mini-batch gradient is a Monte Carlo estimate of the gradient of the risk (Deep Learning, ch. 0, CLT and mini-batch noise). The same proofs serve rendering and learning; only the integrand changes.
8. Noise, denoising, tone mapping
A Monte Carlo render is noisy, and its values are physical radiances that no screen can display directly. This chapter covers the two last steps of the pipeline: removing the noise at a controlled bias, and mapping high-dynamic-range radiance to displayable pixel values.
Definition 8.1 — The noise/time budget An MC render at N samples per pixel (spp) has noise \propto 1/\sqrt{N}; the mean squared error decomposes as bias² + variance; pure path tracing is unbiased (variance only), truncated-bounce or photon mapping methods trade a little bias for a lot of variance.
Typical numbers for an interior lit by the sky through a window: 256 spp leave visible noise in the corners; 4096 spp are “clean” for cinema; real-time path tracing affords only 1–4 spp per frame (ch. 9, definition 9.2) and relies on denoising.
Definition 8.2 — Three families of denoising
- Geometric kernel filters: weighted average by distances (position, normal, depth, albedo) — the bilateral filter and its multi-scale “à-trous” cascade (5 levels, increasing bias, decreasing variance); still the standard of architectural rendering;
- Rendered signal decomposition: separate albedo × lighting before filtering (the noise lives mostly in the lighting); filter then recompose — free texture edges gained;
- Learning: temporal recurrent network (Chaitanya et al., 2017; recurrent networks: Deep Learning, ch. 5) accumulating the frames of an interactive render; OIDN / OptiX denoiser: CNNs trained on (noisy, reference) pairs with auxiliary buffers — from a few samples per pixel, learned denoisers produce images visually close to high-sample references, and they are the essential link of real time (chapter 9).
Example 8.3 — Measuring the gain Uniform patch of mean radiance \bar{L} = 0.5, noise standard deviation \sigma = 0.15 at 16 spp (SNR ≈ 10.5 dB): at 64 spp, \sigma = 0.075 (SNR ≈ 16.5 dB, +6 dB at each quadrupling); a bilateral filter over 9² neighbors of the same normal and albedo divides the variance by ~81 (81 effective samples): SNR ≈ 29 dB at 16 spp — the equivalent of 1300 spp.
This is the weapon of real time: the filter “knows” the patch is flat, MC does not. The difficulty is at edges: different normals ⟹ fewer valid neighbors ⟹ residual noise; hence the multi-scale cascades.
Definition 8.4 — Tone mapping and gamma Radiance is HDR (ratio 10⁴–10⁸ between shadow and sun); the screen is LDR (8 bits, gamma 2.2); the eye is relative (approximately logarithmic response). Tone mapping compresses the scale: Reinhard operator L_d = L/(1 + L) (global), local extensions (block-wise dodge and burn), or artistic curves (filmic ACES).
Then gamma correction: V_{enc} = V_{lin}^{1/2.2}.
Classic mistake: filtering (antialiasing, mipmaps, denoising) in sRGB space — nonlinear: the averages are wrong; the whole pipeline of this module works in linear space and converts only at the output.
Aliasing is noise too Antialiasing by stochastic supersampling (per-pixel jitter, code chapter) is an MC estimator of the pixel integral — same theory as chapter 7; deterministic aliasing (sample at the center) is a quadrature bias that turns into periodic moiré patterns. Rule: as soon as an integral is difficult, jitter it — Gaussian noise is visually preferred over structural moiré.
9. Real time: GPU, hybrid, upscaling
Film rendering spends minutes per image; a game has about 16 ms. This chapter shows how the same equations are solved under that budget: a hardware rasterization pipeline programmed with shaders, a few rays where they matter most, learned reconstruction to fill in the rest, and the classical approximations of global illumination.
Definition 9.1 — The hardware graphics pipeline Hardware rasterization (OpenGL/Vulkan/DirectX) chains: vertex shading (transformations, chapter 2) ⟹ assembly/clipping ⟹ rasterization (Theorem 3.2 in silicon) ⟹ pixel/fragment shading (materials, chapter 5) ⟹ z-buffer ⟹ framebuffer.
Coherence exploited: triangles cover blocks of contiguous pixels — massive parallelism (thousands of SIMT cores), hierarchies of early depth rejection.
Budget: 16.6 ms/frame (60 Hz) for everything — geometry, materials, shadows, post-effects. Pre-MC soft shadow techniques remain essential: filtered shadow maps (PCF, variance shadow maps), screen-space ambient occlusion (SSAO), precomputed lightmaps — a hierarchy of bias/variance trade-offs.
Definition 9.2 — RTX hybrid and upscaling
- Hardware ray tracing (RTX, 2018): units dedicated to BVH traversal (chapter 6) — rays on demand: reflections, soft shadows, GI at 1–2 sampled bounces… then denoised (chapter 8) and recombined over rasterization: the hybrid;
- Temporal upscaling (DLSS, FSR): render at 1080p, display at 4K; the network (or filter) receives the low-resolution frame + the re-projected temporal history (motion) + the geometric buffers; the formulation is a supervised inverse problem — video super-resolution with the constraint set of chapter 8;
- Game path tracing (Cyberpunk 2077, Alan Wake 2): full MC at 1–4 spp + recurrent denoising + temporal reconstruction: the theory of chapters 7 and 8, in 16 ms.
| Rendering | Samples | Bias/variance | Time/frame | Use |
|---|---|---|---|---|
| Pure rasterization | 1 primary ray | structural bias | < 8 ms | games, 3D UI |
| RTX hybrid | 0.5–2 spp per effect | mixed, denoised | < 16 ms | AAA games |
| Real-time path tracing | 1–4 spp | variance → ML | ~16 ms | high-end games |
| Production path tracing | 256–4096 spp | unbiased | minutes–hours | film, architecture |
What “real time” means for an engineer Real time is not a different theory: it is the same set of equations (chapters 1–7) under a budget constraint — and hence a spending strategy: where to accept bias (shadows, approximate GI), where to keep variance (specular reflections, the eye is sensitive there), where to learn (denoising, upscaling). The design of a modern engine is an exercise in managing bias and variance per millisecond of budget — the same trade-offs as in ML, transposed to rendering.
From model to pixels: the model-view-projection chain
The vertex shader of the next section receives three matrices, model, view and projection. They extend the pinhole camera of ch. 2 to the 4 \times 4 homogeneous form the GPU expects, and add a depth that the z-buffer (definition 3.3) can compare.
Definition 9.3 — Model, view and projection matrices A vertex \mathbf{p} of an object, in homogeneous coordinates (w = 1), is sent to clip space by \mathbf{p}_{\text{clip}} = P\, V\, M\, \mathbf{p}.
Model M: object \to world (scale, rotation, translation of the object).
View V: world \to camera, the extrinsics [R \mid t] of definition 2.1 completed into a 4 \times 4 matrix.
OpenGL’s camera looks down -z, with y up. A pose calibrated in the OpenCV convention (z forward, y down) is therefore first multiplied on the left by \mathrm{diag}(1, -1, -1, 1).
Projection P: camera \to clip space. With vertical field of view \varphi, s = \cot(\varphi/2), aspect ratio a = W/H and near and far planes 0 < z_n < z_f, P = \begin{pmatrix} s/a & 0 & 0 & 0 \\ 0 & s & 0 & 0 \\ 0 & 0 & \frac{z_f + z_n}{z_n - z_f} & \frac{2 z_f z_n}{z_n - z_f} \\ 0 & 0 & -1 & 0 \end{pmatrix}.
The GPU divides by w_{\text{clip}} = -z (the perspective divide) to reach normalized device coordinates in [-1, 1]^3, clips what falls outside, and the viewport transform maps them to pixels: u = (x_{\text{ndc}} + 1)\, W/2, \qquad v = (1 - y_{\text{ndc}})\, H/2 .
Chaining P, the divide and the viewport gives back the intrinsics of definition 2.1: with the depth d = -z, u = (H/2)\, s\, x/d + W/2 since W/a = H, and v = H/2 - (H/2)\, s\, y/d. So f_x = f_y = \tfrac{H}{2}\cot(\varphi/2), \qquad (c_x, c_y) = (W/2,\ H/2), with the -f_y of the y-up convention.
The third row of P adds a depth z_{\text{ndc}} = -A - B/z, where A and B are the two nonzero entries of that row: an affine function of 1/z, which proposition 5.6 shows is affine on screen, so the z-buffer interpolates it linearly with no perspective correction.
Normals transform by the inverse transpose of the 3 \times 3 block of M, as in the vertex shader below: if a tangent \mathbf{t} satisfies \mathbf{t}\cdot\mathbf{n} = 0, then (M\mathbf{t}) \cdot (M^{-\top}\mathbf{n}) = \mathbf{t}^\top M^\top M^{-\top} \mathbf{n} = \mathbf{t}\cdot\mathbf{n} = 0 .
Example 9.4 — The camera of example 2.3 as a projection matrix A 640 \times 480 image with f_x = f_y = 1000 px gives s = 2000/480 \approx 4.167, i.e. \varphi = 2\arctan(0.24) \approx 27°, the field of example 2.3, and s/a = 3.125.
Take near and far planes at 0.1 m and 100 m.
- The point 1 m right, 1 m up and 5 m in front, (1, 1, -5), has clip coordinates (3.125,\ 4.167,\ 4.810,\ 5), hence NDC (0.625,\ 0.833,\ 0.962).
- Viewport: u = 1.625 \times 320 = 520 and v = 0.167 \times 240 = 40 — the same pixel as K gives: u = 1000/5 + 320, v = 240 - 1000/5.
- Depth precision: z_{\text{ndc}} = 1.002 - 0.2002/d equals -1 at the near plane, about 0 at d = 0.2 m and 0.962 at d = 5 m.
Half of the depth range is spent between 10 and 20 cm from the camera: pushing z_n as far as the scene allows is the first remedy against z-fighting (definition 3.3).
Programming the GPU: shaders
The programmable stages of the pipeline of definition 9.1 are small programs, shaders, written in a C-like language (GLSL for OpenGL, the API of the official course; HLSL, WGSL or SPIR-V elsewhere).
The application uploads the geometry (vertex buffers), the textures and the uniform parameters (matrices, light positions), then issues draw calls; the GPU runs the vertex shader once per vertex and the fragment shader once per covered pixel sample, on thousands of threads in parallel.
The pair below implements the transformation chain of definition 9.3, the perspective-correct interpolation of proposition 5.6 (done by the hardware for every out/in variable), a texture lookup and the Blinn-Phong model of definition 5.3.
// vertex shader
#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uv;
uniform mat4 model, view, projection;
out vec3 fragPos; out vec3 fragNormal; out vec2 fragUV;
void main() {
vec4 world = model * vec4(position, 1.0);
fragPos = world.xyz;
fragNormal = mat3(transpose(inverse(model))) * normal; // normals transform by the inverse transpose
fragUV = uv;
gl_Position = projection * view * world; // clip space; the divide by w is done by the GPU
}
// fragment shader
#version 330 core
in vec3 fragPos; in vec3 fragNormal; in vec2 fragUV;
uniform sampler2D albedoMap; // mipmapped texture (example 5.7)
uniform vec3 lightPos, lightColor, camPos;
uniform float ks, shininess;
out vec4 color;
void main() {
vec3 n = normalize(fragNormal);
vec3 l = normalize(lightPos - fragPos);
vec3 v = normalize(camPos - fragPos);
vec3 h = normalize(l + v); // half vector (Blinn-Phong)
vec3 albedo = texture(albedoMap, fragUV).rgb; // filtered texture lookup
float d2 = dot(lightPos - fragPos, lightPos - fragPos);
vec3 E = lightColor * max(dot(n, l), 0.0) / d2; // irradiance from a point light
vec3 fr = albedo / 3.14159265 + ks * (shininess + 8.0) / (8.0 * 3.14159265) * pow(max(dot(n, h), 0.0), shininess);
color = vec4(fr * E, 1.0); // linear radiance; tone map and gamma at the end (ch. 8)
}The factor (n + 8)/(8\pi) is the usual approximate energy normalization of the Blinn-Phong lobe, the analogue of the (n + 2)/(2\pi) of Phong (definition 5.3). Shadows are added by sampling a shadow map in the fragment shader (ch. 6); compute shaders run general parallel kernels outside the graphics pipeline — denoisers, BVH builds, the image-space passes of the next section.
Global illumination approximations
Path tracing (ch. 7) solves the rendering equation without bias but needs many samples. Long before real-time path tracing, and still today inside most engines, global illumination is approximated: indirect light is smooth over diffuse surfaces, so it can be computed sparsely, precomputed, or replaced by a cheaper quantity — trading bias for speed, as definition 8.1 anticipated.
Definition 9.5 — Ambient occlusion The ambient occlusion at a point x of normal \mathbf{n} is the cosine-weighted fraction of the hemisphere that is not blocked by nearby geometry, \mathrm{AO}(x) = \frac{1}{\pi}\int_{\Omega^+} V_r(x, \omega)\cos\theta\,d\omega \in [0, 1], where V_r(x, \omega) = 1 if the ray from x in direction \omega meets no surface within a distance r.
It is the exact irradiance (divided by \pi) under a uniform white sky when only geometry closer than r casts shadows; engines multiply the ambient term by it to darken creases and contacts. Screen-space ambient occlusion (SSAO) estimates it from the depth buffer alone, at the price of missing what the camera does not see.
Example 9.6 — Ambient occlusion at the foot of a wall, exact and sampled A point on the floor, right at the foot of an infinite vertical wall, with r = \infty. The vertical plane of the wall cuts the hemisphere into two halves that are mirror images of each other, and \cos\theta takes the same values on both; the wall blocks one half exactly, so \mathrm{AO} = \tfrac{1}{\pi}\cdot\tfrac{\pi}{2} = \tfrac12 . With cosine sampling, p(\omega) = \cos\theta/\pi, the estimator of \mathrm{AO} is V_r(x, \omega) itself (the integrand divided by p, theorem 7.3), so the estimate is the fraction of unoccluded rays: a Bernoulli mean of variance \mathrm{AO}(1 - \mathrm{AO})/N.
With N = 16 rays, the standard deviation is \sqrt{0.25/16} = 0.125 — visible noise, which real-time engines blur spatially and temporally (ch. 8).
The other classical approximations, from the most precomputed to the most dynamic:
- Radiosity (Goral et al. 1984): for purely diffuse scenes, discretize the surfaces into patches of uniform radiosity B_i and solve the linear system B_i = E_i + \rho_i\sum_j F_{ij}B_j, where the form factor F_{ij} is the fraction of the energy leaving patch i that reaches patch j — the rendering equation of ch. 4 projected onto piecewise constant functions; its solution is view-independent and can be baked into light maps.
- Irradiance caching (Ward et al. 1988): compute the indirect diffuse irradiance accurately at sparse points, chosen where it varies quickly (near geometry), and interpolate elsewhere; light probes and irradiance volumes store precomputed incident light, often as a few spherical-harmonic coefficients, on a grid through the scene.
- Photon mapping (Jensen 1996): first trace photons from the lights and store their hits; then estimate reflected radiance by density estimation over the k nearest photons, L_r(x, \omega_o) \approx \sum_p f_r(x, \omega_p \to \omega_o)\,\Delta\Phi_p/(\pi r^2), with r the radius containing them. The estimate is biased (blurred over the radius r) but consistent as the number of photons grows, and it captures caustics that path tracing from the eye finds very hard.
- Instant radiosity / virtual point lights (Keller 1997): turn the photon hits into many small point lights and render with shadow maps; voxel cone tracing and screen-space reflections are their real-time descendants, now competing with the hybrid ray tracing of definition 9.2.
10. Neural rendering: NeRF and 3D Gaussians
The previous chapters render a scene that is given; this chapter recovers a scene from photographs, by making the renderer differentiable and fitting the scene to the images. Both NeRF and 3D Gaussians rest on the same volume rendering equation, whose discretization is alpha compositing.
Definition 10.1 — The volume rendering equation In a participating medium of density \sigma(x) (extinction, m⁻¹) and colored emission c(x, \omega), the radiance along a ray r(t) = o + t\mathbf{d}, t \in [s, \infty), equals L(r) = \int_s^{\infty} T(t)\, \sigma(r(t))\, c(r(t), \mathbf{d})\, dt, \qquad T(t) = \exp\Big(\!-\int_s^{t} \sigma(r(u))\, du\Big), where T is the transmittance — the probability that the ray “survives” (without interaction) until t.
Theorem 10.2 — Transmittance solves a linear ODE T is the unique solution of \frac{dT}{dt}(t) = -\,\sigma(r(t))\,T(t), \qquad T(s) = 1, and for \sigma piecewise constant on intervals [t_i, t_{i+1}) of lengths \delta_i, volume rendering discretizes into alpha compositing
Key result — alpha compositing L \approx \sum_i \Big(\prod_{j<i} (1 - \alpha_j)\Big)\, \alpha_i\, c_i, \qquad \alpha_i = 1 - e^{-\sigma_i \delta_i}.
Proof. The ODE is first-order linear (chapter 0 recall): setting \Sigma(t) = \int_s^t \sigma(r(u))\,du, d(e^{\Sigma} T)/dt = e^{\Sigma}(\Sigma' T + T') = e^{\Sigma}(\sigma T - \sigma T) = 0, so e^{\Sigma(t)}T(t) is constant equal to T(s) = 1 (since \Sigma(s) = 0): T(t) = e^{-\Sigma(t)}.
On an interval of constant \sigma_i, T(t_{i+1}) = T(t_i)\, e^{-\sigma_i\delta_i}, hence the interaction probability \alpha_i = 1 - T(t_{i+1})/T(t_i) = 1 - e^{-\sigma_i \delta_i}; summing “interaction at i AND survival before” gives the discrete series (the telescoping products \prod_{j<i}(1-\alpha_j) = T(t_i)). ∎
Definition 10.3 — NeRF (Neural Radiance Fields) NeRF (Mildenhall et al., 2020) represents the scene with a neural network F_\theta: F_\theta\big(\gamma(x), \gamma(\mathbf{d})\big) = \big(\sigma(x),\ c(x, \mathbf{d})\big), where \gamma is the positional encoding \gamma(u) = (u, \sin 2^k \pi u, \cos 2^k \pi u)_k — it gives the MLP the high-frequency content it struggles to learn from raw coordinates (the Fourier-feature analysis of Tancik et al., 2020).
Training minimizes the photometric loss on batches \mathcal{R} of rays (pixels) drawn from calibrated photos (known pose, SfM): \mathcal{L}(\theta) = \sum_{r \in \mathcal{R}} \big\| \hat{C}_\theta(r) - C(r) \big\|_2^2, \qquad \hat{C}_\theta(r) = \sum_i T_i\, \alpha_i\, c_i , where C(r) is the observed pixel colour and \hat{C}_\theta(r) the alpha-composited colour of theorem 10.2, with T_i = \prod_{j<i}(1 - \alpha_j); NeRF sums this loss over a coarse and a fine network (hierarchical sampling).
Since \partial \hat{C}/\partial c_i = T_i \alpha_i, a sample’s colour is updated in proportion to its compositing weight: samples in empty space (\alpha_i = 0) or behind opaque matter (T_i \approx 0) receive (almost) no colour gradient, and the densities receive theirs through T_i and \alpha_i.
Evaluation renders 100–300 samples per ray and alpha-composites (Theorem 10.2).
Result: photorealism, novel views — at the price of hours of training and one second per image; the successors (Instant-NGP and its hash grids encoding space) bring training down to minutes.
Definition 10.4 — 3D Gaussians (splatting) The 3D Gaussian representation (Kerbl et al., 2023) replaces the network with 10⁵–10⁷ anisotropic Gaussians (position, 3D covariance, opacity, spherical-harmonic color); a ray accumulates them by alpha compositing ordered by depth, each Gaussian being projected (rasterized) into a 2D ellipse.
The gradients of the covariance make the optimization differentiable and fast (the number of Gaussians adapts: cloning/splitting): training in minutes, rendering in real time — today’s standard for scene capture. The loss is again photometric, an L1 error mixed with a structural-similarity term: \mathcal{L} = (1 - \lambda)\, \mathcal{L}_1 + \lambda\, \mathcal{L}_{\text{D-SSIM}}, \qquad \lambda = 0.2 .
The kinship with chapter 3: it is a rasterization (object order!) of volumetric primitives — the two paradigms of chapter 3 are reconciled.
Example 10.5 — Checking alpha compositing by hand Ray crossing 4 equal intervals \delta = 0.5, densities \sigma = (2, 0, 4, 2).
- Alphas: \alpha_1 = 1 - e^{-1} \approx 0.632, \quad \alpha_2 = 0, \quad \alpha_3 = 1 - e^{-2} \approx 0.865, \quad \alpha_4 = 0.632.
- Colors c_i = (1, 0.5, 1, 0.25) and transmittances T_1 = 1, T_2 = 0.368, T_3 = 0.368, T_4 = 0.0498: L \approx 0.632 \cdot 1 + 0.368 \cdot 0 \cdot 0.5 + 0.368 \cdot 0.865 \cdot 1 + 0.368 \cdot 0.135 \cdot 0.632 \cdot 0.25 \approx 0.957.
For densities and colours that are constant on each interval, as here, this sum is the exact integral L = \int T\sigma c\, dt (theorem 10.2): there is no quadrature error.
When \sigma and c vary inside an interval, evaluating them at a single point per interval, as NeRF does, introduces a systematic quadrature error; drawing the evaluation points at random inside the intervals (stratified sampling) turns most of this error into noise that averages out over training.
Differentiable rendering, the common paradigm NeRF, 3D Gaussians, ML denoising (chapter 8), upscaling (chapter 9): everywhere, a differentiable renderer connects parameters and image, and learning does the rest — the rendering equation becomes the final layer of a network, trained by backpropagation (Deep Learning, ch. 2). The theory of chapters 1–7 is not replaced: it provides the structure (physics, estimators, bias/variance) that the networks can only exploit.
11. Research of the instructor
This chapter, read outside the class hours, presents the research of the instructor and links each work to the chapters of the module: the same question — which representation of the scene for which rendering budget — runs through all of them.
Tamy Boubekeur — geometry for rendering, from real time to neural Professor at École Polytechnique (on delegation from Télécom Paris, and Lab Director / Senior Principal Research Scientist at Adobe Research), T. Boubekeur builds geometric representations in the service of rendering — three milestones directly connected to the course:
- Phong Tessellation (with M. Alexa, SIGGRAPH Asia 2008, TOG 27) — ACM DL: interpolating positions the way Phong interpolates normals — one shader line that gives coarse meshes smooth silhouettes, in real time (chapter 9): proof that “rendering” and “modeling” are one when the primitive is well chosen;
- Sphere-Meshes (with J.-M. Thiery, E. Guy, SIGGRAPH Asia 2013, TOG 32) — ACM DL: extreme approximation of shapes by interpolation of spheres (spherical quadrics with exact error): a few dozen primitives for a character — the “proxies” of real-time rendering and scene compression (chapters 6 and 9);
- AtlasNet (with T. Groueix, S. Chaudhuri, D. Ceylan et al., CVPR 2018) — arXiv 1802.05384: generating 3D surfaces as collections of learned parametric patches — the neural “paper-mâché”: the gateway to learned 3D generation; see also the tutorial CreativeAI: Deep Learning for Computer Graphics (Eurographics/EGSR).
The line of force From real-time tessellation to learned primitives, these works pose the same question as the module: which scene representation for which rendering budget? triangles (chapter 3), hierarchies (chapter 6), neural fields and Gaussians (chapter 10): each representational leap has moved the frontier of the possible in image synthesis — and the movement keeps accelerating.
Exercises
★ Guided radiance. (a) Take up the proof of Theorem 1.2 and redo it with a sketch with explicit dimensions (dA_1 = 1 cm², dA_2 = 4 cm², r = 0.5 m, incidences 30° and 0°): verify L_1 = L_2 numerically. (b) Deduce the irradiance of a point 1 m then 3 m from an isotropic 10 W point source — explain the 1/r^2 without invoking radiance.
★ Bounce series. Two planes facing each other with albedo \rho = 0.9, one emissive (L_e = 1). (a) Compute the equilibrium radiance (geometric series). (b) How many bounces to capture 99% of the energy? 99.9%? (c) What is the link with the default path depth (3–4) of real-time engines?
★ Fresnel. Plot Schlick’s F(\theta) for F_0 = 0.04 and for gold (1.00,\,0.71,\,0.29) over [0°, 90°]; recover the values of Example 5.4; explain why wet grass looks shiny (dielectric water layer over a diffuse surface).
★ Guided camera. (a) Implement pixel ⟶ ray (Definition 2.1) and check Example 2.3. (b) For f_x = 1000 px and a 1920×1080 image, compute the horizontal and vertical fields of view. (c) Add a thin lens (sample a disk of radius r in the lens plane, aim at the focal point) and show that the circle of confusion grows linearly with r.
★ Guided Monte Carlo. (a) Re-derive the variance computation of Theorem 7.3 (uniform sampling: \pi^2/3). (b) Implement the two estimators of \int_{\Omega^+}\cos\theta\, d\omega and plot the error as a function of N (log scale); check the -1/2 slopes. (c) Generalize: for f(\theta) = \cos^k\theta, what is the zero-variance density? and a realistic approximate density?
★★ Guided Lambert. (a) Redo the proof of Theorem 5.2 in detail (computation of \int_{\Omega^+} \cos\theta\, d\omega = \pi).
(b) Check the energy conservation of Phong with k_d = 0.7, k_s = 0.3, n = 30 (numerical integration over the hemisphere); what happens for n = 1?
(c) Sample the GGX BRDF by the normal distribution method (draw \mathbf{h} according to D(\mathbf{h})\cos\theta_h, bounce) — check unbiasedness by comparison with a reference integral.
★★ Rasterization. (a) Rasterize by hand the triangle of Example 3.4 over the grid of pixels with integer centers — list of covered pixels and barycentrics (edge functions of Example 3.4, top-left rule for pixels on an edge). (b) Build an example with two triangles where painter’s sorting fails and the z-buffer does not. (c) Prove that the “top-left” rule avoids holes and double counting between adjacent triangles.
★★ BVH/SAH. (a) Build by hand the median BVH of 8 triangles in [0,1]^3 (2 levels); count the boxes traversed by a diagonal ray. (b) Implement the SAH split (12 candidates per axis) and compare the total traversal cost with (a) on a scene of 1000 random triangles. (c) What becomes the BVH if all the triangles move? (refit versus rebuild — measure both.)
★★ Volume rendering. (a) Check Example 10.5 against the exact integral (piecewise-constant densities, analytic integration of \int T \sigma c). (b) Implement alpha compositing over 64 randomly drawn samples (NeRF quadrature): does the bias vanish on average? (c) Simulate a “3D Gaussian” (\sigma(x) = \sigma_0 e^{-\|x - c\|^2/2s^2}) and render a cloud — why must the number of samples per ray grow with 1/s?
★★★ Project — extend the mini tracer. (a) Mirror/glass materials (reflection + Snell refraction, Fresnel scheme); (b) explicit Russian roulette (continuation probability q = \rho, survivors divided by q) and measurement: average depth, per-pixel variance; (c) depth of field; (d) guided bilateral denoising with the buffers (normal, depth) and equal-time comparison against more spp. Conclusion: what budget for what quality?
Projects and assessment
The fiche above gives the official assessment: “Final project (implementation of a research paper with report), carried out outside class time; resit by a similar project; no written or oral exam; course unit earned if final grade ≥ 10”. These modalities are stated for the engineering curriculum; the LLGA-specific ones are not communicated. The subjects below are proposals of these notes, not official rules: each paper study can serve as the official paper-implementation project, and the written exam below is a self-test, since the sheet announces no exam.
The projects exercise mainly O7 (renderers written from the equations and measured against references at equal time) and O6 (images and 3D scenes as a modality: neural fields, rendered views for multimodal retrieval, scenes written from text). O1 enters with the learned denoiser and O3 with the language model that writes scenes.
Solo projects
- [Solo · ≈ 25 h] Paper study: multiple importance sampling (Veach and Guibas, 1995). Can the companion path tracer reproduce the classic figure of glossy plates lit by spherical lights, where BRDF sampling fails on small lights and light sampling fails on sharp lobes?
- Work: extend the companion code with Cook–Torrance materials (GGX, Smith, Schlick: definition 5.3, example 5.4), spherical area lights sampled by NEE, MIS with the balance and power heuristics (proposition 7.5), Russian roulette (definition 7.4) and a BVH (definition 6.2).
- Paper: E. Veach and L. Guibas, “Optimally Combining Sampling Techniques for Monte Carlo Rendering”, SIGGRAPH 1995; the scene is rebuilt from its description (four plates of decreasing roughness, four lights of increasing size), or taken from the “Veach, MIS” scene of Benedikt Bitterli’s public Rendering Resources, whose Mitsuba version also gives the reference image.
- Success criterion: against a 4096 spp reference and at equal sample count, the MIS image has the lowest whole-image RMSE, well below both single strategies. A per-plate RMSE table shows where each strategy fails, and that the power heuristic beats the balance heuristic where one strategy is already nearly perfect, the case that motivates it in the paper. Every BRDF passes a white-furnace test (albedo at most 1) and a reciprocity test on random direction pairs.
- Deliverables: code, a 4–6 page report with RMSE-versus-time curves, a 10-minute talk; chapters 5–7, Exercises 5, 6 and 10.
- [Solo · ≈ 20 h] A real-time renderer with shadows and ambient occlusion. How close does a 16 ms rasterizer come to a path-traced image, and which pass costs what?
- Work: a C++/OpenGL renderer — the language of the official labs — or WebGL2, with the model-view-projection chain (definition 9.3), GGX or Blinn-Phong shading with mipmapped textures (ch. 5), shadow maps with slope-scaled bias, PCF and two cascades (ch. 6), and SSAO (definition 9.5).
- Data: the Crytek Sponza scene of the McGuire Computer Graphics Archive (free, textured, about 260 000 triangles); a 4096 spp reference rendered with Blender Cycles or Mitsuba 3.
- Success criterion: 60 Hz at 1080p on a laptop GPU, with per-pass times from GPU timer queries; no visible shadow acne or peter-panning at three light angles; SSAO compared with ray-traced AO (example 9.6) and the frame with the reference (PSNR, difference images).
- Deliverables: code, report, talk; chapters 2, 5, 6 and 9.
- [Solo · ≈ 20 h] Rendering the ABO catalogue for text-to-product retrieval. Do views rendered with the physically based materials of chapter 5 retrieve products from their titles better than the catalogue photos, and how many views are needed?
- Data: Amazon Berkeley Objects, ABO (Collins et al., CVPR 2022): about 8 000 artist-made 3D models in glTF 2.0, whose metallic-roughness materials use the GGX distribution, a Smith masking term and Schlick’s Fresnel (definition 5.3, example 5.4); licence CC BY-NC 4.0.
- Work: a renderer (pyrender, or the tracer of solo project 1) with fixed cameras (ch. 2) and an environment light; text-to-image retrieval with OpenCLIP on 1, 4 and 12 views per object, against the catalogue images (CLIP: Multimodal Generative AI, ch. 5).
- Success criterion: recall@10 on 500 held-out product titles, with bootstrap confidence intervals. The renders are the rendered-view tower of the Y2.3 transverse project.
- Deliverables: code, report, talk; chapters 2, 5 and 9.
Group projects
- [Group of 3 · ≈ 30 h each] Paper study: a kernel-predicting denoiser (Bako et al., 2017). At equal time, does a small learned denoiser beat both the à-trous filter of chapter 8 and simply tracing more samples?
- Paper: S. Bako, T. Vogels, B. McWilliams, M. Meyer, J. Novák, A. Harvill, P. Sen, T. DeRose and F. Rousselle, “Kernel-Predicting Convolutional Networks for Denoising Monte Carlo Renderings”, SIGGRAPH 2017.
- Data: about 20 scenes from Benedikt Bitterli’s public “Rendering Resources” (Mitsuba format), rendered with Mitsuba 3: 1–16 spp inputs, normal, depth and albedo buffers, 4096 spp references, and a camera fly-through for temporal tests.
- Work: a U-Net that predicts a 21 × 21 kernel per pixel (Deep Learning, ch. 4), trained on albedo-demodulated radiance (definition 8.2); baselines: the edge-avoiding à-trous filter (Dammertz et al., HPG 2010) and Open Image Denoise as an upper reference.
- Split: data generation; network and training; evaluation (PSNR, SSIM, temporal flicker).
- Success criterion: PSNR-versus-time curves at equal wall-clock time on held-out scenes; the network beats à-trous at 4 spp, and its gain over more spp is measured.
- Deliverables: code, an 8–10 page joint report, a 20-minute talk; chapters 7–8.
- [Group of 3 · ≈ 30 h each] Paper study: NeRF versus 3D Gaussian Splatting (Mildenhall et al., 2020; Kerbl et al., 2023). On the same calibrated views, what does each representation buy in quality, training time and rendering speed?
- Data: the NeRF-Synthetic “Blender” scenes released with the NeRF paper (800 × 800 views with known poses), plus views of one scene rendered by the group’s own path tracer, with poses written from definition 2.1.
- Work: a minimal NeRF in PyTorch (positional encoding, stratified sampling, alpha compositing and the photometric loss of definition 10.3) and a minimal 3DGS (projected covariances, depth-sorted compositing, densification; the
gsplatrasterizer is allowed for speed). - Split: NeRF; 3DGS; data, poses and evaluation (PSNR, SSIM, LPIPS, frames per second).
- Success criterion: at least 25 dB mean PSNR on the held-out views of
legoat 400 × 400 for both methods; one table of PSNR, training time, memory and frame rate on the same free-tier GPU, explained with theorem 10.2. - Deliverables: code, joint report, talk; chapters 2, 3 and 10. It fits the official paper-implementation assessment and uses the multi-view geometry of Image Analysis and Computer Vision, ch. 6.
- [Group of 4 · ≈ 30 h each] Language to scene: an LLM writes the scene, the renderer checks it. Can a language model turn a prompt into a scene that a physically based renderer draws faithfully, and can the image be checked against the prompt?
- Work: an open instruction-tuned LLM outputs a JSON scene graph (objects, GGX parameters, lights, camera) under a schema; a rasterizer or the path tracer renders it (ch. 2–5, 9). Relations are checked exactly on the scene graph, and a CLIP score or a vision-language model’s answer on the image is compared with these exact checks; failures drive up to three repair rounds.
- Data: 200 prompts written by the team in the style of CLEVR (Johnson et al., CVPR 2017): counts, colours, materials, “left of”, “behind”.
- Split: prompting and schema; renderer; image-side checks; repair loop and benchmark release.
- Success criterion: the fraction of prompts whose scene satisfies every stated relation, before and after repair; the agreement rate between image-side and exact checks.
- Deliverables: code, joint report, talk; chapters 2–5 and 9. It connects to Introduction to Text Mining and NLP, ch. 7 and to the scene-graph capstone Y1.5.
Written exam (proposal)
Two hours, one handwritten A4 sheet and a simple calculator allowed; four exercises: a course question, two computations and a short proof.
- Question (4 points). Write the rendering equation and name each term. Explain why its bounce series converges, what a path tracer with Russian roulette estimates, and why photon mapping is biased but consistent.
- Expected: definition 4.1 with emission, incident radiance, BRDF and cosine. If the albedo \rho < 1, the transport operator is a contraction, so L = \sum_k T^k L_e with a truncation error in \rho^{k+1} (theorem 4.2).
- Path tracing averages unbiased estimates of this whole series (theorem 7.2); Russian roulette keeps them unbiased, since q \cdot X/q = X in expectation, while paths have unbounded length (definition 7.4).
- Photon mapping averages photon power over a disc of radius r: a blur, hence a bias. As photons are added and r shrinks, bias and variance both vanish: consistent.
- Question (6 points). A triangle has pixel coordinates a = (0, 0), b = (4, 0), c = (0, 4), camera depths (Z_a, Z_b, Z_c) = (1, 1, 3) and texture coordinates (u_a, u_b, u_c) = (0, 0, 1).
- Is the pixel centre p = (1, 1) inside? Give its barycentrics.
- Compute the perspective-correct u at p and compare with naive interpolation.
- Update the three edge functions incrementally to p + (1, 0).
- Expected (a): the edge functions at p are (E_{ab}, E_{bc}, E_{ca}) = (4, 8, 4), all positive: inside. Their values at the opposite vertices all equal 16, so \lambda = (1/2,\ 1/4,\ 1/4).
- (b): 1/Z = 1/2 + 1/4 + 1/12 = 5/6, so Z = 1.2; u/Z = (1/4)(1/3) = 1/12, hence u = 0.1, against the naive 0.25 (proposition 5.6).
- (c): subtract b_y - a_y = 0, c_y - b_y = 4 and a_y - c_y = -4 (theorem 3.2): (4,\ 4,\ 8), which direct evaluation at (2, 1) confirms.
- Question (5 points). Two strategies with densities p_1, p_2 draw one sample each. Prove that the MIS estimator with balance weights is unbiased, give the contribution of one sample, and explain why it avoids counting a light twice with next event estimation.
- Expected: the estimator is F = w_1 f/p_1 at X_1 plus w_2 f/p_2 at X_2. Its expectation is \int w_1 f + \int w_2 f = \int f, because the weights sum to 1 where f \neq 0 and vanish where their density does (proposition 7.5).
- With w_i = p_i/(p_1 + p_2), each sample contributes f(X)/(p_1(X) + p_2(X)), whichever strategy drew it.
- Adding the two unweighted estimates has expectation 2\int f: the light is counted twice. The weights split the integral between the strategies instead.
- Question (5 points). A ray crosses three intervals of length \delta = 1 with densities \sigma = (\ln 2,\ 0,\ \ln 4), colours c = (0.2,\ 0.9,\ 1.0) and a black background.
- Compute the \alpha_i, the transmittances and the composited colour \hat{C}.
- For an observed pixel C = 0.6 and the loss \mathcal{L} = (\hat{C} - C)^2, give \partial\mathcal{L}/\partial c_i for each i and interpret the zero.
- Expected (a): \alpha = (0.5,\ 0,\ 0.75) and T = (1,\ 0.5,\ 0.5), so \hat{C} = 0.1 + 0 + 0.375 = 0.475; the background is seen through a transmittance of 0.125 (theorem 10.2).
- (b): \mathcal{L} = 0.125^2 \approx 0.0156, and \frac{\partial\mathcal{L}}{\partial c_i} = 2(\hat{C} - C)\, T_i \alpha_i = (-0.125,\ 0,\ -0.094).
- The empty interval (\alpha_2 = 0) gets no colour gradient: NeRF learns colours only where it has put density (definition 10.3).
Grading rubric (proposal)
| Criterion | Weight | What earns the marks |
|---|---|---|
| Question and paper | 15 % | the question, the paper’s claim or the rendering effect studied, stated precisely and tied to the chapters |
| Correct implementation | 25 % | code written from the equations (estimators, BRDFs, compositing) and tested on cases with known answers (white furnace, analytic integrals, the worked examples) |
| Equal-time evaluation | 20 % | high-spp references or held-out views, RMSE or PSNR against time, several scenes or seeds |
| Analysis tied to the theory | 25 % | each result, failures included, explained by bias, variance, sampling densities or the scene representation |
| Communication | 15 % | images with error maps and units, a report and a talk on time, honest limits |
References
- J. Kajiya, The Rendering Equation, SIGGRAPH 1986 — the founding paper; T. Whitted, An Improved Illumination Model for Shaded Display, CACM 1980.
- E. Veach, Robust Monte Carlo Methods for Light Transport Simulation, Stanford PhD thesis, 1997 (MIS, MLT — the MC reference of chapter 7); E. Veach, L. Guibas, Optimally Combining Sampling Techniques for Monte Carlo Rendering, SIGGRAPH 1995 (the balance heuristic of proposition 7.5).
- M. Pharr, W. Jakob, G. Humphreys, Physically Based Rendering: From Theory to Implementation (pbrt, 4th ed. online); tutorials: Ray Tracing in One Weekend; Scratchapixel.
- B. Phong, Illumination for Computer Generated Pictures, CACM 1975; R. Cook, K. Torrance, A Reflectance Model for Computer Graphics, SIGGRAPH 1981; C. Schlick, An Inexpensive BRDF Model for Physically-based Rendering, CGF 1994; B. Walter, S. Marschner, H. Li, K. Torrance, Microfacet Models for Refraction through Rough Surfaces, EGSR 2007 (GGX and its Smith factor); B. Burley, Physically-Based Shading at Disney, SIGGRAPH Course 2012.
- T. Möller, B. Trumbore, Fast, Minimum Storage Ray-Triangle Intersection, JGT 1997; I. Wald, On fast Construction of SAH-based Bounding Volume Hierarchies, EGSR 2007.
- B. Mildenhall, P. Srinivasan, M. Tancik, J. Barron, R. Ramamoorthi, N. Ng, NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis, ECCV 2020; B. Kerbl, G. Kopanas, T. Leimkühler, G. Drettakis, 3D Gaussian Splatting for Real-Time Radiance Field Rendering, SIGGRAPH 2023.
- A. Tewari et al., State of the Art on Neural Rendering, Computer Graphics Forum (EG STAR), 2020 — the survey of chapter 10 (followed by Advances in Neural Rendering, 2021).
- C. Chaitanya, A. Kaplanyan, C. Schied, M. Salvi, A. Lefohn, D. Nowrouzezahrai, T. Aila, Interactive Reconstruction of Monte Carlo Image Sequences Using a Recurrent Denoising Autoencoder, SIGGRAPH 2017; Open Image Denoise for the labs.
- T. Boubekeur, M. Alexa, Phong Tessellation, TOG 2008; J.-M. Thiery, E. Guy, T. Boubekeur, Sphere-Meshes: Shape Approximation using Spherical Quadric Error Metrics, TOG 2013; T. Groueix, S. Chaudhuri, D. Ceylan, et al., AtlasNet: A Papier-Mâché Approach to Learning 3D Surface Generation, CVPR 2018; tutorial CreativeAI.
A complete path tracer in Python
The program below (~70 lines, NumPy only) implements chapters 1 (radiance), 2 (camera), 4–5 (the bounces of the rendering equation, Lambertian materials), 6 (brute-force ray-sphere intersection, shadow rays), 7 (cosine sampling, next event estimation towards a point light, a fixed depth of 4 bounces instead of Russian roulette) and 8 (stochastic antialiasing, gamma); its bounces are the terms of the series of Theorem 4.2. Reduce W, H, SPP to test; at full resolution, expect ~1 minute (interpreter): each pixel is an independent MC estimator — parallelization is trivial.
import numpy as np
W, H, SPP = 240, 180, 8
def norm(v): return v / np.linalg.norm(v)
# scene: spheres (center, radius, albedo, albedo rho)
S = [(( 0.0, -100.0, -1.0), 100.0, (0.85, 0.80, 0.75), 0.6), # ground
(( 0.0, 1.0, -5.0), 1.0, (0.75, 0.25, 0.20), 0.7),
((-2.3, 0.7, -4.2), 0.7, (0.25, 0.70, 0.30), 0.7),
(( 2.3, 0.7, -4.2), 0.7, (0.25, 0.35, 0.90), 0.7)]
Lp = np.array([6.0, 9.0, 1.0]) # point light
I = np.array([180.0, 170.0, 150.0])
SKY = np.array([0.45, 0.60, 0.85]) # sky radiance
def intersect(o, d):
tbest, ibest = 1e9, -1 # first intersection (naive ch. 6)
for i, (c, r, _, _) in enumerate(S):
oc = o - c; b = oc @ d; disc = b*b - (oc @ oc - r*r)
if disc > 0:
t = -b - np.sqrt(disc)
if 1e-3 < t < tbest: tbest, ibest = t, i
return tbest, ibest
def trace(o, d):
col, att = np.zeros(3), np.ones(3) # L = sum of T^k Le (th. 4.2)
for depth in range(4): # fixed depth, no Russian roulette (Ex. 10b)
t, i = intersect(o, d)
if i < 0:
return col + att * SKY * max(0.1, d[1])
c, r, albedo, rho = S[i]
p = o + t*d; n = norm(p - c)
# next event estimation: point light + shadow ray (ch. 7)
Lv = Lp - p; dist = np.linalg.norm(Lv); L = Lv / dist
if intersect(p + 1e-3*n, L)[0] > dist:
col += att * rho * albedo / np.pi * I / dist**2 * max(0.0, n @ L)
# diffuse bounce: cosine sampling (p = cos/pi, th. 7.3)
g = np.random.normal(size=3) # uniform direction on the sphere
d = norm(n + g / np.linalg.norm(g))
o, att = p + 1e-3*n, att * rho * albedo
return col
img = np.zeros((H, W, 3))
for y in range(H):
for x in range(W):
acc = np.zeros(3)
for _ in range(SPP): # stochastic AA (ch. 8)
u = (x + np.random.rand()) / W - 0.5
v = 0.5 - (y + np.random.rand()) / H
acc += trace(np.array([0.0, 1.2, 0.0]), norm([u, v, -1.0]))
img[y, x] = acc / SPP
img = np.clip(img, 0, None) ** (1.0 / 2.2) # gamma tone map (ch. 8)
img = img / img.max() # exposure normalization
with open("render.ppm", "wb") as f:
f.write(f"P6\n{W} {H}\n255\n".encode())
f.write((img * 255).astype(np.uint8).tobytes())Code reading exercises
- The line
d = norm(n + g / |g|)is the cosine sampling of Theorem 7.3 — why does adding a uniform unit vector to the normal realize the density \cos\theta/\pi (hint: the point n + g/|g| is uniform on the unit sphere centred at n, which touches the surface at the shading point; compute the solid-angle density of its direction seen from that point)? Why wouldnorm(n + g)with a non-normalized Gaussian g give a different density? - The attenuation
attis the product of transport factors divided by probabilities — identify each term of Definition 7.1. - Replace the point light with an emissive sphere and sample its surface: the shadow noise collapses (NEE on an extended light, Definition 7.4).