Concept: Camera Model


Pinhole Camera

  • The simplest camera model — all rays pass through a single point (the pinhole)
  • No depth of field — everything is in focus
  • Parameters
    • position — camera origin in world space
    • forward — direction the camera looks (normalized)
    • up — world up vector (used to compute right vector)
    • fov — vertical field of view (degrees or radians)
    • aspect_ratio — width / height
  • Building the camera basis
    vec3 forward = normalize(target - position);
    vec3 right   = normalize(cross(forward, world_up));
    vec3 up      = cross(right, forward);
  • Viewport dimensions
    • viewport_height = 2 * tan(fov / 2) (at unit focal distance)
    • viewport_width = viewport_height * aspect_ratio
  • Generating a ray for pixel (i, j) in an (W, H) image
    // Jitter for anti-aliasing
    float u = (i + random()) / W;
    float v = (j + random()) / H;
     
    // Map to [-1, 1] range
    vec2 ndc = vec2(u, v) * 2.0 - 1.0;
     
    vec3 ray_dir = normalize(
        forward
        + ndc.x * (viewport_width  / 2.0) * right
        + ndc.y * (viewport_height / 2.0) * up
    );
    return Ray(position, ray_dir);

Thin Lens Camera (Depth of Field)

  • Real cameras have a lens with finite aperture
  • Objects at the focal distance are sharp; others are blurry (bokeh)
  • Parameters (in addition to pinhole)
    • aperture — lens diameter (larger = more blur)
    • focus_distance — distance to the focal plane
  • Algorithm
    • Sample a random point on the lens disk: lens_offset = aperture/2 * random_disk()
    • Compute the focus point: focus_point = position + focus_distance * ray_dir
    • New ray origin: origin = position + lens_offset.x * right + lens_offset.y * up
    • New ray direction: direction = normalize(focus_point - origin)
vec2 disk = aperture * 0.5 * random_in_unit_disk();
vec3 lens_origin = position + disk.x * right + disk.y * up;
vec3 focus_point = position + focus_distance * normalize(ray_dir);
return Ray(lens_origin, normalize(focus_point - lens_origin));

Field of View

  • Vertical FOV: angle from bottom to top of the image
  • Horizontal FOV: 2 * atan(tan(vfov/2) * aspect_ratio)
  • Common values: 60° (telephoto feel), 90° (wide), 120° (very wide)
  • In Godot: Camera3D.fov is vertical FOV in degrees
  • Relationship to focal length: fov = 2 * atan(sensor_height / (2 * focal_length))
    • 50mm lens on 35mm sensor: fov ≈ 39.6° (normal lens)
    • 24mm lens: fov ≈ 73.7° (wide angle)

Camera in Vulkan Ray Tracing

  • Pass camera data as a uniform buffer to the ray generation shader
layout(set=0, binding=2) uniform CameraData {
    mat4 view_inverse;   // inverse view matrix
    mat4 proj_inverse;   // inverse projection matrix
    float aperture;
    float focus_distance;
} camera;
 
void main() {
    vec2 pixel = vec2(gl_LaunchIDEXT.xy) + vec2(random(), random());
    vec2 uv = pixel / vec2(gl_LaunchSizeEXT.xy) * 2.0 - 1.0;
    
    // Unproject from NDC to world space
    vec4 origin    = camera.view_inverse * vec4(0, 0, 0, 1);
    vec4 target    = camera.proj_inverse * vec4(uv.x, uv.y, 1, 1);
    vec4 direction = camera.view_inverse * vec4(normalize(target.xyz), 0);
    
    traceRayEXT(tlas, ..., origin.xyz, 0.001, direction.xyz, 1e4, 0);
}

Motion Blur

  • Shutter opens for a time interval [t0, t1]
  • Sample a random time t = lerp(t0, t1, random())
  • Evaluate object transforms at time t
  • Requires per-object velocity data or interpolated transforms
  • In path tracing: each ray sample uses a different time → natural motion blur