Two-sided tangent space lighting

Hi, I’m trying to do two sided lighting in tangent space. Here is the problems I’m facing.

I’ve realized that I need to do the backface check in the pixel shader, not the vertex shader. My reason for this is: on a curved surface a vertex’s normal isn’t perpendicular to the surface, it’s usually a blend between itself and the normals of other vertices that are at the same position (from neighouring polygons). So when a polygon is at a very sharp angle where it’s almost backfacing, it’s posible for the closest vertex to be found to be front facing, while the farthest one is found to be back-facing. This causes really bad artifacts on the polygon as the results from these two situaions get interpolated across the polygon.

But if I reverse the normal in the pixel shader, does this not screw up the tangent basis and all the vectors I’ve transformed using this basis (like the light vector and the half angle vector)?

Am I over-complicating the situaion or am I missing something?

Any comments are welcome.



Flip the normal at the fragment level rather than applying it at the vertex (which doesn’t work because you can’t multicast the vertices).

Reversing the normal doesn’t screw anything up, flipping the normal AFTER you fetch the bumpmap texel means the bumpmap surface will also be correctly reversed (pimples -> dimples) which is hopefully what you want.

Thats good to know thanks. What would I do if I didn’t want pimples -> dimples on the backface but instead wanted the bumps to stay as bumps?


You’d have to reflect the replacement normal through the tangent space basis instead of just negating it, using the interpolated normal from the tangent space basis.

Something like:

new_normal = bump_map_vector - 2 * (bump_map_vector . tangent_basis_normal) * tangent_basis_normal

Now normally if you’re in tangent space then the basis normal is constant at 0,0,1 but this formulation could be used to work in object space etc too. So rationalizing this for an implementation in tangent space means you’re simply taking bump_map_vector[2] and negating it instead of the whole vector.

That would leave you with your pimples.

In summary, for reflection and pimples -> dimples it’s bump_map_vector *= -1.0 but for pimples->pimples it’s bump_map_vector[2] *= -1.0