g3d

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 10 Imported by: 0

README

GoGPU Logo

g3d

Pure Go 3D rendering library
Scene graph, PBR materials, forward renderer. Zero CGO.
Built on gogpu/wgpu (Vulkan/Metal/DX12/GLES/Software).

CI Go Reference Go Report Card License Zero CGO


What is g3d?

g3d is a 3D rendering library — not a game engine. It provides the building blocks (scene graph, cameras, lights, materials, geometry primitives) that game engines, CAD viewers, data visualizers, and AR/VR applications build upon.

Think of it like Three.js for Go: simple API, powerful rendering, zero opinion about your application architecture.

package main

import (
    "log"

    "github.com/gogpu/g3d"
    "github.com/gogpu/gogpu"
)

func main() {
    app := gogpu.NewApp(gogpu.DefaultConfig().
        WithTitle("g3d Hello Cube").
        WithSize(800, 600))

    scene := g3d.NewScene()
    scene.SetBackground(g3d.RGB(0.1, 0.1, 0.15))

    // Ambient light (no spatial properties — attached via UserData on a container node)
    ambient := g3d.NewAmbientLight(g3d.WithLightColor(g3d.White), g3d.WithLightIntensity(0.3))
    ambientNode := g3d.NewNode()
    ambientNode.SetUserData(ambient)
    scene.Add(ambientNode)

    // Directional light (sun-like, from upper-right)
    sun := g3d.NewDirectionalLight(g3d.WithLightColor(g3d.White), g3d.WithLightIntensity(1.0))
    sun.LightNode().SetRotation(g3d.Euler{X: g3d.Radians(-45), Y: g3d.Radians(30)})
    scene.Add(sun.LightNode())

    // Cube mesh with PBR material
    cube := g3d.NewMesh(
        g3d.NewBoxGeometry(1, 1, 1),
        g3d.NewStandardMaterial(
            g3d.WithColor(g3d.RGB(0.4, 0.7, 1.0)),
            g3d.WithRoughness(0.6),
        ),
    )
    scene.Add(cube.MeshNode())

    // Camera
    camera := g3d.NewPerspectiveCamera(75, 800.0/600.0, 0.1, 1000)
    camera.CameraNode().SetPosition(g3d.Vec3{X: 0, Y: 0.5, Z: 3})

    var renderer *g3d.Renderer

    app.OnUpdate(func(dt float64) {
        r := cube.MeshNode().Rotation
        r.Y += float32(dt)
        cube.MeshNode().SetRotation(r)
    })
    app.OnDraw(func(ctx *gogpu.Context) {
        if renderer == nil {
            var err error
            renderer, err = g3d.NewRenderer(app.GPUContextProvider())
            if err != nil {
                log.Fatal(err)
            }
        }
        fbW, fbH := ctx.FramebufferSize()
        renderer.SetSize(uint32(fbW), uint32(fbH))
        if view := ctx.SurfaceView(); view != nil {
            _ = renderer.Render(scene, camera, view)
        }
    })
    app.OnClose(func() {
        if renderer != nil {
            renderer.Release()
        }
    })
    if err := app.Run(); err != nil {
        log.Fatal(err)
    }
}

Features

Core (v0.1.0)

  • Scene graph — hierarchical Node tree with parent-child transform propagation and dirty flags
  • Cameras — Perspective and Orthographic with frustum extraction
  • Geometries — Box, Sphere, Plane + custom BufferGeometry
  • Forward renderer — 4-bucket sorting (background, opaque, transmissive, transparent)
  • Frustum culling — automatic AABB visibility testing against camera frustum

Materials (v0.1.0)

  • BasicMaterial — unlit, for prototyping and data visualization
  • StandardMaterial — PBR metallic-roughness with Blinn-Phong shading

Lighting (v0.1.0)

  • AmbientLight — uniform environment lighting
  • DirectionalLight — sun-like parallel light

Performance (v0.1.0)

  • Zero-alloc render path — no GC pressure during frame rendering
  • Pipeline cache — compile shader variants once, reuse forever
  • 3-key opaque sort — PipelineKey → MaterialID → Distance (minimizes GPU state changes)
  • MappedAtCreation — zero-copy GPU buffer upload (WebGPU compliant)

Planned

  • Full PBR — Cook-Torrance BRDF, shadow mapping, normal maps (Phase 2)
  • GLTF 2.0 — binary (.glb) and JSON (.gltf) with PBR materials, animations (Phase 3)
  • Instance batching — thousands of objects with minimal draw calls (Phase 4)
  • Post-processing — bloom, tone mapping, FXAA (Phase 4)

Not a Game Engine

g3d deliberately does not include:

Feature Why Not Where to Get It
Entity Component System Game engine concern Build on top, or use external ECS
Physics Simulation concern Integrate Bullet, ODE, or Pure Go physics
Audio Unrelated to rendering Use gogpu/audio or Oto
Networking Unrelated to rendering Use net/http, gRPC, WebSocket
Scripting Engine concern Use Lua/Wasm/Yaegi on top
Scene editor Tool concern Build with gogpu/ui + g3d

This separation means g3d is reusable everywhere — game engines, CAD tools, scientific visualizations, AR/VR, data dashboards.

GPU Backends

g3d renders through gogpu/wgpu, which supports:

Backend Platforms Status
Vulkan Windows, Linux Stable
Metal macOS Stable
DirectX 12 Windows Stable
OpenGL ES Windows, Linux Stable
Software All Fallback (CI/testing)

All backends are Pure Go — zero CGO, single binary deployment.

# Select backend via environment variable
GOGPU_GRAPHICS_API=vulkan   go run ./examples/hello-cube/
GOGPU_GRAPHICS_API=dx12     go run ./examples/hello-cube/
GOGPU_GRAPHICS_API=software go run ./examples/hello-cube/

Examples

Example Description
hello-cube Rotating PBR cube — minimal g3d + gogpu integration
viewport3d 3D viewport embedded inside gogpu/ui application

Standalone Usage

g3d works without the gogpu application framework. Bring your own window and GPU device:

// Use g3d with any wgpu.Device — no gogpu dependency required
renderer, err := g3d.NewRendererFromDevice(device, queue, surfaceFormat)

scene := g3d.NewScene()
// ... build your scene
renderer.Render(scene, camera, targetView)

To combine g3d with additional render passes in one queue submission, record into a caller-owned command encoder:

encoder, err := device.CreateCommandEncoder(nil)
if err != nil {
	return err
}
if err := renderer.RenderTo(encoder, scene, camera, targetView); err != nil {
	encoder.DiscardEncoding()
	return err
}
// Record other render passes into encoder here.
commands, err := encoder.Finish()
if err != nil {
	return err
}
_, err = queue.Submit(commands)
return err

Architecture

Your Application (game engine, CAD viewer, data viz, AR/VR)
         |
    gogpu/g3d  — Scene Graph + Materials + Render Pipeline
         |
    gogpu/wgpu — Pure Go WebGPU (Vulkan/Metal/DX12/GLES/Software)
         |
    gogpu/naga — Shader Compiler (WGSL → SPIR-V/MSL/GLSL/HLSL)

g3d depends down (wgpu, naga), never up (gogpu, gg, ui). This ensures it can be used in any context.

See docs/ARCHITECTURE.md for full architecture documentation.

Installation

go get github.com/gogpu/g3d

Requirements: Go 1.25+

Roadmap

Phase Features Status
Phase 1 Scene graph, cameras, materials, box/sphere/plane, forward renderer Complete
Phase 2 Full PBR (Cook-Torrance), shadows, normal maps, textures Planned
Phase 3 GLTF 2.0 loader, skeletal animation, morph targets Planned
Phase 4 Instance batching, environment maps, post-processing, skybox Planned
Phase 5 Frustum culling BVH, LOD, SIMD math Planned

Design Principles

  1. Simple API — rotating lit cube in ~20 lines. Progressive complexity.
  2. Zero CGO — Pure Go on all platforms. Single binary deployment.
  3. Reusable — rendering library, not a framework. No opinions about your architecture.
  4. PBR from day one — metallic-roughness workflow, GLTF standard.
  5. Zero-alloc rendering — no GC pressure in the hot path.
  6. All GPU backends — Vulkan, Metal, DX12, GLES, Software through wgpu.

Contributing

See CONTRIBUTING.md for development workflow, code standards, and priority areas.

Part of the GoGPU Ecosystem

g3d is part of GoGPU — a Pure Go GPU computing ecosystem with 800K+ lines of code.

Library Purpose
gogpu Application framework, windowing
wgpu Pure Go WebGPU (Vulkan/Metal/DX12/GLES)
naga Shader compiler (WGSL → SPIR-V/MSL/GLSL/HLSL)
gg 2D graphics with GPU acceleration
g3d 3D rendering (this library)
ui GUI toolkit (22+ widgets, 4 themes)
systray System tray (Win32/macOS/Linux)
audio Pure Go audio engine (WASAPI)

