Files
2026-03-28 13:57:54 +08:00

9.3 KiB

TSL Core Concepts

Types and Constructors

Scalar Types

import { float, int, uint, bool } from 'three/tsl';

const f = float(1.0);
const i = int(42);
const u = uint(100);
const b = bool(true);

Vector Types

import { vec2, vec3, vec4, color } from 'three/tsl';

const v2 = vec2(1.0, 2.0);
const v3 = vec3(1.0, 2.0, 3.0);
const v4 = vec4(1.0, 2.0, 3.0, 1.0);

// Color (RGB, accepts hex or components)
const c = color(0xff0000);     // Red
const c2 = color(1, 0.5, 0);   // Orange

Matrix Types

import { mat2, mat3, mat4 } from 'three/tsl';

const m3 = mat3();
const m4 = mat4();

Type Conversion

const v = vec3(1, 2, 3);
const v4 = v.toVec4(1.0);      // vec4(1, 2, 3, 1)
const f = int(42).toFloat();   // 42.0
const c = v.toColor();         // Convert to color

Vector Swizzling

Access and reorder vector components using standard notation:

const v = vec3(1.0, 2.0, 3.0);

// Single component access
v.x  // 1.0
v.y  // 2.0
v.z  // 3.0

// Multiple components
v.xy   // vec2(1.0, 2.0)
v.xyz  // vec3(1.0, 2.0, 3.0)

// Reorder components
v.zyx  // vec3(3.0, 2.0, 1.0)
v.xxy  // vec3(1.0, 1.0, 2.0)
v.rrr  // vec3(1.0, 1.0, 1.0) - same as xxx

// Alternative accessors (all equivalent)
v.xyz  // position
v.rgb  // color
v.stp  // texture coordinates

Uniforms

Uniforms pass values from JavaScript to shaders:

import { uniform } from 'three/tsl';
import * as THREE from 'three/webgpu';

// Create uniforms
const myColor = uniform(new THREE.Color(0x0066ff));
const myFloat = uniform(0.5);
const myVec3 = uniform(new THREE.Vector3(1, 2, 3));

// Update at runtime
myColor.value.set(0xff0000);
myFloat.value = 0.8;
myVec3.value.set(4, 5, 6);

// Use in material
material.colorNode = myColor;

Auto-Updating Uniforms

// Update every frame
const animatedValue = uniform(0).onFrameUpdate((frame) => {
  return Math.sin(frame.time);
});

// Update per object render
const perObjectValue = uniform(0).onObjectUpdate((object) => {
  return object.userData.customValue;
});

// Update once per render cycle
const renderValue = uniform(0).onRenderUpdate((state) => {
  return state.delta;
});

Operators

Arithmetic

// Method chaining (preferred)
const result = a.add(b).mul(c).sub(d).div(e);

// Individual operations
a.add(b)   // a + b
a.sub(b)   // a - b
a.mul(b)   // a * b
a.div(b)   // a / b
a.mod(b)   // a % b
a.negate() // -a

Comparison

a.equal(b)            // a == b
a.notEqual(b)         // a != b
a.lessThan(b)         // a < b
a.greaterThan(b)      // a > b
a.lessThanEqual(b)    // a <= b
a.greaterThanEqual(b) // a >= b

Logical

a.and(b)   // a && b
a.or(b)    // a || b
a.not()    // !a
a.xor(b)   // a ^ b

Bitwise

a.bitAnd(b)      // a & b
a.bitOr(b)       // a | b
a.bitXor(b)      // a ^ b
a.bitNot()       // ~a
a.shiftLeft(n)   // a << n
a.shiftRight(n)  // a >> n

Assignment (for variables)

const v = vec3(0).toVar();  // Create mutable variable

v.assign(vec3(1, 2, 3));    // v = vec3(1, 2, 3)
v.addAssign(vec3(1));       // v += vec3(1)
v.subAssign(vec3(1));       // v -= vec3(1)
v.mulAssign(2.0);           // v *= 2.0
v.divAssign(2.0);           // v /= 2.0

Variables

Mutable Variables

// Create mutable variable with toVar()
const myVar = vec3(1, 0, 0).toVar();
myVar.assign(vec3(0, 1, 0));
myVar.addAssign(vec3(0, 0, 1));

// Name the variable (useful for debugging)
const named = vec3(0).toVar('myPosition');

Constants

// Create compile-time constant
const PI_HALF = float(Math.PI / 2).toConst();

Properties (named values for shader stages)

import { property } from 'three/tsl';

// Create named property
const myProp = property('vec3', 'customColor');
myProp.assign(vec3(1, 0, 0));

Control Flow

Conditionals

import { If, select } from 'three/tsl';

// If-ElseIf-Else
const result = vec3(0).toVar();

If(value.greaterThan(0.5), () => {
  result.assign(vec3(1, 0, 0));  // Red
}).ElseIf(value.greaterThan(0.25), () => {
  result.assign(vec3(0, 1, 0));  // Green
}).Else(() => {
  result.assign(vec3(0, 0, 1));  // Blue
});

// Ternary operator (select)
const color = select(
  condition,           // if true
  vec3(1, 0, 0),      // return this
  vec3(0, 0, 1)       // else return this
);

Switch-Case

import { Switch } from 'three/tsl';

const col = vec3(0).toVar();

