Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimizing number of Three.js Draws

I'm testing the use of Three.js for my project (http://agentscript.org) and the first test seems quite slow:

https://asx-vqnhxlahpe.now.sh/test.html?three0

(note: the site can take a while, it "sleeps" after a period of non-use)

This is apparently due to my using way too many draws, one per agent. renderer.info.render Object {calls: 10002, vertices: 60090, faces: 20000, points: 0}

I believe using a BufferGeometry would solve this, but I don't know how to use one and still be able to access each individual agent so that my step/animate function can modify (move, rotate) them.

How would I do this?

I realize I could simply go to using my own Shader, and plan to do so, but I'd prefer to have an intermediate solution or two first.

like image 834
backspaces Avatar asked Dec 15 '22 01:12

backspaces


2 Answers

You want to reduce the number of draw calls in your Agent simulation and get something working relatively easily. You can do that by representing your collection of agents as 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 );

This will render in a single draw call and you can update the positions on the CPU and push the data to the GPU each frame. You should be able to render 100,000 agents easily.

fiddle: http://jsfiddle.net/730ffn4x/

If you want to represent your points as an arrow with direction, you would need to provide texture with an arrow image and a custom ShaderMaterial to rotate each point.

InstancedBufferGeometry is appropriate if each agent must be represented as a Mesh. Here is a fine example by @MartinSchuhfuß that is similar to what you want to do.

For intense simulations, you would use GPGPU. See the three.js examples. In GPGPU, the simulation runs entirely on the GPU, and the CPU typically does minimal work.

three.js r.84

like image 74
WestLangley Avatar answered Jan 01 '23 12:01

WestLangley


In your case, instancing is the best way to reduce geometries and draw calls. Three.js supports instancing through ANGLE_instanced_arrays extension.

Take a look at this -

https://threejs.org/docs/#api/core/InstancedBufferGeometry

Also here is an example on how to do instancing in Three.js -

https://threejs.org/examples/webgl_buffergeometry_instancing.html

like image 26
Rasheduzzaman Sourov Avatar answered Jan 01 '23 12:01

Rasheduzzaman Sourov