0

I have a relatively simple setup with custom shader and a custom blend mode:

  • A Scene and a PerspectiveCamera
  • An IcosahedronBufferGeometry
  • A custom new THREE.ShaderMaterial with custom shaders
  • Some new THREE.Points with the icosahedron and that custom shader material

My shader material has a custom blend mode:

shaderMaterial.blending =      THREE.CustomBlending;
shaderMaterial.blendSrc =      THREE.OneFactor;
shaderMaterial.blendDst =      THREE.OneFactor;
shaderMaterial.blendEquation = THREE.AddEquation;

and my fragment shader currently only does this:

gl_FragColor = vec4(0.0, 0.1, 0.0, 0.0);

Since I'm rendering into a fully black canvas, the expected color of drawn points should be, considering the blending mode:

 SRC                     DST
r = 0                   r = 0                 r = 0
g = 0.1   *   1.0   +   g = 0   *   1.0   =   g = 0.1
b = 0                   b = 0                 b = 0

However, I get a totally different result:

points on an icosahedron

The points are rendered much more brightly than the expected 0.1. Using gl_FragColor = vec4(0.0, 0.2, 0.0, 0.0); the points become fully saturated (g = 255).

What causes this? I have some ideas and hopefully can push me in the right direction.

  1. Perhaps three.js appends some code to my fragment shader, modifying the gl_FragColor?
  2. Maybe I'm not aware of some extra rendering / post processing step that three.js does?
  3. Maybe I've misunderstood the math regarding blending modes?
  4. Maybe there is some implementation error in three.js?
  5. Perhaps it's a browser / canvas / color space thing? (However, I cannot imagine any transformation that causes a green value of 0.2 to become fully saturated (255) in any color space transformation!)

Any help would be greatly appreciated. Thanks!

6
  • Is premultipliedAlpha on or off? Commented Jan 3, 2020 at 12:55
  • Premultiplied alpha is not set (default = false) but to my knowledge that should not matter either way since it's a custom shader. I've tested it too: with true or false it doesn't seem to change the outcome. Commented Jan 3, 2020 at 13:22
  • premultipliedAlpha default is true! It matters. See WebGL and Alpha Commented Jan 3, 2020 at 13:25
  • I don't know the details of three.js implementation but naievely, if all it does is take the points and indices for an IcosahedronBufferGeometry and pass gl.POINTS instead of gl.TRIANGLES then every point is going to be drawn 5 times since every point is used by 5 triangles. Commented Jan 3, 2020 at 14:31
  • @Rabbid76 Aha! I thought you were referring to the premultipliedAlpha property of Material which defaults to false and, I believe, shouldn't influence CustomShaders that don't implement them... I will look into this resource and report back, thanks! Commented Jan 3, 2020 at 14:36

1 Answer 1

1

The issue is every point is being drawn 5 times because IcosahedronBufferGeometry is designed for drawing triangles. Each vertex is shared by 5 triangles. We can test this by augmenting drawArrays

Running the code below we see it's asking to draw 60 points even though visually there would only be 12 (the code below does not draw anything visible, it's only designed to check the draw call).

WebGLRenderingContext.prototype.drawArrays = function(origFn) {
  return function(...args) {
    console.log('drawArrays', ...args);
    origFn.call(this, ...args);
  };
}(WebGLRenderingContext.prototype.drawArrays);

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});
  const camera = new THREE.Camera();
  const scene = new THREE.Scene();
  const geo = new THREE.IcosahedronBufferGeometry(1);
  const material = new THREE.MeshBasicMaterial();
  scene.add(new THREE.Points(geo, material));
  renderer.render(scene, camera);
}

main();
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, this was the issue!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.