Three.js 高级主题 #
在掌握了 Three.js 的基础知识后,让我们探索一些高级主题,这些技术将帮助你创建更专业、更复杂的 3D 应用。
后期处理 #
后期处理是在渲染完成后对图像进行额外处理的技术,可以实现各种视觉效果。
基础设置 #
javascript
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
function animate() {
requestAnimationFrame(animate);
composer.render();
}
animate();
常用后期处理效果 #
Bloom(泛光) #
javascript
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
composer.addPass(bloomPass);
抗锯齿 #
javascript
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';
const fxaaPass = new ShaderPass(FXAAShader);
fxaaPass.uniforms['resolution'].value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
composer.addPass(fxaaPass);
色调映射 #
javascript
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
const gammaPass = new ShaderPass(GammaCorrectionShader);
composer.addPass(gammaPass);
景深 #
javascript
import { BokehPass } from 'three/addons/postprocessing/BokehPass.js';
const bokehPass = new BokehPass(scene, camera, {
focus: 1.0,
aperture: 0.025,
maxblur: 0.01
});
composer.addPass(bokehPass);
SSAO(屏幕空间环境光遮蔽) #
javascript
import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js';
const ssaoPass = new SSAOPass(scene, camera, window.innerWidth, window.innerHeight);
ssaoPass.kernelRadius = 16;
ssaoPass.minDistance = 0.005;
ssaoPass.maxDistance = 0.1;
composer.addPass(ssaoPass);
完整后期处理示例 #
javascript
import * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
0.5,
0.4,
0.85
);
composer.addPass(bloomPass);
const gammaPass = new ShaderPass(GammaCorrectionShader);
composer.addPass(gammaPass);
function animate() {
requestAnimationFrame(animate);
composer.render();
}
animate();
粒子系统 #
粒子系统用于创建大量小型对象的集合,如雨、雪、火焰、烟雾等效果。
基础粒子 #
javascript
const particleCount = 1000;
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 20;
positions[i * 3 + 1] = (Math.random() - 0.5) * 20;
positions[i * 3 + 2] = (Math.random() - 0.5) * 20;
colors[i * 3] = Math.random();
colors[i * 3 + 1] = Math.random();
colors[i * 3 + 2] = Math.random();
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
transparent: true,
opacity: 0.8
});
const particles = new THREE.Points(geometry, material);
scene.add(particles);
粒子纹理 #
javascript
const textureLoader = new THREE.TextureLoader();
const particleTexture = textureLoader.load('particle.png');
const material = new THREE.PointsMaterial({
size: 0.5,
map: particleTexture,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false
});
粒子动画 #
javascript
const velocities = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
velocities[i * 3] = (Math.random() - 0.5) * 0.01;
velocities[i * 3 + 1] = Math.random() * 0.02;
velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.01;
}
function animate() {
requestAnimationFrame(animate);
const positions = particles.geometry.attributes.position.array;
for (let i = 0; i < particleCount; i++) {
positions[i * 3] += velocities[i * 3];
positions[i * 3 + 1] += velocities[i * 3 + 1];
positions[i * 3 + 2] += velocities[i * 3 + 2];
if (positions[i * 3 + 1] > 10) {
positions[i * 3 + 1] = -10;
}
}
particles.geometry.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
animate();
GPU 粒子 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uSize: { value: 0.1 }
},
vertexShader: `
uniform float uTime;
uniform float uSize;
attribute float aScale;
attribute vec3 aRandomness;
varying vec3 vColor;
void main() {
vec3 pos = position;
pos.y = mod(pos.y + uTime * 0.5 + aRandomness.y * 2.0, 20.0) - 10.0;
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_Position = projectionMatrix * mvPosition;
gl_PointSize = uSize * aScale * (300.0 / -mvPosition.z);
vColor = vec3(1.0, 0.5, 0.0);
}
`,
fragmentShader: `
varying vec3 vColor;
void main() {
float dist = length(gl_PointCoord - vec2(0.5));
if (dist > 0.5) discard;
float alpha = 1.0 - smoothstep(0.3, 0.5, dist);
gl_FragColor = vec4(vColor, alpha);
}
`,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false
});
物理引擎集成 #
Cannon.js #
javascript
import * as CANNON from 'cannon-es';
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
const groundBody = new CANNON.Body({
mass: 0,
shape: new CANNON.Plane()
});
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);
const sphereBody = new CANNON.Body({
mass: 5,
shape: new CANNON.Sphere(0.5),
position: new CANNON.Vec3(0, 5, 0)
});
world.addBody(sphereBody);
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 32, 16),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
scene.add(sphere);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
world.step(1 / 60, delta, 3);
sphere.position.copy(sphereBody.position);
sphere.quaternion.copy(sphereBody.quaternion);
renderer.render(scene, camera);
}
animate();
Ammo.js #
javascript
import Ammo from 'ammo.js';
Ammo().then((AmmoLib) => {
const collisionConfiguration = new AmmoLib.btDefaultCollisionConfiguration();
const dispatcher = new AmmoLib.btCollisionDispatcher(collisionConfiguration);
const broadphase = new AmmoLib.btDbvtBroadphase();
const solver = new AmmoLib.btSequentialImpulseConstraintSolver();
const world = new AmmoLib.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
world.setGravity(new AmmoLib.btVector3(0, -9.82, 0));
});
模型加载 #
GLTFLoader #
javascript
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'model.glb',
(gltf) => {
const model = gltf.scene;
model.scale.set(1, 1, 1);
model.position.set(0, 0, 0);
scene.add(model);
gltf.animations;
gltf.scenes;
gltf.cameras;
gltf.asset;
},
(progress) => {
console.log('Loading:', (progress.loaded / progress.total * 100).toFixed(2) + '%');
},
(error) => {
console.error('Error:', error);
}
);
FBXLoader #
javascript
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
const loader = new FBXLoader();
loader.load('model.fbx', (object) => {
scene.add(object);
});
OBJLoader #
javascript
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
const loader = new OBJLoader();
loader.load('model.obj', (object) => {
scene.add(object);
});
DRACO 压缩 #
javascript
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('draco/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load('model.glb', (gltf) => {
scene.add(gltf.scene);
});
LOD(细节层次) #
javascript
const lod = new THREE.LOD();
const highDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 16),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
lod.addLevel(highDetail, 0);
const mediumDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 16, 8),
new THREE.MeshStandardMaterial({ color: 0x00ff00 })
);
lod.addLevel(mediumDetail, 10);
const lowDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 8, 4),
new THREE.MeshStandardMaterial({ color: 0x0000ff })
);
lod.addLevel(lowDetail, 20);
scene.add(lod);
实例化渲染 #
javascript
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xff6b6b });
const count = 1000;
const mesh = new THREE.InstancedMesh(geometry, material, count);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(
Math.random() * 20 - 10,
Math.random() * 20 - 10,
Math.random() * 20 - 10
);
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
);
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
dummy.updateMatrix();
mesh.setMatrixAt(i, dummy.matrix);
}
mesh.instanceMatrix.needsUpdate = true;
scene.add(mesh);
WebXR(VR/AR) #
VR 模式 #
javascript
import { VRButton } from 'three/addons/webxr/VRButton.js';
renderer.xr.enabled = true;
document.body.appendChild(VRButton.createButton(renderer));
function animate() {
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
}
animate();
AR 模式 #
javascript
import { ARButton } from 'three/addons/webxr/ARButton.js';
renderer.xr.enabled = true;
document.body.appendChild(ARButton.createButton(renderer));
XR 控制器 #
javascript
const controller1 = renderer.xr.getController(0);
controller1.addEventListener('selectstart', onSelectStart);
controller1.addEventListener('selectend', onSelectEnd);
scene.add(controller1);
const controller2 = renderer.xr.getController(1);
scene.add(controller2);
function onSelectStart(event) {
const controller = event.target;
console.log('Select start');
}
function onSelectEnd(event) {
console.log('Select end');
}
完整示例 #
javascript
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(0, 10, 0);
scene.add(pointLight);
const particleCount = 5000;
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(Math.random() * 2 - 1);
const radius = 5 + Math.random() * 5;
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = radius * Math.cos(phi);
colors[i * 3] = Math.random() * 0.5 + 0.5;
colors[i * 3 + 1] = Math.random() * 0.5;
colors[i * 3 + 2] = Math.random() * 0.5;
}
const particleGeometry = new THREE.BufferGeometry();
particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particleGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const particleMaterial = new THREE.PointsMaterial({
size: 0.05,
vertexColors: true,
transparent: true,
blending: THREE.AdditiveBlending
});
const particles = new THREE.Points(particleGeometry, particleMaterial);
scene.add(particles);
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
composer.addPass(bloomPass);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
particles.rotation.y = elapsedTime * 0.1;
particles.rotation.x = Math.sin(elapsedTime * 0.05) * 0.1;
controls.update();
composer.render();
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
下一步 #
现在你已经掌握了高级主题,接下来学习 性能优化,让你的应用运行更流畅!
最后更新:2026-03-28