Contents
Messing with Tangent Space
 
 
Printer-Friendly VersionPrinter-Friendly Version
 
Latest News
spacer View All spacer
 
November 22, 2009
 
Video Game Watchdog National Institute On Media And The Family Shutting Down [11]
 
Modern Warfare 2 Infinity Ward's 'Most Successful PC Version' Yet [12]
 
New Tech, Design Details Of Project Natal To Emerge At Gamefest In February
spacer
Latest Jobs
spacer View All     Post a Job     RSS spacer
 
November 22, 2009
 
Sucker Punch Productions
Character Artist
 
Sucker Punch Productions
3D Environment Artist
 
Sucker Punch Productions
Network Programmer
 
Sucker Punch Productions
Texture Artist
 
Sony Online Entertainment
Brand Manager
 
Monolith Productions
Sr. Software Engineer, Engine - Monolith Productions - #113767
 
Crystal Dynamics
Sr. Level Designer
 
Gargantuan Studios
Lead World Designer
spacer
Latest Features
spacer View All spacer
 
November 22, 2009
 
arrow Upping The Craft: Susan O'Connor On Games Writing [6]
 
arrow Small Developers: Minimizing Risks in Large Productions - Part II [6]
 
arrow iPhone Piracy: The Inside Story [48]
 
arrow And Yet It Grows: Analyzing the Size and Growth of the European Game Market [5]
 
arrow NPD: Behind the Numbers, October 2009 [13]
 
arrow Reflecting On Uncharted 2: How They Did It [5]
 
arrow Sponsored Feature: Rasterization on Larrabee -- Adaptive Rasterization Helps Boost Efficiency
 
arrow Postmortem: Wadjet Eye's The Blackwell Convergence [2]
spacer
Latest Blogs
spacer View All     Post     RSS spacer
 
November 22, 2009
 
Accepting the Inherent Value of Games
 
Planckogenesis, Part II: Song Structure & Gravy Train [1]
 
Designing Games Is About Matching Personalities [1]
spacer
About
spacer News Director:
Leigh Alexander
Features Director:
Christian Nutt
Editor At Large:
Chris Remo
Advertising:
John 'Malik' Watson
Recruitment/Education:
Gina Gross
 
Features
  Messing with Tangent Space
by Siddharth Hegde
5 comments
Share RSS
 
 
July 16, 2007 Article Start Previous Page 6 of 7 Next
 

Now you may be a little confused as to how an image which holds RGB data encodes normals which have floating point x, y, z data. This is quite simple. A normalized vector always has 3 components x, y, z. The values of x, y, z always will range from -1.0 to 1.0. A 24-bit texture holds RGB values that range between 0 and 255. The x component of the vector is stored in the red channel, the y component in the green channel and the z component in blue channel. The x, y, z values are scaled and biased to a range between 0 to 255 before they are stored in the RGB values.

Example: Let us imagine we have a vector that identifies the normal at a point

Advertisement

Vn = (Vn.x, Vn.y, Vn.z) = (0.0, 0.0, 1.0)

Before we store Vn.x, Vn.y, Vn.z in the normal map as r, g, b data, we scale and bias the values as follows

In = (In.R, In.G, In.B) = (Vn * 127.5) + 127.5 = (128, 128, 255)

The reverse of the above step is done in the pixel shader to get the surface normal from the normal map's RGB data. We then take the dot product of the surface normal at a point and the normalized light vector. Since the dot product of two unit/normalized vectors is equal to cos of the angle between them, no further calculations need to be done to get the brightness of a point at a texel.

Why does a normal map always look blueish?
The z component of a surface normal at point never points backwards

Vn.z > 0.0

This results in

In.B > 127.5

That is why you will always see a normal map having a blueish/purpleish tint. See Figure 10 for a sample height map and normal map. Note the difference between the two.

Another point to remember is that a normal map is always encoded in the tangent space coordinate system instead of the world space coordinate system. Go back to the section ‘Why use tangent space’ to understand why. You should get it now.

The vertex shader -calculating the light vector

