Jump to content
  • Advertisement
Sign in to follow this  
meatbeef

normals in a heightmap

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi Just got my heightmap working(yay!) and I'd like to be able to light it up. Basically I have my vertices in a single dimensional array and I render it using triangle strips but I'm unsure on how to calculate the normals correctly. What's the easiest way to do this? Thankx

Share this post


Link to post
Share on other sites
Advertisement
Plenty of threads on this in the past. I suggest you search for some. Here's one I participated on. You also need to delve a little into vector math, namely the cross product of two vectors.

hth
F451

Share this post


Link to post
Share on other sites
Thanks for the response

Err..I'm not any good at math [grin]. Anyway I did some searching around before and so far this is the closest I got to it to display correctly it still does not show very well though...


for(int y = 0;y < image->getHeight();++y)
{
if( right )
{
vertices[pos].v[0] = current_x;
vertices[pos].v[1] = data.get_height(0,y,pixels);
vertices[pos].v[2] = current_z;

++pos;
for(int x =0;x < image->getWidth();++x)
{
vertices[pos].v[0] = current_x;
vertices[pos].v[1] = data.get_height(x,y+1,pixels);
vertices[pos].v[2] = current_z - tri_size;

++pos;
current_x+=tri_size;

vertices[pos].v[0] = current_x;
vertices[pos].v[1] = data.get_height(x,y,pixels);
vertices[pos].v[2] = current_z;



++pos;

//calculate normals

vect vector1(vertices[pos-3].v);
vect vector2(vertices[pos-2].v);
vect vector3(vertices[pos-1].v);

vector1 = vector2 + vector1;
vector2 = vector3 + vector1;

vector1 = vector1.cross_product( vector2 );
vector1.normalize();

set_array(vertices[pos-3].n,vector1[0],vector1[1],vector1[2]);
set_array(vertices[pos-2].n,vector1[0],vector1[1],vector1[2]);
set_array(vertices[pos-1].n,vector1[0],vector1[1],vector1[2]);
}
}else
{
vertices[pos].v[0] = current_x;
vertices[pos].v[1] = data.get_height(image->getWidth()-1,y,pixels);
vertices[pos].v[2] = current_z;


++pos;
for(int x = image->getWidth()-1;x >= 0;--x)
{
vertices[pos].v[0] = current_x;
vertices[pos].v[1] = data.get_height(x,y+1,pixels);
vertices[pos].v[2] = current_z - tri_size;


++pos;
current_x-=tri_size;

vertices[pos].v[0] = current_x;
vertices[pos].v[1]= data.get_height(x,y,pixels);
vertices[pos].v[2] = current_z;

++pos;

//calculate normals

vect vector1(vertices[pos-3].v);
vect vector2(vertices[pos-2].v);
vect vector3(vertices[pos-1].v);

vector1 = vector2 - vector1;
vector2 = vector3 - vector1;

vector1 = vector1.cross_product( vector2 );
vector1.normalize();

set_array(vertices[pos-3].n,vector1[0],vector1[1],vector1[2]);
set_array(vertices[pos-2].n,vector1[0],vector1[1],vector1[2]);
set_array(vertices[pos-1].n,vector1[0],vector1[1],vector1[2]);
}
}

right = !right;
current_z-=tri_size;
}








Here are some screenshots of what it looks like:

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

Share this post


Link to post
Share on other sites
how did you get the overhanging spikes by using a height map? From the pictures, it looks like it has more than one polygon per x,z coordinate, or is that just due to perspective?

Share this post


Link to post
Share on other sites
Quote:
Original post by HalcyonX
how did you get the overhanging spikes by using a height map? From the pictures, it looks like it has more than one polygon per x,z coordinate, or is that just due to perspective?


Nah I rotated it slightly so it'd be easier to see that's all

Share this post


