Ray Tracing Fundamentals
This page explains the core ray tracing algorithm implemented in RayTracer Studio.
What is Ray Tracing?β
Ray tracing is a rendering technique that simulates the physical behavior of light. Instead of calculating how light bounces around a scene (which is computationally expensive), we trace rays backwards from the camera into the scene.
Traditional Light: Ray Tracing:
β ββ β β β π β β β β β π
Light to Eye Eye to Light
The Algorithmβ
For each pixel in the image:
- Generate a ray from the camera through the pixel
- Find intersections with objects in the scene
- Calculate shading at the closest intersection
- Write color to the output buffer
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
Ray ray = camera.getRay(x, y);
HitRecord hit = scene.trace(ray);
Vec3 color = scene.shade(ray, hit);
buffer[y * width + x] = color;
}
}
Ray Generationβ
Rays originate from the camera and pass through each pixel:
Ray Camera::getRay(float u, float v) const {
// u, v are normalized screen coordinates [-1, 1]
Vec3 horizontal = right * (u * viewportWidth * 0.5f);
Vec3 vertical = upDir * (v * viewportHeight * 0.5f);
Vec3 direction = forward + horizontal + vertical;
return Ray(position, direction);
}
Image Plane
βββββββββββββ
β β pixel β
β β± β
ββ± β
βββββββββββββββ Camera
Intersection Testingβ
Ray-Sphere Intersectionβ
A sphere is defined as points at distance r from center C:
|P - C|Β² = rΒ²
Substituting the ray equation P = O + tD:
|O + tD - C|Β² = rΒ²
This gives a quadratic equation in t:
atΒ² + bt + c = 0
where:
a = DΒ·D
b = 2DΒ·(O-C)
c = (O-C)Β·(O-C) - rΒ²
float discriminant = b*b - 4*a*c;
if (discriminant < 0) {
// No intersection
} else {
float t = (-b - sqrt(discriminant)) / (2*a);
// t is the distance to intersection
}
Ray-Plane Intersectionβ
A plane is defined by point Pβ and normal N:
(P - Pβ) Β· N = 0
Solving for t:
t = (Pβ - O) Β· N / (D Β· N)
Shading Modelβ
We use the Blinn-Phong shading model, which combines:
Ambient Lightβ
Simulates indirect illumination. Prevents completely black shadows:
Vec3 ambient = color * 0.1f;
Diffuse Reflection (Lambertian)β
Light scatters equally in all directions. Intensity depends on angle:
float diffuse = max(0, N Β· L); // N = normal, L = light direction
Vec3 diffuseColor = color * diffuse;
Specular Highlightβ
Shiny spots from direct reflection:
Vec3 H = normalize(L + V); // Half-vector
float specular = pow(max(0, N Β· H), shininess);
Vec3 specularColor = lightColor * specular;
Combinedβ
Vec3 finalColor = ambient + (diffuse + specular) * shadowFactor;
Shadow Raysβ
To check if a point is in shadow:
- Cast a ray from the point toward the light
- If it hits anything before reaching the light, the point is in shadow
bool isInShadow(Vec3 point, Vec3 lightPos) {
Vec3 toLight = lightPos - point;
float distance = toLight.length();
Ray shadowRay(point + offset, normalize(toLight));
for (object in scene) {
if (object.intersect(shadowRay).t < distance) {
return true; // Blocked!
}
}
return false;
}
Putting It Togetherβ
Vec3 shade(Ray ray, HitRecord hit) {
Vec3 color = Vec3(0, 0, 0);
for (light in lights) {
Vec3 L = normalize(light.position - hit.point);
Vec3 V = normalize(ray.origin - hit.point);
Vec3 H = normalize(L + V);
// Diffuse
float diff = max(0, hit.normal.dot(L));
// Specular
float spec = pow(max(0, hit.normal.dot(H)), shininess);
// Shadow
float shadow = isInShadow(hit.point, light.position) ? 0.3 : 1.0;
color += (diffuse * diff + specular * spec) * shadow;
}
// Ambient
color += hit.material.color * ambient;
return color;
}
Performanceβ
Ray tracing is computationally intensive:
| Resolution | Rays | Time Factor |
|---|---|---|
| 256Γ256 | 65,536 | 1Γ |
| 512Γ512 | 262,144 | 4Γ |
| 1024Γ1024 | 1,048,576 | 16Γ |
Each ray may spawn additional rays for:
- Shadows (1 per light)
- Reflections (1 per bounce)
- Future: Refractions, ambient occlusion