Hi everyone.
I want to create a cloud of points placed on the surface of a geometry. The desired result should look like this but with circle-shaped particles.
This is the code I am using to instantiate the Points object:
const baseGeometry = new THREE.SphereGeometry(...)
const particlesMaterial = new THREE.ShaderMaterial({
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
})
const pointsPositions = baseGeometry.getAttribute('position').clone()
const particlesGeometry = new THREE.BufferGeometry()
particlesGeometry.setAttribute('position', pointsPositions)
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles)
In the vertex shader I just increase the gl_PointSize
and account for size attenuation using the formula gl_PointSize *= (1.0 / - viewPosition.z)
.
Now the most relevant part: the fragment shader. I tried different solutions:
- get the circle shape using this fragment shader:
float strength = distance(gl_PointCoord, vec2(0.5));
strength = step(0.4, strength);
strength = 1.0 - strength;
vec3 color = vec3(0.5137, 0.8039, 1.0);
gl_FragColor = vec4(color * strength, 1.0);
But with this I can still see the black square outline, as expected.
To solve this, I thought adding blending: THREE.AdditiveBlending
to the ShaderMaterial
initialization parameters should have solved the issue, but it turns out background particles can be seen through foreground ones as if the latter were semi-transparent. After a second though this made sense given pixel color values were added each other so overlapping particles would result in white pixels.
- use the alpha channel to make parts of a particles’ pixels transparent: the fragment code was the same as before but instead of multiplying the
strength
variable with the color, I used it as the value for the alpha channel ofgl_FragColor
.
I addedtransparent: true
to theShaderMaterial
initialization parameters as well to make three.js respect the transparency values (alphaTest: 0.001
didn’t seem to have any effect).
As you can see in the image, some particles still have the dark square outline, even though it should be transparent. I noticed anyway that only SOME particles have this problem, and that the outline isn’t black as before but it has the background color instead.
What solved this issue was addingdepthWrite: false
to theShaderMaterial
initialization parameters.
So, is this the correct way to do this kind of thing? Does it have any drawbacks?
And why does this work? I read several articles and questions about the order three.js uses to render things and from what I understand disabling depthWrite
prevents rendering the material to affect the depth buffer [1]. Does this means that rendering materials on already “placed” objects would normally cause their depth buffer to be rewritten, which in turns potentially lead to background objects being rendered on top of foreground ones?
Sorry for the quite long post and thanks in advance to anyone who will help me!