目录
基本模型加载
导入库:
首先,你需要在HTML文件中引入Three.js库,如果是CDN,可以这样添加:
<script src="https://cdn.jsdelivr.net/npm/three@0.145.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.145.0/examples/js/loaders/GLTFLoader.js"></script>
GLTFLoader用于加载.gltf或.glb格式的模型。
初始化场景、相机和渲染器:
在JavaScript中设置Three.js的基本元素:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
加载模型: 使用GLTFLoader加载模型文件:
const loader = new THREE.GLTFLoader();
loader.load('path_to_model.gltf', function(gltf) {
const model = gltf.scene;
// 将模型添加到场景中
scene.add(model);
// 如果需要处理模型的骨骼,可以这样:
const skeletonHelper = new THREE.SkeletonHelper(model);
skeletonHelper.visible = false; // 默认隐藏骨骼,可根据需求调整
scene.add(skeletonHelper);
}, undefined, function(error) {
console.error(error);
});
load方法接受模型文件的URL,加载成功后会调用回调函数,其中gltf对象包含了模型的所有数据。
设置动画(如果有的话):
如果模型包含动画,你可以从gltf.animations中获取并应用到模型上:
const mixer = new THREE.AnimationMixer(model);
gltf.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
更新和渲染:
在每一帧中,你需要更新混合器和渲染场景:
function animate() {
requestAnimationFrame(animate);
mixer.update(delta); // delta 是时间差,通常由THREE.Clock提供
renderer.render(scene, camera);
}
animate();
事件监听:
根据需要,你还可以添加鼠标或触摸事件来交互控制模型。
请注意,不同格式的模型(如.fbx、.obj等)可能需要不同的加载器,例如FBXLoader或OBJLoader。确保正确引入对应的加载器,并使用相应的加载方法。此外,模型的路径应根据实际部署情况调整,确保浏览器能够正确访问到模型文件。
3D模型操作
设置光照:
为了使模型看起来更真实,通常需要添加光源:
const ambientLight = new THREE.AmbientLight(0x404040); // soft white light
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(0, 1, 1).normalize();
scene.add(directionalLight);
缩放模型:
可以通过修改模型的scale属性来缩放模型:
model.scale.set(0.5, 0.5, 0.5); // 缩小到原来的50%
移动模型: 调整模型的位置:
model.position.set(0, -1, -2); // 移动到XYZ坐标为(0, -1, -2)
旋转模型: 设置模型的旋转角度:
model.rotation.set(0, Math.PI / 2, 0); // 使模型绕Y轴旋转90度
交互控制:
如果你想让用户可以通过鼠标或触摸来旋转、平移或缩放模型,可以使用OrbitControls插件:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
自定义材质和颜色:
对于某些3D模型,你可能希望改变其材质或颜色。这需要深入理解Three.js的材质系统。例如,如果你的模型使用了MeshStandardMaterial,你可以这样做:
const materials = model.material;
if (Array.isArray(materials)) {
materials.forEach(mat => mat.color.setHex(0xff0000)); // 设置所有材质为红色
} else {
materials.color.setHex(0xff0000); // 单一材质设置为红色
}
以上就是Three.js加载和处理3D模型的一些常见操作。请根据项目需求进行选择和调整。
功能优化
加载纹理:
有些模型可能需要纹理贴图,你可以使用TextureLoader加载纹理并应用到材质上:
const textureLoader = new THREE.TextureLoader();
textureLoader.load('path_to_texture.png', function(texture) {
const material = new THREE.MeshStandardMaterial({ map: texture });
// 将材质应用到模型上,这可能需要根据模型结构来实现
model.traverse(node => {
if (node.isMesh) {
node.material = material;
}
});
});
阴影: 如果需要,可以启用阴影以增加场景的真实感:
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 确保光源和接收阴影的对象都开启阴影支持
directionalLight.castShadow = true;
model.receiveShadow = true;
性能优化:
- LOD(Level of Detail):对于大模型,可以使用LOD来根据相机距离加载不同细节级别的模型。
- 批处理渲染:将多个相似的几何体组合成一个批次,减少渲染次数。
- 剔除:使用THREE.BackSide或THREE.FrontSide来剔除不需要渲染的面。
- 缓存和复用:避免重复加载和创建相同的资源。
动画帧率控制:
使用requestAnimationFrame的替代方案,如THREE.Clock,以控制动画的帧率:
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta(); // 获取时间差
mixer.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
响应式设计:
当窗口大小变化时,调整相机和渲染器的尺寸:
javascript
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
错误处理和日志记录:
为了调试和优化,确保捕获和记录加载或渲染过程中可能出现的错误。
粒子系统基础
基础的粒子系统
使用THREE.ParticleSystem和THREE.ParticleBasicMaterial实现:
// 导入Three.js库
import * as THREE from 'three';
// 初始化场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建粒子几何体
const particleCount = 1000;
const particles = new THREE.Geometry();
for (let i = 0; i < particleCount; i++) {
const particle = new THREE.Vector3(
Math.random() * 200 - 100,
Math.random() * 200 - 100,
Math.random() * 200 - 100
);
particles.vertices.push(particle);
}
// 创建粒子材质
const particleMaterial = new THREE.PointsMaterial({
color: 0xFFFFFF,
size: 10,
map: new THREE.TextureLoader().load('path_to_particle_texture.png'), // 如果有纹理
blending: THREE.AdditiveBlending,
transparent: true
});
// 创建粒子系统
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
// 更新粒子位置(简单示例,实际可能需要动画循环)
function updateParticles() {
for (let i = 0; i < particles.vertices.length; i++) {
const vertex = particles.vertices[i];
vertex.y -= 0.1; // 假设粒子向下移动
if (vertex.y < -100) {
vertex.y = 100; // 重置粒子位置
}
}
particles.verticesNeedUpdate = true; // 通知粒子系统更新
}
// 渲染场景
function animate() {
requestAnimationFrame(animate);
updateParticles();
renderer.render(scene, camera);
}
animate();
上面创建了一个简单的粒子系统,每个粒子随机分布在3D空间中,然后以一定的速度向下移动。当粒子到达屏幕底部时,它们会被重置到顶部。注意,粒子的运动和行为在这个示例中是非常基础的,实际的粒子系统可能需要更复杂的动画逻辑。
自定义着色器创建粒子爆炸效果
首先,定义两个着色器:
particleVertexShader和particleFragmentShader,它们定义粒子的行为和外观:
// particleVertexShader.glsl
uniform float time;
attribute vec3 velocity;
void main() {
vec3 newPosition = position + velocity * time;
vec4 mvPosition = modelViewMatrix * vec4(newPosition, 1.0);
gl_PointSize = 10.0;
gl_Position = projectionMatrix * mvPosition;
}
// particleFragmentShader.glsl
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
然后在JavaScript中创建自定义材质并加载着色器:
// 加载着色器
const particleVertexShader = document.getElementById('particleVertexShader').textContent;
const particleFragmentShader = document.getElementById('particleFragmentShader').textContent;
// 创建粒子材质
const particleMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color: { value: new THREE.Color(0xffffff) },
},
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
blending: THREE.AdditiveBlending,
transparent: true,
depthWrite: false,
});
接下来,创建粒子系统:
// 创建粒子
const particleCount = 1000;
const positions = new Float32Array(particleCount * 3);
const velocities = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
// 初始化粒子位置、速度和颜色
// ... 这部分取决于你的粒子行为
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const particleSystem = new THREE.Points(geometry, particleMaterial);
scene.add(particleSystem);
最后,更新粒子系统:
function animate() {
requestAnimationFrame(animate);
const time = performance.now() / 1000; // 获取当前时间
particleMaterial.uniforms.time.value = time; // 更新时间 uniform
// 更新粒子位置,速度,颜色等
// ... 这部分取决于你的粒子行为
renderer.render(scene, camera);
}
animate();
three-emitter库
three-emitter库提供了Emitter类,简化了创建粒子发射器的过程。它允许你定义粒子的生命周期、速度、颜色变化等。以下是一个使用three-emitter库创建粒子发射器的例子:
首先,确保安装了three-emitter库:
npm install three three-emitter
然后在代码中导入并使用它:
import * as THREE from 'three';
import { Emitter, Particle } from 'three-emitter';
// 初始化场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建粒子材质
const particleMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 10,
blending: THREE.AdditiveBlending,
transparent: true
});
// 创建粒子发射器
const emitter = new Emitter({
rate: new Emitter.Constant(1), // 每秒发射1个粒子
life: new Emitter.Range(2, 4), // 粒子生命周期2-4秒
position: new Emitter.Box(new THREE.Vector3(-50, 0, -50), new THREE.Vector3(50, 100, 50)), // 发射范围
velocity: new Emitter.Range(new THREE.Vector3(-10, 20, -10), new THREE.Vector3(10, 20, 10)), // 初始速度范围
acceleration: new Emitter.Constant(new THREE.Vector3(0, -10, 0)), // 重力
color: new Emitter.Range(new THREE.Color(0xff0000), new THREE.Color(0x00ff00)), // 颜色范围
size: new Emitter.Range(1, 5), // 粒子大小范围
});
// 创建粒子几何体并添加发射器
const particleGeo = new THREE.Geometry();
emitter.create(particleGeo, particleMaterial);
// 添加到场景
const particleSystem = new THREE.Points(particleGeo, particleMaterial);
scene.add(particleSystem);
// 更新和渲染
function animate() {
requestAnimationFrame(animate);
emitter.update(0.01); // 更新粒子状态
renderer.render(scene, camera);
}
animate();
在这个例子中,我们创建了一个粒子发射器,粒子从一个立方体区域内的随机位置发射,初始速度、颜色和大小都是随机的。粒子受到一个向下的加速度,模拟重力效果。粒子的生命周期也在2到4秒之间随机,颜色从红色渐变到绿色。每秒发射一个粒子,但你可以根据需要调整rate参数。
常见粒子系统特效
火花效果
火花通常使用白色或暖色调的粒子,具有向上或向外扩散的运动。
const sparkMaterial = new THREE.PointsMaterial({
color: 0xffaa00,
size: 1,
map: new THREE.TextureLoader().load('spark_texture.png'),
blending: THREE.AdditiveBlending,
transparent: true,
});
const sparkPositions = new Float32Array(sparkCount * 3);
// ... 初始化火花位置和速度
const sparkGeometry = new THREE.BufferGeometry();
sparkGeometry.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
const sparkPoints = new THREE.Points(sparkGeometry, sparkMaterial);
scene.add(sparkPoints);
// 更新火花位置
function updateSparks() {
// ... 根据速度和时间更新火花位置
}
烟雾效果
烟雾通常是灰白色的,可以使用THREE.Points和烟雾纹理。
const smokeMaterial = new THREE.PointsMaterial({
color: 0xaaaaaa,
size: 2,
map: new THREE.TextureLoader().load('smoke_texture.png'),
blending: THREE.AdditiveBlending,
transparent: true,
});
const smokePositions = new Float32Array(smokeCount * 3);
// ... 初始化烟雾位置和速度
const smokeGeometry = new THREE.BufferGeometry();
smokeGeometry.setAttribute('position', new THREE.BufferAttribute(smokePositions, 3));
const smokePoints = new THREE.Points(smokeGeometry, smokeMaterial);
scene.add(smokePoints);
// 更新烟雾位置
function updateSmoke() {
// ... 根据速度和时间更新烟雾位置
}
雨雪效果
雨滴和雪花可以使用类似的方法,但可能需要不同的纹理和动画逻辑。
const snowMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.5,
map: new THREE.TextureLoader().load('snow_texture.png'),
blending: THREE.AdditiveBlending,
transparent: true,
});
const snowPositions = new Float32Array(snowCount * 3);
// ... 初始化雪花位置和速度
const snowGeometry = new THREE.BufferGeometry();
snowGeometry.setAttribute('position', new THREE.BufferAttribute(snowPositions, 3));
const snowPoints = new THREE.Points(snowGeometry, snowMaterial);
scene.add(snowPoints);
// 更新雪花位置
function updateSnow() {
// ... 根据速度和时间更新雪花位置
}
火焰效果
火焰通常使用复杂的颜色变化和动画。可以使用THREE.Points配合自定义着色器来实现。
// 定义火焰着色器
// ... 类似上面的粒子着色器,但要包含火焰颜色变化的计算
const flameMaterial = new THREE.ShaderMaterial({
uniforms: {
// ... 包括火焰颜色、时间等uniforms
},
vertexShader: flameVertexShader,
fragmentShader: flameFragmentShader,
blending: THREE.AdditiveBlending,
transparent: true,
});
// 初始化火焰粒子
// ... 创建火焰粒子的位置、颜色和速度
const flamePoints = new THREE.Points(flameGeometry, flameMaterial);
scene.add(flamePoints);
// 更新火焰
function updateFlame() {
// ... 更新火焰颜色、位置等
}
爆炸效果
爆炸通常涉及多个粒子层,从中心向外扩散,并逐渐消失。可以使用多个粒子系统和不同的颜色变化来模拟。
// 创建多个粒子系统,每个系统代表爆炸的不同阶段
const explosionMaterials = [/* ... */];
const explosionGeometries = [/* ... */];
for (let i = 0; i < explosionMaterials.length; i++) {
const explosionPoints = new THREE.Points(explosionGeometries[i], explosionMaterials[i]);
scene.add(explosionPoints);
}
// 更新爆炸粒子
function updateExplosion() {
// ... 更新每个粒子系统的位置、颜色、透明度等
}
光晕效果
光晕可以模拟发光或模糊的效果,通常使用半透明粒子和自定义着色器。
const haloMaterial = new THREE.ShaderMaterial({
uniforms: {
glowColor: { value: new THREE.Color(0x00ffff) },
glowIntensity: { value: 1.0 },
blur: { value: 1.0 },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
},
vertexShader: haloVertexShader,
fragmentShader: haloFragmentShader,
blending: THREE.AdditiveBlending,
transparent: true,
});
const haloPoints = new THREE.Points(haloGeometry, haloMaterial);
scene.add(haloPoints);
// 更新光晕
function updateHalo() {
// ... 更新光晕的颜色、强度、模糊程度等
}
喷射流效效果
喷射流可以模拟水流、火焰或其他流动效果。
const jetMaterial = new THREE.PointsMaterial({
color: 0x00ff00,
size: 0.5,
map: new THREE.TextureLoader().load('jet_texture.png'),
blending: THREE.AdditiveBlending,
transparent: true,
});
const jetPositions = new Float32Array(jetCount * 3);
// ... 初始化喷射流位置和速度
const jetGeometry = new THREE.BufferGeometry();
jetGeometry.setAttribute('position', new THREE.BufferAttribute(jetPositions, 3));
const jetPoints = new THREE.Points(jetGeometry, jetMaterial);
scene.add(jetPoints);
// 更新喷射流
function updateJet() {
// ... 根据速度和时间更新喷射流位置
}
旋转涡旋效果
涡旋可以模拟旋转的气流或漩涡。
const vortexMaterial = new THREE.PointsMaterial({
color: 0x990000,
size: 1,
map: new THREE.TextureLoader().load('vortex_texture.png'),
blending: THREE.AdditiveBlending,
transparent: true,
});
const vortexPositions = new Float32Array(vortexCount * 3);
// ... 初始化涡旋位置和速度
const vortexGeometry = new THREE.BufferGeometry();
vortexGeometry.setAttribute('position', new THREE.BufferAttribute(vortexPositions, 3));
const vortexPoints = new THREE.Points(vortexGeometry, vortexMaterial);
scene.add(vortexPoints);
// 更新涡旋
function updateVortex() {
// ... 更新涡旋的位置和旋转
}