# Tangents for heightmap from limited information

This topic is 1864 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

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 11
• 13
• 9
• 9
• 15
• ### Forum Statistics

• Total Topics
634078
• Total Posts
3015367
×