Jump to content

  • Log In with Google      Sign In   
  • Create Account


Calculating terrain normals in real time


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
5 replies to this topic

#1 Naruto-kun   Members   -  Reputation: 333

Like
0Likes
Like

Posted 27 July 2014 - 12:06 PM

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, 28 July 2014 - 05:29 AM.


Sponsor:

#2 Naruto-kun   Members   -  Reputation: 333

Like
0Likes
Like

Posted 28 July 2014 - 07:13 AM

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;  
}


#3 Jason Z   Crossbones+   -  Reputation: 4911

Like
0Likes
Like

Posted 28 July 2014 - 08:19 PM

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?



#4 Naruto-kun   Members   -  Reputation: 333

Like
0Likes
Like

Posted 29 July 2014 - 01:45 AM

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, 29 July 2014 - 01:46 AM.


#5 Naruto-kun   Members   -  Reputation: 333

Like
0Likes
Like

Posted 29 July 2014 - 09:10 AM

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?



#6 Naruto-kun   Members   -  Reputation: 333

Like
0Likes
Like

Posted 31 July 2014 - 02:46 AM

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






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS