# Bump mapping with triangles and height maps

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.

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.