License

MIT License — see LICENSE for details.

Documentation

Overview

Package g3d is a Pure Go 3D rendering library built on gogpu/wgpu.

g3d provides a scene graph, PBR materials, cameras, lights, geometry primitives, and a forward rendering pipeline with zero CGO dependencies. It is designed as a reusable foundation — not a game engine — so that game engines, CAD viewers, data visualizers, and AR/VR applications can build on top of it.

Quick start:

scene := g3d.NewScene()

ambient := g3d.NewAmbientLight(g3d.WithLightIntensity(0.3))
ambientNode := g3d.NewNode()
ambientNode.SetUserData(ambient)
scene.Add(ambientNode)

sun := g3d.NewDirectionalLight(g3d.WithLightIntensity(1.0))
scene.Add(sun.LightNode())

cube := g3d.NewMesh(g3d.NewBoxGeometry(1, 1, 1), g3d.NewStandardMaterial())
scene.Add(cube.MeshNode())

camera := g3d.NewPerspectiveCamera(75, aspect, 0.1, 1000)
camera.CameraNode().SetPosition(g3d.Vec3{0, 0, 3})

renderer.Render(scene, camera, targetView)

g3d depends on gogpu/wgpu for GPU abstraction (Vulkan, Metal, DX12, GLES, Software) and gogpu/naga for shader compilation (WGSL to SPIR-V/MSL/GLSL/HLSL).

Part of the GoGPU ecosystem: https://github.com/gogpu

Index

Constants

View Source
const LightUniformSize = 32

LightUniformSize is the byte size of a single LightUniform (32 bytes).

Variables

View Source
var (
	White   = Color{1, 1, 1, 1}
	Black   = Color{0, 0, 0, 1}
	Red     = Color{1, 0, 0, 1}
	Green   = Color{0, 1, 0, 1}
	Blue    = Color{0, 0, 1, 1}
	Yellow  = Color{1, 1, 0, 1}
	Cyan    = Color{0, 1, 1, 1}
	Magenta = Color{1, 0, 1, 1}
	Gray    = Color{0.5, 0.5, 0.5, 1}
)

Named color constants. All have alpha = 1 (fully opaque).

Functions

func Clamp

func Clamp(val, lo, hi float32) float32

Clamp constrains val to the range [lo, hi].

func Degrees

func Degrees(radians float32) float32

Degrees converts radians to degrees.

func Radians

func Radians(degrees float32) float32

Radians converts degrees to radians.

Types

type AABB

type AABB struct {
	Min, Max Vec3
}

AABB represents an axis-aligned bounding box defined by its minimum and maximum corners.

func NewAABBFromPoints

func NewAABBFromPoints(points []Vec3) AABB

NewAABBFromPoints computes the smallest AABB that contains all given points. Returns a zero AABB if points is empty.

func (AABB) Center

func (a AABB) Center() Vec3

Center returns the center point of the AABB.

func (AABB) ClosestPoint

func (a AABB) ClosestPoint(p Vec3) Vec3

ClosestPoint returns the closest point on or in the AABB to p.

func (AABB) ContainsPoint

func (a AABB) ContainsPoint(p Vec3) bool

ContainsPoint returns true if the AABB contains point p.

func (AABB) HalfExtents

func (a AABB) HalfExtents() Vec3

HalfExtents returns the half-size of the AABB (distance from center to each face).

func (AABB) IsEmpty

func (a AABB) IsEmpty() bool

IsEmpty returns true if the AABB has zero volume.

func (AABB) Merge

func (a AABB) Merge(other AABB) AABB

Merge returns the smallest AABB containing both a and other.

func (AABB) Size

func (a AABB) Size() Vec3

Size returns the dimensions (width, height, depth) of the AABB.

func (AABB) SurfaceArea

func (a AABB) SurfaceArea() float32

SurfaceArea returns the surface area of the AABB.

func (AABB) Transform

func (a AABB) Transform(m Mat4) AABB

Transform returns a new AABB that encloses the original AABB after applying the transformation matrix m. The result is axis-aligned (not an OBB).

func (AABB) Volume

func (a AABB) Volume() float32

Volume returns the volume of the AABB.

type AlphaMode

type AlphaMode uint8

AlphaMode determines how alpha values are handled during rendering. Matches GLTF 2.0 alphaMode and Three.js material side conventions.

const (
	// AlphaModeOpaque renders the material fully opaque, ignoring alpha.
	AlphaModeOpaque AlphaMode = iota

	// AlphaModeMask discards fragments with alpha below the cutoff threshold.
	AlphaModeMask

	// AlphaModeBlend enables standard alpha blending (src-over compositing).
	AlphaModeBlend
)

type AmbientLight

type AmbientLight struct {
	// contains filtered or unexported fields
}

AmbientLight provides uniform illumination across the entire scene regardless of position or direction. It does not embed Node because ambient light has no spatial properties.

Ambient light contributes equally to all surfaces:

finalColor += ambientColor * ambientIntensity * surfaceColor

Typical usage:

ambient := g3d.NewAmbientLight(
    g3d.WithLightColor(g3d.White),
    g3d.WithLightIntensity(0.3),
)

func NewAmbientLight

func NewAmbientLight(opts ...LightOption) *AmbientLight

NewAmbientLight creates an ambient light with the given options. Defaults: color=White, intensity=1.

func (*AmbientLight) Color

func (l *AmbientLight) Color() Color

Color returns the ambient light color.

func (*AmbientLight) Intensity

func (l *AmbientLight) Intensity() float32

Intensity returns the ambient light intensity.

func (*AmbientLight) LightColor

func (l *AmbientLight) LightColor() Color

LightColor returns the ambient light color.

func (*AmbientLight) LightIntensity

func (l *AmbientLight) LightIntensity() float32

LightIntensity returns the ambient light intensity.

func (*AmbientLight) LightType

func (l *AmbientLight) LightType() LightKind

LightType returns LightKindAmbient.

func (*AmbientLight) LightUniform

func (l *AmbientLight) LightUniform() LightUniform

LightUniform returns the GPU-side representation of this ambient light. Direction is zero (unused for ambient); Kind is LightKindAmbient (0).

func (*AmbientLight) SetColor

func (l *AmbientLight) SetColor(c Color)

SetColor sets the ambient light color.

func (*AmbientLight) SetIntensity

func (l *AmbientLight) SetIntensity(i float32)

SetIntensity sets the ambient light intensity.

type BasicMaterial

type BasicMaterial struct {
	// contains filtered or unexported fields
}

BasicMaterial is an unlit material that renders a solid color. Suitable for prototyping, data visualization, wireframe overlays, and any situation where lighting calculations are not needed.

The GPU uniform layout is 16 bytes:

offset 0:  color RGBA  [4]float32

func NewBasicMaterial

func NewBasicMaterial(opts ...MaterialOption) *BasicMaterial

NewBasicMaterial creates an unlit material with the given options. Defaults: color=White, opacity=1.

func (*BasicMaterial) Color

func (m *BasicMaterial) Color() Color

Color returns the base color.

func (*BasicMaterial) DoubleSided

func (m *BasicMaterial) DoubleSided() bool

DoubleSided reports whether back-face culling is disabled.

func (*BasicMaterial) Opacity

func (m *BasicMaterial) Opacity() float32

Opacity returns the opacity value.

func (*BasicMaterial) RenderBucket

func (m *BasicMaterial) RenderBucket() RenderBucket

RenderBucket returns RenderBucketTransparent if opacity < 1, otherwise RenderBucketOpaque.

func (*BasicMaterial) ShaderID

func (m *BasicMaterial) ShaderID() string

ShaderID returns "basic", identifying the unlit shader pipeline.

func (*BasicMaterial) UniformData

func (m *BasicMaterial) UniformData() []byte

UniformData returns 16 bytes encoding the material color as RGBA float32 values. The alpha component is pre-multiplied with opacity.

Layout (matches WGSL vec4<f32>):

offset 0:  R  float32
offset 4:  G  float32
offset 8:  B  float32
offset 12: A  float32 (color.A * opacity)

func (*BasicMaterial) Wireframe

func (m *BasicMaterial) Wireframe() bool

Wireframe reports whether the material renders as wireframe.

type BoxGeometry

type BoxGeometry struct {
	// contains filtered or unexported fields
}

BoxGeometry is a rectangular cuboid geometry centered at the origin.

Each face has 4 unique vertices with outward-pointing normals and UV coordinates mapped to [0,1]. Total: 24 vertices, 36 indices.

