Skip to content

Camera

Camera class is used to configure the camera settings for the rendering.

Positioning

We use a right-handed spherical coordinate system for camera positioning.

alt

from fdray import *

Scene(
    Camera(longitude=30, latitude=40),
    LightSource(0, ColorName.WHITE),  # at camera location
    Cylinder(0, (1, 0, 0), 0.1, Color("red")),
    Cylinder(0, (0, 1, 0), 0.1, Color("green")),
    Cylinder(0, (0, 0, 1), 0.1, Color("blue")),
).render(width=300, height=300)

In the above figure, the camera is positioned at:

  • Longitude: 30 degrees
  • Latitude: 40 degrees

The camera position is determined through a two-step transformation:

  1. First, rotate 30 degrees counterclockwise from the x-axis toward the y-axis in the x-y plane
  2. Then, elevate 40 degrees from that position toward the z-axis

This coordinate system allows for intuitive camera positioning:

  • Longitude controls the horizontal rotation around the scene
  • Latitude controls the vertical angle from the ground plane

Implementation

The camera implementation is based on the following Qiita article:

This implementation adopts the spherical coordinate system and uses the calculation methods for direction, right, and up vectors as proposed in the article. The sky parameter is also included as it's essential for proper orientation.

View scale

The view_scale parameter controls how much of the scene is visible in the rendered image. It functions similarly to adjusting a camera's field of view.

view_scale = 1 view_scale = 2 view_scale = 3
def scene_view_scale(view_scale: float):
    return Scene(
        Camera(0, 0, view_scale=view_scale),
        LightSource(0, Color("white")),
        Cylinder("-y", "y", 0.2, Color("green")),
        Cylinder("-2*y", "2*y", 0.15, Color("green")),
        Cylinder("-3*y", "3*y", 0.1, Color("green")),
        Cylinder("-z", "z", 0.2, Color("blue")),
        Cylinder("-2*z", "2*z", 0.15, Color("blue")),
        Cylinder("-3*z", "3*z", 0.1, Color("blue")),
        Torus(1, 0.1, Color("purple", 0.5)).rotate("90*z"),
        Torus(2, 0.1, Color("gold", 0.5)).rotate("90*z"),
        Torus(3, 0.1, Color("cyan", 0.5)).rotate("90*z"),
        Background(Color("gray")),
    ).render(width=150, height=150)

Basic Operation

  • A larger view scale shows more of the scene by "zooming out"
  • A smaller view scale shows less of the scene by "zooming in"

Technical Implementation

  • The coordinate range rendered extends from -view_scale to +view_scale
  • Directly affects the apparent size of objects in the scene
  • Controls the viewing frustum without changing camera position

Parameter Relationships

  • Independent of camera position and orientation (longitude, latitude)
  • Works in conjunction with the camera's distance parameter

Distance

The distance parameter controls the camera's perspective effect. This parameter significantly affects how the 3D scene is rendered, particularly the perspective distortion.

distance = 3 distance = 10 distance = 30
def scene_distance(distance: float):
    return Scene(
        Camera(15, 10, view_scale=2, distance=distance),
        LightSource((1, 20, 40), Color("white")),
        Box(-1, 1, Color("green", 0.8)),
        Background(Color("gray", 0.2)),
    ).render(width=200, height=200)

Visual Effects

When distance is small

  • Creates a fisheye lens-like effect
  • Produces strong perspective distortion
  • Objects appear more curved at the edges
  • Similar to viewing through a wide-angle lens

When distance is large

  • Approaches orthogonal projection
  • Reduces perspective distortion
  • Creates a more natural depth perception
  • Similar to viewing through a telephoto lens

Technical Details

The distance parameter:

  • Affects the perspective matrix calculation
  • Does not change the actual size of objects, only their apparent perspective
  • Represents the distance between the camera position (camera_pos) and the object's center (center)
  • Smaller distances bring the camera closer to the object, creating stronger perspective effects
  • Larger distances move the camera away from the object, resulting in a more orthogonal-like view

Important Notes

  • The distance parameter only affects perspective, not object size
  • Smaller distances create more dramatic perspective effects
  • Larger distances create more natural, less distorted views
  • The effect is similar to changing the focal length of a camera lens

Orbital Location

Calculate a position in orbit around the camera's location

Imagine tilting your head up (angle) and then rotating counter-clockwise (rotation):

  • First, move forward along viewing direction (0: at camera.location, 1: at camera.look_at). Negative values move behind the camera.
  • Then, tilt up from viewing direction by angle degrees
  • Finally, rotate counter-clockwise from up by rotation degrees (0: up, 90: left, 180: down, 270: right)
from math import asin, degrees, sqrt


def render(camera: Camera, *args):
    return Scene(
        camera,
        LightSource(0, "white"),
        Cylinder(0, "2*x", 0.02, Color("red")),
        Cylinder(0, "2*y", 0.02, Color("green")),
        Cylinder(0, "2*z", 0.02, Color("blue")),
        *args,
    ).render(width=300, height=300)


distance = 4
camera = Camera(0, 0, view_scale=2.5, look_at=(0, 0, 0), distance=distance)
norm = sqrt(distance**2 + 2**2)
angle = degrees(asin(2 / norm))
p1 = camera.orbital_location(norm / distance, angle, -90)
s1 = Sphere(p1, 0.2, Color("purple", 0.5))
p2 = camera.orbital_location(0.5, angle, -90)
s2 = Sphere(p2, 0.05, Color("orange", 0.5))
render(camera, s1, s2)
Rendered image from x axis Top view (y-z plane)
alt alt

Movies

Camera

Orbital Location