Project-owned shaders
When the authored material types aren’t enough, import Three.js and create a
THREE.ShaderMaterial directly in project source. Shader code, uniforms, update rules, and
cleanup are game-specific behavior; keeping them together makes their owner and lifecycle
explicit.
Use the Three.js API directly
Section titled “Use the Three.js API directly”The setup function can create the mesh and register its update in a normal engine phase:
import type { GameCleanup, GameContext } from '@engine/runtime/types';import * as THREE from 'three';
export async function setup(ctx: GameContext): Promise<GameCleanup> { const uniforms = { uTime: { value: 0 } }; const material = new THREE.ShaderMaterial({ uniforms, vertexShader: ` uniform float uTime; void main() { vec3 p = position; p.z += sin(p.x * 0.8 + uTime) * 0.25; gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0); } `, fragmentShader: ` void main() { gl_FragColor = vec4(0.04, 0.37, 0.65, 1.0); } `, }); const geometry = new THREE.PlaneGeometry(12, 12, 48, 48); const water = new THREE.Mesh(geometry, material); water.rotation.x = -Math.PI / 2; ctx.scene.add(water);
const animate = (dt: number) => { uniforms.uTime.value += dt; }; ctx.systems.add('gameLogic', animate);
return { dispose() { ctx.systems.remove('gameLogic', animate); ctx.scene.remove(water); geometry.dispose(); material.dispose(); }, };}- The material is a normal Three.js object—there is no vgai shader wrapper or registry.
- Put animation in a
useFramecallback in the component that owns the mesh. - Dispose GPU resources in the component’s cleanup (the effect teardown or unmount).
For an authored mesh, the owning React component is the ownership boundary: create or
replace the material where the mesh is declared, update uniforms in useFrame, and
restore/dispose in the unmount cleanup.
The WaterSurface example
Section titled “The WaterSurface example”Feature-scenes deliberately demonstrates the same ownership rule without a shader-specific
engine abstraction. Its scene uses an ordinary standard material, while the project-owned
WaterSurface component animates native BufferGeometry in Play mode. A shader component
would own a ShaderMaterial through the same lifecycle.
See also
Section titled “See also”- Materials — the built-in material types.
- What’s New / Roadmap — the inline-shader gap and other rendering plans.
- Entities & behavior — where world code lives.