The light vector is calculated at each vertex and then interpolated across the face. Although this is not an ideal method, there are some simple steps that we can follow at the game design level to prevent the disadvantages of using this method.

Step 1: Calculate the light vector.

In order to calculate the light vector, we assume that both the light position and the vertex position are in the world space coordinate system. The light vector is then calculated as follows

Lv = Li.Pos - Vj.Pos

Where

Lv = the light vector

Li.Pos = Position of the light in world space

Vj.Pos = Position of the vertex in world space

Step 2: Convert the light vector from the world space coordinate system to a vector in the tangent space coordinate system.
Assuming that you have already calculated the tangent space matrix for each vertex and passed it into the vertex shader via the texture channels, this is just a simple matrix multiplication.

Lv =Mwt X Lv

Step 3: Normalize the light vector

The light vector is then normalized.

Lv = Normalize(Lv)

This light vector is passed to the pixel shader in a previously decided texture stage. One very big advantage of doing this is that the light vector gets interpolated across the face for free.
Note: The interpolated light vector is no longer normalized when it reaches the pixel shader, but in our case this minor inaccuracy does not make any difference.

The pixel shader

Calculating the diffuse brightness at the pixel

Step 1: Get the light vector

Unlike getting a normal texture, you need to load the texture coordinate from the texture stage. This texture stage will generally not have any texture assigned to it. The light vector is cleverly stored as texture coordinates as a means of transporting data from the vertex shader to the pixel shader. This can be done using the texcrd instruction available in pixel shader 1.4.

Lv = texcrd(Vx)

There is no need to re-normalize the light vector at this stage. We can live with the inaccuracies and we will continue to assume that it is normalized.

Step 2: Get the normal at the texel

Assuming the normal map is assigned to a pre defined texture stage, load the normal / surface orientation at the texel and then scale and bias the values to fit back to a range from -1.0 to 1.0. If you remember, the values in RGB data range from 0 to 255 and when these are sent to the pixel shader they are converted to a range 0 to 1. The color data can be loaded using the texld instruction available in pixel shaders version 1.4

R1 = texld(Vy)

Sv = (R1 - 0.5) * 2.0

The first line loads the normal that is encoded as color data. The second line converts the color data back to a normal.

Step 3: Calculating the surface brightness

Now that we have the light vector and the surface normal at the texel, we can calculate the brightness of the surface at the using a simple dot3 product

Bi = Dot3(Sv, Lv)

As both the light vector and surface normal have already been normalized, the dot product will always range from a value from 0.0 to 1.0

Step 4: Modulate with the diffuse color

To get the color of the output pixel, just modulate [multiply] the color of the diffuse texture with the brightness at the current texel [pixel].

ColorOut = Di X Bi

That is all that we need to do in order to get a bump mapped surface.

 
Article Start Previous Page 6 of 7 Next
 
Comments

Kiran Sudhakara
profile image
Step 2: Calculate the T vector
T = E2-1.xyz / E2-1.u

This formula does not seem correct. Wouldn't this just scale the edge?

David Larsson
profile image
Responce to Kiran's comment:

I think the formula is supposed to be more like this(could be wrong though):

NE2-1 = Normalize(E2-1)
NE3-1 = Normalize(E3-1)
T = (NE2-1.xyz / E2-1.u) + (NE3-1.xyz / E3-1.u)

Kiran Sudhakara
profile image
Unfortunately I believe this is wrong aswell. Consider a simple case where E2-1.uv is 1,0 (so E2-1.xyz is already the tangent!), and E3-1.uv is an angled vector across the UV space.

Shih-Kai Lai
profile image
http://www.terathon.com/code/tangent.html
http://www.blacksmith-studios.dk/projects/downloads/tangent_matrix_derivation.ph
p

These 2 website have good explanations about tangent space.

Does any body know, if 2 vertex of a triangle have the same UV, then should I ignore that triangle, or ask art to change the UV? The determinant will become 0 in this case...

Julian Hainsworth
profile image
No two vertices should have the exact same UV-Coords, if this is a problem with your meshes you should see your artist


none
 
Comment:
 


Submit Comment