我正在测试我的项目使用Three.js(http://agentscript.org),第一次测试看起来很慢:
(注意:该网站可能需要一段时间,它会在一段时间不使用后“睡觉”)
这显然是由于我的使用方式太多,每个代理人一个。
renderer.info.render
Object {calls: 10002, vertices: 60090, faces: 20000, points: 0}
我相信使用BufferGeometry可以解决这个问题,但我不知道如何使用它仍然能够访问每个代理,以便我的步/动画功能可以修改(移动,旋转)它们。
我该怎么做?
我意识到我可以简单地使用我自己的Shader,并计划这样做,但我更愿意先使用一个或两个中间解决方案。
答案 0 :(得分:5)
您希望减少代理模拟中的绘制调用次数,并使相对容易的工作变得简单。您可以通过将代理集合表示为THREE.Points
来完成此操作。
// geometry
var geometry = new THREE.BufferGeometry();
// attributes
var positions = new Float32Array( numAgents * 3 ); // 3 values per vertex
var rotations = new Float32Array( numAgents * 1 ); // 1 values per vertex
for ( var i = 0; i < numAgents; i ++ ) {
positions[ i ] = 0;
positions[ i + 1 ] = 0;
positions[ i + 2 ] = 0;
rotations[ i ] = 2 * Math.PI * Math.random();
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.addAttribute( 'rotation', new THREE.BufferAttribute( rotations, 1 ) );
// material
var material = new THREE.PointsMaterial( {
color: 0xff0000,
size: 4,
map: null
} );
// points
points = new THREE.Points( geometry, material );
scene.add( points );
这将在单个绘图调用中呈现,您可以更新CPU上的位置并将数据推送到每帧的GPU。您应该能够轻松地渲染100,000个代理。
小提琴:http://jsfiddle.net/730ffn4x/
如果您想将点表示为带方向的箭头,则需要使用箭头图像和自定义ShaderMaterial
来提供纹理以旋转每个点。
InstancedBufferGeometry
,则 Mesh
是合适的。 {MartinSchuhfuß的Here is a fine example与您想要的相似。
对于激烈的模拟,您将使用GPGPU。 See the three.js examples。在GPGPU中,模拟完全在GPU上运行,而CPU通常只做很少的工作。
three.js r.84
答案 1 :(得分:3)
在您的情况下,实例化是减少几何和绘制调用的最佳方法。 Three.js
通过ANGLE_instanced_arrays
扩展支持实例化。
看看这个 -
https://threejs.org/docs/#api/core/InstancedBufferGeometry
此处还有一个关于如何在Three.js
-
https://threejs.org/examples/webgl_buffergeometry_instancing.html
答案 2 :(得分:2)
你是对的,使用BufferGeometries会对你的情况有所帮助。
这是我提出的:首先,我们将有一个代表箭头的小助手类:
// the base-shape of the arrow, two triangles
const arrowVertices = new Float32Array([
-1,-1,0, 0,0,0, 0,2,0,
0,2,0, 0,0,0, 1,-1, 0
]);
// some helper-variables
const v3 = new THREE.Vector3();
const m4 = new THREE.Matrix4();
class Arrow {
constructor(buffer, offset) {
this.numVertices = 6;
this.buffer = buffer;
this.offset = offset;
this.heading = 0;
this.position = new THREE.Vector3();
}
update() {
m4.makeRotationZ(this.heading)
.setPosition(this.position);
for (let i = 0; i < this.numVertices; i++) {
v3.fromArray(arrowVertices, i * 3);
v3.applyMatrix4(m4a);
v3.toArray(this.buffer, this.offset + i * 3);
}
}
}
然后,要使用它,请设置BufferGeometry并将箭头“附加”到它
// create the bufferGeometry (note: if you want proper shading, you will
// also need an attribute 'normal' with face-vertex-normals)
const geometry = new THREE.BufferGeometry();
const positionBuffer = new Float32Array(NUM_INSTANCES * 6 * 3);
geometry.addAttribute('position',
new THREE.BufferAttribute(positionBuffer, 3));
const arrow = new Arrow(positionBuffer, 0);
const anotherArrow = new Arrow(positionBuffer, 6);
arrow.position.set(2, 1, 0);
arrow.heading = Math.PI/180 * 30;
arrow.update();
此技术的主要缺点是您需要手动处理箭头顶点的所有变换。对于只有6个顶点的简单箭头,这不应该是一个问题,它可能比每个箭头的网格更好。但是,我建议您再看看实例几何,它可以在顶点着色器中执行上面更新方法中发生的所有事情,从而以更少的开销执行更好。