Cookyt
Cookyt

Reputation: 1607

WebGL fragment shader not branching correctly on if statement

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?

Upvotes: 8

Views: 4715

Answers (3)

Arnaud
Arnaud

Reputation: 266

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.

Upvotes: 0

Andon M. Coleman
Andon M. Coleman

Reputation: 43319

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.

Upvotes: 0

Marcel Blanck
Marcel Blanck

Reputation: 866

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

You can check this with getContextAttributes()

Upvotes: 0

Related Questions