SceneCamera

July 26, 2026 · View on GitHub

CI Codecov Go Reference Go Report Card

SceneCamera provides camera movement and view/projection matrices for Go 3D applications. It supports museum, first-person, and real-time strategy movement, plus side-by-side stereo rendering.

Demos

FPS mode

FPS camera moving over a flat plane with trees

RTS mode

RTS camera circling over the map and trees

Flight mode

Flight camera diving toward the trees, leveling out, banking, and turning

It is designed for OpenGL but only depends on mathgl. The returned matrices can be copied into another graphics library's matrix format.

Install

go get github.com/donomii/sceneCamera@latest

Quick start

package main

import (
	"fmt"

	sceneCamera "github.com/donomii/sceneCamera"
)

func main() {
	camera := sceneCamera.New(2)
	camera.Move(0, 0.5)

	viewMatrix := camera.ViewMatrix()
	fmt.Println(viewMatrix)
}

Modes are selected when creating a camera:

  • sceneCamera.New(1) — museum mode, which orbits a target and zooms in or out.
  • sceneCamera.New(2) — FPS/flight mode, with translation, pitch, and yaw. Roll inputs are ignored.
  • sceneCamera.New(3) — RTS mode, which moves over a ground plane and orbits a point on that plane.

Move takes a direction and an amount. Translation amounts use world units; rotation amounts use radians.

DirectionOperation
0Forward
1Backward
2Left
3Right
4Up
5Down
6Pitch up
7Pitch down
8Yaw left
9Yaw right
10Roll left
11Roll right

Each mode applies only the operations that make sense for that camera style.

Side-by-side stereo rendering

SceneCamera returns separate view and projection matrices for each eye without taking control of rendering:

func RenderStereoFrame(state *State) {
	// Set the inter-pupillary distance in world units.
	camera.SetIPD(2.0)

	width, height := MainWin.GetSize()
	camera.Screenwidth = float32(width) / 2
	camera.Screenheight = float32(height)

	leftViewMatrix := camera.LeftEyeViewMatrix()
	leftProjectionMatrix := camera.LeftEyeFrustum()
	gl.Viewport(0, 0, int32(width/2), int32(height))
	RenderFrame(state, leftViewMatrix, leftProjectionMatrix)

	rightViewMatrix := camera.RightEyeViewMatrix()
	rightProjectionMatrix := camera.RightEyeFrustum()
	gl.Viewport(int32(width/2), 0, int32(width/2), int32(height))
	RenderFrame(state, rightViewMatrix, rightProjectionMatrix)

	gl.Viewport(0, 0, int32(width), int32(height))
}

Default position

Museum and FPS cameras start at (0, 0, 5), looking at the origin, with positive Y as up.

RTS cameras start at (5, 5, 5), looking at the origin, with positive Z as up. The default ground plane is z=0, with the normal (0, 0, 1).

These settings can be changed through the camera fields and setter methods.

Example application

The example directory contains an OpenGL application:

cd example
go run .

Regenerating the demo GIFs

Run either no-argument launcher from the example directory:

./record-rts-demo.sh
./record-flight-demo.sh

Each launcher renders a deterministic 480×270 animation with 72 frames over 5.04 seconds and replaces its corresponding GIF in the repository root.