Calculating terrain normals in real time

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

Recommended Posts

Hi guys

Is there any way I can convert my terrain height map into a normal map that I can use for ambient occlusion and terrain shadowing in the vertex shader?

Here is my current vertex shader code.

	PS_INPUT vert;
float offset = dist*-39.0f;
vert.t.x = 1.0f-(input.pos.x *0.0125f);
vert.t.y = input.pos.y *0.0125f;

vert.p = float4((input.pos.x*dist)+offset, 0, (input.pos.y*dist)+offset, 1);
vert.p.y = HMap.SampleLevel(linearSampler, vert.t, 0);

vert.p = mul(vert.p, view);
vert.p = mul(vert.p, prj);
return vert;


I am sending a 80x80 grid of vertices to the shader. I first calculate the offset used to position the verts around the center point where the camera will be when it initially starts.

Then I convert the grid coordinates into texture coordinates, followed by repositioning the verts, then sampling the height map (single 32bit channel per pixel cuz the vertex positions are all in meters as it is a terrain model of the earth).

Since I can predict the vertex positions surrounding current vert using the height map, how would I go about this?

Edited by Naruto-kun

Share on other sites

Any ideas? Updated the shader code to this but still not getting the desired result.

float dist = 92.77443083f;

//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
PS_INPUT VS( SPRITE_INPUT input )
{
PS_INPUT vert;
float offset = dist*-39.0f;
vert.t.x = (input.pos.x *0.0125f);
vert.t.y = 1.0f-(input.pos.y *0.0125f);

vert.p = float4((input.pos.x*dist)+offset, 0, (input.pos.y*dist)+offset, 1);
vert.p.y = (float)HMap.SampleLevel(linearSampler, vert.t, 0);

float3 vsum = {0, 0, 0};
float count = 0;
if(vert.t.x + 0.0125f <= 1.0f && vert.t.y + 0.0125f <= 1.0f)//Bottom Right
{
float3 vert2 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 0)), vert.p.z);
float3 vert3 = float3(vert.p.x, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, 1)), vert.p.z-dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.y + 0.0125f <= 1.0f)//Bottom Left
{
float3 vert2 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 0)), vert.p.z);
float3 vert3 = float3(vert.p.x, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, 1)), vert.p.z-dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.y - 0.0125f >= 0)//Top Left
{
float3 vert2 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 0)), vert.p.z);
float3 vert3 = float3(vert.p.x, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, -1)), vert.p.z+dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x + 0.0125f <= 1.0f && vert.t.y - 0.0125f >= 0)//Top Right
{
float3 vert2 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 0)), vert.p.z);
float3 vert3 = float3(vert.p.x, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, -1)), vert.p.z+dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x + 0.0125f <= 1.0f && vert.t.y + 0.0125f <= 1.0f && vert.t.y - 0.0125f >= 0)//Right
{
float3 vert2 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, -1)), vert.p.z+dist);
float3 vert3 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 1)), vert.p.z-dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.x + 0.0125f <= 1.0f && vert.t.y + 0.0125f <= 1.0f)//Bottom
{
float3 vert2 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 1)), vert.p.z-dist);
float3 vert3 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 1)), vert.p.z-dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.y + 0.0125f <= 1.0f && vert.t.y - 0.0125f >= 0)//Left
{
float3 vert2 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, -1)), vert.p.z+dist);
float3 vert3 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 1)), vert.p.z-dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.x + 0.0125f <= 1.0f && vert.t.y - 0.0125f >= 0)//Top
{
float3 vert2 = float3(vert.p.x+dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, -1)), vert.p.z+dist);
float3 vert3 = float3(vert.p.x-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, -1)), vert.p.z+dist);
float3 vec1 = vert2 - (float3)vert.p;
float3 vec2 = vert3 - (float3)vert.p;
vsum += (vec1+vec2);
count ++;
}

float3 total = vsum/count;
vert.n = normalize(total);

vert.p = mul(vert.p, view);
vert.p = mul(vert.p, prj);
return vert;
}


Share on other sites

I typically calculate terrain normals in a pre-processing step that just performs a simple gradient calculation for the 4 sampling points surrounding a vertex.  Do you need to do this on the fly, or can you pre-calculate the values?

Share on other sites

I would prefer not to pre calculate as I will be switching to tessellation soon. Anyways, you are saying your normal is just delta_y/delta_x or delta_z?

Edited by Naruto-kun

Share on other sites

Ok I think I found where I was originally going wrong. However something is still not right here.

float dist = 92.77443083f;

//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
PS_INPUT VS( SPRITE_INPUT input )
{
PS_INPUT vert;
float offset = dist*-39.0f;
vert.t.x = (input.pos.x *0.0125f);
vert.t.y = 1.0f-(input.pos.y *0.0125f);

vert.p = float4((input.pos.x*dist)+offset, 0, (input.pos.y*dist)+offset, 1);
vert.p.y = (float)HMap.SampleLevel(linearSampler, vert.t, 0);

float3 vsum = {0, 0, 0};
float count = 0;
if(vert.t.x + 0.0125f <= 1.0f && vert.t.y + 0.0125f <= 1.0f)//Bottom Right
{
float3 vert2 = float3(dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 0)), 0);
float3 vert3 = float3(0, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, 1)), -dist);
float3 vert1 = float3(0, vert.p.y, 0);
float3 vec1 = vert2 - vert1;
float3 vec2 = vert3 - vert1;
vsum += cross(vec1, vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.y + 0.0125f <= 1.0f)//Bottom Left
{
float3 vert2 = float3(-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 0)), 0);
float3 vert3 = float3(0, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, 1)), -dist);
float3 vert1 = float3(0, vert.p.y, 0);
float3 vec1 = vert2 - vert1;
float3 vec2 = vert3 - vert1;
vsum += cross(vec1, vec2);
count ++;
}
if(vert.t.x - 0.0125f >= 0 && vert.t.y - 0.0125f >= 0)//Top Left
{
float3 vert2 = float3(-dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(-1, 0)), 0);
float3 vert3 = float3(0, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, -1)), dist);
float3 vert1 = float3(0, vert.p.y, 0);
float3 vec1 = vert2 - vert1;
float3 vec2 = vert3 - vert1;
vsum += cross(vec1, vec2);
count ++;
}
if(vert.t.x + 0.0125f <= 1.0f && vert.t.y - 0.0125f >= 0)//Top Right
{
float3 vert2 = float3(dist, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(1, 0)), 0);
float3 vert3 = float3(0, (float)HMap.SampleLevel(linearSampler, vert.t, 0, int2(0, -1)), dist);
float3 vert1 = float3(0, vert.p.y, 0);
float3 vec1 = vert2 - vert1;
float3 vec2 = vert3 - vert1;
vsum += cross(vec1, vec2);
count ++;
}

float3 total = vsum/count;
vert.n = normalize(total);
if(vert.n.y > 0.0f)
vert.n.y = -vert.n.y;

vert.p = mul(vert.p, view);
vert.p = mul(vert.p, prj);
return vert;
}


And here is my pixel shader

float4 ambcolor = {0.0f, 0.0f, 0.0f, 1.0f};
float4 diffcolor = {1.0f, 1.0f, 1.0f, 1.0f};
float3 lightdir = {0.75f, 0.25f, 0};
float4 PS( PS_INPUT input ) : SV_Target
{
float4 color = ambcolor;
float3 ldir = -lightdir;
float lightint = saturate(dot(input.n, ldir));
if(lightint > 0.0f)
color += (diffcolor*lightint);
color = saturate(color);
return color;
}


Any ideas whats happening here?

Share on other sites

Found the problem. Needed to add the vertex position to the normal before normalizing and now it works perfect.