Created via NewBoxGeometry:

cube := g3d.NewBoxGeometry(1, 1, 1)        // unit cube
box  := g3d.NewBoxGeometry(2, 0.5, 3)      // custom dimensions

func NewBoxGeometry

func NewBoxGeometry(width, height, depth float32) *BoxGeometry

NewBoxGeometry creates a box geometry with the given width (X), height (Y), and depth (Z). The box is centered at the origin.

func (*BoxGeometry) BoundingBox

func (g *BoxGeometry) BoundingBox() AABB

BoundingBox returns the axis-aligned bounding box.

func (*BoxGeometry) Depth

func (g *BoxGeometry) Depth() float32

Depth returns the box depth along the Z axis.

func (*BoxGeometry) Height

func (g *BoxGeometry) Height() float32

Height returns the box height along the Y axis.

func (*BoxGeometry) Indices

func (g *BoxGeometry) Indices() []uint32

Indices returns the index buffer.

func (*BoxGeometry) VertexCount

func (g *BoxGeometry) VertexCount() int

VertexCount returns the number of vertices (always 24 for a box).

func (*BoxGeometry) Vertices

func (g *BoxGeometry) Vertices() []float32

Vertices returns the interleaved vertex data.

func (*BoxGeometry) Width

func (g *BoxGeometry) Width() float32

Width returns the box width along the X axis.

type BufferGeometry

type BufferGeometry struct {
	// contains filtered or unexported fields
}

BufferGeometry is a concrete Geometry holding precomputed vertex and index data.

Use NewBufferGeometry to create custom geometry from raw data, or use the built-in constructors (NewBoxGeometry, NewSphereGeometry, NewPlaneGeometry).

func NewBufferGeometry

func NewBufferGeometry(vertices []float32, indices []uint32) *BufferGeometry

NewBufferGeometry creates a Geometry from raw interleaved vertex data and indices.

The vertices slice must contain interleaved data with 8 floats per vertex: position(3) + normal(3) + uv(2). The indices slice may be nil for non-indexed geometry.

The bounding box is computed from the vertex positions.

func (*BufferGeometry) BoundingBox

func (g *BufferGeometry) BoundingBox() AABB

BoundingBox returns the axis-aligned bounding box.

func (*BufferGeometry) Indices

func (g *BufferGeometry) Indices() []uint32

Indices returns the index buffer.

func (*BufferGeometry) VertexCount

func (g *BufferGeometry) VertexCount() int

VertexCount returns the number of vertices.

func (*BufferGeometry) Vertices

func (g *BufferGeometry) Vertices() []float32

Vertices returns the interleaved vertex data.

type Camera

type Camera interface {
	// ViewMatrix returns the view (camera-space) matrix. It is the inverse of
	// the camera's world matrix — transforming world coordinates into the
	// camera's local space where the camera sits at the origin looking down -Z.
	ViewMatrix() Mat4

	// ProjectionMatrix returns the projection matrix that maps view-space
	// coordinates into WebGPU clip space (X/Y [-1,1], Z [0,1]).
	ProjectionMatrix() Mat4

	// ViewProjectionMatrix returns Projection * View, suitable for frustum
	// culling and transforming world-space positions directly to clip space.
	ViewProjectionMatrix() Mat4

	// Frustum returns the view frustum extracted from the ViewProjectionMatrix.
	// Used for frustum culling of scene objects.
	Frustum() Frustum

	// CameraNode returns the underlying Node for transform access.
	// Use this to set Position, Rotation, call LookAt, or attach the camera
	// to the scene graph.
	CameraNode() *Node
}

Camera provides the view and projection matrices needed by the renderer. Every camera embeds a Node so it can be positioned and rotated within the scene graph. Use CameraNode to access the underlying Node for transform manipulation (Position, Rotation, LookAt, etc.).

Two concrete implementations are provided:

  • PerspectiveCamera — field-of-view perspective projection
  • OrthographicCamera — parallel projection for CAD, 2D-in-3D, UI overlays

type Color

type Color struct {
	R, G, B, A float32
}

Color represents an RGBA color with float32 components in [0,1] range. Used for material colors, light colors, and scene background.

func RGB

func RGB(r, g, b float32) Color

RGB creates a Color from red, green, blue components with alpha = 1.

func RGBA

func RGBA(r, g, b, a float32) Color

RGBA creates a Color from red, green, blue, alpha components.

func (Color) Lerp

func (c Color) Lerp(other Color, t float32) Color

Lerp linearly interpolates between c and other by t in [0,1].

type DirectionalLight

type DirectionalLight struct {
	// contains filtered or unexported fields
}

DirectionalLight represents a light source that illuminates all objects from the same direction, like sunlight. It embeds Node for scene graph integration — the direction is derived from the node's world rotation.

The default forward vector is (0, 0, -1). A freshly created DirectionalLight with no rotation shines along (0, 0, -1). Rotating the node changes the effective light direction accordingly.

Typical usage:

sun := g3d.NewDirectionalLight(
    g3d.WithLightColor(g3d.White),
    g3d.WithLightIntensity(1.0),
)
sun.LightNode().SetRotation(g3d.Euler{X: g3d.Radians(-45), Y: 0, Z: 0})
scene.Add(sun.LightNode())

func NewDirectionalLight

func NewDirectionalLight(opts ...LightOption) *DirectionalLight

NewDirectionalLight creates a directional light with the given options. Defaults: color=White, intensity=1, direction=(0,0,-1) (from node identity rotation).

func (*DirectionalLight) Color

func (l *DirectionalLight) Color() Color

Color returns the directional light color.

func (*DirectionalLight) Direction

func (l *DirectionalLight) Direction() Vec3

Direction returns the world-space direction the light is shining towards. This is computed by rotating the default forward vector (0, 0, -1) by the node's world rotation (extracted from the world matrix).

func (*DirectionalLight) Intensity

func (l *DirectionalLight) Intensity() float32

Intensity returns the directional light intensity.

func (*DirectionalLight) LightColor

func (l *DirectionalLight) LightColor() Color

LightColor returns the directional light color.

func (*DirectionalLight) LightIntensity

func (l *DirectionalLight) LightIntensity() float32

LightIntensity returns the directional light intensity.

func (*DirectionalLight) LightNode

func (l *DirectionalLight) LightNode() *Node

LightNode returns a pointer to the embedded Node for scene graph operations. Add this node to a scene or group to position the light in the hierarchy.

func (*DirectionalLight) LightType

func (l *DirectionalLight) LightType() LightKind

LightType returns LightKindDirectional.

func (*DirectionalLight) LightUniform

func (l *DirectionalLight) LightUniform() LightUniform

LightUniform returns the GPU-side representation of this directional light. The direction is the normalized world-space direction from Direction().

func (*DirectionalLight) SetColor

func (l *DirectionalLight) SetColor(c Color)

SetColor sets the directional light color.

func (*DirectionalLight) SetIntensity

func (l *DirectionalLight) SetIntensity(i float32)

SetIntensity sets the directional light intensity.

type Euler

type Euler struct {
	X, Y, Z float32
}

Euler represents rotation in radians using intrinsic XYZ rotation order. All angles are in radians.

type Frustum

type Frustum [6]Plane

Frustum represents a view frustum defined by 6 clipping planes. Planes are ordered: left, right, bottom, top, near, far. Each plane's normal points inward (toward the visible region).

func FrustumFromMat4

func FrustumFromMat4(vp Mat4) Frustum

FrustumFromMat4 extracts 6 frustum planes from a view-projection matrix. The planes are normalized so that distance calculations are correct.

This works for both perspective and orthographic projections.

Uses WebGPU Z [0,1] clip space convention. The near plane is extracted from row2 alone (z >= 0), not row3+row2 which would be the OpenGL Z [-1,1] convention.

func (Frustum) ContainsPoint

func (f Frustum) ContainsPoint(p Vec3) bool

ContainsPoint returns true if the point p is inside all 6 frustum planes.

func (Frustum) IntersectsAABB

func (f Frustum) IntersectsAABB(box AABB) bool

IntersectsAABB returns true if the AABB is at least partially inside the frustum. Uses the "positive vertex" test for each plane — fast and conservative.

type Geometry

type Geometry interface {
	// Vertices returns the interleaved vertex data as float32 values.
	// Each vertex has 8 floats: position(3) + normal(3) + uv(2).
	Vertices() []float32

	// Indices returns the index buffer for indexed drawing.
	// Returns nil for non-indexed geometry.
	Indices() []uint32

	// VertexCount returns the number of vertices.
	VertexCount() int

	// BoundingBox returns the axis-aligned bounding box enclosing all vertices.
	BoundingBox() AABB
}

Geometry provides vertex and index data for rendering a mesh.

