9.3 KiB
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)