299 lines
6.7 KiB
Markdown
299 lines
6.7 KiB
Markdown
# Node Materials (TSL - Three Shading Language)
|
|
|
|
Modern node-based material system for creating custom shaders visually.
|
|
|
|
## What is TSL?
|
|
|
|
Three Shading Language (TSL) is a node-based system for creating materials and shaders:
|
|
- Functional approach to shader composition
|
|
- Type-safe node graph
|
|
- Unified GLSL/WGSL output (WebGL & WebGPU)
|
|
- No manual shader code required
|
|
|
|
## Basic Node Material
|
|
|
|
```javascript
|
|
import * as THREE from 'three/webgpu';
|
|
import { color, texture, normalMap, MeshStandardNodeMaterial } from 'three/nodes';
|
|
|
|
const material = new MeshStandardNodeMaterial();
|
|
|
|
// Set base color node
|
|
material.colorNode = color(0xff0000);
|
|
|
|
// Or use texture
|
|
material.colorNode = texture(colorTexture);
|
|
|
|
// Combine nodes
|
|
material.colorNode = texture(colorTexture).mul(color(0xffffff));
|
|
|
|
// Normal mapping
|
|
material.normalNode = normalMap(normalTexture);
|
|
```
|
|
|
|
## Node Types
|
|
|
|
### Input Nodes
|
|
|
|
```javascript
|
|
import {
|
|
attribute,
|
|
uniform,
|
|
texture,
|
|
cubeTexture,
|
|
instancedArray,
|
|
storage
|
|
} from 'three/nodes';
|
|
|
|
// Geometry attributes
|
|
const positionNode = attribute('position');
|
|
const normalNode = attribute('normal');
|
|
const uvNode = attribute('uv');
|
|
|
|
// Uniforms
|
|
const timeNode = uniform(0); // value
|
|
|
|
// Textures
|
|
const colorNode = texture(diffuseTexture);
|
|
const envNode = cubeTexture(cubeMapTexture);
|
|
|
|
// Instanced data
|
|
const instanceColorNode = instancedArray('instanceColor');
|
|
|
|
// Storage buffers (compute)
|
|
const storageNode = storage(buffer, 'vec4', count);
|
|
```
|
|
|
|
### Math Nodes
|
|
|
|
```javascript
|
|
import { add, sub, mul, div, pow, sin, cos, length, normalize } from 'three/nodes';
|
|
|
|
// Basic operations
|
|
const result = add(a, b); // a + b
|
|
const result = sub(a, b); // a - b
|
|
const result = mul(a, b); // a * b
|
|
const result = div(a, b); // a / b
|
|
|
|
// Trigonometry
|
|
const result = sin(angle);
|
|
const result = cos(angle);
|
|
|
|
// Vector operations
|
|
const result = length(vector);
|
|
const result = normalize(vector);
|
|
|
|
// Chaining
|
|
const result = mul(texture(tex), color(0xff0000));
|
|
```
|
|
|
|
### Procedural Nodes
|
|
|
|
```javascript
|
|
import { checker, dots, noise, voronoi } from 'three/nodes';
|
|
|
|
// Checker pattern
|
|
material.colorNode = checker(uvNode.mul(10));
|
|
|
|
// Noise
|
|
material.colorNode = noise(uvNode.mul(5));
|
|
|
|
// Voronoi cells
|
|
material.colorNode = voronoi(uvNode.mul(3));
|
|
```
|
|
|
|
## Custom Shader Function
|
|
|
|
```javascript
|
|
import { Fn, vec3, float } from 'three/nodes';
|
|
|
|
// Define custom function
|
|
const customColor = Fn(([uv, time]) => {
|
|
const r = sin(uv.x.mul(10).add(time));
|
|
const g = cos(uv.y.mul(10).add(time));
|
|
const b = float(0.5);
|
|
return vec3(r, g, b);
|
|
});
|
|
|
|
// Use in material
|
|
material.colorNode = customColor(uvNode, timeNode);
|
|
```
|
|
|
|
## Animation with Nodes
|
|
|
|
```javascript
|
|
import { uniform, oscSine, timerLocal } from 'three/nodes';
|
|
|
|
// Oscillating value
|
|
const oscillator = oscSine(timerLocal(0.5)); // frequency = 0.5
|
|
|
|
// Pulsing color
|
|
material.colorNode = color(0xff0000).mul(oscillator.add(0.5));
|
|
|
|
// Rotating UV
|
|
const rotatedUV = uvNode.rotateUV(timerLocal());
|
|
material.colorNode = texture(tex, rotatedUV);
|
|
```
|
|
|
|
## Advanced Effects
|
|
|
|
### Fresnel Effect
|
|
|
|
```javascript
|
|
import { normalView, positionView, dot, pow } from 'three/nodes';
|
|
|
|
const fresnel = pow(
|
|
float(1).sub(dot(normalView, positionView.normalize())),
|
|
3
|
|
);
|
|
|
|
material.colorNode = mix(baseColor, edgeColor, fresnel);
|
|
```
|
|
|
|
### Vertex Displacement
|
|
|
|
```javascript
|
|
import { positionLocal, normalLocal, timerLocal, sin } from 'three/nodes';
|
|
|
|
// Displace vertices along normal
|
|
const displacement = sin(positionLocal.y.add(timerLocal())).mul(0.5);
|
|
material.positionNode = positionLocal.add(normalLocal.mul(displacement));
|
|
```
|
|
|
|
### Custom Normal Mapping
|
|
|
|
```javascript
|
|
import { normalMap, normalView, TBNViewMatrix } from 'three/nodes';
|
|
|
|
const normalMapNode = normalMap(normalTexture);
|
|
const transformedNormal = TBNViewMatrix.mul(normalMapNode);
|
|
material.normalNode = transformedNormal;
|
|
```
|
|
|
|
## Compute Shaders (WebGPU)
|
|
|
|
```javascript
|
|
import { compute, uniform, storage, Fn } from 'three/nodes';
|
|
|
|
// Define compute shader
|
|
const computeShader = Fn(() => {
|
|
const storageBuffer = storage(buffer, 'vec4', count);
|
|
const index = instanceIndex; // built-in
|
|
|
|
// Modify buffer
|
|
const value = storageBuffer.element(index);
|
|
storageBuffer.element(index).assign(value.mul(2));
|
|
})();
|
|
|
|
// Create compute node
|
|
const computeNode = compute(computeShader, 256); // workgroup size
|
|
|
|
// Execute
|
|
renderer.compute(computeNode);
|
|
```
|
|
|
|
## Node Material Types
|
|
|
|
```javascript
|
|
import {
|
|
MeshStandardNodeMaterial,
|
|
MeshPhysicalNodeMaterial,
|
|
MeshBasicNodeMaterial,
|
|
PointsNodeMaterial,
|
|
LineBasicNodeMaterial,
|
|
SpriteNodeMaterial
|
|
} from 'three/nodes';
|
|
|
|
// Standard PBR
|
|
const material = new MeshStandardNodeMaterial();
|
|
material.colorNode = colorNode;
|
|
material.roughnessNode = roughnessNode;
|
|
material.metalnessNode = metalnessNode;
|
|
|
|
// Physical (clearcoat, transmission, etc.)
|
|
const material = new MeshPhysicalNodeMaterial();
|
|
material.clearcoatNode = clearcoatNode;
|
|
material.transmissionNode = transmissionNode;
|
|
```
|
|
|
|
## Post-Processing with Nodes
|
|
|
|
```javascript
|
|
import { pass, PassNode } from 'three/nodes';
|
|
|
|
// Custom post-processing pass
|
|
const customPass = new PassNode('customPass', (input, output) => {
|
|
// input: previous pass texture
|
|
// output: render target
|
|
|
|
// Apply effect
|
|
const modifiedColor = input.mul(color(1, 0.5, 0.5));
|
|
output.assign(modifiedColor);
|
|
});
|
|
|
|
// Add to post-processing chain
|
|
postProcessing.addPass(customPass);
|
|
```
|
|
|
|
## Practical Example: Animated Material
|
|
|
|
```javascript
|
|
import * as THREE from 'three/webgpu';
|
|
import {
|
|
MeshStandardNodeMaterial,
|
|
texture,
|
|
uniform,
|
|
timerLocal,
|
|
sin,
|
|
cos,
|
|
vec2
|
|
} from 'three/nodes';
|
|
|
|
const material = new MeshStandardNodeMaterial();
|
|
|
|
// Animated UV scroll
|
|
const time = timerLocal();
|
|
const scrollSpeed = uniform(0.1);
|
|
const uvOffset = vec2(
|
|
time.mul(scrollSpeed),
|
|
sin(time).mul(0.1)
|
|
);
|
|
const scrolledUV = uv().add(uvOffset);
|
|
|
|
// Apply to color
|
|
material.colorNode = texture(diffuseTexture, scrolledUV);
|
|
|
|
// Animated emission
|
|
const pulseSpeed = uniform(2);
|
|
const emission = sin(time.mul(pulseSpeed)).mul(0.5).add(0.5);
|
|
material.emissiveNode = color(1, 0.5, 0).mul(emission);
|
|
```
|
|
|
|
## Migration from ShaderMaterial
|
|
|
|
```javascript
|
|
// Old way (ShaderMaterial)
|
|
const material = new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
time: { value: 0 }
|
|
},
|
|
vertexShader: `...`,
|
|
fragmentShader: `...`
|
|
});
|
|
|
|
// New way (Node Material)
|
|
const material = new MeshStandardNodeMaterial();
|
|
material.colorNode = customFunction(timerLocal());
|
|
// Much cleaner, type-safe, and reusable
|
|
```
|
|
|
|
## When to Use Node Materials
|
|
|
|
- Creating complex procedural materials
|
|
- Need both WebGL and WebGPU support
|
|
- Want visual/functional shader composition
|
|
- Reusable shader components
|
|
- Compute shader integration (WebGPU only)
|
|
|
|
**Note**: Node materials require WebGPU renderer for full features. Some features work with WebGL backend but compute shaders require WebGPU.
|