On the alternative to gl_FrontFacing

Ok, this post is still about three.js and GLSL shaders, but unlike raytracing loads of cubes or relativity simulations, this is something more down-to-earth. In this post, we shall discuss some possible alternatives to gl_FrontFacing in your WebGL apps…

What’s gl_FrontFacing and why’d we even need it ?

Suppose you wrote this nice WebGL app with the sphere and custom lighting:
sphere + custom lightingVery basic so far, we have vertex normal transformed to view space and colored the surface based on its dot product with some fixed “light” direction. In real world, however, we are dealing with meshes that do not look quite like this sphere, so let’s move on to something more complex:
something more complexSuddenly we discover that three.js only renders one side of the mesh by default, so let us fix that:
gl_FrontFacing_02At this point our shader is broken – the lighting on the inside is all wrong. To fix it, we need to know which side of the mesh does current pixel belong to, and this information is provided in gl_FrontFacing. So we add following code to the fragment shader:

vec3 normal = v_normal;
if (!gl_FrontFacing) {
	normal *= -1.0;

And – voila:


What then seems to be the problem ?

No, we did nothing wrong, and this code should totally work. Except it does not. As soon as I shipped this code, the client came back with the screenshots where the lighting was now all wrong on the front side:
Screen Shot 2015-09-17 at 2.00.20
WTF? After raping the google for some time, I’ve found that other people had the same problem on the same hardware, and their workaround was this:

vec3 normal = v_normal;
if (gl_FrontFacing == false) {
	normal *= -1.0;

That’s bullshit, but it worked! For about a month, until the problem resurfaced again on another hardware. Simply put, you just can’t use gl_FrontFacing, like… at all.

But you need an alternative. Of course, I was not the first one looking for it, so SO already had this solution:

poor man gl_FrontFacingThe comment warns the solution is bad, but I tried it any way:


Everything was exactly as advertised – unpleasant artifacts around the edges. So, better solution was needed and, after some amount of trial and error, it was found. I have noticed that vertex position in view space is, basically, the direction away from the camera, and so the sign of its dot product with our normal vector would tell us quite precisely if the normal is pointing towards the camera or not. All we have to do now is to export this position for the fragment shader:

varying vec4 v_position;
void main () {
	v_position = modelViewMatrix * vec4 (position, 1.0);

and our test now becomes:

vec3 normal = v_normal;
if (dot (normal, v_position.xyz) > 0.0) {
	normal *= -1.0;

Here is how it looks:

better solution

Ah, but wait – what are those out-of-place pixels? They are reminders that our new test is very good approximation of gl_FrontFacing, but still only an approximation. So we need some kind of hack to get rid of these bad pixels. The hack that I am using at the moment is to gradually change the normal instead of rapid jump:

vec3 normal = v_normal;
normal *= 1.0 - 2.0 * smoothstep (0.0, ,
	dot (normal, v_position.xyz));

which propagates nicely into the lighting:

even better fix

This hack removes the most of the artifacts but, if pushed too far, brings this shit back:

hello old friend

and so its implementation requires some tweaking. But it gets the shader working where gl_FrontFacing does not, for now, which is a good thing, don’t you think.

Update :)

We can do even better job here if we use WebGL extensions. Following code is pretty good estimate of the face normal, that works on both front and back sides:

#extension GL_OES_standard_derivatives : enable
vec3 fdx = dFdx(v_position.xyz);
vec3 fdy = dFdy(v_position.xyz);
vec3 faceNormal = normalize(cross(fdx,fdy));

Therefore, testing its dot product with our vertex normal results in this probably pixel-perfect shader:

hello old friend


0 Responses to “On the alternative to gl_FrontFacing”

  1. Leave a Comment

Ask a Question

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Old stuff

September 2015
« Aug   Nov »

Oh, btw…


%d bloggers like this: