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);
上面的例子中,还可以用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);