I'm trying to add bump mapping to my ray tracer, and I'm currently struggling to add bump mapping to it, as I don't seem to get the transformations from texture space to world space correctly.

I have an arbitary Triangle in world-Space defined by it's vertices. Each of the vertices store a normalized UV-Coordinate to interpolate the position on the height map for an Intersection Point. The height map is a gray-Scale Image So when I intersect with the Triangle, I calculate the x and y derivatives of the height map at the corresponding uv-coordinate, transform the x and y unit Vectors (of the Texture) to world-Space, and calculate the changed normal. This currently looks like this:

        Point p = lerpbar(uv_vert1, uv_vert2, uv_vert3, 
               intersection.barycentric.u, intersection.barycentric.v);
        float gx = this->_bmap->getColorDX(p);
        float gy = this->_bmap->getColorDY(p);
        Vector wx = (To_World_Matrix * Vector(1, 0, 0))).normalize();
        Vector wy = (To_World_Matrix * Vector(0, 1, 0))).normalize();
        Vector n = intersection.normal();
        return n + (gx * cross(n, wy) - gy * cross(n, wx));

However, I just can't figure out what Matrix I'd need to use so this produces correct results, as I just don't really understand how to map from unit Vectors to World-Space Vectors when I only know how arbitay Triangle Edges relate to arbitay Vectors in Texture Space.


1 Answer 1


The normal of a given point on the height map is perpendicular to the 2 vectors defined by gx and gy. The original surface normal is not relevant to this. So in tangent space, the normal is:

Vector tangent = Vector(1, 0, gx);
Vector bitangent = Vector(0, 1, gy);
Vector normal = normalize(cross(tangent, bitangent));

You then need to convert the normal from tangent space to world space. For this you need the so-called "TBN matrix" (tangent, bitangent, normal matrix). There are plenty of articles around explaining this. But the gist is:

Vector T = normalize(To_World_Matrix * intersection.tangent());
Vector B = normalize(To_World_Matrix * cross(intersection.tangent(), intersection.normal()));
Vector N = normalize(To_World_Matrix * intersection.normal());
mat3 TBN = mat3(T, B, N); // GLSL syntax

The last thing is to do a change of basis to move the normal from tangent space to world space:

Vector normal_world_space = normalize(TBN * normal);

Keep in mind that the order of some cross-products might be inverted in what I wrote if you get normals pointing inside the surface.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.