In this tutorial:
For every point in the grid, calculate the vector dir from the point (0.5, 0.5). If this vector's length is smaller than 0.5, displace the surface in the Z direction, using the equation of a (hemi)sphere:
distance = sqrt(dir.x × dir.x + dir.y × dir.y) displacement = sqrt(0.25 - distance × distance)
This will create a hemispherical bump in the plane. Note that the grid must be sufficiently tessellated or the bumps will not be smooth.
Now create a grid of bumps with variable dimensions. Instead of using the single point (0.5, 0.5), find the distance to the nearest bump for the displacement. Hint: the bump coordinate can be found by casting each scaled vertex coordinate to an integer.
We now have a grid of geometric bumps with a correct silhouette however it still looks flat as the normals have not been set yet. The normals for each bump (Nb) can be calculated using the displacement value and the direction vector from the center of the bump to the point on the surface.
Nb = vec3(dir.x, dir.y, displacement) × 2
To normalise the normal of a sphere we divide by the radius, or equivalently multiply the reciprocal of the radius. So here we multiply by 2 instead of dividing by the bump radius of 0.5. Same as for displacing vertices, normals should not be altered unless the distance to the bump is less than 0.5.
Now include the code from last week's tutorial to draw a torus.
To map the bumps to the torus, we use the u and v values from the torus parametric equation as the input coordinates to generate the displacement, e.g. dir = vec2(u, v).
Instead of displacing in the Z direction (the plane's normal direction) we displace in the direction of the normal of the torus, Nt. This gives the correct geometry but just like before, normals are needed to make it look good.
Imagine wrapping our original flat bumpy surface around an object, essentially rotating the normals and bumps to match the surface of the object.
This normal Nb from the bumpy plane's surface cannot be applied directly. Instead, it is applied in the tangent space of the torus' surface.
Given a point on the torus, we generate not only a normal N but also tangent T and binormal B vectors. These direction vectors are orthogonal, and if normalised are also orthonormal, and form a basis - the TBN basis - used for the bump normal. In other words, the axis vectors T, B and N in the image above are used instead of the standard basis axes X, Y and Z.
Lighting calculations can be done in either eye space or tangent space. When normal maps stored as textures are used it is common to transform all vectors to tangent space however for this application using analytic calculations and procedural generation it is easier to transform the tangent space normal (which is equal to our bumpy plane's normal from above) into eye space.
To transform the tangent space bumpy normal Nbts into the eye space bumpy normal Nbes we perform a change of basis calculation using the TBN matrix (as discussed in the lectures)
Nbes = TBN × Nbts
Instead of actually creating the TBN matrix and using matrix multiplication, the calculation can be performed by multiplying the components of the tangent space bump normal Nbts by T, B and N vectors and adding the result
Nbes = T × Nbts.x + B × Nbts.y + N × Nbts.z
Two tangent vectors Tu and Tv, not necessarily orthogonal to each other, in the direction of u and v may be found by taking partial derivatives with respect to parameters u and v. The cross product Tu × Tv then gives the normal. The binormal B may be found by taking the cross product N × T, choosing Tu as T.
(For a torus (but not in general) Tu and Tv are orthogonal, so T and B are given directly by the partial derivatives. Analytic expressions for all three vector T, B and N can be determined.)
In the following B is calculated using N × T.
N.x = cos(u) × cos(v); N.y = cos(u) × sin(v); N.z = sin(u); T.x = -sin(u) × cos(v); T.y = -sin(u) × sin(v); T.z = cos(u); B.x = N.y × T.z - N.z × T.y; B.y = -(N.x × T.z - N.z × T.x); B.z = N.x × T.y - N.y × T.x;
Now both vertex positions and vertex normals have been generated for a bumpy torus. Supply the normals to OpenGL and enable lighting.
Additionally, the bumping may be performed in a vertex shader. Parameters u and v are passed to gl_Vertex instead of positions x, y, z. Vectors T and B can then be generated in the shader as well as N and the vertex positions before applying the bumps.