Three.js 实例化网格(Instanced Meshes

412 阅读3分钟

一、什么是实例化网格

在 Three.js 中,实例化网格(Instanced Meshes)是一种用于高效渲染大量相似对象的技术。想象一下你要在场景中绘制 1000 棵一模一样的树,如果使用传统方式,需要为每一棵树创建一个独立的网格对象,这样会占用大量的内存和计算资源。而实例化网格则允许我们只创建一个网格模型,然后通过不同的实例参数,在场景中渲染出多个外观相似但位置、旋转、缩放等属性不同的对象,大大提高渲染效率。

二、实例化网格的优势

  1. 内存优化:只存储一份网格的几何数据和材质数据,多个实例共享这些数据,减少内存占用。
  1. 性能提升:通过一次绘制调用渲染多个实例,减少 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 场景。