使用场景

当我们有大量的相同的几何体形状和相同的材质时,比如我有一千个立方几何体要渲染,他们的材质时相同的,但是坐标、大小矩阵变换这些不相同。

如果按照常规的一个个Mesh的渲染,要生成一千个geometry,一千个material,一千个Mesh,占用太多内存和性能。

我们可以使用合并几何体的方式,但这样合并后变为一个个体 ,失去了对单个小模型的控制。three.js还提供了InstanceMesh实例化模型可以实现。

关于两种方法的对比:

InstanceMesh

合并几何体

Material

相同

相同

Material

相同

✔不同

单个控制

✔可以通过索引来更改矩阵变换和颜色

难以实现

生成时间

✔快速

缓慢

渲染性能

较优

✔更优

内存占用

极少

✔较多

参考:Threejs性能优化:Instance实例化几何体 和 Merge合并几何体

这里我们只讨论实例化网格模型。

**

实例化网格InstancedMesh

官方定义: 具有实例渲染支持的特殊版本的Mesh。如果您必须渲染大量具有相同几何形状和材质但具有不同世界变换的对象,请使用InstancedMesh。InstancedMesh的使用将帮助您减少绘图调用的次数,从而提高应用程序的整体渲染性能。
当前的实现要求InstancedMesh和其他三维对象之间不共享材质。
指路:three.js官网InstancedMesh

.setMatrixAt ( index : Integer, matrix : Matrix4 )
index: 实例的索引。值必须在[0,count]范围内
matrix: 表示单个实例的局部变换的4x4矩阵。
将给定的局部变换矩阵设置到指定的实例。

在更新所有矩阵之后,一定要设置 .instanceMatrix.needsUpdate 为 true

.setColorAt ( index : Integer, color : Color )
index: 实例的索引。值必须在[0,count]范围内
color: 单个实例的颜色。
将给定的颜色设置到指定的实例。

在更新所有颜色之后,一定要设置 .instanceColor.instanceColor为 true

//typeItemArr 这个是我存放要生成模型信息的数组
var initGeometry = new THREE.BoxBufferGeometry(1, 1, 1);//
var material = new THREE.MeshLambertMaterial(color : 1710847);
var mesh = new THREE.InstancedMesh(initGeometry, material , typeItemArr.length);
var transform = new THREE.Object3D();
typeItemArr.forEach((item, index) => {
	var boxinfo = item.boxinfo;//这个是我存放物体的位置和长宽高信息的
	transform.position.set(boxinfo.x, boxinfo.y, boxinfo.z);//坐标
	transform.scale.set(boxinfo.width, boxinfo.height, boxinfo.depth);//放大到想要的大小
	transform.updateMatrix();
	//修改实例化几何体中的单个实例的矩阵以改变大小、方向、位置等
	mesh.setMatrixAt(index, transform.matrix);
});
transform = null;
//再把mesh添加到场景中就可以了

!!!注意:官方定义的第二段话:这里的材质不可以和其他的对象共用,如果复用材质可能会出现渲染不成功的情况。表现:不报任何错,仅仅是未渲染成功,目前暂时没有较好的解决方案,现仅避免在InstancedMesh和其他对象共用材质。

**

点击更改单个实例的颜色或矩阵

(关于three.js点击事件的基础写法之前文章说过,不再赘述)

//this.clickObjects是我存放可点击模型的数组
var intersects = this.raycaster.intersectObjects(this.clickObjects);
if (intersects.length) {
	var mesh = intersects[0].object;//这里就是需要操作的网格模型了
	var instanceId = intersects[0].instanceId;//这里的instanceId就是该实例的索引,对应我们之前初始化时的index
	//判断点击得到的是不是isInstancedMesh
	if (mesh.isInstancedMesh && instanceId>= 0) {
		//如果要更改颜色
		mesh.setColorAt(instanceId, 0x424242);
		mesh.instanceColor.needsUpdate = true;
		//如果要更改矩阵,matrix是要改成的矩阵,可以参考初始化时的那样得到矩阵
		mesh.setMatrixAt(instanceId, matrix);
		mesh.instanceMatrix.needsUpdate = true;
	}
}