I'm implementing normal mapping on my toy pathtracer. I need to compute the tangent (and bitangent) of any sphere point in order to create the matrix that will transform tangent space to world space.

More specifically, I want the tangent that runs along the u texture coordinate, and the bitangent that runs along the v coordinate.

Here's how I implemented it:

  • At the intersection point P, compute the spherical coordinates (theta, phi) of P.
  • Add a tiny delta to the angle that relates to the u texture coordinate (phi), finding another point Q on the sphere.
  • Compute Q - P and normalize it to have the tangent.
  • The bitangent is then computed by doing the cross product of the tangent and the normal at the point P.

Relevant code on the sphere collision function:

    hitRecord.point = ray.pointAtParameter(t);

    Vec3 localCoord = hitRecord.point - center;
    float theta = asin(localCoord.y / radius); //[-pi/2,pi/2]
    float phi = atan2(localCoord.z, localCoord.x); //[-pi,+pi]

    float deltaPhi = phi + FLOAT_BIAS;
    Vec3 deltaPoint(radius * cos(theta) * cos(deltaPhi),
        radius * sin(theta),
        radius * cos(theta) * sin(deltaPhi));

    //Assume local space to be aligned with world space
    //So this is also the tangent in world space
    hitRecord.tangent = deltaPoint - localCoord;

    hitRecord.normal = localCoord / radius;
    hitRecord.bitangent = hitRecord.tangent.cross(hitRecord.normal);

This seems to work, but all this trigonometry is not light and makes me wonder if there is a better way of doing it. Does anyone has any ideias/pointers?

  • $\begingroup$ Would like to add that normal map is about bump mapping, it wouldn't be "lighting" the surface. Lighting equation does that and so check that if you aren't getting them lit. $\endgroup$ Commented Aug 15, 2017 at 6:29
  • $\begingroup$ Couldn't you get the tangent by just doing a cross product between the normal and the main axis of your spherical coordinate system? $\endgroup$
    – russ
    Commented Aug 18, 2017 at 8:57
  • $\begingroup$ whoops so he did, i didn't read that far down ;) $\endgroup$
    – russ
    Commented Aug 23, 2017 at 5:13

1 Answer 1


You can do this a little bit more efficiently and accurately by calculating derivatives of $P$ with respect to $u, v$—or the derivatives with respect to $\phi, \theta$, equivalently (up to a scalar multiple).

Start by expressing $P$ in terms of the spherical coordinates: $$\begin{aligned} P_x &= r \cos \theta \cos \phi \\ P_y &= r \sin \theta \\ P_z &= r \cos \theta \sin \phi \end{aligned}$$

Now you can get the tangent as $T = \partial P / \partial \phi$, which works out as: $$\begin{aligned} T_x &= -r \cos \theta \sin \phi \\ T_y &= 0 \\ T_z &= r \cos \theta \cos \phi \end{aligned}$$

Once you normalize it, the $r \cos \theta$ factors drop out and you're left with just $T = (-\sin \phi, 0, \cos \phi)$. Pretty simple!

You can similarly get the bitangent as $B = \partial P / \partial \theta$, or you can just calculate it as the cross product $N \times T$, which is probably faster. (Note it should be $N \times T$, not $T \times N$, assuming you want $T, B, N$ to form a right-handed basis in that order.)

The above approach will work for pretty much any kind of surface that can be parameterized, not just spheres. However, in the case of spheres with the usual $\phi, \theta$ coordinates, there's an even quicker shortcut that you can do with just vector math, no trig at all. Let $C$ be the center of the sphere, and $A$ be a vector representing the axis of the spherical coordinates—i.e. pointing from the center toward the north pole. Then you can just calculate $T = \text{normalize}\bigl(A \times (P - C) \bigr)$, then $B = N \times T$ as before.

  • $\begingroup$ Thank you! I knew derivatives were the answer, just couldn't figure it out by myself. The vector way is really elegant and clever. $\endgroup$
    – MadEqua
    Commented Aug 15, 2017 at 14:26

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.