I have the following code in three.js which takes color data from a loaded point cloud (a collection of colored vertices in a single mesh), and makes vertices transparent in accordance with how bright the color is:
const uniforms = { //These are defaults for brightness threshold options
color: { value: new THREE.Color(0xffffff) },
brightnessThreshold: { value: 0.5 }, //set `value: 0.5` for 50% threshhold
size: { value: 0.2 }, // this defines the size of the points in the point cloud
invertAlpha: { value: 1 } // this defines if the alpha channel is inverted or not in translucency
};
basicMaterial = new THREE.PointsMaterial({ size: 0.2, vertexColors: true }); //`size: 0.2`, usually
thresholdMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: `
uniform float size;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform vec3 color;
uniform float brightnessThreshold;
varying vec3 vColor;
void main() {
float brightness = dot(vColor, vec3(0.299, 0.587, 0.114)); // correct way to calculate brightness
if (brightness < brightnessThreshold) {
discard;
} else {
gl_FragColor = vec4( vColor * color, 1.0 );
}
}
`,
transparent: true,
depthTest: true,
depthWrite: true, // originally set to false
vertexColors: true, // ensures that the colors from geometry.attributes.color are used
blending: THREE.NormalBlending
});
translucentMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: `
uniform float size;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform vec3 color;
uniform float brightnessThreshold;
uniform int invertAlpha;
varying vec3 vColor;
void main() {
float brightness = dot(vColor, vec3(0.299, 0.587, 0.114));
float alpha = brightness;
if (invertAlpha == 1) {
alpha = 1.0 - alpha;
}
gl_FragColor = vec4( vColor * color, alpha );
}
`,
transparent: true,
depthTest: true, // enable depth testing
depthWrite: false, // disable depth writing
vertexColors: true,
blending: THREE.NormalBlending
});
For the above code, I notice the translucency shading works well when looking from one side of the mesh (where if multiple “voxels” are in front of each other in the point cloud, the frontmost voxels obscure the backmost ones). This is what the mesh should look like:
However when I rotate the camera to look at the “back” of the mesh, only the backmost vertices are displayed when two or more colors are in front of each other:
I’ve already tried setting depthTest
and depthWrite
to both true and false (and all combinations thereof), and found that doesn’t seem to make a difference. Note again that everything works perfectly fine when the camera is on one half of the scene, and this problem only occurs once you rotate past a certain point (here you can see how abrupt it can be, with the left half rendering correctly, and the right just showing the “back” of the point cloud):
My guess would be that there should be some way to locally flip the z-buffer or something if a ray lands on the camera from the “wrong” angle, but I have no idea how I’d do that. If anyone here could help me fix this issue, I’d be incredibly grateful!