8
votes

I'm working on a WebGL program which implements shadow mapping. After computing the depth values at each fragment, I have an if statement at the end which computes the lighting based on whether the fragment is in a shadow or not.

gl_FragColor = vec4(calcLighting(light0, eye_dir, eye_dir_norm, normal), 1);
if (light_tex_coord.z - depth <= 0.0) {
  gl_FragColor += vec4
      ( calcLighting(light1, eye_dir, eye_dir_norm, normal)
      , 0.0
      );
}

Shadows not being correctly computed, branch never taken

where depth is the depth of the shadow map and light_tex_coord is the fragment's position in the scene space of light1. light1 is a point light rotating around the model, and light0 is a point light statically positioned at the camera.

The issue here is that the if branch is never taken, so the scene only has light0 applied to it. I've checked that depth, light_tex_coord, and calculateLighting work correctly.

Here's the strange thing, though, replacing the above with the following code:

if (light_tex_coord.z - depth <= 0.0) {
  gl_FragColor = vec4(0,1,0,1);
} else {
  gl_FragColor = vec4(1,0,0,1);
}

Shadowed fragments correctly computed and drawn as red and green

Causes shadowed areas to be correctly drawn in red, and unshadowed to be drawn in green. That is, the branch is correctly evaluated. Replacing it with this:

gl_FragColor = vec4(calcLighting(light0, eye_dir, eye_dir_norm, normal), 1);
gl_FragColor += vec4
    ( calcLighting(light1, eye_dir, eye_dir_norm, normal)
    , 0.0
    );

Lighting computed without shadows

Causes lighting to be correctly computed (sans shadows, though). It seems that when I call the more expensive calcLighting function in the if statement, it doesn't even bother taking it.

Further, I've tried applying the lighting in several ways including using the clamp function; always doing the addition and using a terinary operator to multiply the second calcLighting call by 1 or 0, and arranging my if statement in different ways. Nothing seems to work.

Is there something I'm missing about how branching works in webgl?

3
Have you tried accumulating the color to a separate vec4 variable and only assigning gl_FragColor the value of this other variable one time. Writing to gl_FragColor multiple times in a shader is supposed to be supported as is reading the value of it (which is necessary to implement +=) but by all accounts this issue seems to be related to one or both of these actions given your control flow. - Andon M. Coleman
I tried replacing all instances of gl_FragColor in the above snippets with an accumulator vec4 acc;. I only assigned at the end of the shader with gl_FragColor = acc; No change in any of the above cases. - Cookyt
Could you provide the entire fs code? Also, would it make any difference if you change code to gl_FragColor += vec4( calcLight(..), 1.0 )? [note 1.0 alpha value] - Abstract Algorithm
Changing the alpha from 0.0 to 1.0 has no effect. It's a large chunk of code, so I put it on pastebin. vertex shader: pastebin.com/XLYSwvL7 fragment shader: pastebin.com/bYdvuRjj The snippet in question is from the bottom of the fragment shader. - Cookyt
Since this is WebGL, it would be helpful if you can make a page where we can try this out / change stuff if we manage to reproduce the error. It kind of seems like a driver issue. - the swine

3 Answers

0
votes

Maybe the WebGl context does not support depth. Then expressions using it would be ignored.

You can check this with getContextAttributes()

0
votes

Instead of the branch, have you considered something really radical like this:

gl_FragColor += vec4 (
    calcLighting(light1, eye_dir, eye_dir_norm, normal)
    , 0.0
    ) * abs (min (sign (light_tex_coord.z - depth), 0.0));

It is not the prettiest solution in the world, but it just might get you something more along the lines of what you are looking for. It basically takes your light_tex_coord.z - depth <= 0.0 conditional expression and makes it into a nice floating-point 0.0 or 1.0 that you can multiply the lighting by to either keep or discard the branch result. Kind of the way shaders used to work before dynamic flow control was introduced.

0
votes

The topic is 2 years old but here's my 2 cents:

I had a similar problem and found an page on the Internet with a discussion containing the following sentence :

[...] (unless you do crazy stuff like image writes inside conditional blocks). [...]

So I tried to put the pixel color in a temporary variable

vec4 col;

local to main(), in the "if" statements I modify only col, and I set the pixel color only at the very end:

gl_FragColor = col;

This solved my problem. Of course it may require annoying additionnal programming to let the flow reach that last line in every situation.