Can't seem to multiply something in fragment shader...precision issue?

Viewed 114

I'm trying to unravel a bit of the dark unknown that is webgl via PixiJS library. At this point, it probably should be noted that I haven't used PixiJS for awhile either, but it seemed to be a much more preferable approach than to manipulate the lower level underlying webgl state.

My logic is currently this: I create two buffers ("current" and "previous") with the same fragment shader as well as a render buffer and a "render" fragment shader. Both buffers start off with the same content: a fill that is rgb(255, 10, 0) for every pixel.

Right now, the fragment shader does this:

void main(){
    vec2 pixel = texture2D(buffer, vTextureCoord).xy;
    pixel.x = pixel.x * 0.95; // decrease red a bit
    pixel.y = pixel.y * 1.05; // increase green a bit
    gl_FragColor = vec4(pixel, 0.0, 1.0);
}

In the animation loop this is what is supposed to happen:

  1. render previous buffer into the current buffer
  2. render the current buffer to the render buffer (for screen display)
  3. current and previous buffers are swapped: current buffer now becomes the previous buffer

Thus what should be happening is the red progressively fades out and green fades in. Right now, what happens is red fades out to black and green never fades in. It settles on rgb(9, 10, 0).

From playing with (lots of random guessing), I've found that if I start with a higher green, say 11 or higher it works. Or if I increase the 'green rate' to 1.06 in the shader. Then it works. But for some reason, it does not at green 10 and 1.05 rate.

My only guess is maybe some precision/rounding issues on the GLSL side but since I can't trace out any values within the shader, I don't really know. Nor do I know how to fix it. Though, quite possible/probable I'm just doing something else wrong entirely.

What I currently have in jsfiddle: https://jsfiddle.net/nfrLjhgk/1/

1 Answers

The problem is related to the texture format. The color channels are stored in a single byte using a normalized floating point format. The floating point values in range [0.0, 1.0] are mapped to the integral values in range [0, 255].

The start value of the green color channel is 10. 10 * 1.05 is 10.5. Since the value is truncated when stored in the texture, the result is 10.

The simples solution is to change the factor:

pixel.y = pixel.y * 1.05; // increase green a bit

pixel.y = pixel.y * 1.1; // increase green a bit

Or calculate the maximum of a multiplication and addition:

pixel.y = max(pixel.y * 1.05, pixel.y + 0.005); // increase green a bit

Alternatively you can create a WebGL 2.0 context and use a different texture format.

Related