_Sergey_ 123 Report post Posted March 7, 2007 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? 0 Share this post Link to post Share on other sites
PlayerX 279 Report post Posted March 8, 2007 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) 0 Share this post Link to post Share on other sites
_Sergey_ 123 Report post Posted March 9, 2007 But normals generated this way will not rotate continuously to follow the landscape, will they? 0 Share this post Link to post Share on other sites
cyber_wiz_2007 124 Report post Posted March 9, 2007 heres a link to an algorithm for smooth terrain normalshttp://www.devmaster.net/forums/showthread.php?t=1963 0 Share this post Link to post Share on other sites
_Sergey_ 123 Report post Posted March 9, 2007 Again, that example works with integer grid and doesn't do any interpolation for floating point coordinates. :( 0 Share this post Link to post Share on other sites
Dragon_Strike 264 Report post Posted March 9, 2007 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] 0 Share this post Link to post Share on other sites
cyber_wiz_2007 124 Report post Posted March 9, 2007 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;andceil(x1) == 1.0f;whilefloor( 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 samplesxdiff = 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.7then finally the adjusted x value isx1_adj = ( x1 - xmin ) / xdiff = ( 0.1 - 0.1 ) / 1.7 = 0.0f;and for x2x2_adj = ( x2 - xmin ) / xdiff = ( 1.8 - 0.1 ) / 1.7 = 1.0f;and for x3 which would be 3.5x3_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 0 Share this post Link to post Share on other sites
cyber_wiz_2007 124 Report post Posted March 9, 2007 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 :) 0 Share this post Link to post Share on other sites
_Sergey_ 123 Report post Posted March 10, 2007 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. 0 Share this post Link to post Share on other sites