PDA

View Full Version : Averaging normal vectors for TORUS



Bert De Craene
07-24-2006, 06:25 AM
To draw a part of a torus I use the following code. The problem is that this torus looks faceted (not smoothly). Does someone know how to modify this code in order to let this torus look more natural ?


twopi := 2*PI;
for i:=0 to numc-1 do
begin
glBegin(GL_QUAD_STRIP);
j:=0;
d:=numt*morc;
while (j<=d) do
begin
d2:=OuterRadius+InnerRadius*cos((i+1)*twopi/numc);
x_norm := d2*cos((j+0.5)*twopi/numt)-OuterRadius*cos((j+0.5)*twopi/numt);
y_norm := d2*sin((j+0.5)*twopi/numt)-OuterRadius*sin((j+0.5)*twopi/numt);
z_norm := InnerRadius*sin((i+1)*twopi/numc);
norm := sqrt(x_norm*y_norm+y_norm*y_norm+z_norm*z_norm);
x_norm := x_norm/norm;
y_norm := y_norm/norm;
z_norm := z_norm/norm;

glNormal3d(x_norm,y_norm,z_norm);

for k_:=1 downto 0 do
begin
s_ := (i+k_) mod numc + 0.5;
t := j mod numt;
d2 := OuterRadius+InnerRadius*cos(s_*twopi/numc);
x1 := d2*cos(t*twopi/numt);
y1 := d2*sin(t*twopi/numt);
z1 := InnerRadius*sin(s_*twopi/numc);
glVertex3d(x1,y1,z1);
end;
inc(j);
end;
glEnd;
end;

des
07-24-2006, 09:05 PM
Hooh! I'm understand noting in your code, but one advice - you need to specify diffrent normals to each vertex to obtainsmooth result (as I anderstand you draw more than one vertex with one normal). Or try to use glEnable(GL_AUTO_NORMAL) to enable automatic normal generation;

Bert De Craene
07-25-2006, 12:36 AM
Hi Des,

I tried to use glEnable(GL_AUTO_NORMAL) but this doesn't solve the problem.

The code above is written in FreePASCAL. Please find the sourcecode in C++, maybe it's easier for you to understand :


/************************************************** *****/
/*fonction de construction de morceau de tore */
/************************************************** ****/


static void torus(int numc, int numt,float morc,float radius1,float radius2)


{
int i, j, k;
double s, t, x, y, z,x_norm,y_norm,z_norm,norm, twopi;

twopi = 2 * PI_;
for (i = 0; i < numc; i++) {
glBegin(GL_QUAD_STRIP);

for (j = 0; j <= numt*morc; j++) {

x_norm=(radius2+radius1*cos((i+1)*twopi/numc))*cos((j+0.5)*twopi/numt)-radius2*cos((j+0.5)*twopi/numt);
y_norm=(radius2+radius1*cos((i+1)*twopi/numc))*sin((j+0.5)*twopi/numt)-radius2*sin((j+0.5)*twopi/numt);
z_norm=radius1 * sin((i+1)*twopi / numc);
norm = sqrt(x_norm*x_norm+y_norm*y_norm+z_norm*z_norm);
x_norm=x_norm/norm;
y_norm=y_norm/norm;
z_norm=z_norm/norm;

glNormal3f(x_norm,y_norm,z_norm);

for (k = 1; k >= 0; k--) {
s = (i + k) % numc + 0.5;
t = j % numt;

x = (radius2+radius1*cos(s*twopi/numc))*cos(t*twopi/numt);
y = (radius2+radius1*cos(s*twopi/numc))*sin(t*twopi/numt);
z = (radius1 * sin(s * twopi / numc));
glVertex3f(x, y, z);
}
}
glEnd();
}
}

k_szczech
07-25-2006, 03:53 AM
Ok, here is what you should do:
1. Create vertex array (3 floats per vertex) - fill it with vertex coordinates
2. Create index array (4 integers per quad) - fill it with vertices in the order they should be drawn .
3. Create normal array for faces (3 floats for each quad) - compute all normals
4. Create normal array for vertices - fill it up with (0.0, 0.0, 0.0).
5. For each quad add it's normal vector to all 4 normals of vertices it uses
6. Delete normal array for faces
7. Normalize every normal in vertex normal array

Now just use vertex arrays and normal arrays and call glDrawElements, passing your index array. Use GL_QUADS.

If you do it right, you will se that not only it will look good, but the code will be a bit cleaner, and rendering will be a whole lot faster.

Bert De Craene
07-25-2006, 04:40 AM
Hi,

Thanks a lot for your answer.

I never used vertex, index and normal arrays before. May I ask you if you want to modify the code above ?

These are the values I use :

numc = 40
numt = 40
morc = 0.25 (this means that only 25 percent of the torus is drawn)
radius1 = 28 (=inner radius)
radius2 = 56 (=outer radius)

Thanks a lot in advance.
Bert.

k_szczech
07-25-2006, 08:58 AM
> May I ask you if you want to modify the code above ?

You may ask... ;)

Sorry - I have code that generates torus but it's based on my own framework so it would take me too much time to explain everything.

What I gave you is a general idea of computing smoothed normals for any mesh. If you just need a torus then things can get a bit simplier:

a1, a2 - angle
r1, r2 - radius
pos - position of vertex
normal - normal vector of vertex

Now use a1 and a2 in a loop - the equations below generate vertex position and it's normal.

pos.x = sin(a1) * r1;
pos.y = (cos(a1) * r1 + r2) * sin(a2);
pos.z = (cos(a1) * r1 + r2) * cos(a2);

normal.x = sin(a1);
normal.y = cos(a1) * sin(a2);
normal.z = cos(a1) * cos(a2);Simply put vertices into vertex array and normals into normal array. You do not have to use OpenGL's vertex arrays if you don't want to - you can just have your own arrays and use glNormal / glVertex.

All you need to do now is to 'connect the dots'.

Bert De Craene
07-26-2006, 02:53 AM
Hi,

:confused:

I first trie to rewrite the code using your algorithm, without usage of vertex-arrays.
If this works fine, I will rewrite it using vertex-arrays.

The problem is that no torus is drawn but something completely different. Do you see what is wrong in the code below (maybe the position of glBegin ... glEnd ?)



{************************************************* ********************************************}
Procedure TCadObj.Torus2(Tx,Ty,Tz,Ax,Ay,Az,InnerRadius,Outer Radius,Angle1,Angle2,Step:double);
{************************************************* ********************************************}

var PosX,PosY,PosZ : double;
NormX,NormY,NormZ : double;
CosA1,SinA1 : double;
CosA2,SinA2 : double;
a1,a2 : double;


begin
PiOver180:=PI/180;
glTranslated(Tx,Ty,Tz);
if Ax<>0 then glRotated(Ax,1,0,0);
if Ay<>0 then glRotated(Ay,0,1,0);
if Az<>0 then glRotated(Az,0,0,1);
glDisable(GL_CULL_FACE);

Angle1 := PiOver180*Angle1; {degrees to radians}
Angle2 := PiOver180*Angle2; {degrees to radians}
Step := PiOver180*Step; {degrees to radians}

a1:=0;
while a1<=Angle1 do
begin
CosA1:=cos(a1);
SinA1:=sin(a1);
a2:=0;
glBegin(GL_QUAD_STRIP);

while a2<=Angle2 do
begin
CosA2:=cos(a2);
SinA2:=sin(a2);

PosX := SinA1 * InnerRadius;
PosY := (CosA1 * InnerRadius + OuterRadius) * SinA2;
PosZ := (CosA1 * InnerRadius + OuterRadius) * CosA2;
NormX := SinA1;
NormY := CosA1 * SinA2;
NormZ := CosA1 * CosA2;
glNormal3d(NormX, NormY, NormZ);
glVertex3d(PosX, PosY, PosZ);

a2:=a2+step;
end;

glEnd;
a1:=a1+step;
end;

glEnable(GL_CULL_FACE);
end;

k_szczech
07-26-2006, 10:01 AM
Your prevoius code used 3 loops - i / j / k, but now you're using 2 loops, so you only generate one side of quad strip.

Bert De Craene
07-27-2006, 08:21 AM
Would you please modify the code ?
The problem is that I don't understand the mathematics behind it.

k_szczech
07-27-2006, 03:47 PM
This is the vertex array version.

#define gridX 64
#define gridY 32

struct vec3
{
float x;
float y;
float z;
};

vec3 vertexArray[gridX * gridY];
vec3 normalArray[gridX * gridY];
unsigned short indexArray[gridX * gridY * 4];

void RenderTorus(void)
{
glVertexPointer(3, GL_FLOAT, 0, (const GLvoid*)vertexArray);
glNormalPointer(GL_FLOAT, 0, (const GLvoid*)normalArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDrawElements(GL_QUADS, gridX * gridY * 4, GL_UNSIGNED_SHORT, (const GLvoid*)indexArray);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}

void CreateTorus(void)
{
unsigned int x, y;
float r1 = 1.0f;
float r2 = 3.0f;
unsigned int i1 = 0;
unsigned int i2 = 0;
for (y = 0; y < gridY; y++)
{
float a1 = (float)y * 6.2831843f / (float)gridY;
float sa1 = sin(a1);
float ca1 = cos(a1);
unsigned int y1 = (y + 1) % gridY;
for (x = 0; x < gridX; x++)
{
float a2 = (float)x * 6.2831843f / (float)gridX;
float sa2 = sin(a2);
float ca2 = cos(a2);
unsigned int x1 = (x + 1) % gridX;
vertexArray[i1].x = sa1 * r1;
vertexArray[i1].y = (ca1 * r1 + r2) * sa2;
vertexArray[i1].z = (ca1 * r1 + r2) * ca2;
normalArray[i1].x = sa1;
normalArray[i1].y = ca1 * sa2;
normalArray[i1].z = ca1 * ca2;
i1++;
indexArray[i2++] = x + gridX * y;
indexArray[i2++] = x1 + gridX * y;
indexArray[i2++] = x1 + gridX * y1;
indexArray[i2++] = x + gridX * y1;
}
}
}

Bert De Craene
08-01-2006, 04:46 AM
Thanks a lot !!!

Bert De Craene
08-01-2006, 06:04 AM
This works perfectly, the torus is as smooth as possible.

But how do I modify the code to draw only a part (e.g. a quarter) of this torus ?