InstancedMesh

608 阅读1分钟

InstancedMesh

对每个instance应用不同的变换

对于InstancedBufferGeometry来说,除了要有顶点属性之外,还需要有差异化的信息,如果想要对每个instance进行变换,我们可以把每个instance的局部变换矩阵作为差异化的数据,然后在顶点着色器中应用这个变换矩阵即可。

const COUNT = 10000;

const scene = new THREE.Scene();

const positions = [
  -2, 2, 0,
  -2, -2, 0,
  2, 2, 0
];

const instancedGeoemetry = new InstancedBufferGeometry();
instancedGeoemetry.setAttribute('position', new Float32BufferAttribute(positions, 3))

function getRandomLocalMatrix(): Matrix4 {
  const _objectForMatrix = new Object3D();
  _objectForMatrix.translateX(Math.random() * 2 - 1); // -1~1
  _objetForMatrix.translateY(Math.random() * 2 - 1); // -1~1
  _objectForMatrix.translateZ(Math.random() * 2 - 1); // -1~1

  _objectForMatrix.rotateX(Math.random() * 2 * Math.PI - Math.PI);
  _objectForMatrix.rotateY(Math.random() * 2 * Math.PI - Math.PI);
  _objectForMatrix.rotateZ(Math.random() * 2 * Math.PI - Math.PI);

  _objectForMatrix.scale.setScalar(Math.random() * 0.01);

  _objectForMatrix.updateMatrix();
  return _objectForMatrix.matrix;
}

const localMatrix = new InstancedBufferAttribute(new Float32Array(COUNT * 16), 16, false);
for(let i = 0; i < COUNT; ++i) {
  getRandomLocalMatrix().toArray(localMatrix.array, i * 16);
}

instancedGeoemetry.setAttribute('localMatrix', localMatrix);

const material = new RawShaderMaterial({
  vertexShader: `
    precision highp float;

    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;

    attribute vec3 position;
    attribute mat4 localMatrix;

    void main(){
      gl_Position = projectionMatrix * modelViewMatrix * localMatrix * vec4(position, 1.0 );
    }`,
});

const mesh = new Mesh(instancedGeoemetry, material);
instancedGeoemetry.instanceCount = COUNT;

scene.add(mesh);
const boxHelper = new BoxHelper(mesh);
scene.add(boxHelper);
example-4.PNG

上面的例子中,还可以用InstancedBufferAttribute.setUsage(THREE.DynamicDrawUsage),将传递矩阵的缓冲区设置为THREE.DynamicDrawUsage,这样,就可以在每一帧绘制时,都传递一次变暖矩阵,这样,就可以做一些动画效果。

InstancedMesh

就像我们上面说描述的,可以给每个instance传递矩阵,在Three.js中,有一个内置的InstancedMesh,封装了上述传递矩阵的使用方式。

除了矩阵之外,还可以给每个instance指定颜色,示例代码如下:

const COUNT = 10000;

const scene = new THREE.Scene();

const positions = [
  -2, 2, 0,
  -2, -2, 0,
  2, 2, 0
];
const bufferGeoemetry = new BufferGeometry();
bufferGeoemetry.setAttribute('position', new Float32BufferAttribute(positions, 3));
const material = new MeshBasicMaterial();
const mesh = new InstancedMesh(bufferGeoemetry, material, COUNT);
for(let i = 0; i < COUNT; ++i) {
    mesh.setMatrixAt(i, getRandomLocalMatrix());
    mesh.setColorAt(i, getRandomColor());
}


function getRandomLocalMatrix(): Matrix4 {
  const _objectForMatrix = new Object3D();
  _objectForMatrix.translateX(Math.random() * 2 - 1); // -1~1
  _objectForMatrix.translateY(Math.random() * 2 - 1); // -1~1
  _objectForMatrix.translateZ(Math.random() * 2 - 1); // -1~1

  _objectForMatrix.rotateX(Math.random() * 2 * Math.PI - Math.PI);
  _objectForMatrix.rotateY(Math.random() * 2 * Math.PI - Math.PI);
  _objectForMatrix.rotateZ(Math.random() * 2 * Math.PI - Math.PI);

  _objectForMatrix.scale.setScalar(Math.random() * 0.01);

  _objectForMatrix.updateMatrix();
  return _objectForMatrix.matrix;
}

function getRandomColor(): Color {
  return new Color(Math.random(), Math.random(), Math.random());
}
scene.add(mesh);
const boxHelper = new BoxHelper(mesh);
scene.add(boxHelper);
example-5.PNG