Terrain normals interpolation. Is this code correct?

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

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 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.0Normalise(&Normal)

Share on other sites
But normals generated this way will not rotate continuously to follow the landscape, will they?

Share on other sites
heres a link to an algorithm for smooth terrain normals

Share on other sites
Again, that example works with integer grid and doesn't do any interpolation for floating point coordinates. :(

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 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 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 on other sites
Quote:
 Original post by cyber_wiz_2007what 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.

1. 1
2. 2
Rutin
20
3. 3
khawk
16
4. 4
A4L
14
5. 5

• 11
• 16
• 26
• 10
• 11
• Forum Statistics

• Total Topics
633756
• Total Posts
3013709
×