Switch(intValue)
  .Case(0, () => { col.assign(color(1, 0, 0)); })
  .Case(1, () => { col.assign(color(0, 1, 0)); })
  .Case(2, () => { col.assign(color(0, 0, 1)); })
  .Default(() => { col.assign(color(1, 1, 1)); });

Loops

import { Loop, Break, Continue } from 'three/tsl';

// Simple loop (0 to 9)
const sum = float(0).toVar();
Loop(10, ({ i }) => {
  sum.addAssign(float(i));
});

// Ranged loop with options
Loop({ start: int(0), end: int(count), type: 'int' }, ({ i }) => {
  // Loop body
});

// Nested loops
Loop(width, height, ({ i, j }) => {
  // i = outer loop index
  // j = inner loop index
});

// Loop control
Loop(100, ({ i }) => {
  If(shouldStop, () => {
    Break();  // Exit loop
  });
  If(shouldSkip, () => {
    Continue();  // Skip to next iteration
  });
});

Flow Control

import { Discard, Return } from 'three/tsl';

// Discard fragment (make transparent)
If(alpha.lessThan(0.5), () => {
  Discard();
});

// Return from function
const myFn = Fn(() => {
  If(condition, () => {
    Return(vec3(1, 0, 0));
  });
  return vec3(0, 0, 1);
});

Custom Functions with Fn()

Basic Function

import { Fn } from 'three/tsl';

const addVectors = Fn(([a, b]) => {
  return a.add(b);
});

// Usage
const result = addVectors(vec3(1, 0, 0), vec3(0, 1, 0));

Default Parameters

const oscillate = Fn(([frequency = 1.0, amplitude = 1.0]) => {
  return time.mul(frequency).sin().mul(amplitude);
});

// Call variations
oscillate();           // Uses defaults
oscillate(2.0);        // frequency = 2.0
oscillate(2.0, 0.5);   // frequency = 2.0, amplitude = 0.5

Named Parameters (Object Style)

const createGradient = Fn(({ colorA = vec3(0), colorB = vec3(1), t = 0.5 }) => {
  return mix(colorA, colorB, t);
});

// Call with named parameters
createGradient({ colorA: vec3(1, 0, 0), t: uv().x });

Function with Context

// Access shader context
const customShader = Fn(({ material, geometry, object }) => {
  if (material.userData.customColor) {
    return uniform(material.userData.customColor);
  }
  return vec3(1);
});

Time and Animation

import { time, deltaTime } from 'three/tsl';

// time - seconds since start
const rotation = time.mul(0.5);  // Half rotation per second

// deltaTime - time since last frame
const velocity = speed.mul(deltaTime);

Oscillators

import { oscSine, oscSquare, oscTriangle, oscSawtooth } from 'three/tsl';

// All oscillators return 0-1 range
oscSine(time)      // Smooth sine wave
oscSquare(time)    // Square wave (0 or 1)
oscTriangle(time)  // Triangle wave
oscSawtooth(time)  // Sawtooth wave

// Custom frequency
oscSine(time.mul(2.0))  // 2Hz oscillation

Math Functions

Basic Math

import { abs, sign, floor, ceil, fract, mod, min, max, clamp } from 'three/tsl';

abs(x)           // Absolute value
sign(x)          // -1, 0, or 1
floor(x)         // Round down
ceil(x)          // Round up
fract(x)         // Fractional part (x - floor(x))
mod(x, y)        // Modulo
min(x, y)        // Minimum
max(x, y)        // Maximum
clamp(x, 0, 1)   // Clamp to range

Trigonometry

import { sin, cos, tan, asin, acos, atan, atan2 } from 'three/tsl';

sin(x)
cos(x)
tan(x)
asin(x)
acos(x)
atan(x)
atan2(y, x)

Exponential

import { pow, exp, log, sqrt, inverseSqrt } from 'three/tsl';

pow(x, 2.0)      // x^2
exp(x)           // e^x
log(x)           // Natural log
sqrt(x)          // Square root
inverseSqrt(x)   // 1 / sqrt(x)

Interpolation

import { mix, step, smoothstep } from 'three/tsl';

mix(a, b, 0.5)              // Linear interpolation
step(0.5, x)                // 0 if x < 0.5, else 1
smoothstep(0.0, 1.0, x)     // Smooth 0-1 transition

Vector Math

import { length, distance, dot, cross, normalize, reflect, refract } from 'three/tsl';

length(v)              // Vector length
distance(a, b)         // Distance between points
dot(a, b)              // Dot product
cross(a, b)            // Cross product (vec3 only)
normalize(v)           // Unit vector
reflect(incident, normal)
refract(incident, normal, eta)

Constants

import { PI, TWO_PI, HALF_PI, EPSILON } from 'three/tsl';

PI        // 3.14159...
TWO_PI    // 6.28318...
HALF_PI   // 1.57079...
EPSILON   // Very small number

Utility Functions

import { hash, checker, remap, range, rotate } from 'three/tsl';

// Pseudo-random hash
hash(seed)                    // Returns 0-1

// Checkerboard pattern
checker(uv())                 // Returns 0 or 1

// Remap value from one range to another
remap(x, 0, 1, -1, 1)        // Map 0-1 to -1 to 1

// Generate value in range
range(min, max)               // Random in range (per instance)

// Rotate 2D vector
rotate(vec2(1, 0), angle)