Calculating Vertex-Normals [Solved]

Started by
40 comments, last by fredrik90 18 years, 5 months ago
Thanks for your efforts, OpenGL_Guru. I went with your suggestion and think I have it pretty much working by now, except one thing, that is giving me some headache:

I am very sure it is this part:

	for ( int i = 0; i < MAP_SIZE; i++ )	{		for ( int j = 0; j < MAP_SIZE; j++ )		{			trn.x = ( <span class="cpp-keyword">float</span> ) i;<br>			trn.y = get_height ( ( <span class="cpp-keyword">float</span> ) i, ( <span class="cpp-keyword">float</span> ) j );<br>			trn.z = ( <span class="cpp-keyword">float</span> ) j;<br>		}<br>	}<br><br><br></pre></div><!–ENDSCRIPT–><br><br>I loop through the horizontal data and then the vertical data and then try to set the trn.x, .y and .z values to my actual datas, but the trn array is &#111;nly &#111;ne-dimensional, while my data is two-dimensional.<br><br>My hack above works kinda, but displays a wierd version of my height-map (looks kinda sliced):<br><br><img src=http://i8.photobucket.com/albums/a7/dkh2/BasecodeNormalsProblem2.jpg><br><br>How can I fix this issue?<br><br><!–EDIT–><span class=editedby><!–/EDIT–>[Edited by - d h k on November 1, 2005 4:47:04 PM]<!–EDIT–></span><!–/EDIT–>
Advertisement
it looks like you are overstepping your bounds in either you X loop or Y loop when storing your vertices. so it looks like the triangle is trying to be completed at the very beginning rather than just to stop drawing. i had this sort of thing happen to me before on a few occasions. at least from that angle thats what it looks like. any chance we could have a top down view?
heh
Yes, it looks also for me that the vertices used on the right at the horizon are positionally incorrect (I would wonder if this effect could be caused by an incorrect normal computation ;-) Either the drawing loops overshot (as OpenGL_Guru has mentioned) or some index computation is not limited correctly, or the vertex import is incorrect, or ... something else in this direction.
Yes, the problem is not normals-related. If you view the field from the top, then you don't see a thing, because the strips are all very thin on top. The normals do seem to work on those trips though. ;)

This is really strange, but still I believe that it comes from setting those trn values wrong somehow, as I stated in my first post.

Thanks though, you're helping me out big time!

EDIT: The whole thing varies, when I change...

trn.x = ( float ) i;<br></pre><br>… to …<br><pre><br>trn.x = ( float ) i;<br></pre><br><br>… the whole thing looks even worse (almost like a building or something) any my frames drop below 1…<br><br>I am sure I just need to find the proper method here to make my two-dimensional data &#111;ne-dimensional and store it in trn.x, .y and .z…<br><br>I'll keep &#111;n experimenting.
Hmm... I can't get it to work, although it didn't seem too difficult in the beginning.

Let me show you three variations I tried of the little piece of code, that I posted before, and how it makes the terrain look like...

VERSION 1

	for ( int x = 0; x < MAP_SIZE; x++ )	{		for ( int z = 0; z < MAP_SIZE; z++ )		{			trn[x * MAP_SIZE + z].x = ( float ) x;			trn[x * MAP_SIZE + z].y = get_height ( ( float ) x, ( float ) z );			trn[x * MAP_SIZE + z].z = ( float ) z;		}	}



Strange terrain...

VERSION 2

	for ( int x = 0; x < MAP_SIZE; x++ )	// loop through horizontal data	{		for ( int z = 0; z < MAP_SIZE; z++ )		// loop through vertical data		{			// get vertex data			trn[x + ( z * MAP_SIZE )].x = ( float ) x;			trn[x + ( z * MAP_SIZE )].y = get_height ( ( float ) x, ( float ) z );			trn[x + ( z * MAP_SIZE )].z = ( float ) z;		}	}



Pretty much the same thing...

VERSION 3

	for ( int x = 0; x < ( MAP_SIZE - STEP_SIZE ); x += STEP_SIZE )	// loop through horizontal data	{		for ( int z = 0; z < ( MAP_SIZE - STEP_SIZE ); z += STEP_SIZE )		// loop through vertical data		{			// get vertex data			trn[x + ( z * MAP_SIZE )].x = ( float ) x;			trn[x + ( z * MAP_SIZE )].y = get_height ( ( float ) x, ( float ) z );			trn[x + ( z * MAP_SIZE )].z = ( float ) z;		}	}



This is interesting, too... ;)

When I try things like trn[x + z], I get nothing at all, and with trn[x * z], I get some really rare and strange artefacts...

I managed to find out the official name of this problem, though: HASHING A 2D-ARRAY INTO A 1-D ONE. Is anybody experienced in this?

Thanks in advance, I know I am really close, but this really gets me confused for several days now.
trn[x + ( z * MAP_SIZE )]

This fragment from your third approach should be the proper way of addressing the array. In general, mapping a 2D array access:
array[x][y] (array was initialized as: array[x_size][y_size])

is done in 1D as:
array[x + x_size * y] (array was initialized as: array[x-size * y-size])

This stores rows first.

The reason why your third approach doesn't work is unclear from the presented code. One problem definately is the STEP_SIZE, which is subtracted from the MAP_SIZE in the for statements. This way (if STEP_SIZE != 0, which is always) you will not fill all elements of the array. Uninitialized elements also remain when MAP_SIZE % STEP_SIZE != 0. Uninitialized elements can be any value including 0. In the picture, it is clear that along the edges certain points are connected to a single point, which might very well be (0,0,0). Why do you use STEP_SIZE anyway? It seems rather error prone.

Tom
Thank you for your answer!

STEP_SIZE is used when I load the heightmap. If it is 1, it would create a polygon for every pixel of the heightmap, if it is 8, it would take 8 pixels of the heightmap and create one polygon for them.

I am not sure if it is a good idea to use the STEP_SIZE in the loops or not...
If I use my third approach, but get rid of the STEP_SIZE in the loops, then the terrain looks exactly like in my first, and second versions.

This is the code now:
	for ( int x = 0; x < MAP_SIZE; x++ )	// loop through horizontal data	{		for ( int z = 0; z < MAP_SIZE; z++ )		// loop through vertical data		{			// get vertex data			trn[x + ( z * MAP_SIZE )].x = ( float ) x;			trn[x + ( z * MAP_SIZE )].y = get_height ( ( float ) x, ( float ) z );			trn[x + ( z * MAP_SIZE )].z = ( float ) z;		}	}