Implementations include BoxGeometry, SphereGeometry, and PlaneGeometry. Custom geometry can be created by implementing this interface directly or by using NewBufferGeometry with raw vertex/index data.

Vertex data is interleaved in standard layout:

position(vec3, 12 bytes) + normal(vec3, 12 bytes) + uv(vec2, 8 bytes) = 32 bytes per vertex

This layout matches the WGSL shader attribute locations:

@location(0) position: vec3<f32>  (offset 0)
@location(1) normal:   vec3<f32>  (offset 12)
@location(2) uv:       vec2<f32>  (offset 24)

type Group

type Group struct {
	Node
}

Group is an empty transform container in the scene graph. It has no visual representation — its sole purpose is to group children under a shared local transform. Moving/rotating/scaling a Group applies that transform to all of its descendants.

Typical uses:

  • Grouping related objects (e.g., all parts of a robot arm)
  • Applying a shared offset or scale to a set of meshes
  • Organizing the scene tree for logical structure

func NewGroup

func NewGroup() *Group

NewGroup returns a new Group with identity transform (Scale = {1,1,1}).

type Light

type Light interface {
	// LightType returns the kind discriminant for the GPU uniform.
	LightType() LightKind

	// LightColor returns the light's color.
	LightColor() Color

	// LightIntensity returns the light's intensity multiplier.
	LightIntensity() float32
}

Light provides illumination data for the renderer. Every light type (ambient, directional, and future point/spot) implements this interface. The renderer collects all lights from the scene and packs their data into a uniform buffer each frame.

type LightKind

type LightKind uint32

LightKind identifies the type of a light source. The value is stored in the GPU uniform struct so the shader can branch on light type.

const (
	// LightKindAmbient is a uniform light with no direction or position.
	LightKindAmbient LightKind = 0

	// LightKindDirectional is a parallel light from an infinite distance.
	LightKindDirectional LightKind = 1
)

type LightOption

type LightOption interface {
	// contains filtered or unexported methods
}

LightOption configures light properties via functional options.

func WithLightColor

func WithLightColor(c Color) LightOption

WithLightColor sets the light color. Applies to all light types.

func WithLightIntensity

func WithLightIntensity(v float32) LightOption

WithLightIntensity sets the light intensity. Applies to all light types.

type LightUniform

type LightUniform struct {
	Direction [3]float32 // world-space direction (unused for ambient)
	Kind      uint32     // LightKind discriminant
	Color     [3]float32 // linear RGB color
	Intensity float32    // intensity multiplier
}

LightUniform is the GPU-side representation of a single light source. The struct layout matches the WGSL DirectionalLight struct exactly (32 bytes, 16-byte aligned). Fields are ordered to satisfy WGSL alignment rules:

offset  0: Direction [3]float32  (12 bytes — vec3<f32>)
offset 12: Kind      uint32      (4 bytes  — padding slot + discriminant)
offset 16: Color     [3]float32  (12 bytes — vec3<f32>)
offset 28: Intensity float32     (4 bytes)
Total: 32 bytes, 16-byte aligned

The Direction+Kind pair fills a full 16-byte row, and Color+Intensity fills the second 16-byte row, satisfying WGSL vec3<f32> alignment requirements.

func (LightUniform) Bytes

func (u LightUniform) Bytes() []byte

Bytes encodes the LightUniform as 32 bytes in little-endian format for GPU upload. The layout matches the WGSL struct byte-for-byte.

type Mat4

type Mat4 [16]float32

Mat4 is a 4x4 transformation matrix stored in column-major order. This matches the WGSL mat4x4<f32> memory layout for direct GPU upload.

Storage: m[col*4+row]

m[0..3]   = column 0 (X-axis + translation.x)
m[4..7]   = column 1 (Y-axis + translation.y)
m[8..11]  = column 2 (Z-axis + translation.z)
m[12..15] = column 3 (translation + w=1)

Index layout (row, col):

[0]  [4]  [8]  [12]     (row 0)
[1]  [5]  [9]  [13]     (row 1)
[2]  [6]  [10] [14]     (row 2)
[3]  [7]  [11] [15]     (row 3)

func Mat4FromQuat

func Mat4FromQuat(q Quat) Mat4

Mat4FromQuat returns a rotation matrix equivalent to the given quaternion.

func Mat4Identity

func Mat4Identity() Mat4

Mat4Identity returns the 4x4 identity matrix.

func Mat4LookAt

func Mat4LookAt(eye, target, up Vec3) Mat4

Mat4LookAt returns a view matrix looking from eye toward target with the given up direction.

func Mat4Ortho

func Mat4Ortho(left, right, bottom, top, near, far float32) Mat4

Mat4Ortho returns an orthographic projection matrix.

Uses WebGPU clip space Z [0,1], NOT OpenGL Z [-1,1].

func Mat4Perspective

func Mat4Perspective(fovYRadians, aspect, near, far float32) Mat4

Mat4Perspective returns a perspective projection matrix.

CRITICAL: Uses WebGPU clip space Z [0,1], NOT OpenGL Z [-1,1]. Formula validated against Three.js Matrix4.makePerspective(WebGPUCoordinateSystem).

Parameters:

  • fovYRadians: vertical field of view in radians
  • aspect: width / height ratio
  • near: near clipping plane distance (must be > 0)
  • far: far clipping plane distance (must be > near)

func Mat4RotateX

func Mat4RotateX(radians float32) Mat4

Mat4RotateX returns a rotation matrix around the X axis by radians.

func Mat4RotateY

func Mat4RotateY(radians float32) Mat4

Mat4RotateY returns a rotation matrix around the Y axis by radians.

func Mat4RotateZ

func Mat4RotateZ(radians float32) Mat4

Mat4RotateZ returns a rotation matrix around the Z axis by radians.

func Mat4Scale

func Mat4Scale(v Vec3) Mat4

Mat4Scale returns a scale matrix that scales by v.

func Mat4Translate

func Mat4Translate(v Vec3) Mat4

Mat4Translate returns a translation matrix that moves by v.

func (Mat4) Determinant

func (m Mat4) Determinant() float32

Determinant returns the determinant of m.

func (Mat4) Inverse

func (m Mat4) Inverse() Mat4

Inverse returns the inverse of m. If m is singular (determinant = 0), returns the zero matrix.

func (Mat4) Mul

func (m Mat4) Mul(other Mat4) Mat4

Mul returns the matrix product m * other. Matrix multiplication is associative: (A*B)*C = A*(B*C).

func (Mat4) MulVec4

func (m Mat4) MulVec4(v Vec4) Vec4

MulVec4 returns the product m * v (matrix-vector multiplication).

func (Mat4) Translation

func (m Mat4) Translation() Vec3

Translation extracts the translation component from column 3.

func (Mat4) Transpose

func (m Mat4) Transpose() Mat4

Transpose returns the transpose of m.

type Material

type Material interface {
	// ShaderID returns an identifier used for render pipeline caching.
	// Materials with the same ShaderID share a GPU pipeline.
	ShaderID() string

	// RenderBucket determines which render pass draws this material.
	RenderBucket() RenderBucket

	// DoubleSided reports whether back-face culling is disabled.
	DoubleSided() bool

	// UniformData returns the material-specific uniform bytes for GPU upload.
	// The byte layout must match the WGSL material uniform struct exactly.
	UniformData() []byte
}

Material describes the surface appearance of a mesh. Materials provide the shader identifier for pipeline caching, the render bucket for draw ordering, and the serialized uniform data for GPU upload.

Two materials with the same ShaderID share a render pipeline. The UniformData layout must match the corresponding WGSL material struct.

type MaterialOption

type MaterialOption interface {
	// contains filtered or unexported methods
}

MaterialOption configures material properties via functional options. Options that apply to both BasicMaterial and StandardMaterial implement both apply methods; standard-only options are no-ops on BasicMaterial.

func WithAlphaCutoff

func WithAlphaCutoff(cutoff float32) MaterialOption

WithAlphaCutoff sets the alpha test threshold for AlphaModeMask. Fragments with alpha below this value are discarded. Only affects StandardMaterial; ignored by BasicMaterial.

func WithAlphaMode

func WithAlphaMode(mode AlphaMode) MaterialOption

WithAlphaMode sets the alpha handling mode (Opaque, Mask, or Blend). Only affects StandardMaterial; ignored by BasicMaterial.

func WithColor

func WithColor(c Color) MaterialOption

WithColor sets the base color. Applies to both BasicMaterial and StandardMaterial.

func WithDoubleSided

func WithDoubleSided(v bool) MaterialOption

WithDoubleSided disables back-face culling when true. Applies to both materials.

func WithEmissive

func WithEmissive(c Color) MaterialOption

