一、什么是实例化网格
在 Three.js 中,实例化网格(Instanced Meshes)是一种用于高效渲染大量相似对象的技术。想象一下你要在场景中绘制 1000 棵一模一样的树,如果使用传统方式,需要为每一棵树创建一个独立的网格对象,这样会占用大量的内存和计算资源。而实例化网格则允许我们只创建一个网格模型,然后通过不同的实例参数,在场景中渲染出多个外观相似但位置、旋转、缩放等属性不同的对象,大大提高渲染效率。
二、实例化网格的优势
- 内存优化:只存储一份网格的几何数据和材质数据,多个实例共享这些数据,减少内存占用。
- 性能提升:通过一次绘制调用渲染多个实例,减少 CPU 与 GPU 之间的数据传输和绘制指令的开销,提升渲染性能,特别适合大规模场景渲染。
三、Three.js 中使用实例化网格的步骤
3.1 引入 Three.js 库
首先,在 HTML 文件中引入 Three.js 库。可以通过 CDN 引入,代码如下:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r152/three.min.js"></script>
也可以下载 Three.js 库文件,然后在 HTML 中以相对路径的方式引入。
3.2 创建基础场景
在 JavaScript 文件中,创建 Three.js 场景、相机和渲染器。示例代码如下:
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
3.3 准备几何和材质
接下来,创建用于实例化的几何图形和材质。这里以简单的立方体为例:
// 创建几何图形
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
3.4 创建实例化网格
使用THREE.InstancedMesh类创建实例化网格。InstancedMesh构造函数接收三个参数:几何图形、材质和实例数量。我们先创建一个包含 100 个实例的网格:
const count = 100;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
scene.add(instancedMesh);
此时,100 个一模一样的立方体已经添加到场景中,但它们都堆叠在原点位置,我们需要设置每个实例的位置等属性。
3.5 设置实例属性
通过InstancedMesh的setMatrixAt方法可以设置每个实例的变换矩阵,从而控制实例的位置、旋转和缩放。这里我们为每个实例随机设置一个位置:
for (let i = 0; i < count; i++) {
const matrix = new THREE.Matrix4();
matrix.makeTranslation(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
);
instancedMesh.setMatrixAt(i, matrix);
}
上述代码中,通过循环为每个实例生成一个随机的平移矩阵,并使用setMatrixAt方法将其应用到对应的实例上。
3.6 渲染场景
最后,在动画循环中不断渲染场景:
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
四、进阶应用
4.1 改变实例的颜色
如果想要为每个实例设置不同的颜色,可以通过自定义材质并使用InstancedMesh的实例属性来实现。首先,创建一个自定义着色器材质:
const vertexShader = `
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
attribute mat4 instanceMatrix;
void main() {
gl_Position = projectionMatrix * viewMatrix * instanceMatrix * modelMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
`;
const customMaterial = new THREE.ShaderMaterial({
uniforms: {
color: { value: new THREE.Vector3(1, 1, 1) }
},
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: false,
vertexColors: false
});
然后,创建实例化网格,并为每个实例设置不同的颜色:
const customInstancedMesh = new THREE.InstancedMesh(geometry, customMaterial, count);
scene.add(customInstancedMesh);
for (let i = 0; i < count; i++) {
const matrix = new THREE.Matrix4();
matrix.makeTranslation(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
);
customInstancedMesh.setMatrixAt(i, matrix);
const randomColor = new THREE.Vector3(
Math.random(),
Math.random(),
Math.random()
);
customMaterial.uniforms.color.value.copy(randomColor);
}
4.2 结合纹理
实例化网格同样可以结合纹理使用。首先,加载纹理:
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('textures/your_texture.jpg');
然后,修改材质,将纹理应用到材质上:
const texturedMaterial = new THREE.MeshBasicMaterial({ map: texture });
const texturedInstancedMesh = new THREE.InstancedMesh(geometry, texturedMaterial, count);
scene.add(texturedInstancedMesh);
for (let i = 0; i < count; i++) {
const matrix = new THREE.Matrix4();
matrix.makeTranslation(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
);
texturedInstancedMesh.setMatrixAt(i, matrix);
}
通过以上步骤和示例,你已经掌握了 Three.js 中实例化网格的基本使用和进阶应用。在实际项目中,可以根据需求灵活运用实例化网格,优化场景渲染性能,创建出更加丰富和高效的 3D 场景。