Skip to content

Instantly share code, notes, and snippets.

@pollend
Created May 12, 2026 04:02
Show Gist options
  • Select an option

  • Save pollend/c21c09a8194eff61635dfddb70646f20 to your computer and use it in GitHub Desktop.

Select an option

Save pollend/c21c09a8194eff61635dfddb70646f20 to your computer and use it in GitHub Desktop.

To integrate the Phong lighting model into a raytracer, you essentially use the raytracer to handle the geometry and visibility (finding what the light hits), while the Phong model handles the color and intensity calculation at that specific hit point.

In a standard raytracer, for every pixel, you cast a primary ray from the camera into the scene. When that ray intersects an object, you calculate the lighting for that point using the three components of the Phong model: Ambient, Diffuse, and Specular.


1. The Intersection Point

When your ray $R(t) = O + tD$ hits a surface, you need three pieces of information to compute Phong lighting:

  • $P$: The hit point (the 3D coordinates of the intersection).
  • $N$: The surface normal at point $P$.
  • $V$: The view vector (a unit vector pointing from $P$ back toward the camera).

2. The Phong Components

Once you have the hit point, you calculate the following:

Ambient ($I_a$)

This represents constant background light. It doesn't depend on the light source's position or the camera.

$$I_{ambient} = k_a \cdot L_a$$

(Where $k_a$ is the material's ambient coefficient and $L_a$ is the ambient light intensity).

Diffuse ($I_d$)

This simulates the rough reflection of light. To calculate this in a raytracer, you cast a shadow ray from $P$ toward the light source $L$. If the shadow ray hits another object before reaching the light, the point is in shadow and you skip the diffuse/specular steps. If not in shadow, the intensity depends on the angle between the normal ($N$) and the light vector ($L$):

$$I_{diffuse} = k_d \cdot L_d \cdot (N \cdot L)$$

Specular ($I_s$)

This creates the "shiny" highlight. It depends on the reflection vector $R$ (how light bounces off the surface) and the view vector $V$.

$$I_{specular} = k_s \cdot L_s \cdot (R \cdot V)^n$$

(Where $n$ is the "shininess" exponent. A higher $n$ results in a smaller, tighter highlight).


3. The Raytracing Workflow

The process follows these logical steps for every pixel:

  1. Primary Ray: Fire a ray from the camera through the pixel.
  2. Intersection: Find the closest object the ray hits.
  3. Shadow Ray: Fire a secondary ray from the hit point toward each light source.
  • If occluded: Only apply the Ambient component.
  • If clear: Apply Ambient + Diffuse + Specular.
  1. Reflection/Refraction (Optional): If the material is reflective, fire a new "recursive" ray in the direction of $R$ and add that color to your Phong result.

4. Mathematical Summary

The final color of the pixel is the sum of these interactions:

$$I_{total} = k_a L_a + \sum_{lights} (k_d L_d (N \cdot L) + k_s L_s (R \cdot V)^n)$$

Key Considerations for Implementation

  • Normal Normalization: Ensure your $N, L, V,$ and $R$ vectors are all normalized (length of 1) before performing dot products.
  • Shadow Acne: When firing shadow rays, start the ray at a tiny offset (epsilon) away from the surface along the normal to prevent the ray from "hitting" the surface it just started from.
  • Energy Conservation: Basic Phong isn't energy-conserving, meaning it can technically reflect more light than it receives. For a more physically grounded raytracer, many developers move toward the Blinn-Phong variation, which uses a "halfway vector" between $L$ and $V$ for better performance and realism at steep angles.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment