Files
three-offshore-vibe/.codex/skills/webgpu-threejs-tsl/REFERENCE.md
2026-03-28 13:57:54 +08:00

7.8 KiB

TSL Quick Reference

Imports

// WebGPU Three.js
import * as THREE from 'three/webgpu';

// Core TSL
import {
  float, int, uint, bool,
  vec2, vec3, vec4, color,
  mat2, mat3, mat4,
  uniform, texture, uv,
  Fn, If, Loop, Break, Continue,
  time, deltaTime
} from 'three/tsl';

Types

TSL WGSL Example
float(1.0) f32 Scalar float
int(1) i32 Signed integer
uint(1) u32 Unsigned integer
bool(true) bool Boolean
vec2(x, y) vec2<f32> 2D vector
vec3(x, y, z) vec3<f32> 3D vector
vec4(x, y, z, w) vec4<f32> 4D vector
color(0xff0000) vec3<f32> RGB color
uniform(value) uniform Dynamic value

Operators

Operation TSL GLSL Equivalent
Add a.add(b) a + b
Subtract a.sub(b) a - b
Multiply a.mul(b) a * b
Divide a.div(b) a / b
Modulo a.mod(b) mod(a, b)
Negate a.negate() -a
Less Than a.lessThan(b) a < b
Greater Than a.greaterThan(b) a > b
Equal a.equal(b) a == b
And a.and(b) a && b
Or a.or(b) a || b
Assign a.assign(b) a = b
Add Assign a.addAssign(b) a += b

Swizzling

const v = vec3(1, 2, 3);
v.x        // 1
v.xy       // vec2(1, 2)
v.zyx      // vec3(3, 2, 1)
v.rgb      // same as xyz

Math Functions

Function Description
abs(x) Absolute value
sign(x) Sign (-1, 0, 1)
floor(x) Round down
ceil(x) Round up
fract(x) Fractional part
min(a, b) Minimum
max(a, b) Maximum
clamp(x, lo, hi) Clamp to range
mix(a, b, t) Linear interpolation
step(edge, x) Step function
smoothstep(a, b, x) Smooth step
sin(x), cos(x) Trigonometry
pow(x, y) Power
sqrt(x) Square root
length(v) Vector length
distance(a, b) Distance
dot(a, b) Dot product
cross(a, b) Cross product
normalize(v) Unit vector
reflect(i, n) Reflection

Geometry Nodes

Node Description
positionLocal Model space position
positionWorld World space position
positionView Camera space position
normalLocal Model space normal
normalWorld World space normal
normalView Camera space normal
uv() UV coordinates
uv(1) Secondary UVs
tangentLocal Tangent vector
vertexColor() Vertex colors

Camera Nodes

Node Description
cameraPosition Camera world position
cameraNear Near plane
cameraFar Far plane
cameraViewMatrix View matrix
cameraProjectionMatrix Projection matrix
screenUV Screen UV (0-1)
screenSize Screen dimensions

Time

Node Description
time Seconds since start
deltaTime Frame delta
oscSine(t) Sine wave (0-1)
oscSquare(t) Square wave
oscTriangle(t) Triangle wave
oscSawtooth(t) Sawtooth wave

Material Properties

const mat = new THREE.MeshStandardNodeMaterial();

// Basic
mat.colorNode = color(0xff0000);
mat.opacityNode = float(0.8);
mat.alphaTestNode = float(0.5);

// PBR
mat.roughnessNode = float(0.5);
mat.metalnessNode = float(0.0);
mat.emissiveNode = color(0x000000);
mat.normalNode = normalMap(tex);

// Physical (MeshPhysicalNodeMaterial)
mat.clearcoatNode = float(1.0);
mat.transmissionNode = float(0.9);
mat.iridescenceNode = float(1.0);
mat.sheenNode = float(1.0);

// Vertex
mat.positionNode = displaced;

Control Flow

// If-Else
If(condition, () => {
  // true
}).ElseIf(other, () => {
  // other true
}).Else(() => {
  // false
});

// Select (ternary)
const result = select(condition, trueVal, falseVal);

// Loop
Loop(10, ({ i }) => {
  // i = 0 to 9
});

// Loop control
Break();
Continue();
Discard();  // Fragment only

Custom Functions

// Basic function
const myFn = Fn(([a, b]) => {
  return a.add(b);
});

// With defaults
const myFn = Fn(([a = 1.0, b = 2.0]) => {
  return a.add(b);
});

// Usage
myFn(x, y);
myFn();  // uses defaults

Compute Shaders

// Storage buffers
const positions = instancedArray(count, 'vec3');
const values = instancedArray(count, 'float');

// Compute shader
const compute = Fn(() => {
  const pos = positions.element(instanceIndex);
  pos.addAssign(vec3(0.01, 0, 0));
})().compute(count);

// Execute
await renderer.computeAsync(compute);  // Once
renderer.compute(compute);              // Each frame

Post-Processing

import { pass } from 'three/tsl';
import { bloom } from 'three/addons/tsl/display/BloomNode.js';

// Setup
const postProcessing = new THREE.PostProcessing(renderer);
const scenePass = pass(scene, camera);
const color = scenePass.getTextureNode('output');

// Apply effects
const bloomPass = bloom(color);
postProcessing.outputNode = color.add(bloomPass);

// Render
postProcessing.render();

Common Patterns

Fresnel

const viewDir = cameraPosition.sub(positionWorld).normalize();
const fresnel = float(1).sub(normalWorld.dot(viewDir).saturate()).pow(3);

Animated UV

const animUV = uv().add(vec2(time.mul(0.1), 0));

Noise Hash

const noise = fract(position.dot(vec3(12.9898, 78.233, 45.543)).sin().mul(43758.5453));

Dissolve

const noise = hash(positionLocal.mul(50));
If(noise.lessThan(threshold), () => Discard());

Color Gradient

const gradient = mix(colorA, colorB, positionLocal.y.mul(0.5).add(0.5));

Node Materials

Material Use Case
MeshBasicNodeMaterial Unlit
MeshStandardNodeMaterial PBR
MeshPhysicalNodeMaterial Advanced PBR
MeshPhongNodeMaterial Phong shading
MeshToonNodeMaterial Cel shading
PointsNodeMaterial Point clouds
LineBasicNodeMaterial Lines
SpriteNodeMaterial Sprites

Device Loss Handling

// Listen for device loss
renderer.backend.device.lost.then((info) => {
  if (info.reason === 'unknown') {
    // Unexpected loss - recover
    renderer.dispose();
    initWebGPU();  // Reinitialize
  }
});

// Simulate loss for testing
renderer.backend.device.destroy();
Loss Reason Meaning
'destroyed' Intentional via destroy()
'unknown' Unexpected (driver crash, timeout, etc.)

Recovery tips:

  • Always get fresh adapter before new device
  • Save/restore application state (not transient data)
  • Use Chrome about:gpucrash to test real GPU crashes

Compute Shader Built-ins

Node Description
instanceIndex Current instance/invocation index
vertexIndex Current vertex index
drawIndex Current draw call index
globalId Global invocation position (uvec3)
localId Local workgroup position (uvec3)
workgroupId Workgroup index (uvec3)
numWorkgroups Number of workgroups dispatched (uvec3)
subgroupSize Size of the subgroup

Version Notes

r178+:

  • PI2 is deprecated → use TWO_PI
  • transformedNormalView → use normalView
  • transformedNormalWorld → use normalWorld

r171+:

  • Recommended minimum version for stable TSL
  • Requires separate three/webgpu import map entry

Resources