1

I'm working on a WebGL project that resembles a particle system. For aesthetic purposes, my single rendering pass is configured to blend additively, and I've disabled depth testing. I'm also clearing my viewport buffer to 50% gray, for the sake of argument.

gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.disable(gl.DEPTH_TEST);
gl.clearColor(0.5, 0.5, 0.5, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

I've uploaded a vertex buffer and index buffer to the GPU representing two partially overlapping triangles. Their vertices have a vec3 color attribute. I've assigned each vertex a color of 50% gray (0.5, 0.5, 0.5).

When I draw the triangles with my shaders, I'm relieved to report that my viewport buffer now looks 50% gray with two overlapping triangular regions that are white. The triangles are white because their fragments' color values were additively blended with those already in the color buffer.

Now, I re-upload the vertex buffer with the following change: the color of the vertices of the second triangle are now -50% gray (-0.5, -0.5, -0.5).

What I hope to accomplish is that my viewport buffer would look 50% gray with two overlapping triangular regions– one white, one black– which intersect, and produce 50% gray at their intersection. After all, adding a negative number should be the same as subtracting a positive number of the same magnitude.

What I see instead is a 50% gray viewport with only one triangular region– the white one.

I assume that this is because the output of my fragment shader is being clamped to a range whose lower bound is zero, before it's blended with the color buffer. I would like to know how to circumvent this clamping– ideally in WebGL, without requiring multiple render passes.

I'll be testing solutions in the source of the page at this URL: http://rezmason.net/scourge/issues/positive_negative_fragments.html

UPDATE

As an investigation, I've experimented with performing my additive blending in a frame buffer, and then drawing the frame buffer to the viewport buffer by texturing it to a unit quad– that's two separate draw calls, of course, which ideally I'd like to avoid.

That said, because I can explicitly set the format of the frame buffer to floating point, no clamping occurs with any value while I perform operations within that buffer. When I draw the buffer to the viewport, I assume that clamping finally occurs, but by then all the blending is already done.

My question is now substantially simpler: is there any way to initialize a WebGL or OpenGL context, so that its viewport is formatted as a floating point buffer?

Rezmason
  • 115
  • 1
  • 1
  • 10

1 Answers1

1

Use gl.blendEquation( gl.FUNC_SUBTRACT ). Then use positive values in your shader.

If you want do something in the middle, you can make some hacky things:

gl.enable(gl.BLEND);
gl.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);

It will give you this equation: equation description

You can now draw white triangle if you set color to (0.5, 0.5, 0.5, 0) and black triangle with color (0.5, 0.5, 0.5, 1).

If you want different colors I hope you get the point. You can compare different blending functions here: http://www.andersriggelsen.dk/glblendfunc.php

Edit: Sorry, my mistake. You should change

gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE);

to

gl.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);

I've forgotten which one is source and which one is destination. I've updated my answer.

Adrian Krupa
  • 1,877
  • 1
  • 15
  • 24
  • This does work, but I neglected to mention that I intend to upload triangles whose fragments will have positive _and_ negative values. I'll add this to my question. – Rezmason Sep 27 '15 at 03:41
  • I'm trying to rule out human error in my test, but `gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE)` seems to result in two black triangles, regardless of the value I assign to their fragments' alphas. – Rezmason Sep 27 '15 at 18:02
  • In your app or website? – Adrian Krupa Sep 27 '15 at 18:04
  • In my app, but I've made a miniature version that can be inspected: http://rezmason.net/scourge/issues/positive_negative_fragments.html – Rezmason Sep 27 '15 at 19:19
  • use `vec4 color attribute`. This blend functions use alpha value – Adrian Krupa Sep 27 '15 at 19:33
  • I believe I'm already representing the `color` vertex attribute as a `vec4`. – Rezmason Sep 27 '15 at 19:41
  • Try rendering triangle with constant color : (0.5, 0.5, 0.5, 0). Please post you vertex/fragment shaders – Adrian Krupa Sep 27 '15 at 19:50
  • You've taught me a bunch of new things about the way blend functions work, and your current solution **does** produce a white triangle and a black one, but when I modify their vertices so they overlap, the resulting color is only gray if the white triangle is drawn first. I think I need to address the central issue, which is that the fragment shader output and color buffer value are both being clamped before they're passed into the blend function in the first place. – Rezmason Sep 29 '15 at 14:27
  • @rezmason That's true and I think you cannot achieve more without separating black/white triangles. Bear in mind that only `gl.blendFunc(gl.ONE, gl.ONE);` will give you order independent blending/transparency – Adrian Krupa Sep 30 '15 at 12:23