Link to post
Share on other sites
I had the same problem a couple of weeks ago.
I copied/pasted code together from different sources and got it working.
The code definitly isn't THE way to do it and far away from being optimized, but it works and looks pretty good ;)
Not to mention, that I almost don't have any clue about vectormaths - it just works ;)

Here is the code I use:

Routine to call:

void ReCalcNormals(void)
{
float *NewNormal;
for(int i=0;i<BufferCount;i++)
{
NewNormal=ComputeNormals(i);
if(NewNormal!=NULL)
{
CNormal normal;
normal.Set(NewNormal[0],NewNormal[1],NewNormal[2]);
UpdateNormalBuffer(i,&normal);
}
}
}



This function is called to calculate normals for all your vertices. Call this once after your vertexbuffer is filled.
BufferCount=Amount of vertices (elements) within your vertex buffer.
UpdateNormalBuffer does nothing else, than updating element i within your normalbuffer.


ComputeNormals function

float *ComputeNormals(long ElementID)
{

float *norm1,*norm2,*norm3,*norm4;

int Elemsx=OpenGLWindow->Elemsx;
int Vertsx=Elemsx*6;

norm1 = NULL;
norm2 = NULL;
norm3 = NULL;
norm4 = NULL;

if(ElementID >0 && ElementID%(Elemsx*6)!=0 && (ElementID+Elemsx*6)%(Elemsx*6)!=0 && ElementID>(Elemsx*6) && ElementID<(Elemsx*Elemsx*6)-Elemsx*6) //Kein Randvertex
{
CVertex *VCentral,*VTop,*VTopRight,*VRight,*VBottomRight,*VBottom,*VBottomLeft,*VLeft,*VTopLeft;

VCentral=OpenGLWindow->GetVertexBufferElement(ElementID);
VRight=OpenGLWindow->GetVertexBufferElement(ElementID+6);
VLeft=OpenGLWindow->GetVertexBufferElement(ElementID-6);
VTop=OpenGLWindow->GetVertexBufferElement(ElementID-Vertsx);
VBottom=OpenGLWindow->GetVertexBufferElement(ElementID+Vertsx);
VTopRight=OpenGLWindow->GetVertexBufferElement(ElementID-Vertsx+6);
VTopLeft=OpenGLWindow->GetVertexBufferElement(ElementID-Vertsx-6);
VBottomRight=OpenGLWindow->GetVertexBufferElement(ElementID+Vertsx+6);
VBottomLeft=OpenGLWindow->GetVertexBufferElement(ElementID+Vertsx-6);

norm1 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VLeft->GetX(),VLeft->GetY(),VLeft->GetZ(), VTopLeft->GetX(),VTopLeft->GetY(),VTopLeft->GetZ());
Normalize(norm1);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VBottomLeft->GetX(),VBottomLeft->GetY(),VBottomLeft->GetZ(), VBottom->GetX(),VBottom->GetY(),VBottom->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VBottom->GetX(),VBottom->GetY(),VBottom->GetZ(),VBottomRight->GetX(),VBottomRight->GetY(),VBottomRight->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VBottomRight->GetX(),VBottomRight->GetY(),VBottomRight->GetZ(),VRight->GetX(),VRight->GetY(),VRight->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VRight->GetX(),VRight->GetY(),VRight->GetZ(),VTopRight->GetX(),VTopRight->GetY(),VTopRight->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VTopRight->GetX(),VTopRight->GetY(),VTopRight->GetZ(),VTop->GetX(),VTop->GetY(),VTop->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VTop->GetX(),VTop->GetY(),VTop->GetZ(),VTopLeft->GetX(),VTopLeft->GetY(),VTopLeft->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);

norm2 = CrossProduct(VCentral->GetX(),VCentral->GetY(),VCentral->GetZ(),VTopLeft->GetX(),VTopLeft->GetY(),VTopLeft->GetZ(),VLeft->GetX(),VLeft->GetY(),VLeft->GetZ());
Normalize(norm2);
AddVector(norm1,norm2);
free(norm2);
Normalize(norm1);

