Performance improvements for CircleGeometry, ShaderMaterial, and SimplexNoise

Hi members.
I’m working on a project that uses CircleGeometry, ShaderMaterial, and SimplexNoise for noise in their animations. Link of a live demo to see the results.

If you open the demo, you can see there is a lack of performance in it.
I saw many examples that using something like my demo in their pages, but way smoother!

Now, I need some performance tips to improve the performance of this project. It’s very critical that the performance should be very good, especially on mobile devices.
Sorry to ask it this way, but I really need some help with this.

I can’t share public code, but if someone needed to see the source, I can give limited access to its repo (:
Thanks.

The first thing I would recommend is the usage of BufferGeometry. So instead of:

this.geometry = new THREE.CircleGeometry(10, 128, 6, 6.3);

use

this.geometry = new THREE.CircleBufferGeometry(10, 128, 6, 6.3);

All instances of THREE.Geometry have to be internally converted to BufferGeometry for rendering. Besides, THREE.Geometry allocates much more memory than BufferGeometry. By simply using CircleBufferGeometry, you avoid all this overhead.

Thanks, @Mugen87.
When I use CircleBufferGeometry , I got some errors for this.mesh.geometry.vertices because I’m using vertices to shape the circle in every render, and it seems with CircleBufferGeometry the vertices are undefined. How can I fix this?

for (let i = 0; i < this.mesh.geometry.vertices.length; i++) {
    const bubble = this.mesh.geometry.vertices[i];

    bubble.normalize().multiplyScalar(1 + 0.3 * this.simplex.noise4D(bubble.x * spikes, bubble.y * spikes, bubble.z * spikes + time, 3));
}

this.mesh.geometry.computeVertexNormals();
this.mesh.geometry.normalsNeedUpdate = true;
this.mesh.geometry.verticesNeedUpdate = true;

You have to work with BufferAttribute instead. The official examples are full of related code since they were migrated to BufferGeometry since a while now. I’ve not tested this code but it should be:

const positionAttribute = this.mesh.geometry.getAttribute( 'position' );
const vertex = new THREE.Vector3();

for (let i = 0; i < positionAttribute.count; i++) {
    vertex.fromBufferAttribute( positionAttribute, i );

    vertex.normalize().multiplyScalar(1 + 0.3 * this.simplex.noise4D(bubble.x * spikes, bubble.y * spikes, bubble.z * spikes + time, 3));

    positionAttribute.setXYZ( i, vertex.x, vertex.y, vertex,z );
}

positionAttribute.needsUpdate = true;
geometry.computeVertexNormals();

Thanks. Now it works fine.
Do you have any other recommendations? Because I fill it’s not smooth enough yet (:

Do you really have to compute the vertex normals all the time? If you are not using the normals in your custom shader, I suggest you remove this line.

Or event better, remove the normals (and all other attributes your are not using) right form the beginning via:

geometry.deleteAttribute( 'normal' );

You mean outside of update function?

Yes, when creating the circle geometry.

Ok, I’m testing it.

Now I fill its animation is smoother than before. Thank you @Mugen87.
Other than this, when I scroll, the lack is more sensible. I think when I move my objects, it needs more process and calculations.
This is the part of the code that moves the object:

anime({
    targets: this.mesh.position,
    x: this.offset.x,
    y: this.offset.y,
    duration: 90
})

I’m using Anime.js for this transition. Do You think there are better libs for this?

I’ve made good experiences with GSAP. There are already many topics about the usage of GSAP and three.js: https://discourse.threejs.org/search?q=gsap

1 Like

OK, will test it. Many thanks.

One more tip regarding your geometry: If you are updating the position attribute of your circle geometry per frame, it’s recommended to set a special value for the usage property. In your case, do this when creating the geometry:

const positionAttribute = geometry.getAttribute( 'position' );
positionAttribute.setUsage( THREE.DynamicDrawUsage );

This will indicate WebGL that you are going to dynamically update the buffer data per frame.

1 Like

Yes, now it looks better.

Do you think there is a better way for using simplex noises? I use simplex-noise that is a noise lib with js. But I read somewhere that noises can work better if use them in shaders.

It is possible to do this in the shader, yes. It could help to improve the app’s overall performance if it is still CPU bound.

The following article is quite interesting in this context. You might want to check out the presented approaches and see if it helps: https://thebookofshaders.com/11/

1 Like

What do you mean by that? And how can I make sure of that?

The idea is to make a performance analysis e.g. with Chrome dev tools and check for hot spots in your JavaScrip code.

Apart from that you can easily detect GPU bound applications by just shrinking the browser window and thus reducing the resolution. If the performance gets better, there is app is fragment-shader bound (meaning too much work happens in the fragment shader). If the performance gets better by reducing just the vertex count, the application is probably vertex shader bound.

3 Likes

Yes, you right. It’s vertex shader bound.
So now, there is any way that I can improve the performance of shaders even on big screens?