Initializing 3D Canvas...

Raymarching

2 min read•1 page

Camera Ray:

1. Map pixel coordinates to normalized device coordinates. 2. Apply perspective projection with field-of-view angle. 3. Normalize the direction vector for unit-length marching steps.
python
1# Camera ray setup for raymarching
2import numpy as np
3
4def generate_ray(pixel_x, pixel_y, width, height, fov):
5 """Convert pixel coordinates to a 3D ray direction."""
6 aspect = width / height
7 # Normalize pixel to [-1, 1]
8 ndc_x = (2.0 * pixel_x / width - 1.0) * aspect
9 ndc_y = 1.0 - 2.0 * pixel_y / height
10
11 # Perspective projection
12 import math
13 z = -1.0 / math.tan(math.radians(fov) / 2.0)
14 direction = np.array([ndc_x, ndc_y, z])
15 direction /= np.linalg.norm(direction)
16 return direction

Raymarching starts by generating a ray for each pixel. The camera origin and pixel-to-ray mapping define the viewing frustum.

2 min read•1 page

Sphere Tracing:

1. Evaluate SDF at current ray position. 2. Distance value = radius of empty sphere around point. 3. Advance ray parameter by that distance. Repeat.
python
1# Single sphere tracing step
2def sphere_trace_step(origin, direction, sdf_fn, current_t):
3 """Advance ray by the SDF distance at current position."""
4 position = origin + current_t * direction
5 distance = sdf_fn(position)
6 # Safe to advance by 'distance' without missing surface
7 new_t = current_t + distance
8 return new_t, distance

Each step advances the ray by exactly the SDF distance at the current position. This is safe because the SDF guarantees no surface exists within that radius.

2 min read•1 page

Raymarch Loop:

1. Initialize ray parameter t = 0. 2. Each iteration: evaluate SDF, advance by d, check termination. 3. Return hit position and convergence status.
python
1# Full sphere tracing loop
2def raymarch(origin, direction, sdf_fn, max_steps=128, max_dist=100.0, eps=1e-4):
3 t = 0.0
4 for i in range(max_steps):
5 pos = origin + t * direction
6 d = sdf_fn(pos)
7 if d < eps: # Hit surface
8 return t, pos, True
9 if t > max_dist: # Escaped scene
10 return t, pos, False
11 t += d
12 return t, origin + t * direction, False

The full solver loops until convergence (surface hit) or escape (exceeding max distance or iteration count).

Max Steps
10.00
2 min read•1 page

Gradient Normal:

1. Sample SDF at 6 offset positions (±eps per axis). 2. Differences give partial derivatives. 3. Normalize to get unit surface normal.
python
1# Gradient-based normal estimation at hit point
2def estimate_normal(sdf_fn, pos, eps=1e-4):
3 """Central differences gradient = surface normal."""
4 nx = sdf_fn([pos[0]+eps, pos[1], pos[2]]) - sdf_fn([pos[0]-eps, pos[1], pos[2]])
5 ny = sdf_fn([pos[0], pos[1]+eps, pos[2]]) - sdf_fn([pos[0], pos[1]-eps, pos[2]])
6 nz = sdf_fn([pos[0], pos[1], pos[2]+eps]) - sdf_fn([pos[0], pos[1], pos[2]-eps])
7 import math
8 length = math.sqrt(nx*nx + ny*ny + nz*nz) or 1.0
9 return [nx/length, ny/length, nz/length]

After the ray hits a surface, the normal is estimated via the SDF gradient using central finite differences.

Normal Samples
8.00
2 min read•1 page

Diffuse Shade:

1. Compute N dot L between surface normal and light direction. 2. Clamp to [0, 1] to prevent negative illumination. 3. Multiply base color by this intensity.
python
1# Lambertian diffuse shading
2def shade_diffuse(normal, light_dir, base_color):
3 """N dot L diffuse lighting."""
4 import numpy as np
5 n_dot_l = max(np.dot(normal, light_dir), 0.0)
6 return [c * n_dot_l for c in base_color]
7
8# After computing the hit normal and light direction,
9# the diffuse intensity is simply clamp(N . L, 0, 1).

The simplest lighting model uses the dot product between the surface normal and light direction (Lambertian reflectance).

2 min read•1 page

Soft Shadows:

1. Cast ray from surface point toward light. 2. Track minimum d/t ratio along the march. 3. Smaller ratio = darker shadow (closer occluder pass).
python
1# Soft shadow via secondary sphere trace
2def soft_shadow(origin, light_dir, sdf_fn, k=8.0, max_t=50.0):
3 """Raymarch toward light; closest approach gives penumbra."""
4 result = 1.0
5 t = 0.01
6 while t < max_t:
7 d = sdf_fn(origin + t * light_dir)
8 if d < 1e-4:
9 return 0.0 # Full shadow
10 result = min(result, k * d / t)
11 t += d
12 return clamp(result, 0.0, 1.0)
13
14# k controls penumbra softness.
15# Higher k = sharper shadows, lower k = softer.

Soft shadows trace a secondary ray toward the light source. The closest approach to any surface during the march determines the penumbra softness.

Shadow Softness (k)
8.00
2 min read•1 page

SDF AO:

1. March along normal in small steps. 2. Compare expected distance (step * i) with actual SDF. 3. Deficit indicates occlusion. Weight decreases with distance.
python
1# SDF-based ambient occlusion
2def ambient_occlusion(pos, normal, sdf_fn, steps=5, step_size=0.1):
3 """Sample SDF along normal to estimate occlusion."""
4 ao = 0.0
5 weight = 1.0
6 for i in range(1, steps + 1):
7 sample_pos = pos + normal * (i * step_size)
8 expected_dist = i * step_size
9 actual_dist = sdf_fn(sample_pos)
10 # Difference between expected and actual indicates occlusion
11 ao += weight * (expected_dist - actual_dist)
12 weight *= 0.5 # Diminishing influence
13 return max(1.0 - ao, 0.0)

SDF-based AO samples the field along the surface normal. If actual SDF values are less than expected (indicating nearby geometry), the point is occluded.

AO Steps
5.00
2 min read•1 page

Fog:

1. Compute fog factor from ray travel distance and density. 2. Exponential falloff: fog = 1 - exp(-density * distance). 3. Lerp between surface color and fog color.
python
1# Distance-based fog for atmospheric effects
2def apply_fog(color, distance, fog_color, fog_density):
3 """Exponential fog blending."""
4 import math
5 fog_factor = 1.0 - math.exp(-fog_density * distance)
6 fog_factor = max(0.0, min(1.0, fog_factor))
7 # Blend between object color and fog color
8 result = [c * (1 - fog_factor) + f * fog_factor
9 for c, f in zip(color, fog_color)]
10 return result
11
12# Fog uses the ray's travel distance (t parameter)
13# to blend toward a background fog color.

Distance-based exponential fog blends the surface color toward a fog color based on how far the ray traveled before hitting the surface.

Fog Density
0.15

Related Concepts