WithEmissive sets the emissive color. Objects emit this color regardless of lighting. Only affects StandardMaterial; ignored by BasicMaterial.

func WithMetallic

func WithMetallic(v float32) MaterialOption

WithMetallic sets the metallic factor in [0,1]. 0 = dielectric, 1 = metal. Only affects StandardMaterial; ignored by BasicMaterial.

func WithOpacity

func WithOpacity(v float32) MaterialOption

WithOpacity sets the opacity in [0,1]. Applies to both BasicMaterial and StandardMaterial. For BasicMaterial, opacity < 1 moves the material to the transparent render bucket.

func WithRoughness

func WithRoughness(v float32) MaterialOption

WithRoughness sets the roughness factor in [0,1]. 0 = mirror, 1 = matte. Only affects StandardMaterial; ignored by BasicMaterial.

func WithWireframe

func WithWireframe(v bool) MaterialOption

WithWireframe enables wireframe rendering when true. Applies to both materials.

type Mesh

type Mesh struct {
	// contains filtered or unexported fields
}

Mesh combines a Geometry and a Material into a renderable scene object. It participates in the scene graph via an internal Node (accessed through MeshNode) and provides the data the Renderer needs to issue draw calls.

Mesh is a pure data container — it does not manage GPU resources. Buffer creation and pipeline binding are the Renderer's responsibility (TASK-G3D-007).

The Node is stored by value (not embedded) to avoid method name conflicts between Mesh and Node. Use MeshNode() to access the underlying Node for transform manipulation (SetPosition, SetRotation, Add children, etc.).

Example:

cube := g3d.NewMesh(g3d.NewBoxGeometry(1, 1, 1), g3d.NewStandardMaterial())
scene.Add(cube.MeshNode())
cube.MeshNode().SetPosition(g3d.Vec3{2, 0, 0})

func NewMesh

func NewMesh(geometry Geometry, material Material) *Mesh

NewMesh creates a Mesh from a Geometry and a Material. The internal Node is initialized with identity transform (Scale = {1,1,1}) and named "Mesh".

Both geometry and material may be nil at construction time, allowing deferred setup. The Renderer will skip meshes that have a nil geometry or material.

func (*Mesh) Geometry

func (m *Mesh) Geometry() Geometry

Geometry returns the current Geometry, or nil if none is set.

func (*Mesh) Material

func (m *Mesh) Material() Material

Material returns the current Material, or nil if none is set.

func (*Mesh) MeshNode

func (m *Mesh) MeshNode() *Node

MeshNode returns a pointer to the underlying Node for scene graph operations. Use this to set Position, Rotation, Scale, call LookAt, or add the mesh to a scene:

scene.Add(mesh.MeshNode())
mesh.MeshNode().SetPosition(g3d.Vec3{1, 2, 3})

func (*Mesh) SetGeometry

func (m *Mesh) SetGeometry(g Geometry)

SetGeometry replaces the mesh's Geometry. Pass nil to clear.

func (*Mesh) SetMaterial

func (m *Mesh) SetMaterial(mat Material)

SetMaterial replaces the mesh's Material. Changing the material may change the ShaderID and thus the render pipeline used for this mesh.

func (*Mesh) WorldBoundingBox

func (m *Mesh) WorldBoundingBox() AABB

WorldBoundingBox returns the geometry's axis-aligned bounding box transformed into world space by the node's world matrix. This is used for frustum culling.

If geometry is nil, returns a zero AABB.

type Node

type Node struct {
	// Position is the local translation relative to the parent.
	Position Vec3
	// Rotation is the local rotation in radians (intrinsic XYZ order).
	Rotation Euler
	// Scale is the local scale. Default is {1,1,1}.
	Scale Vec3
	// contains filtered or unexported fields
}

Node is the base building block of the g3d scene graph. Every object in a scene — meshes, cameras, lights, groups — embeds a Node. It holds a local transform (Position, Rotation, Scale), a cached local and world matrix with dirty-flag propagation, and parent-child relationships.

Setting Position, Rotation, or Scale through the provided setters marks the local matrix dirty. A dirty local matrix automatically dirties the world matrix of the node and all of its descendants, ensuring that WorldMatrix always returns a correct result without manual intervention.

Design: Three.js Object3D + Kaiju Transform dirty propagation + idiomatic Go.

func NewNode

func NewNode() *Node

NewNode returns a new Node with identity transform (Scale = {1,1,1}), visible, no parent, and no children.

func (*Node) Add

func (n *Node) Add(child *Node)

Add appends child to this node's children list. If child already has a parent, it is first removed from that parent. Circular references (adding a node to itself or to one of its descendants) are silently ignored.

func (*Node) ChildCount

func (n *Node) ChildCount() int

ChildCount returns the number of direct children.

func (*Node) Children

func (n *Node) Children() []*Node

Children returns a shallow copy of the children slice. The caller may modify the returned slice without affecting the node's internal state.

func (*Node) LocalMatrix

func (n *Node) LocalMatrix() Mat4

LocalMatrix returns the local transformation matrix, lazily recomputed from Position, Rotation, and Scale when dirty.

Local = Translate(Position) * Rotate(QuatFromEuler(Rotation)) * Scale(Scale)

func (*Node) LookAt

func (n *Node) LookAt(target Vec3)

LookAt orients the node so that its local -Z axis points at the target position. The node's world position is used as the eye. The up vector is {0,1,0}. This method works correctly only for nodes without rotated parents; for arbitrary hierarchies the caller should account for the parent transform.

func (*Node) Name

func (n *Node) Name() string

Name returns the node's name. The empty string is the default.

func (*Node) Parent

func (n *Node) Parent() *Node

Parent returns the parent node, or nil if this node is a root.

func (*Node) Remove

func (n *Node) Remove(child *Node)

Remove detaches child from this node's children list. If child is not a direct child, this is a no-op.

func (*Node) SetName

func (n *Node) SetName(name string)

SetName sets the node's name. Names are not required to be unique.

func (*Node) SetPosition

func (n *Node) SetPosition(p Vec3)

SetPosition sets the local position and marks the transform dirty.

func (*Node) SetRotation

func (n *Node) SetRotation(r Euler)

SetRotation sets the local rotation (radians, intrinsic XYZ) and marks the transform dirty.

func (*Node) SetScale

func (n *Node) SetScale(s Vec3)

SetScale sets the local scale and marks the transform dirty.

func (*Node) SetUserData

func (n *Node) SetUserData(data any)

SetUserData attaches arbitrary application-specific data to this node.

func (*Node) SetVisible

func (n *Node) SetVisible(v bool)

SetVisible sets the node's visibility flag.

func (*Node) UserData

func (n *Node) UserData() any

UserData returns the application-specific data attached to this node.

func (*Node) Visible

func (n *Node) Visible() bool

Visible returns whether the node is visible. Invisible nodes (and their entire subtree) are skipped during TraverseVisible.

func (*Node) WorldMatrix

func (n *Node) WorldMatrix() Mat4

WorldMatrix returns the world (model) transformation matrix. It is computed as parent.WorldMatrix() * node.LocalMatrix(). If there is no parent, the world matrix equals the local matrix. Recomputed lazily when dirty.

func (*Node) WorldPosition

func (n *Node) WorldPosition() Vec3

WorldPosition returns the world-space position by extracting column 3 of the world matrix. This is equivalent to the translation component after all parent transforms have been applied.

type OrthographicCamera

type OrthographicCamera struct {
	// contains filtered or unexported fields
}

OrthographicCamera implements the Camera interface with an orthographic (parallel) projection. Objects retain their size regardless of distance from the camera, making this suitable for CAD viewers, 2D-in-3D overlays, isometric games, and technical visualization.

The viewing volume is defined by six planes: left, right, bottom, top, near, and far. The projection uses WebGPU clip space (Z [0,1]).

Position, rotation, and scene-graph hierarchy are managed by the embedded Node, accessible via CameraNode().

Example:

cam := g3d.NewOrthographicCamera(-10, 10, -10, 10, 0.1, 100)
cam.CameraNode().SetPosition(g3d.Vec3{0, 10, 0})
cam.CameraNode().LookAt(g3d.Vec3{0, 0, 0})

func NewOrthographicCamera

func NewOrthographicCamera(left, right, bottom, top, near, far float32) *OrthographicCamera

NewOrthographicCamera creates an OrthographicCamera with the given viewing volume bounds and near/far clipping plane distances. The parameters define the edges of the orthographic projection box in camera space.

func (*OrthographicCamera) Bottom

func (c *OrthographicCamera) Bottom() float32

Bottom returns the bottom edge of the viewing volume.

func (*OrthographicCamera) CameraNode

func (c *OrthographicCamera) CameraNode() *Node

CameraNode returns the underlying Node for transform access.

