Sign in to follow this  
_Sergey_

Terrain normals interpolation. Is this code correct?

Recommended Posts

I'm trying to do a bilinear normals interpolation on a heightmap and wrote this sort of code:
vec3 HeightMap::GetNormal( float X, float Y ) const
{
   int I;
   int J;
   float FracI;
   float FracJ;

   WorldToData( X, Y, I, J, FracI, FracJ ); // transforms world X, Y into heightmap integer coords I, J and their fractional parts

   vec3 Normal0 = GetDataNormal( I,   J   );
   vec3 Normal1 = GetDataNormal( I,   J+1 );
   vec3 Normal2 = GetDataNormal( I+1, J   );
   vec3 Normal3 = GetDataNormal( I+1, J+1 );

   if ( FracI + FracJ >= 1 ) Normal0 = Normal3;

   // bilinear interpolation
   vec3 Edge1 = Normal0 + (Normal1-Normal0) * FracJ;
   vec3 Hypot = Normal2 + (Normal1-Normal2) * FracJ;

   vec3 Normal = Edge1 + (Hypot - Edge1) * FracI;

   return Normal.GetNormalized();
}
Helper function:
vec3 HeightMap::GetDataNormal( int I, int J ) const
{
   // find heights
   vec3 H0 = DataToWorld( I,   J   );  // returns X,Y,Z world coordinates of the point
   vec3 H1 = DataToWorld( I,   J+1 ); // for simplicity consider this I, J, Height(I,J)
   vec3 H2 = DataToWorld( I+1, J   ); // but actualy some scaling is done

   vec3 Edge1 = H0-H2;
   vec3 Edge2 = H0-H1;

   vec3 Normal = ( Edge2.Cross( Edge1 ) ).GetNormalized();

   if ( Normal.Z < 0 ) Normal *= -1.0f;

   return Normal;
}
The normals for integer values of X and Y are ok, but as soon as i try to supply float coordinates to GetNormal() the result is wrong. :( What could be wrong with this code?

Share this post


Link to post
Share on other sites
This has always worked for me:


Normal.x = Height[x-1][y] - Height[x+1][y]
Normal.y = Height[x][y-1] - Height[x][y+1]
Normal.z = Distance_Between_Grid_Samples * 2.0

Normalise(&Normal)


Share this post


Link to post
Share on other sites
it seems to me ur makin this abit mroe complicated than it is... you shouldnt be interpolating the normals.. instead simply interpolate the heightvalues and use those to calculate the normals...

EDIT:: not that its wrong to interpolate the normals... but u get better quality with cosine interpolation of the heightfield



float Interpolate(float a, float b, float x)
{
float f = ( 1.0f - (float)cos ( x * PI ) ) * 0.5f;

return ( a * ( 1.0f - f ) + b * f );
}

float GetInterpolatedHeight(float x, float z)
{
int ix1 = (int) x;
float fx1 = x - ix1;

int iz1 = (int) z;
float fz1 = z - iz1;

float v1 = GetHeight(ix1, iz1);
float v2 = GetHeight(ix1+1, iz1);
float v3 = GetHeight(ix1, iz1+1);
float v4 = GetHeight(ix1+1, iz1+1);

float v12 = Interpolate(v1,v2,fx1);
float v34 = Interpolate(v2,v3,fx1);

float v1234 = Interpolate(v12,v34,fz1);//(1.0f-fz1)*v12 + fz1*v34;

return v1234;

}






[Edited by - Dragon_Strike on March 9, 2007 3:59:58 PM]

Share this post


Link to post
Share on other sites
Floating point coordinates should easily transform properly. The problem might be with whether you are getting the lowest integer value or the highest integer value:-

eg:-

float x1 = 0.1f;
float y1 = 0.1f;

float x2 = 1.1f;
float y2 = 1.1f;

now:

floor(x1) == 0.0f;
and
ceil(x1) == 1.0f;

while
floor( x2 ) == 1.0f == ceil( x1 );

additionally, you might have a different range than the above numbers which are 1.0f apart.

x1 = 0.1f;
x2 = 1.8f;

then you need to compute the distance between the min and the max values like:-

xdist = xmax - xmin;

and then divide by the number of samples

xdiff = xdist / num_samples;

for example:-
xmin = 0.1;
xmax = 217.7;

xdist = 217.7 - 0.1 = 217.6;

for 128 samples :-

xdiff = xdist / num_samples = 217.6 / 128 = 1.7

then finally the adjusted x value is

x1_adj = ( x1 - xmin ) / xdiff = ( 0.1 - 0.1 ) / 1.7 = 0.0f;

and for x2

x2_adj = ( x2 - xmin ) / xdiff = ( 1.8 - 0.1 ) / 1.7 = 1.0f;

and for x3 which would be 3.5

x3_adj = ( x3 - xmin ) / xdiff = ( 3.5 - 0.1 ) / 1.7 = 2.0f;

then by mathematical induction it is easy to show that:-

xn_adj = ( xn - xmin )/ xdiff;



I hope this works or at least helps somehow!


edit: corrected a dodgy bracket

Share this post


Link to post
Share on other sites
apparently the samplers in pixel shaders use bilinear filtering all the time without too many problems.

what is the difference between a bilinear filtered normal map and a bilinear filtered height map that has had normals recalculated?

I'd be interested to see the result :)

Share this post


Link to post
Share on other sites
Quote:
Original post by cyber_wiz_2007

what is the difference between a bilinear filtered normal map and a bilinear filtered height map that has had normals recalculated?


In my case normals are used not for rendering, but for the physics simulation of vehicles on a landscape.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this