If you need more code, then tell me as I am not sure which parts could be of interest (and I seriously still believe that it's this part, that is causing the error)...
Quote:Original post by d h k
Thank you for your answer!

STEP_SIZE is used when I load the heightmap. If it is 1, it would create a polygon for every pixel of the heightmap, if it is 8, it would take 8 pixels of the heightmap and create one polygon for them.

I am not sure if it is a good idea to use the STEP_SIZE in the loops or not...
If I use my third approach, but get rid of the STEP_SIZE in the loops, then it looks exactly like in my first, and second versions.

This is the code now:
*** Source Snippet Removed ***

If you need more code, then tell me as I am not sure which parts coould be of interest (and I seriously still believe that it's this part, that is causing the error)...

I believe that in that case the error is in the rendering code.

Tom
Ah, that's a good idea... :)

Here we go:

void cterrain::draw ( void )// draws a terrain{	// set up render modes	glColorMask ( 1, 1, 1, 1 );	glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f );	glDisable ( GL_BLEND );	glDisable ( GL_CLIP_PLANE0 );	glEnable ( GL_DEPTH_TEST );	glDisable ( GL_STENCIL_TEST );	// activate the texture	texture.activate ( );	// push the matrix	glPushMatrix ( );	// scale the terrain	glScalef ( scale, scale * HEIGHT_RATIO, scale );	// begin drawing the terrain	glBegin ( GL_TRIANGLES );	for ( int i = 0; i < ( ( MAP_SIZE - STEP_SIZE ) * ( MAP_SIZE - STEP_SIZE ) ); i += STEP_SIZE )	// loop through all data		{			// draw the first triangle			glNormal3f ( normals.x, normals.y, normals.z );			glTexCoord2f ( 0.0f, 0.0f );			glVertex3f ( trn.x, trn.y, trn.z );		}		// done drawing the terrain	glEnd ( );	// pop the matrix	glPopMatrix ( );}


This is the render function. Maybe it has to do with:
for ( int i = 0; i < ( ( MAP_SIZE - STEP_SIZE ) * ( MAP_SIZE - STEP_SIZE ) ); i += STEP_SIZE )

EDIT:

Hmm.. I changed the previously mentioned line to:
for ( int i = 0; i < MAP_SIZE * MAP_SIZE * 3; i++ )

But this creates tons of very small polygons... Looks kinda wierd, I may take a screenshot sooner or later.

Thanks for your help though!
At going through all the posts wriiten by dimebolt and dhk since my last constribution to this thread, there are two things that I've noticed:

(1) If you access a 2D defined array with a 1D index, then you have to know that the first index has the highest priority. So
int array[2][2];array[0][0] = 0;array[0][1] = 1;array[1][0] = 2;array[1][1] = 3;for(int index=0;index<3;++index)   printf("%d\n",((int*)array)[index]);

results in the sequence 0123 !

However, if you define a 1D array, and access it w/ a 2D index, then you may use either [x+y*x_size] or [y+x*y_size] w/ 0<=x<x_size, 0<=y<y_size. Which you choose play no role, but always use the same!

(2) The trn array is filled up linearly, say a vertex in trn[0], the next in trn[1], and so on, with an index dependency like this:
x,z=0,0 . x,z=1,0 . x,z=2,0 ... x,z=0,1 . x,z=1,1 . x,z=2,1 ... x,z=0,2 ...
So, when wrapping around at the limit of x == SIZE-1, one gets
x,z=0,0 . x,z=1,0 . x,z=2,0 ...
x,z=0,1 . x,z=1,1 . x,z=2,1 ...
x,z=0,2 . x,z=1,2 . x,z=2,2 ...

As a 1D index, the arrangement is (just that x+z*SIZE)
0 . 1 . 2 ...
SIZE+0 . SIZE+1 . SIZE+2 ...
2*SIZE+0 . 2*SIZE+1 . 2*SIZE+2 ...

When rendering, neighboured vertices have to be used to build up a tri. Since your glBegin uses GL_TRIANGLES, the indices for the first tri have to be {0,1,SIZE+0}, and for the second tri {1,SIZE+1,SIZE+0}.

If you still want to go with an incrementing index in your rendering routine, you should do something like this:
for(int i=0;i<(SIZE-1)*(SIZE-1);++i) {   glNormal3f ( normals.x, normals.y, normals.z );   glTexCoord2f ( 0.0f, 0.0f );   if((i&1)==0) {      glVertex3f ( trn.x, trn.y, trn.z );      glVertex3f ( trn[i+1].x, trn[i+1].y, trn[i+1].z );      glVertex3f ( trn[i+SIZE].x, trn[i+SIZE].y, trn[i+SIZE].z );   } else {      glVertex3f ( trn[i+1].x, trn[i+1].y, trn[i+1].z );      glVertex3f ( trn[i+SIZE+1].x, trn[i+SIZE+1].y, trn[i+SIZE+1].z );      glVertex3f ( trn[i+SIZE].x, trn[i+SIZE].y, trn[i+SIZE].z );   }}

This is, in fact, not optimal, but hopefully shows the dependency between the index calculations I mean. If I made no mistakes, then index i iterates the upper left corner of quads of your terrain, and the quad is rendered as two opposite tris.

I recommend to use tri-strips instead of particular tris, since it would make the rendering routine much more effective _and_ simpler.

BTW: Nice indexing problem ;-)

This topic is closed to new replies.

Advertisement