func (*OrthographicCamera) Far

func (c *OrthographicCamera) Far() float32

Far returns the far clipping plane distance.

func (*OrthographicCamera) Frustum

func (c *OrthographicCamera) Frustum() Frustum

Frustum returns the view frustum planes extracted from the current view-projection matrix. Used for frustum culling.

func (*OrthographicCamera) Left

func (c *OrthographicCamera) Left() float32

Left returns the left edge of the viewing volume.

func (*OrthographicCamera) Near

func (c *OrthographicCamera) Near() float32

Near returns the near clipping plane distance.

func (*OrthographicCamera) ProjectionMatrix

func (c *OrthographicCamera) ProjectionMatrix() Mat4

ProjectionMatrix returns the orthographic projection matrix using WebGPU clip space (Z [0,1]). The matrix is cached and only recomputed when the bounds or clip planes change.

func (*OrthographicCamera) Right

func (c *OrthographicCamera) Right() float32

Right returns the right edge of the viewing volume.

func (*OrthographicCamera) SetBounds

func (c *OrthographicCamera) SetBounds(left, right, bottom, top float32)

SetBounds sets all four edges of the viewing volume and invalidates the cached projection matrix.

func (*OrthographicCamera) SetClipPlanes

func (c *OrthographicCamera) SetClipPlanes(near, far float32)

SetClipPlanes sets the near and far clipping plane distances and invalidates the cached projection matrix.

func (*OrthographicCamera) Top

func (c *OrthographicCamera) Top() float32

Top returns the top edge of the viewing volume.

func (*OrthographicCamera) ViewMatrix

func (c *OrthographicCamera) ViewMatrix() Mat4

ViewMatrix returns the view matrix, which is the inverse of the camera's world matrix. This transforms world-space coordinates into camera-local space where the camera is at the origin looking down -Z.

func (*OrthographicCamera) ViewProjectionMatrix

func (c *OrthographicCamera) ViewProjectionMatrix() Mat4

ViewProjectionMatrix returns Projection * View. This single matrix transforms world-space positions directly to clip space.

type PerspectiveCamera

type PerspectiveCamera struct {
	// contains filtered or unexported fields
}

PerspectiveCamera implements the Camera interface with a perspective projection. Objects farther from the camera appear smaller, matching human visual perception.

The field of view is specified in degrees (user-friendly) but stored and used in radians internally. The projection uses WebGPU clip space (Z [0,1]).

Position, rotation, and scene-graph hierarchy are managed by the embedded Node, accessible via CameraNode().

Example:

cam := g3d.NewPerspectiveCamera(75, 16.0/9.0, 0.1, 1000)
cam.CameraNode().SetPosition(g3d.Vec3{0, 0, 5})
cam.CameraNode().LookAt(g3d.Vec3{0, 0, 0})

func NewPerspectiveCamera

func NewPerspectiveCamera(fovDegrees, aspect, near, far float32) *PerspectiveCamera

NewPerspectiveCamera creates a PerspectiveCamera with the given vertical field of view (in degrees), aspect ratio (width/height), and near/far clipping plane distances.

The near plane must be > 0 and the far plane must be > near.

func (*PerspectiveCamera) Aspect

func (c *PerspectiveCamera) Aspect() float32

Aspect returns the aspect ratio (width / height).

func (*PerspectiveCamera) CameraNode

func (c *PerspectiveCamera) CameraNode() *Node

CameraNode returns the underlying Node for transform access.

func (*PerspectiveCamera) FOV

func (c *PerspectiveCamera) FOV() float32

FOV returns the vertical field of view in degrees.

func (*PerspectiveCamera) Far

func (c *PerspectiveCamera) Far() float32

Far returns the far clipping plane distance.

func (*PerspectiveCamera) Frustum

func (c *PerspectiveCamera) Frustum() Frustum

Frustum returns the view frustum planes extracted from the current view-projection matrix. Used for frustum culling.

func (*PerspectiveCamera) Near

func (c *PerspectiveCamera) Near() float32

Near returns the near clipping plane distance.

func (*PerspectiveCamera) ProjectionMatrix

func (c *PerspectiveCamera) ProjectionMatrix() Mat4

ProjectionMatrix returns the perspective projection matrix using WebGPU clip space (Z [0,1]). The matrix is cached and only recomputed when FOV, aspect, near, or far changes.

func (*PerspectiveCamera) SetAspect

func (c *PerspectiveCamera) SetAspect(aspect float32)

SetAspect sets the aspect ratio (width / height) and invalidates the cached projection matrix. Call this when the viewport is resized.

func (*PerspectiveCamera) SetClipPlanes

func (c *PerspectiveCamera) SetClipPlanes(near, far float32)

SetClipPlanes sets the near and far clipping plane distances and invalidates the cached projection matrix.

func (*PerspectiveCamera) SetFOV

func (c *PerspectiveCamera) SetFOV(degrees float32)

SetFOV sets the vertical field of view in degrees and invalidates the cached projection matrix.

func (*PerspectiveCamera) ViewMatrix

func (c *PerspectiveCamera) ViewMatrix() Mat4

ViewMatrix returns the view matrix, which is the inverse of the camera's world matrix. This transforms world-space coordinates into camera-local space where the camera is at the origin looking down -Z.

func (*PerspectiveCamera) ViewProjectionMatrix

func (c *PerspectiveCamera) ViewProjectionMatrix() Mat4

ViewProjectionMatrix returns Projection * View. This single matrix transforms world-space positions directly to clip space.

type Plane

type Plane struct {
	Normal Vec3
	D      float32
}

Plane represents a 3D plane defined by the equation: Normal . P + D = 0. The normal vector points toward the positive half-space.

func (Plane) DistanceToPoint

func (p Plane) DistanceToPoint(pt Vec3) float32

DistanceToPoint returns the signed distance from the plane to point p. Positive means p is on the side the normal points to.

type PlaneGeometry

type PlaneGeometry struct {
	// contains filtered or unexported fields
}

PlaneGeometry is a flat rectangular plane in the XZ plane.

The plane is centered at the origin with its normal pointing in the +Y direction. This matches the standard ground plane convention used by Three.js and most 3D engines. Total: 4 vertices, 6 indices.

Created via NewPlaneGeometry:

ground := g3d.NewPlaneGeometry(10, 10)    // 10x10 ground plane
wall   := g3d.NewPlaneGeometry(5, 3)      // 5x3 plane

func NewPlaneGeometry

func NewPlaneGeometry(width, height float32) *PlaneGeometry

NewPlaneGeometry creates a plane in the XZ plane with the given width (X) and height (Z). The plane is centered at the origin with its normal pointing up (+Y).

func (*PlaneGeometry) BoundingBox

func (g *PlaneGeometry) BoundingBox() AABB

BoundingBox returns the axis-aligned bounding box.

func (*PlaneGeometry) Height

func (g *PlaneGeometry) Height() float32

Height returns the plane height along the Z axis.

func (*PlaneGeometry) Indices

func (g *PlaneGeometry) Indices() []uint32

Indices returns the index buffer.

func (*PlaneGeometry) VertexCount

func (g *PlaneGeometry) VertexCount() int

VertexCount returns the number of vertices (always 4 for a plane).

func (*PlaneGeometry) Vertices

func (g *PlaneGeometry) Vertices() []float32

Vertices returns the interleaved vertex data.

func (*PlaneGeometry) Width

func (g *PlaneGeometry) Width() float32

Width returns the plane width along the X axis.

type Quat

type Quat struct {
	X, Y, Z, W float32
}

Quat represents a quaternion rotation. W is the scalar component, X/Y/Z are the vector components. The identity quaternion is {0,0,0,1}.

Quaternions avoid gimbal lock and provide smooth interpolation (Slerp).

func QuatFromAxisAngle

func QuatFromAxisAngle(axis Vec3, radians float32) Quat

QuatFromAxisAngle creates a quaternion representing rotation around axis by radians. The axis should be normalized.

func QuatFromEuler

func QuatFromEuler(e Euler) Quat

QuatFromEuler creates a quaternion from Euler angles (intrinsic XYZ rotation order). Angles are in radians.

func QuatIdentity

func QuatIdentity() Quat

QuatIdentity returns the identity quaternion (no rotation).

func (Quat) Conjugate

func (q Quat) Conjugate() Quat

Conjugate returns the conjugate of q (negated vector part).

func (Quat) Dot

func (q Quat) Dot(other Quat) float32

Dot returns the dot product of q and other.

func (Quat) Inverse

func (q Quat) Inverse() Quat

Inverse returns the inverse of q. For unit quaternions, this equals the conjugate.

func (Quat) Length

func (q Quat) Length() float32

Length returns the length (norm) of the quaternion.

