ch10_NormalMap.html
in an HTML5 browser.We already know what the normal represents, so let's look at the other two vectors. The tangent essentially represents the up (positive Y) vector for the texture relative to the polygon surface. Likewise, the bitangent represents the left (positive X) vector for the texture relative to the polygon surface.
We only need to provide two of the three vectors as vertex attributes, traditionally the normal and tangent. The third vector can be calculated as the cross-product of the other two in the vertex shader code.
js/webgl/Utils.js
as calculateTangents
and used in Scene.addObject
.var tangentBufferObject = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, tangentBufferObject); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(Utils.calculateTangents(object.vertices, object.texture_coords, object.indices)), gl.STATIC_DRAW);
ch10_NormalMap.html
, the tangent needs to be transformed by the Normal matrix just like the normal does to ensure that it's appropriately oriented relative to the world-space mesh. The two transformed vectors can be used to calculate the third as mentioned earlier.vec3 normal = vec3(uNMatrix * vec4(aVertexNormal, 1.0)); vec3 tangent = vec3(uNMatrix * vec4(aVertexTangent, 1.0)); vec3 bitangent = cross(normal, tangent);
The three vectors can then be used to create a matrix that transforms vectors into tangent space.
mat3 tbnMatrix = mat3( tangent.x, bitangent.x, normal.x, tangent.y, bitangent.y, normal.y, tangent.z, bitangent.z, normal.z );
//light direction, from light position to vertex vec3 lightDirection = uLightPosition - vertex.xyz; vTangentLightDir = lightDirection * tbnMatrix;
[-1,1]
range into the [0,1]
range. Therefore, they must be unpacked back into the correct range before use in the shader. Fortunately, the algorithm to do so is simple to express in ESSL:vec3 normal = normalize(2.0 * (texture2D(uNormalSampler, vTextureCoord).rgb - 0.5));
// Normalize the light direction and determine how much light is hitting this point vec3 lightDirection = normalize(vTangentLightDir); float lambertTerm = max(dot(normal,lightDirection),0.20); // Combine lighting and material colors vec4 Ia = uLightAmbient * uMaterialAmbient; vec4 Id = uLightDiffuse * uMaterialDiffuse * texture2D(uSampler, vTextureCoord) * lambertTerm; gl_FragColor = Ia + Id;
The code sample also includes calculation of a specular term, to help accentuate the normal mapping effect.
18.118.2.15