336 lines
7.8 KiB
Markdown
336 lines
7.8 KiB
Markdown
# TSL Quick Reference
|
|
|
|
## Imports
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
const viewDir = cameraPosition.sub(positionWorld).normalize();
|
|
const fresnel = float(1).sub(normalWorld.dot(viewDir).saturate()).pow(3);
|
|
```
|
|
|
|
### Animated UV
|
|
|
|
```javascript
|
|
const animUV = uv().add(vec2(time.mul(0.1), 0));
|
|
```
|
|
|
|
### Noise Hash
|
|
|
|
```javascript
|
|
const noise = fract(position.dot(vec3(12.9898, 78.233, 45.543)).sin().mul(43758.5453));
|
|
```
|
|
|
|
### Dissolve
|
|
|
|
```javascript
|
|
const noise = hash(positionLocal.mul(50));
|
|
If(noise.lessThan(threshold), () => Discard());
|
|
```
|
|
|
|
### Color Gradient
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
- [TSL Wiki](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language)
|
|
- [TSL Docs](https://threejs.org/docs/pages/TSL.html)
|
|
- [WebGPU Examples](https://github.com/mrdoob/three.js/tree/master/examples)
|
|
- [Three.js Docs](https://threejs.org/docs/)
|
|
- [WebGPU Best Practices - Device Loss](https://toji.dev/webgpu-best-practices/device-loss)
|