func (Quat) LengthSq

func (q Quat) LengthSq() float32

LengthSq returns the squared length of the quaternion.

func (Quat) Mul

func (q Quat) Mul(other Quat) Quat

Mul returns the Hamilton product q * other. This combines rotations: q.Mul(other) applies other first, then q.

func (Quat) Normalize

func (q Quat) Normalize() Quat

Normalize returns a unit quaternion in the same direction. Returns the identity quaternion if q has zero length.

func (Quat) RotateVec3

func (q Quat) RotateVec3(v Vec3) Vec3

RotateVec3 rotates v by the rotation represented by q. Equivalent to q * (0,v,0) * q^-1, optimized to avoid full quaternion multiply.

func (Quat) Slerp

func (q Quat) Slerp(other Quat, t float32) Quat

Slerp performs spherical linear interpolation between q and other by t in [0,1]. Produces constant-speed rotation along the shortest arc.

func (Quat) ToEuler

func (q Quat) ToEuler() Euler

ToEuler converts the quaternion to Euler angles (intrinsic XYZ rotation order). Returns angles in radians.

func (Quat) ToMat4

func (q Quat) ToMat4() Mat4

ToMat4 converts the quaternion to a 4x4 rotation matrix (column-major).

type RenderBucket

type RenderBucket uint8

RenderBucket determines the render order for a material. Four buckets follow the Three.js validated pattern (see PHASE1-ARCHITECTURE-VALIDATION.md). Within each bucket, draw calls are sorted by pipeline key, material ID, then distance.

const (
	// RenderBucketBackground is drawn first. Used for skyboxes and IBL.
	RenderBucketBackground RenderBucket = iota

	// RenderBucketOpaque is drawn second with depth testing and no blending.
	// Front-to-back sorted to minimize overdraw.
	RenderBucketOpaque

	// RenderBucketTransmissive is drawn third. Used for transparent objects
	// that still write to the depth buffer (e.g., refraction).
	RenderBucketTransmissive

	// RenderBucketTransparent is drawn last with alpha blending enabled.
	// Back-to-front sorted for correct compositing.
	RenderBucketTransparent
)

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer orchestrates the g3d forward rendering pipeline. It manages the scene traversal, frustum culling, render list sorting, GPU resource creation, and draw call submission via wgpu.

Two construction modes:

  • NewRenderer(provider) — shared device from gogpu app
  • NewRendererFromDevice(device, queue, format) — standalone (tests, headless)

The renderer is NOT thread-safe. Call Render from a single goroutine.

func NewRenderer

func NewRenderer(provider gpucontext.DeviceProvider) (*Renderer, error)

NewRenderer creates a Renderer using a shared GPU device from a DeviceProvider (e.g., gogpu.App). Returns an error if the provider has a software adapter or the device type is incompatible.

func NewRendererFromDevice

func NewRendererFromDevice(
	device *wgpu.Device,
	queue *wgpu.Queue,
	surfaceFormat gputypes.TextureFormat,
) (*Renderer, error)

NewRendererFromDevice creates a Renderer from an explicit wgpu device and queue. Use this for standalone rendering, tests, or headless scenarios where there is no gogpu application framework.

func (*Renderer) Release

func (r *Renderer) Release()

Release frees all GPU resources owned by the renderer. After this call the renderer cannot be used.

func (*Renderer) Render

func (r *Renderer) Render(scene *Scene, camera Camera, targetView *wgpu.TextureView) error

Render draws the scene from the camera's perspective into the given target view.

The render flow follows the validated TASK-G3D-007 design:

  1. Update world transforms (propagate dirty matrices).
  2. Extract frustum from camera, build sorted render list.
  3. Upload frame uniforms (ViewProjection, camera, lights).
  4. Record draw calls and submit command buffer.

Per-frame GPU buffers are created with MappedAtCreation, filled, unmapped, used, and released after submission. Pipeline creation is lazy and cached.

func (*Renderer) RenderTo added in v0.1.3

func (r *Renderer) RenderTo(
	encoder *wgpu.CommandEncoder,
	scene *Scene,
	camera Camera,
	targetView *wgpu.TextureView,
) error

RenderTo records the scene's render pass into a caller-owned command encoder. It does not finish, submit, or discard the encoder. This lets applications combine g3d with overlays and other renderers in one command buffer and one queue submission.

The encoder and target view must belong to the renderer's device. The caller must keep them valid through submission and must not use the encoder concurrently.

func (*Renderer) SetSize

func (r *Renderer) SetSize(width, height uint32)

SetSize sets the render target dimensions and (re)creates the depth texture. Must be called before the first Render call and whenever the viewport resizes.

type Scene

type Scene struct {
	Node

	// Background is the clear color used when rendering this scene.
	Background Color
}

Scene is the root of a g3d scene graph. It embeds Node and adds a background clear color used by the renderer. All objects (meshes, lights, cameras) are added as children of the Scene using Add.

Scene provides traversal methods for walking the entire node hierarchy:

  • Traverse visits every descendant in depth-first order.
  • TraverseVisible visits only visible descendants, skipping hidden subtrees.
  • UpdateWorldTransforms forces a top-down recomputation of all dirty world matrices.

func NewScene

func NewScene() *Scene

NewScene returns a new Scene with a black background and an identity transform. The scene itself is always visible.

func (*Scene) SetBackground

func (s *Scene) SetBackground(c Color)

SetBackground sets the background clear color for the scene.

func (*Scene) Traverse

func (s *Scene) Traverse(fn func(*Node))

Traverse walks every descendant node in depth-first, pre-order. The scene's own Node is NOT passed to fn — only its descendants are visited.

func (*Scene) TraverseVisible

func (s *Scene) TraverseVisible(fn func(*Node))

TraverseVisible walks only visible descendant nodes in depth-first, pre-order. If a node is invisible, its entire subtree is skipped. The scene's own Node is NOT passed to fn.

func (*Scene) UpdateWorldTransforms

func (s *Scene) UpdateWorldTransforms()

UpdateWorldTransforms forces a top-down traversal that recomputes every dirty world matrix. After this call, WorldMatrix() on any node in the scene returns the correct value without further lazy computation.

This is useful before rendering to ensure all matrices are up-to-date in a single pass rather than on-demand per node.

type SphereGeometry

type SphereGeometry struct {
	// contains filtered or unexported fields
}

SphereGeometry is a UV sphere geometry centered at the origin.

The sphere uses latitudinal/longitudinal subdivision with proper pole handling (single vertex at each pole, triangle fans instead of degenerate quads).

Created via NewSphereGeometry with optional segment configuration:

sphere := g3d.NewSphereGeometry(1.0)                              // default 32x16 segments
sphere := g3d.NewSphereGeometry(0.5, g3d.WithSegments(64, 32))   // high detail

func NewSphereGeometry

func NewSphereGeometry(radius float32, opts ...SphereOption) *SphereGeometry

NewSphereGeometry creates a UV sphere with the given radius and optional segment configuration. Default segments are 32 (width) and 16 (height) if no options are provided.

func (*SphereGeometry) BoundingBox

func (g *SphereGeometry) BoundingBox() AABB

BoundingBox returns the axis-aligned bounding box.

func (*SphereGeometry) HeightSegments

func (g *SphereGeometry) HeightSegments() int

HeightSegments returns the number of latitudinal segments.

func (*SphereGeometry) Indices

func (g *SphereGeometry) Indices() []uint32

Indices returns the index buffer.

func (*SphereGeometry) Radius

func (g *SphereGeometry) Radius() float32

Radius returns the sphere radius.

func (*SphereGeometry) VertexCount

func (g *SphereGeometry) VertexCount() int

VertexCount returns the number of vertices.

func (*SphereGeometry) Vertices

func (g *SphereGeometry) Vertices() []float32

Vertices returns the interleaved vertex data.

func (*SphereGeometry) WidthSegments

func (g *SphereGeometry) WidthSegments() int

WidthSegments returns the number of longitudinal segments.

type SphereOption

type SphereOption func(*sphereConfig)

SphereOption configures sphere geometry generation.

func WithSegments

func WithSegments(width, height int) SphereOption

WithSegments sets the number of longitudinal (width) and latitudinal (height) segments. Width segments must be at least 3, height segments at least 2.

type StandardMaterial

type StandardMaterial struct {
	// contains filtered or unexported fields
}

StandardMaterial is a PBR material following the GLTF 2.0 metallic-roughness model. Phase 1 uses Blinn-Phong approximation; full PBR in Phase 2.

The GPU uniform layout is 32 bytes:

offset  0: color RGBA        [4]float32  (16 bytes)
offset 16: metallic          float32     (4 bytes)
offset 20: roughness         float32     (4 bytes)
offset 24: alpha_cutoff      float32     (4 bytes)
offset 28: _padding          float32     (4 bytes)
                                         Total: 32 bytes

