Calculating terrain normals in real time

Started by
4 comments, last by JB3DG 9 years, 9 months ago

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?

Advertisement

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


float dist = 92.77443083f;

//--------------------------------------------------------------------------------------
// VERTEX SHADER
//--------------------------------------------------------------------------------------
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;  
}

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?

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?

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

normals_zps349e857e.png

Here is my vertex shader


float dist = 92.77443083f;

//--------------------------------------------------------------------------------------
// VERTEX SHADER
//--------------------------------------------------------------------------------------
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?

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

This topic is closed to new replies.

Advertisement