6.4 KiB
6.4 KiB
WGSL Integration
TSL allows embedding raw WGSL (WebGPU Shading Language) code when you need direct GPU control.
wgslFn - Custom WGSL Functions
Basic Usage
import { wgslFn, float, vec3 } from 'three/tsl';
// Define WGSL function
const gammaCorrect = wgslFn(`
fn gammaCorrect(color: vec3<f32>, gamma: f32) -> vec3<f32> {
return pow(color, vec3<f32>(1.0 / gamma));
}
`);
// Use in TSL
material.colorNode = gammaCorrect(inputColor, float(2.2));
Function with Multiple Parameters
const blendColors = wgslFn(`
fn blendColors(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {
return mix(a, b, t);
}
`);
material.colorNode = blendColors(colorA, colorB, blendFactor);
Advanced Math Functions
const fresnelSchlick = wgslFn(`
fn fresnelSchlick(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {
return F0 + (vec3<f32>(1.0) - F0) * pow(1.0 - cosTheta, 5.0);
}
`);
const GGX = wgslFn(`
fn distributionGGX(N: vec3<f32>, H: vec3<f32>, roughness: f32) -> f32 {
let a = roughness * roughness;
let a2 = a * a;
let NdotH = max(dot(N, H), 0.0);
let NdotH2 = NdotH * NdotH;
let num = a2;
let denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = 3.14159265359 * denom * denom;
return num / denom;
}
`);
Noise Functions
const simplexNoise = wgslFn(`
fn mod289(x: vec3<f32>) -> vec3<f32> {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
fn permute(x: vec3<f32>) -> vec3<f32> {
return mod289(((x * 34.0) + 1.0) * x);
}
fn snoise(v: vec2<f32>) -> f32 {
let C = vec4<f32>(
0.211324865405187,
0.366025403784439,
-0.577350269189626,
0.024390243902439
);
var i = floor(v + dot(v, C.yy));
let x0 = v - i + dot(i, C.xx);
var i1: vec2<f32>;
if (x0.x > x0.y) {
i1 = vec2<f32>(1.0, 0.0);
} else {
i1 = vec2<f32>(0.0, 1.0);
}
var x12 = x0.xyxy + C.xxzz;
x12 = vec4<f32>(x12.xy - i1, x12.zw);
i = mod289(vec3<f32>(i, 0.0)).xy;
let p = permute(permute(i.y + vec3<f32>(0.0, i1.y, 1.0)) + i.x + vec3<f32>(0.0, i1.x, 1.0));
var m = max(vec3<f32>(0.5) - vec3<f32>(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3<f32>(0.0));
m = m * m;
m = m * m;
let x = 2.0 * fract(p * C.www) - 1.0;
let h = abs(x) - 0.5;
let ox = floor(x + 0.5);
let a0 = x - ox;
m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));
let g = vec3<f32>(
a0.x * x0.x + h.x * x0.y,
a0.y * x12.x + h.y * x12.y,
a0.z * x12.z + h.z * x12.w
);
return 130.0 * dot(m, g);
}
`);
// Use noise
const noiseValue = simplexNoise(uv().mul(10.0));
FBM (Fractal Brownian Motion)
const fbm = wgslFn(`
fn fbm(p: vec2<f32>, octaves: i32) -> f32 {
var value = 0.0;
var amplitude = 0.5;
var frequency = 1.0;
var pos = p;
for (var i = 0; i < octaves; i = i + 1) {
value = value + amplitude * snoise(pos * frequency);
amplitude = amplitude * 0.5;
frequency = frequency * 2.0;
}
return value;
}
`);
WGSL Types Reference
Scalar Types
bool // Boolean
i32 // 32-bit signed integer
u32 // 32-bit unsigned integer
f32 // 32-bit float
f16 // 16-bit float (if enabled)
Vector Types
vec2<f32> // 2D float vector
vec3<f32> // 3D float vector
vec4<f32> // 4D float vector
vec2<i32> // 2D integer vector
vec2<u32> // 2D unsigned integer vector
Matrix Types
mat2x2<f32> // 2x2 matrix
mat3x3<f32> // 3x3 matrix
mat4x4<f32> // 4x4 matrix
mat2x3<f32> // 2 columns, 3 rows
Texture Types
texture_2d<f32>
texture_3d<f32>
texture_cube<f32>
texture_storage_2d<rgba8unorm, write>
WGSL Syntax Reference
Variables
let x = 1.0; // Immutable
var y = 2.0; // Mutable
const PI = 3.14159; // Compile-time constant
Control Flow
// If-else
if (condition) {
// ...
} else if (other) {
// ...
} else {
// ...
}
// For loop
for (var i = 0; i < 10; i = i + 1) {
// ...
}
// While loop
while (condition) {
// ...
}
// Switch
switch (value) {
case 0: { /* ... */ }
case 1, 2: { /* ... */ }
default: { /* ... */ }
}
Built-in Functions
// Math
abs(x), sign(x), floor(x), ceil(x), round(x)
fract(x), trunc(x)
min(a, b), max(a, b), clamp(x, lo, hi)
mix(a, b, t), step(edge, x), smoothstep(lo, hi, x)
sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y, x)
pow(x, y), exp(x), log(x), exp2(x), log2(x)
sqrt(x), inverseSqrt(x)
// Vector
length(v), distance(a, b)
dot(a, b), cross(a, b)
normalize(v), faceForward(n, i, nref)
reflect(i, n), refract(i, n, eta)
// Matrix
transpose(m), determinant(m)
// Texture
textureSample(t, s, coord)
textureLoad(t, coord, level)
textureStore(t, coord, value)
textureDimensions(t)
Combining TSL and WGSL
TSL Wrapper for WGSL
import { Fn, wgslFn, float, vec2, vec3 } from 'three/tsl';
// WGSL implementation
const wgslNoise = wgslFn(`
fn noise2d(p: vec2<f32>) -> f32 {
return fract(sin(dot(p, vec2<f32>(12.9898, 78.233))) * 43758.5453);
}
`);
// TSL wrapper with nice API
const noise = Fn(([position, scale = 1.0]) => {
return wgslNoise(position.xy.mul(scale));
});
// Use
material.colorNode = vec3(noise(positionWorld, 10.0));
Hybrid Approach
// Complex math in WGSL
const complexMath = wgslFn(`
fn complexOperation(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {
let blended = mix(a, b, t);
let rotated = vec3<f32>(
blended.x * cos(t) - blended.y * sin(t),
blended.x * sin(t) + blended.y * cos(t),
blended.z
);
return normalize(rotated);
}
`);
// Simple logic in TSL
const finalColor = Fn(() => {
const base = texture(diffuseMap).rgb;
const processed = complexMath(base, vec3(1, 0, 0), time);
return mix(base, processed, oscSine(time));
});
material.colorNode = finalColor();
Performance Tips
Avoid Branching When Possible
// Instead of:
if (x > 0.5) {
result = a;
} else {
result = b;
}
// Use:
result = mix(b, a, step(0.5, x));
Use Local Variables
fn compute(p: vec2<f32>) -> f32 {
// Cache repeated calculations
let p2 = p * p;
let p4 = p2 * p2;
return p2.x + p2.y + p4.x * p4.y;
}
Minimize Texture Samples
// Sample once, use multiple times
let sample = textureSample(tex, sampler, uv);
let r = sample.r;
let g = sample.g;
let b = sample.b;