func NewStandardMaterial

func NewStandardMaterial(opts ...MaterialOption) *StandardMaterial

NewStandardMaterial creates a PBR material with the given options. Defaults: color=White, metallic=0, roughness=0.5, emissive=Black, opacity=1, alphaMode=Opaque, alphaCutoff=0.5.

func (*StandardMaterial) AlphaCutoff

func (m *StandardMaterial) AlphaCutoff() float32

AlphaCutoff returns the alpha test threshold for AlphaModeMask.

func (*StandardMaterial) AlphaMode

func (m *StandardMaterial) AlphaMode() AlphaMode

AlphaMode returns the alpha handling mode.

func (*StandardMaterial) Color

func (m *StandardMaterial) Color() Color

Color returns the base color.

func (*StandardMaterial) DoubleSided

func (m *StandardMaterial) DoubleSided() bool

DoubleSided reports whether back-face culling is disabled.

func (*StandardMaterial) Emissive

func (m *StandardMaterial) Emissive() Color

Emissive returns the emissive color.

func (*StandardMaterial) Metallic

func (m *StandardMaterial) Metallic() float32

Metallic returns the metallic factor.

func (*StandardMaterial) Opacity

func (m *StandardMaterial) Opacity() float32

Opacity returns the opacity value.

func (*StandardMaterial) RenderBucket

func (m *StandardMaterial) RenderBucket() RenderBucket

RenderBucket returns the appropriate bucket based on AlphaMode:

  • AlphaModeOpaque, AlphaModeMask -> RenderBucketOpaque
  • AlphaModeBlend -> RenderBucketTransparent

func (*StandardMaterial) Roughness

func (m *StandardMaterial) Roughness() float32

Roughness returns the roughness factor.

func (*StandardMaterial) ShaderID

func (m *StandardMaterial) ShaderID() string

ShaderID returns "standard", identifying the PBR shader pipeline.

func (*StandardMaterial) UniformData

func (m *StandardMaterial) UniformData() []byte

UniformData returns 32 bytes encoding the material properties for GPU upload. The alpha component of color is pre-multiplied with opacity.

Layout (matches WGSL MaterialUniforms struct):

offset  0: R             float32
offset  4: G             float32
offset  8: B             float32
offset 12: A             float32 (color.A * opacity)
offset 16: metallic      float32
offset 20: roughness     float32
offset 24: alpha_cutoff  float32
offset 28: _padding      float32 (zero)

func (*StandardMaterial) Wireframe

func (m *StandardMaterial) Wireframe() bool

Wireframe reports whether the material renders as wireframe.

type Vec2

type Vec2 struct {
	X, Y float32
}

Vec2 represents a 2D vector. Used for UV texture coordinates.

func (Vec2) Add

func (v Vec2) Add(other Vec2) Vec2

Add returns the component-wise sum of v and other.

func (Vec2) Dot

func (v Vec2) Dot(other Vec2) float32

Dot returns the dot product of v and other.

func (Vec2) Length

func (v Vec2) Length() float32

Length returns the Euclidean length of v.

func (Vec2) LengthSq

func (v Vec2) LengthSq() float32

LengthSq returns the squared length of v. Avoids a sqrt call.

func (Vec2) Lerp

func (v Vec2) Lerp(other Vec2, t float32) Vec2

Lerp linearly interpolates between v and other by t in [0,1].

func (Vec2) Normalize

func (v Vec2) Normalize() Vec2

Normalize returns a unit-length vector in the same direction as v. Returns the zero vector if v has zero length.

func (Vec2) Scale

func (v Vec2) Scale(s float32) Vec2

Scale returns v with each component multiplied by s.

func (Vec2) Sub

func (v Vec2) Sub(other Vec2) Vec2

Sub returns the component-wise difference of v and other.

type Vec3

type Vec3 struct {
	X, Y, Z float32
}

Vec3 represents a 3D vector. Used for positions, directions, and scale.

func (Vec3) Add

func (v Vec3) Add(other Vec3) Vec3

Add returns the component-wise sum of v and other.

func (Vec3) Cross

func (v Vec3) Cross(other Vec3) Vec3

Cross returns the cross product of v and other.

func (Vec3) Distance

func (v Vec3) Distance(other Vec3) float32

Distance returns the Euclidean distance between v and other.

func (Vec3) DistanceSq

func (v Vec3) DistanceSq(other Vec3) float32

DistanceSq returns the squared Euclidean distance between v and other.

func (Vec3) Dot

func (v Vec3) Dot(other Vec3) float32

Dot returns the dot product of v and other.

func (Vec3) Length

func (v Vec3) Length() float32

Length returns the Euclidean length of v.

func (Vec3) LengthSq

func (v Vec3) LengthSq() float32

LengthSq returns the squared length of v. Avoids a sqrt call.

func (Vec3) Lerp

func (v Vec3) Lerp(other Vec3, t float32) Vec3

Lerp linearly interpolates between v and other by t in [0,1].

func (Vec3) Max

func (v Vec3) Max(other Vec3) Vec3

Max returns a vector with the maximum of each component.

func (Vec3) Min

func (v Vec3) Min(other Vec3) Vec3

Min returns a vector with the minimum of each component.

func (Vec3) Mul

func (v Vec3) Mul(other Vec3) Vec3

Mul returns the component-wise product of v and other (Hadamard product).

func (Vec3) Negate

func (v Vec3) Negate() Vec3

Negate returns -v.

func (Vec3) Normalize

func (v Vec3) Normalize() Vec3

Normalize returns a unit-length vector in the same direction as v. Returns the zero vector if v has zero length.

func (Vec3) Scale

func (v Vec3) Scale(s float32) Vec3

Scale returns v with each component multiplied by s.

func (Vec3) Sub

func (v Vec3) Sub(other Vec3) Vec3

Sub returns the component-wise difference of v and other.

type Vec4

type Vec4 struct {
	X, Y, Z, W float32
}

Vec4 represents a 4D vector. Used for homogeneous coordinates and shader data.

func (Vec4) Add

func (v Vec4) Add(other Vec4) Vec4

Add returns the component-wise sum of v and other.

func (Vec4) Dot

func (v Vec4) Dot(other Vec4) float32

Dot returns the dot product of v and other.

func (Vec4) Length

func (v Vec4) Length() float32

Length returns the Euclidean length of v.

func (Vec4) LengthSq

func (v Vec4) LengthSq() float32

LengthSq returns the squared length of v. Avoids a sqrt call.

func (Vec4) Lerp

func (v Vec4) Lerp(other Vec4, t float32) Vec4

Lerp linearly interpolates between v and other by t in [0,1].

func (Vec4) Normalize

func (v Vec4) Normalize() Vec4

Normalize returns a unit-length vector in the same direction as v. Returns the zero vector if v has zero length.

func (Vec4) Scale

func (v Vec4) Scale(s float32) Vec4

Scale returns v with each component multiplied by s.

func (Vec4) Sub

func (v Vec4) Sub(other Vec4) Vec4

Sub returns the component-wise difference of v and other.

func (Vec4) XYZ

func (v Vec4) XYZ() Vec3

XYZ returns the first three components as a Vec3.

type VertexAttribute

type VertexAttribute struct {
	// Name identifies the attribute (e.g. "position", "normal", "uv").
	Name string

	// Offset is the byte offset of this attribute within a vertex.
	Offset uint64

	// FloatCount is the number of float32 components (e.g. 3 for vec3, 2 for vec2).
	FloatCount int
}

VertexAttribute describes a single attribute within a vertex.

type VertexLayout

type VertexLayout struct {
	// Stride is the number of bytes between consecutive vertices.
	Stride uint64

	// Attributes describes each vertex attribute within the stride.
	Attributes []VertexAttribute
}

VertexLayout describes the memory layout of interleaved vertex data. This is used when creating GPU vertex buffer layouts for the render pipeline.

func StandardVertexLayout

func StandardVertexLayout() VertexLayout

StandardVertexLayout returns the vertex layout used by all built-in geometries.

Layout: position(vec3, offset 0) + normal(vec3, offset 12) + uv(vec2, offset 24). Stride: 32 bytes.

Directories

Path Synopsis
examples
hello-cube module
internal
geom
Package geom implements geometry generation algorithms for g3d.
Package geom implements geometry generation algorithms for g3d.
gpu
Package gpu manages wgpu pipeline state for the g3d forward renderer.
Package gpu manages wgpu pipeline state for the g3d forward renderer.
render
Package render manages the draw call list and bucket-based sorting for the g3d forward renderer.
Package render manages the draw call list and bucket-based sorting for the g3d forward renderer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL