Tangents for heightmap from limited information

This topic is 2142 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

(solved)

Hi all, I'm working on normal mapping for my terrain. My terrain system calculates a normal map on the fly when a heightmap is loaded (or generated), which means I can use per-pixel normals. This makes distant terrain look a lot nicer. However, I am wondering if there is a clever way to calculate the tangent space for normal mapping, given some extra information

1. You are given the normal for a pixel, and this is already normalized

2. The first tangent is guaranteed to have X component = 0, because it's just a straightforward texture-splattedgrid geometry

3. Similarly, the second tangent has Z component = 0

4. Tangent space should be orthonormal

How can I fill in the unknowns in the most performance-friendly way possible?

Normal: (nx, ny, nz)

Tangent: (0, ?, ?)

Bitangent (?, ?, 0)

EDIT: Solved it.

// tangent known to be perpendicular to normal
dot(n, t) = 0
nx*tx + ny*ty + nz*tz = 0

// x component is 0
ny*ty + nz*tz = 0

// length of tangent is 1
ty*ty + tz*tz = 1



Now I have 2 equations for 2 unknowns which gives the z component as solution to a quadratic, where I'll take the positive answer! and I can work out the bitangent similarly, or use a cross product

Edited by hupsilardee

Share on other sites

Since the tangent has the X component = 0 you can assume that the tangent might be (0,0,1) so you can calculate the tangent basis with two cross products:

temp_tangent = ( 0, 0, 1 )

bitangent = cross( temp_tangent, normal )

tangent = cross( normal, bitangent )

float3x3 TBN = float3x3( tangent, bitangent, normal )

Edited by TiagoCosta

Share on other sites

Thanks for the alternative! Your method actually looks faster.

(While we're here, would anyone mind reassuring me that this HLSL code to calculate the normal from the heightfield is actually correct?)

float hxm = Heightmap.Load(int3(iPos.x-1, iPos.y, 0)).r;
float hxp = Heightmap.Load(int3(iPos.x+1, iPos.y, 0)).r;
float hzm = Heightmap.Load(int3(iPos.x, iPos.y-1, 0)).r;
float hzp = Heightmap.Load(int3(iPos.x, iPos.y+1, 0)).r;
float h = Heightmap.Load(int3(iPos.x, iPos.y, 0)).r;

float3 t1 = float3(2, hxp - hxm, 0);
float3 t2 = float3(0, hzp - hzm, 2);

float3 n = cross(t2, t1);


Share on other sites

Thanks for the alternative! Your method actually looks faster.

(While we're here, would anyone mind reassuring me that this HLSL code to calculate the normal from the heightfield is actually correct?)

float hxm = Heightmap.Load(int3(iPos.x-1, iPos.y, 0)).r;
float hxp = Heightmap.Load(int3(iPos.x+1, iPos.y, 0)).r;
float hzm = Heightmap.Load(int3(iPos.x, iPos.y-1, 0)).r;
float hzp = Heightmap.Load(int3(iPos.x, iPos.y+1, 0)).r;
float h = Heightmap.Load(int3(iPos.x, iPos.y, 0)).r;

float3 t1 = float3(2, hxp - hxm, 0);
float3 t2 = float3(0, hzp - hzm, 2);

float3 n = cross(t2, t1);

float hxm = Heightmap.Load(int3(iPos.x-1, iPos.y, 0)).r;
float hxp = Heightmap.Load(int3(iPos.x+1, iPos.y, 0)).r;
float hzm = Heightmap.Load(int3(iPos.x, iPos.y-1, 0)).r;
float hzp = Heightmap.Load(int3(iPos.x, iPos.y+1, 0)).r;
float h = Heightmap.Load(int3(iPos.x, iPos.y, 0)).r;

float3 n = 2.0 * float3(hxp - hxm, 2, hzp - hzm); //2.0 * can be optimized off if normalized


Optimized version.

Edited by kalle_h

• Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 16
• 9
• 15
• 9
• 11