Transparency artifacts depending on distance to camera

There are many threads regarding difficulties getting good results with semi-transparent objects and I have non-systematically explored various solutions proposed. This include changing the renderOrder, multiple combinations of depthWrite and depthTest as well as all blending modes all to no avail.

I have an object leaving a trail that becomes more transparent as the objects is further away from the tail of the trail. When the camera is quite close to the trail I see no artifacts but when it’s further apart I see what appear to be triangles that are simply not rendered.

Here is how it looks like when it’s glitchy

but if I zoom in (same frame, the animation is stopped)

it all looks good.

Here is the constructor for my class which I believe has the relevant parts to get this working

export class Trail {
  private geometry: THREE.BufferGeometry;
  private material: THREE.ShaderMaterial;
  private mesh: THREE.Mesh;
  private trailVertices: Float32Array;
  private trailIndices: Uint16Array;
  private trailAlpha: Float32Array;
  private currentSegmentIndex: number = 0;
  private currentCurveIndex: number = 0;
  private totalSegments: number = 0;
  private prevCurve: THREE.Vector3[] | null;
  private lastPosition: THREE.Vector3;
  private color: THREE.Color;

  constructor(
    pointGroup: THREE.Group,
    initialPosition: THREE.Vector3,
    scene: THREE.Scene,
  ) {
    this.trailVertices = new Float32Array(MAX_VERTICES * 3);
    this.trailIndices = new Uint16Array(
      MAX_SEGMENTS * (NUM_CURVE_POINTS - 1) * 6,
    );
    this.trailAlpha = new Float32Array(MAX_VERTICES).fill(1.0);
    this.lastPosition = initialPosition.clone();

    this.geometry = new THREE.BufferGeometry();
    this.geometry.setAttribute(
      'position',
      new THREE.BufferAttribute(this.trailVertices, 3),
    );
    this.geometry.setIndex(new THREE.BufferAttribute(this.trailIndices, 1));
    this.geometry.setAttribute(
      'alpha',
      new THREE.BufferAttribute(this.trailAlpha, 1),
    );

    const sphere = pointGroup.getObjectByName('point-sphere');
    // @ts-ignore `point-sphere` is a THREE.Mesh
    this.color = sphere!.material.color;

    this.material = new THREE.ShaderMaterial({
      vertexShader: `
        precision highp float;
        varying float vAlpha;
        attribute float alpha;
        void main() {
          vAlpha = alpha;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        precision highp float;
        varying float vAlpha;
        uniform vec3 uColor;
        void main() {
          gl_FragColor = vec4(uColor, vAlpha);
        }
      `,
      uniforms: {
        uColor: { value: this.color },
      },
      //transparent: true,
      //depthWrite: false,
      //depthTest: true,
      blending: THREE.AdditiveBlending,
      side: THREE.DoubleSide,
    });

    this.mesh = new THREE.Mesh(this.geometry, this.material);
    this.mesh.renderOrder=1;
    scene.add(this.mesh);
    this.mesh.layers.set(1);
    this.prevCurve = null;
  }

Commenting in/out this lines:

//transparent: true,
//depthWrite: false,
//depthTest: true,

makes no difference to the final result.

If anybody could help with this it would be greatly appreciated.

Finally if somebody wants to see this in action you could attempt to visit my app: https://quaternions.maxwellrules.com/

and then once it’s loaded:

  1. control+o (or cmd if mac)
  2. select the adding satellites script
  3. click execute script
  4. play with the time control on the bottom of the canvas to make things go faster and make the trail appear more clearly. (The moving objects starts somewhere between South Africa and Antarctica)

To overlay geometry like that… you’ll have to either move the geometry further from the sphere, or use

material.polygonOffset to give it an artificial depth offset to prevent that z-fighting.
https://threejs.org/docs/#api/en/materials/Material.polygonOffset

      material1.polygonOffset = true;
      material1.polygonOffsetFactor = 1;
      material1.polygonOffsetUnits = 0.1;

(related: How to use polygonOffset solving Z-fighting poblems · Issue #2593 · mrdoob/three.js · GitHub )

1 Like

Thanks for the advice! I tried it and while it ameliorated the issue, the z-fighting gets better faster as you zoom in close to the trail, it still persists. I forgot to mention on my original post that I had already added an offset to the trail itself to make sure it is above the globe (1.003 * radius). Increasing the radius more makes it even better but I start to lose the decal effect.

did you try cranking the factor and/or units?
they interact with each other… and sorta control how the offset is scaled over depth…

1 Like

What are the near and far values of your camera frustum? If the values are quite large, on a scale of a planet for instance, you may want to look at using logarithmic depth buffer on your camera…

@manthrax I did play with the factor and units but didn’t make a noticeable difference.

@Lawrence3DPK My far camera fulcrum is BIG. The reason for that is that the simulation I have accurately represents the position of the Moon too and I have a mode where i can look at the Moon from the POV of a satellite. For that I need the large far fulcrum. I work in units of km so my sphere is actually 6371 km and the far fulcrum reaches the Moon at a distance of ~384_000 km.

give logarithmic depth buffer a try and let us know…

1 Like

@Lawrence3DPK You were right on your diagnostic. The issue was that near and far fulcrum were trying to span too many orders of magnitude. When enabling the logarithmic depth buffer I completely lost the trail. (Presumably because I lost the resolution to separate the globe from it?). However once it was diagnosed the problem was easy to fix.

For the main view of the app with the globe I have sensible near and far fulcrums which combined with some of the suggestions from @manthrax resolves all glitches robustly.

I actually didn’t want the POV from satellite cameras to render the trail [*]. Since the only obvious artifact was the glitchy trail I just used the large near to far fulcrums (I did cut back a bit to just what is required) so both the globe and Moon could be seen.

[*] I had in fact already placed the trail on a different layer to make it invisible to the POV cameras.

Thanks everybody for the help and in particular to @Lawrence3DPK for pin pointing the issue so precisely.

2 Likes