Bump mapping with triangles and height maps
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.