norm1[2] = - norm1[2];
}

return norm1;
}



GetVertexBufferElement does nothing else, than returning vertex n from the vertexbuffer. Can be replaced with a direct =VertexBuffer[ElementID].

This part of the code posted above

if(ElementID >0 && ElementID%(Elemsx*6)!=0 && (ElementID+Elemsx*6)%(Elemsx*6)!=0 && ElementID>(Elemsx*6) && ElementID<(Elemsx*Elemsx*6)-Elemsx*6) //Kein Randvertex



is used to not calculate normals for border vertices - because I was too lazy to do that and I don't really need it :D

Additional functions:

float* CrossProduct(float x1,float y1,float z1, float x2,float y2,float z2, float x3,float y3,float z3)
{
float *auxNormal,v1[3],v2[3];

v1[0] = x2-x1;
v1[1] = y2-y1;
v1[2] = -z1+z2;


v2[0] = x3-x1;
v2[1] = y3-y1;
v2[2] = -z1+z3;

auxNormal = (float *)malloc(sizeof(float)*3);

auxNormal[2] = v1[0] * v2[1] - v1[1] * v2[0];
auxNormal[0] = v1[1] * v2[2] - v1[2] * v2[1];
auxNormal[1] = v1[2] * v2[0] - v1[0] * v2[2];

return(auxNormal);
}

void Normalize(float *v)
{

float d;

d = (float)sqrt((v[0]*v[0]) + (v[1]*v[1]) + (v[2]*v[2]));
if(d==0)
return;

v[0] = v[0] / d;
v[1] = v[1] / d;
v[2] = v[2] / d;
}

void AddVector(float *a, float *b)
{

a[0] += b[0];
a[1] += b[1];
a[2] += b[2];
}




You will probably have to rename some of the variables and functions, but I guess piece of code to be customized is quite simple.
If it doesn't work or you have questions, just post ;)

Greets

Chris

Share this post


Link to post
Share on other sites
U can approximate it by making four triangles around every point, with adjacent points, and average the normals of those triangles.
For better result make more triangles, that is those triangles that u actually draw, if u use triangle strips that probably won't be the triangles i use in the following code, but the difference will probably be very small.

also, when doing it for "every" point, dont do it for the edge of the heightmap, cause those points dont have all adjacent points.


for( every point ) {
Vector3d point, point1, point2, point3, point4;
Vector3d normal1, normal2, normal3, normal4;

point = currentPointInLoop;
point1 = point to the right of current in the heightmap;
point2 = point above current;
point3 = point left of current;
point4 = point below current;

normal1 = triangleNormal(point, point1, point2);
normal2 = triangleNormal(point, point2, point3);
normal3 = triangleNormal(point, point3, point4);
normal4 = triangleNormal(point, point4, point1);

normal.x = (normal1.x + normal2.x + normal3.x + normal4.x) / 4.0;
normal.y = (normal1.y + ....) / 4.0;
normal.z = (normal1.z + ....) / 4.0;
}



to calculate normal:

Vector3d triangleNormal(Vector3d p1, Vector3d p2, Vector3d p3) {
Vector3d v1, v2;

v1.x = p2.x - p1.x;
v1.y = ...;
v1.z = ...;

v2.x = p3.x - p1.x;
v2.y = ...;
v2.z = ...;

return crossProduct(v1, v2);
}

Vector3d crossProduct(Vector3d v1, Vector3d v2) {
Vector3d v;

v.x = v1.y * v2.z - v1.z * v2.y;
v.y = v1.z * v2.x - v1.x * v2.z;
v.z = v1.x * v2.y - v1.y * v2.x;

return v;
}

Share this post


Link to post
Share on other sites
Thanks, I'll take a look at the code I've got it nearly working right now. Whatever I do I don't seem to get rid of those black stripes that go across the heightmap [crying] everything else seems ok though.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!