Example 4 - GLSL Dot3 Bump MappingBased on the previous example we add a Dot3 Bump Map (Normal map). You may be making Bumpmaps yourself soon! The result looks like this:
The textures are in the Texture Pack 1 and Texture Pack 2 . To run this demo, you need Pyglet and Tristam Macdonald's Library Shader.py. You find everything on the installation page. Program descriptionTo implement dot3 bumpmaps, we want to change the normal vector of a surface element according to a direction vector in a selfmade bumpmap (normal map) image. A bumpmap image contains direction information encoded as RGB-Color values. A flat surface would be represented by normal vectors pointing straight up - a vector with the value 0/0/1. Generally the value of the vector components will vary between -1 and 1 (strictly speaking, the z-component just between 0 and 1 - it never points into the surface). To store it as an RGB value with 8 bit color components, you simply map
So a vector pointing straight up will be R/G/B = 127/127/255: this is a pastel blue tone. This is the reason why most bumpmaps look blueish. For illustration, here is a convex (pointing out) half-sphere: We load this image: texturecnt = 2 # Texturemap0.jpg = Colormap Texturemap1.jpg = Bumpmap Then a button would be nice to turn the effect on and off (let's take “B”) and make its state known to the shader program: global togglebump def on_draw(): .... shader.uniformi('togglebump', togglebump ) .... def on_key_press(...): .... elif symbol == key.B: togglebump = not togglebump print 'Toggle Bumpmap ', togglebump This out of the way, we face a challenge: up to this point it was sufficient to know what point in the texture map we are dealing with. Now we need to know in which direction the map is applied, because the bump map can not be rotated arbitrarily. This is called “uv-mapping”, because we need to know in which direction the texture coordinates (traditionally called u and v) point for every vertex. This direction vector is interpolated between the vertices. We can have our tiny little coordinate system for every point on the surface - this is called “tangent space”. We hide the direction vector for “u” in the vertex color (gl_Color.rgb). “v” is not needed - we want an orthogonal coordinate system, so we can get “v” by calculating a vector that points 90 degrees off “u” and 90 degrees off the Normal vector (gl_Normal.xyz). Color components go from 0 to 255 in OpenGL, so we store the vector like this: tangents.extend([ int(round(255 * (0.5 - 0.5 * tx))), int(round(255 * (0.5 - 0.5 * ty))), int(round(255 * (0.5 - 0.5 * tz))) ]) for every vertex in our polygon model. Well, there is another change in the example program. I did away with the rather complicated code for a sphere (remember, you can toggle between “sphere” and “torus” with the “F” key) and thereby simplified the code and made it faster. The only drawback is the non-uniform size of the triangles, but that seems to be a small price to pay. Look at the code for sphere generation, it is quite readable. GLSL Dot3 BumpmappingNow that we have added the “u” vector to the polygon model, we can have a look at the shader. In the vertex shader we build the “tiny little coordinate system” - 3 orthogonal vectors: // Create the Texture Space Matrix normal = normalize(gl_NormalMatrix * gl_Normal); tangent = normalize(gl_NormalMatrix * (gl_Color.rgb - 0.5)); binormal = cross(normal, tangent); mat3 TBNMatrix = mat3(tangent, binormal, normal); The “T”angent/“B”inormal/“N”ormal Matrix code is ok, when you know that the “cross” (or “outer”) product of two vectors produces a new vector 90 degrees off as we needed. If we want to do further calculations in “tangent space”, we need to transform the light- and eye-vectors to this coordinate system. Sorry that this sounds dreadful : vVertex = vec3(gl_ModelViewMatrix * gl_Vertex); lightDir0 = vec3(gl_LightSource[0].position.xyz - vVertex) * TBNMatrix; lightDir1 = vec3(gl_LightSource[1].position.xyz - vVertex) * TBNMatrix; eyeVec = -vVertex * TBNMatrix; Once all relevant vectors are transformed, you can do your calculations (in the pixel shader) just as before. The “dot3” calculation is done in the line vec3 norm = normalize( texture2D(my_color_texture[1], gl_TexCoord[0].st).rgb - 0.5); .... vec3 N = (togglebump != 0) ? normalize(norm) : vec3(0.0, 0.0, 1.0 ); We sample the color from the Texturemap1 (0.0 to 1.0), shift it to -0.5..0.5 an normalize (-1.0 to 1.0). If you have not activated bumpmapping, the normal vector will point “straight up” (x=0,y=0,z=1) - remember, this is the “tangent space” coordinate system! With togglebump != 0 the normal will point in the direction given by the bumpmap. Enjoy ! But there is more - let us look at Example 5 - Parallax Mapping with GLSL. References: |