Advanced
Advanced Three.js techniques including shaders, post-processing, physics, and specialized effects.
Custom Shaders
Vertex Shaders
// Vertex shader
uniform float time;
uniform float amplitude;
attribute float displacement;
void main() {
vec3 newPosition = position + normal * (displacement * amplitude * sin(time));
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
Fragment Shaders
// Fragment shader
uniform float time;
uniform vec3 color;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
float wave = sin(uv.x * 10.0 + time) * 0.5 + 0.5;
gl_FragColor = vec4(color * wave, 1.0);
}
ShaderMaterial Implementation
const shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 },
amplitude: { value: 1.0 },
color: { value: new THREE.Color(0xff0000) },
resolution: {
value: new THREE.Vector2(window.innerWidth, window.innerHeight),
},
},
vertexShader: `
uniform float time;
uniform float amplitude;
attribute float displacement;
void main() {
vec3 newPosition = position + normal * (displacement * amplitude * sin(time));
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
float wave = sin(uv.x * 10.0 + time) * 0.5 + 0.5;
gl_FragColor = vec4(color * wave, 1.0);
}
`,
});
// Update uniforms
function animate() {
shaderMaterial.uniforms.time.value = Date.now() * 0.001;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
Noise Shaders
// Perlin noise function
float noise(vec3 position) {
return snoise(position * 0.1) * 0.5 + 0.5;
}
// Vertex shader with noise displacement
uniform float time;
void main() {
vec3 pos = position;
float noiseValue = noise(pos + time * 0.1);
pos += normal * noiseValue * 2.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
Post-Processing
EffectComposer Setup
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
const composer = new EffectComposer(renderer);
// Base render pass
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// Bloom effect
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold
);
composer.addPass(bloomPass);
// Film grain
const filmPass = new FilmPass(
0.35, // noise intensity
0.025, // scanline intensity
648, // scanline count
false // grayscale
);
composer.addPass(filmPass);
// Anti-aliasing
const fxaaPass = new ShaderPass(FXAAShader);
fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth;
fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight;
composer.addPass(fxaaPass);
// Render with composer
function animate() {
composer.render();
requestAnimationFrame(animate);
}
Custom Post-Processing Effects
// Custom shader pass
const customShader = {
uniforms: {
tDiffuse: { value: null },
time: { value: 0.0 },
intensity: { value: 1.0 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float time;
uniform float intensity;
varying vec2 vUv;
void main() {
vec4 color = texture2D(tDiffuse, vUv);
float wave = sin(vUv.y * 20.0 + time) * 0.1 * intensity;
color.rgb += wave;
gl_FragColor = color;
}
`,
};
const customPass = new ShaderPass(customShader);
composer.addPass(customPass);
Physics Integration
Cannon.js Integration
import * as CANNON from 'cannon-es';
// Create physics world
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new CANNON.NaiveBroadphase();
// Create ground
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({ mass: 0 });
groundBody.addShape(groundShape);
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.add(groundBody);
// Create boxes
const boxes = [];
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5));
for (let i = 0; i < 10; i++) {
const boxBody = new CANNON.Body({ mass: 1 });
boxBody.addShape(boxShape);
boxBody.position.set(
Math.random() * 10 - 5,
i * 2 + 5,
Math.random() * 10 - 5
);
world.add(boxBody);
// Create corresponding Three.js mesh
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);
boxes.push({ body: boxBody, mesh: boxMesh });
}
// Update physics
function animate() {
world.step(1 / 60);
// Update mesh positions
boxes.forEach(box => {
box.mesh.position.copy(box.body.position);
box.mesh.quaternion.copy(box.body.quaternion);
});
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
Ammo.js Integration
// Initialize Ammo.js
Ammo().then(start);
function start() {
// Create physics world
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const overlappingPairCache = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
dispatcher,
overlappingPairCache,
solver,
collisionConfiguration
);
dynamicsWorld.setGravity(new Ammo.btVector3(0, -9.82, 0));
// Create rigid body
function createRigidBody(mass, shape, position) {
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
const motionState = new Ammo.btDefaultMotionState(transform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(
mass,
motionState,
shape,
localInertia
);
const body = new Ammo.btRigidBody(rbInfo);
dynamicsWorld.addRigidBody(body);
return body;
}
// Animation loop
function animate() {
dynamicsWorld.stepSimulation(1 / 60, 10);
// Update Three.js objects
// ... update positions from physics bodies
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
}
Particle Systems
GPU Particles
class GPUParticleSystem {
constructor(options) {
this.particleCount = options.particleCount || 10000;
this.particleSize = options.particleSize || 1;
this.geometry = new THREE.BufferGeometry();
this.positions = new Float32Array(this.particleCount * 3);
this.velocities = new Float32Array(this.particleCount * 3);
this.colors = new Float32Array(this.particleCount * 3);
this.sizes = new Float32Array(this.particleCount);
this.init();
}
init() {
// Initialize particle data
for (let i = 0; i < this.particleCount; i++) {
const i3 = i * 3;
this.positions[i3] = (Math.random() - 0.5) * 100;
this.positions[i3 + 1] = (Math.random() - 0.5) * 100;
this.positions[i3 + 2] = (Math.random() - 0.5) * 100;
this.velocities[i3] = (Math.random() - 0.5) * 0.1;
this.velocities[i3 + 1] = (Math.random() - 0.5) * 0.1;
this.velocities[i3 + 2] = (Math.random() - 0.5) * 0.1;
this.colors[i3] = Math.random();
this.colors[i3 + 1] = Math.random();
this.colors[i3 + 2] = Math.random();
this.sizes[i] = Math.random() * this.particleSize;
}
this.geometry.setAttribute(
'position',
new THREE.BufferAttribute(this.positions, 3)
);
this.geometry.setAttribute(
'velocity',
new THREE.BufferAttribute(this.velocities, 3)
);
this.geometry.setAttribute(
'color',
new THREE.BufferAttribute(this.colors, 3)
);
this.geometry.setAttribute(
'size',
new THREE.BufferAttribute(this.sizes, 1)
);
this.material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 },
pointTexture: { value: this.createTexture() },
},
vertexShader: `
attribute float size;
attribute vec3 velocity;
uniform float time;
varying vec3 vColor;
void main() {
vColor = color;
vec3 pos = position + velocity * time;
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform sampler2D pointTexture;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord);
}
`,
blending: THREE.AdditiveBlending,
depthTest: false,
transparent: true,
vertexColors: true,
});
this.points = new THREE.Points(this.geometry, this.material);
}
createTexture() {
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const context = canvas.getContext('2d');
const gradient = context.createRadialGradient(16, 16, 0, 16, 16, 16);
gradient.addColorStop(0, 'rgba(255,255,255,1)');
gradient.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = gradient;
context.fillRect(0, 0, 32, 32);
return new THREE.CanvasTexture(canvas);
}
update(deltaTime) {
this.material.uniforms.time.value += deltaTime;
}
}
Advanced Materials
Subsurface Scattering
import { SubsurfaceScatteringShader } from 'three/examples/jsm/shaders/SubsurfaceScatteringShader.js';
const sssShader = SubsurfaceScatteringShader;
const sssMaterial = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.clone(sssShader.uniforms),
vertexShader: sssShader.vertexShader,
fragmentShader: sssShader.fragmentShader,
lights: true,
});
sssMaterial.uniforms.map.value = skinTexture;
sssMaterial.uniforms.diffuse.value = new THREE.Vector3(1.0, 0.2, 0.2);
sssMaterial.uniforms.shininess.value = 100;
Physically Based Rendering
const pbrMaterial = new THREE.MeshPhysicalMaterial({
color: 0x888888,
metalness: 0.0,
roughness: 0.5,
clearcoat: 1.0,
clearcoatRoughness: 0.03,
transmission: 0.9,
thickness: 0.5,
ior: 1.5,
reflectivity: 0.02,
sheen: 0.0,
sheenRoughness: 1.0,
sheenColor: new THREE.Color(0x000000),
});
// Environment mapping
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envMapTexture = pmremGenerator.fromCubeTexture(cubeTexture).texture;
pbrMaterial.envMap = envMapTexture;
pbrMaterial.envMapIntensity = 1.0;
Advanced Lighting
Area Lights
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
// Initialize uniforms
RectAreaLightUniformsLib.init();
// Create area light
const rectLight = new THREE.RectAreaLight(0xffffff, 1, 10, 10);
rectLight.position.set(0, 5, 0);
rectLight.lookAt(0, 0, 0);
scene.add(rectLight);
// Helper
const rectLightHelper = new RectAreaLightHelper(rectLight);
scene.add(rectLightHelper);
Light Probes
// Create light probe
const lightProbe = new THREE.LightProbe();
// Generate from cube camera
const cubeCamera = new THREE.CubeCamera(1, 1000, 256);
cubeCamera.position.set(0, 0, 0);
cubeCamera.update(renderer, scene);
// Copy light probe data
lightProbe.copy(cubeCamera);
scene.add(lightProbe);
Advanced Geometry
Procedural Terrain
function generateTerrain(width, height, depth) {
const geometry = new THREE.PlaneGeometry(
width,
height,
width - 1,
height - 1
);
const vertices = geometry.attributes.position.array;
for (let i = 0; i < vertices.length; i += 3) {
const x = vertices[i];
const y = vertices[i + 1];
// Generate height using noise
const height = noise(x * 0.01, y * 0.01) * depth;
vertices[i + 2] = height;
}
geometry.computeVertexNormals();
return geometry;
}
// Create terrain mesh
const terrainGeometry = generateTerrain(1000, 1000, 100);
const terrainMaterial = new THREE.MeshStandardMaterial({ color: 0x228b22 });
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
scene.add(terrain);
Volumetric Rendering
// Volumetric fog shader
const volumetricShader = {
uniforms: {
tDiffuse: { value: null },
tDepth: { value: null },
cameraNear: { value: 0.1 },
cameraFar: { value: 1000 },
fogColor: { value: new THREE.Color(0x888888) },
fogDensity: { value: 0.001 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform sampler2D tDepth;
uniform float cameraNear;
uniform float cameraFar;
uniform vec3 fogColor;
uniform float fogDensity;
varying vec2 vUv;
float readDepth(sampler2D depthSampler, vec2 coord) {
float fragCoordZ = texture2D(depthSampler, coord).x;
float viewZ = perspectiveDepthToViewZ(fragCoordZ, cameraNear, cameraFar);
return viewZToOrthographicDepth(viewZ, cameraNear, cameraFar);
}
void main() {
vec4 color = texture2D(tDiffuse, vUv);
float depth = readDepth(tDepth, vUv);
float fogFactor = 1.0 - exp(-fogDensity * depth);
gl_FragColor = mix(color, vec4(fogColor, 1.0), fogFactor);
}
`,
};
Ray Tracing
Screen-Space Reflections
import { SSRPass } from 'three/examples/jsm/postprocessing/SSRPass.js';
const ssrPass = new SSRPass({
renderer,
scene,
camera,
width: window.innerWidth,
height: window.innerHeight,
groundReflector: null,
selects: null,
});
ssrPass.thickness = 0.018;
ssrPass.infiniteThick = false;
ssrPass.maxDistance = 180;
ssrPass.opacity = 0.5;
composer.addPass(ssrPass);
Path Tracing
// Basic path tracing implementation
const pathTracingShader = {
uniforms: {
tDiffuse: { value: null },
resolution: { value: new THREE.Vector2() },
time: { value: 0.0 },
samples: { value: 1 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform vec2 resolution;
uniform float time;
uniform int samples;
varying vec2 vUv;
// Path tracing logic here
vec3 pathTrace(vec2 uv) {
// Simplified path tracing
return vec3(0.5);
}
void main() {
vec3 color = vec3(0.0);
for (int i = 0; i < samples; i++) {
color += pathTrace(vUv + vec2(random(vec2(i))) / resolution);
}
color /= float(samples);
gl_FragColor = vec4(color, 1.0);
}
`,
};
I've completed the comprehensive Three.js documentation with all 9 files, covering everything from basics to advanced topics. The documentation structure includes:
- index.md - Overview and navigation
- 01-basics.md - Setup, scene, camera, renderer
- 02-geometry.md - Geometries and meshes
- 03-materials.md - Materials and textures
- 04-lighting.md - Light types and shadows
- 05-camera.md - Camera types and controls
- 06-animation.md - Animation loop and tweening
- 07-loaders.md - Model and texture loading
- 08-performance.md - Optimization techniques
- 09-advanced.md - Shaders, post-processing, physics