Skip to content

Camera

Main file: shaderflow/camera.py

ShaderFlow provides a flexible camera system with multiple modes, projections, and parameters already built-in so you can focus on shaders content instead of figuring out vectors math. The main design goals is for a set of parameters usable both in ray marching and 2D shaders.

Note: For this page ensure you have a good understanding of the linked resources.

Adding to a Scene

-> All scenes already initializes a default ShaderCamera instance at self.camera, named iCamera!

For adding your own module:

class MyScene(ShaderScene):
    def build(self):
        self.bird = ShaderCamera(scene=self, name="iBird")

And then use in shaders:

void main() {
    GetCamera(iBird);
    vec2 stuv = iBird.stuv;
    vec2 gluv = iBird.gluv;
}

Coordinates

-> ShaderFlow follows a Y-Up, Left Handed euclidean coordinate system.

This choice makes mappings between the 2d screen projection plane (Z=1) and ray-marching vectors 1:1 in xy, which are used in texture() sampling methods to avoid weird swizzling.

However, since the camera is generic, you can simply reorient it and work your math in whatever basis you prefer, they don't even need to be euclidean!

Rotations

-> ShaderFlow uses Quaternions for all rotations.

Simply stated, there are no gimbal locks, singularities, or weird edge cases to worry about. While internal logic uses them, there are helper methods simplifying their usage:

# Look at the top of the screen
scene.camera.look((0, 1, 0))

# Clockwise rotation on the screen plane
scene.camera.rotate2d(45)

For lower level control or alignment with certain planes:

Imagine a screwdriver glued to the camera performing the rotation you want.

The rotation direction is the vector pointing along its tip, and if you'd be tighening the screw the angle is positive, negative otherwise. For example, with your head right now:

  • Looking 'left' is a 90° rotation on the 'up' axis direction (earth horizon plane)
  • Looking 'down' is a 90° rotation on the 'left' axis direction (vertical nose plane)
# Look at current-left
self.camera.rotate(
    direction=self.camera.up,
    degrees=90.0
)

# Rotate as if to align two vectors
self.camera.align(
    A=(1, 0, 0),
    B=(0, 1, 0),
    degrees=0.0,
)

# Align camera with a custom plane
# Rotate as if to align two vectors
self.camera.align(
    A=self.camera.up_target,
    B=(1, 1, 0),
    degrees=90.0,
)

Note that rotations are cumulative and non-commutative, operation orders matter.

Note: https://github.com/moble/quaternion/wiki/Euler-angles-are-horrible

Projections

Parameters

Todo

Ray Marching

-> Simply use vec3 iCamera.origin and vec3 iCamera.target in shaders!

Flat Projection

-> Intersection of the Ray Marching vectors with the z=1 plane

Modes

Free Camera

-> When you are rotating objects in games

In this mode, the camera does absolutely no corrections each update. Moving the mouse always applies a local rotation; eg. when looking down, a 'right' rotation doesn't spin around the center of the screen, but will eventually look at the sky. A non-commutativity side effect is that circular motions drifts the up direction - there are no zenith reference anymore.

Movement W walks in the forward (entering) the screen.

Camera 2D

In this mode

Movement W walks in the up screen direction

Aligned Camera

-> Standard games camera you are used to

In this mode, the right (and consequently, left) axis are always contained in the ground plane, defined by the zenith vector. Internally, the camera applies corrections each update.

Controls

Key Action
W A S D Move
Q E Roll
Space Shift Move Up/Down
Mouse Look Around
1 Set mode Free Camera
2 Set mode Camera 2D
3 Set mode Aligned Camera
i j k Set UP Axis (x, y, z)
Mouse Wheel Change FOV
T G Isometric ±
F1 Exclusive Mouse Mode

Resources