Trouble Calculating Normals

Started by
4 comments, last by Vortez 10 years, 1 month ago

Hello everyone!

I think this maybe more related to perhaps more of how I calculated my normals, then a OpenGL issue. But I assume the best place to ask would be OpenGL forum encase if it's the way I told OpenGL to handle my normals.

Anyways I dont think my normals are calculated correctly, because I'm throwing in a rotating cube into my scene using triangles, and it appears only some triangles seem to light up correctly. Otherwise most of them are simply just black. Btw my scene is drawn clockwise if that makes a difference... And I'm using glNormalPointer, glVertexPointer, etc.. to draw everything and to make my OpenGL calls optimized as much as I can. ;)

Cube class constructor: Not really important I suspect since all this does is put together the cube and does nothing with the normals, but just encase...


	Cube[0] = m_pos.m_X-m_size.m_X;
	Cube[1] = m_pos.m_Y+m_size.m_Y;
	Cube[2] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[3] = m_pos.m_X+m_size.m_X;
	Cube[4] = m_pos.m_Y+m_size.m_Y;
	Cube[5] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[6] = m_pos.m_X-m_size.m_X;
	Cube[7] = m_pos.m_Y-m_size.m_Y;
	Cube[8] = m_pos.m_Z+m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[9] =  m_pos.m_X-m_size.m_X;
	Cube[10] = m_pos.m_Y-m_size.m_Y;
	Cube[11] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[12] = m_pos.m_X+m_size.m_X;
	Cube[13] = m_pos.m_Y+m_size.m_Y;
	Cube[14] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[15] = m_pos.m_X+m_size.m_X;
	Cube[16] = m_pos.m_Y-m_size.m_Y;
	Cube[17] = m_pos.m_Z+m_size.m_Z;

	//BACK face...
	//First triangle.

	//Vector 1

	Cube[18] = m_pos.m_X+m_size.m_X;
	Cube[19] = m_pos.m_Y+m_size.m_Y;
	Cube[20] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[21] = m_pos.m_X-m_size.m_X;
	Cube[22] = m_pos.m_Y+m_size.m_Y;
	Cube[23] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[24] = m_pos.m_X+m_size.m_X;
	Cube[25] = m_pos.m_Y-m_size.m_Y;
	Cube[26] = m_pos.m_Z-m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[27] = m_pos.m_X-m_size.m_X;
	Cube[28] = m_pos.m_Y+m_size.m_Y;
	Cube[29] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[30] = m_pos.m_X-m_size.m_X;
	Cube[31] = m_pos.m_Y-m_size.m_Y;
	Cube[32] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[33] = m_pos.m_X+m_size.m_X;
	Cube[34] = m_pos.m_Y-m_size.m_Y;
	Cube[35] = m_pos.m_Z-m_size.m_Z;


	//LEFT face...
	//First triangle.

	//Vector 1

	Cube[36] = m_pos.m_X+m_size.m_X;
	Cube[37] = m_pos.m_Y-m_size.m_Y;
	Cube[38] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[39] = m_pos.m_X+m_size.m_X;
	Cube[40] = m_pos.m_Y-m_size.m_Y;
	Cube[41] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[42] = m_pos.m_X+m_size.m_X;
	Cube[43] = m_pos.m_Y+m_size.m_Y;
	Cube[44] = m_pos.m_Z+m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[45] = m_pos.m_X+m_size.m_X;
	Cube[46] = m_pos.m_Y+m_size.m_Y;
	Cube[47] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[48] = m_pos.m_X+m_size.m_X;
	Cube[49] = m_pos.m_Y-m_size.m_Y;
	Cube[50] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[51] = m_pos.m_X+m_size.m_X;
	Cube[52] = m_pos.m_Y+m_size.m_Y;
	Cube[53] = m_pos.m_Z+m_size.m_Z;

	//RIGHT face...
	//First triangle.

	//Vector 1

	Cube[54] = m_pos.m_X-m_size.m_X;
	Cube[55] = m_pos.m_Y-m_size.m_Y;
	Cube[56] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[57] = m_pos.m_X-m_size.m_X;
	Cube[58] = m_pos.m_Y+m_size.m_Y;
	Cube[59] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[60] = m_pos.m_X-m_size.m_X;
	Cube[61] = m_pos.m_Y-m_size.m_Y;
	Cube[62] = m_pos.m_Z+m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[63] = m_pos.m_X-m_size.m_X;
	Cube[64] = m_pos.m_Y+m_size.m_Y;
	Cube[65] = m_pos.m_Z-m_size.m_Z;

	//Vector2
	Cube[66] = m_pos.m_X-m_size.m_X;
	Cube[67] = m_pos.m_Y+m_size.m_Y;
	Cube[68] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[69] = m_pos.m_X-m_size.m_X;
	Cube[70] = m_pos.m_Y-m_size.m_Y;
	Cube[71] = m_pos.m_Z+m_size.m_Z;


	//TOP face...
	//First triangle.

	//Vector 1

	Cube[72] = m_pos.m_X+m_size.m_X;
	Cube[73] = m_pos.m_Y+m_size.m_Y;
	Cube[74] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[75] = m_pos.m_X-m_size.m_X;
	Cube[76] = m_pos.m_Y+m_size.m_Y;
	Cube[77] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[78] = m_pos.m_X+m_size.m_X;
	Cube[79] = m_pos.m_Y+m_size.m_Y;
	Cube[80] = m_pos.m_Z-m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[81] = m_pos.m_X-m_size.m_X;
	Cube[82] = m_pos.m_Y+m_size.m_Y;
	Cube[83] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[84] = m_pos.m_X-m_size.m_X;
	Cube[85] = m_pos.m_Y+m_size.m_Y;
	Cube[86] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[87] = m_pos.m_X+m_size.m_X;
	Cube[88] = m_pos.m_Y+m_size.m_Y;
	Cube[89] = m_pos.m_Z-m_size.m_Z;

	//BOTTOM face...
	//First triangle.

	//Vector 1

	Cube[90] = m_pos.m_X-m_size.m_X;
	Cube[91] = m_pos.m_Y-m_size.m_Y;
	Cube[92] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[93] = m_pos.m_X+m_size.m_X;
	Cube[94] = m_pos.m_Y-m_size.m_Y;
	Cube[95] = m_pos.m_Z+m_size.m_Z;

	//Vector3
	Cube[96] = m_pos.m_X+m_size.m_X;
	Cube[97] = m_pos.m_Y-m_size.m_Y;
	Cube[98] = m_pos.m_Z-m_size.m_Z;

	//Second triangle...

	//Vector 1
	Cube[99] = m_pos.m_X-m_size.m_X;
	Cube[100] = m_pos.m_Y-m_size.m_Y;
	Cube[101] = m_pos.m_Z+m_size.m_Z;

	//Vector2
	Cube[102] = m_pos.m_X+m_size.m_X;
	Cube[103] = m_pos.m_Y-m_size.m_Y;
	Cube[104] = m_pos.m_Z-m_size.m_Z;

	//Vector3
	Cube[105] = m_pos.m_X-m_size.m_X;
	Cube[106] = m_pos.m_Y-m_size.m_Y;
	Cube[107] = m_pos.m_Z-m_size.m_Z;

	normals = GLShortcuts::calculateNormal(Cube, 12);

calculateNormal... I know it's a tiny bit messy, but it's partly because of debugging reasons...


float* GLShortcuts::calculateNormal(float vec[], size_t triangles)
{

	float* normals = new float[triangles*3];
	size_t normPos = 0;
	size_t vexPos = 0;
	
	for(size_t i = 0; i<triangles; i++)
	{

	VECTOR_3D v1 = VECTOR_3D(vec[vexPos], vec[vexPos+1], vec[vexPos+2]);
	VECTOR_3D v2 = VECTOR_3D(vec[vexPos+3], vec[vexPos+4], vec[vexPos+5]);
	VECTOR_3D v3 = VECTOR_3D(vec[vexPos+6], vec[vexPos+7], vec[vexPos+8]);

	vexPos += 9;
		
	VECTOR_3D crossVec1 = (v2 - v1);
	VECTOR_3D crossVec2 = (v3 - v1);
	VECTOR_3D normal;
	normal.m_X = crossVec2.m_Y*crossVec1.m_Z - crossVec1.m_Y*crossVec2.m_Z;
	normal.m_Y = crossVec1.m_X*crossVec2.m_Z - crossVec2.m_X*crossVec1.m_Z;
	normal.m_Z = crossVec2.m_X*crossVec1.m_Y - crossVec2.m_Y*crossVec1.m_X;
	normal.Normalize();

		normals[normPos+0] = normal.m_X;
		normals[normPos+1] = normal.m_Y; 
		normals[normPos+2] = normal.m_Z;



		normPos+=3;
	}

void CubeGL::draw(void)
{
	if(m_texId > 0)
	{
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
	}

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);

	glVertexPointer(3, GL_FLOAT, 0, Cube);
	glTexCoordPointer(2, GL_FLOAT, 0, UV_Coords);
	glNormalPointer(GL_FLOAT, 0, normals);

		glDrawArrays(GL_TRIANGLES, 0, 36);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);


	if(m_texId > 0)
	{
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_BLEND);
	}
}

And thats the draw code. Assuming if I'm being a tard with how I'm calling the normals.

Check out my open source code projects/libraries! My Homepage You may learn something.
Advertisement

Well you could have a look at my 3ds exporter code, it's not very clean but it always worked just fine, ill post the most revelent code, hope i wont forget anything.


// This is our 3D point class. This will be used to store the vertices of our model.
class CVector3
{
public:
float x, y, z;
void Set(float i1, float i2, float i3){this->x=i1;this->y=i2;this->z=i3;};
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This calculates a vector between 2 points and returns the result
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVector3 _Vector(CVector3 vPoint1, CVector3 vPoint2)
{
CVector3 vVector;
vVector.x = vPoint1.x - vPoint2.x;
vVector.y = vPoint1.y - vPoint2.y;
vVector.z = vPoint1.z - vPoint2.z;
return vVector;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This adds 2 vectors together and returns the result
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVector3 _AddVector(CVector3 vVector1, CVector3 vVector2)
{
CVector3 vResult;
vResult.x = vVector2.x + vVector1.x;
vResult.y = vVector2.y + vVector1.y;
vResult.z = vVector2.z + vVector1.z;
return vResult;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This divides a vector by a single number (scalar) and returns the result
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVector3 _DivideVectorByScaler(CVector3 vVector1, float Scaler)
{
CVector3 vResult;
vResult.x = vVector1.x / Scaler;
vResult.y = vVector1.y / Scaler;
vResult.z = vVector1.z / Scaler;
return vResult;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This returns the cross product between 2 vectors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVector3 _Cross(CVector3 vVector1, CVector3 vVector2)
{
CVector3 vCross;
vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
return vCross;	// Return the cross product
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This returns the normal of a vector
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CVector3 _Normalize(CVector3 vNormal)
{
double Magnitude;
Magnitude = Mag(vNormal);
vNormal.x /= (float)Magnitude;
vNormal.y /= (float)Magnitude;
vNormal.z /= (float)Magnitude;
return vNormal;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Save the vertex normals (smooth)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Model3DExport::SaveNormals(Mesh* pMesh, Matrix3 tm, CModelDataStruct* pModelData, bool SaveNonIndexed)
{
if(!Options.GenNormalsVA){return;}

UINT NumVerts = pModelData->IndexedVerticesCount;
UINT NumFaces = pModelData->FacesCount;

int Indx[3];
CVector3 vVector1, vVector2, vNormal, vPoly[3];

//Allocate memory for our normals and the shared info
CVector3 *pVertexNormals = new CVector3[NumVerts];
int *pShared = new int[NumVerts];
//Initialize our array
ZeroMemory(pVertexNormals, sizeof(CVector3) * NumVerts);
ZeroMemory(pShared, sizeof(int) * NumVerts);

///////////////////////////////////////////////////////////////////////////////
//Here we'll compute normals(this new algorithm of mine is BLAZING FAST!)...
///////////////////////////////////////////////////////////////////////////////

// Go though all of the faces of this object
UINT i = 0;
for(i = 0; i < NumFaces; i++){	

//Read the 3 face vertex
for(int j = 0; j < 3; j++){
Indx[j] = pMesh->faces[i].v[j];
Point3 v = (tm * pMesh->verts[Indx[j]]);
vPoly[j].Set(v.x, v.z, 0.0f - v.y);	
}

// Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)
vVector1 = _Vector(vPoly[0], vPoly[2]);	// Get the vector of the polygon (we just need 2 sides for the normal)
vVector2 = _Vector(vPoly[2], vPoly[1]);	// Get a second vector of the polygon

//This get our FACE normals
vNormal = _Cross(vVector1, vVector2);	// Return the cross product of the 2 vectors (normalize vector, but not a unit vector)

//Accum the value for the 3 VERTEX normals and keep track of a shared value
#ifdef _VS2010_
int j = 0;
#endif
for(j = 0; j < 3; j++){
pVertexNormals[Indx[j]] = _AddVector(pVertexNormals[Indx[j]], vNormal);
pShared[Indx[j]]++;
}
}

///////////////////////////////////////////////////////////////////////////////////////

#ifdef WRITE_TEXT_OUTPUT
char Buf[128];
ZeroMemory(&Buf[0], 128);
sprintf(&Buf[0], "Vertex Normals: {\n");
fputs(&Buf[0], TextOutF);
#endif

// Now Get The Vertex Normals and save them
for(i = 0; i < NumVerts; i++){	
pVertexNormals[i] = _DivideVectorByScaler(pVertexNormals[i], float(-pShared[i]));
pVertexNormals[i] = _Normalize(pVertexNormals[i]);	
memcpy(&pModelData->pNormalsArray[VA_INDEXED][i], &pVertexNormals[i].x, sizeof(float3));

#ifdef WRITE_TEXT_OUTPUT
sprintf(&Buf[0], " {%.4f, %.4f, %.4f},\n", pVertexNormals[i].x, pVertexNormals[i].y, pVertexNormals[i].z);
fputs(&Buf[0], TextOutF);
#endif
}

#ifdef WRITE_TEXT_OUTPUT
sprintf(&Buf[0], "}\n\n");
fputs(&Buf[0], TextOutF);
#endif

///////////////////////////////////////////////////////////////////////////////////////

// Delete temp buffers
SAFE_DELETE_ARRAY(pShared);
SAFE_DELETE_ARRAY(pVertexNormals);

if(SaveNonIndexed){
if(Options.ForceSmoothNormals){
ConvertToNonIndexed((BYTE*)pModelData->pNormalsArray[VA_NON_INDEXED], (BYTE*)pModelData->pNormalsArray[VA_INDEXED], sizeof(float3), pModelData);
} else {
SaveFacesNormals(pMesh, tm, pModelData);
}
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Save the face normals
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Model3DExport::SaveFacesNormals(Mesh* pMesh, Matrix3 tm, CModelDataStruct* pModelData, bool SaveNonIndexed)
{
if(!Options.GenNormalsVA){return;}

#ifdef WRITE_TEXT_OUTPUT
char Buf[128];
ZeroMemory(&Buf[0], 128);
sprintf(&Buf[0], "Faces Normals: {\n");
fputs(&Buf[0], TextOutF);
#endif

UINT NumVerts = pModelData->VerticesCount;
UINT NumFaces = pModelData->FacesCount;

CVector3 vVector1, vVector2, vNormal, vPoly[3];
int Indx[3];

// Go though all of the faces of this object
for(UINT i = 0; i < NumFaces; i++){	

//Read the 3 face vertex
for(int j = 0; j < 3; j++){
Indx[j] = pMesh->faces[i].v[j];
Point3 v = (tm * pMesh->verts[Indx[j]]);
vPoly[j].Set(v.x, v.z, 0.0f - v.y);	
}

// Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)
vVector1 = _Vector(vPoly[0], vPoly[2]);	// Get the vector of the polygon (we just need 2 sides for the normal)
vVector2 = _Vector(vPoly[2], vPoly[1]);	// Get a second vector of the polygon

//This get our FACE normals
vNormal = _Cross(vVector1, vVector2);	// Return the cross product of the 2 vectors (normalize vector, but not a unit vector)

vNormal = _DivideVectorByScaler(vNormal, -1.0f);
vNormal = _Normalize(vNormal);	

//Save them in our FACE normals array
memcpy(&pModelData->pNormalsArray[VA_NON_INDEXED][(i * 3) ], &vNormal.x, sizeof(float3));
memcpy(&pModelData->pNormalsArray[VA_NON_INDEXED][(i * 3)+1], &vNormal.x, sizeof(float3));
memcpy(&pModelData->pNormalsArray[VA_NON_INDEXED][(i * 3)+2], &vNormal.x, sizeof(float3));
#ifdef WRITE_TEXT_OUTPUT
sprintf(&Buf[0], " {%.4f, %.4f, %.4f},\n", vNormal.x, vNormal.x, vNormal.x);
fputs(&Buf[0], TextOutF);
#endif
}
#ifdef WRITE_TEXT_OUTPUT
sprintf(&Buf[0], "}\n\n");
fputs(&Buf[0], TextOutF);
#endif
}


Somehow, copy/paste ate the tabs...

Here's the full source code if your interested. Most stuffs in the 2 last functions are useless for you so you gonna have to clean that up smile.png

The bottom one calculate face normals, and the one before that calculate per-vertex normals.

I really should refactor that old code... but if it ain't broke, don't fix it? ph34r.png

EDIT: Also, i noticed you're calling glEnable(GL_TEXTURE_2D) but not glEnable(GL_LIGHTING), though i havent done modern opengl so i don't know if that's an error or not. Just wanted to point it out.

I tried your example of calculating face normals, but the only result I'm getting is that two triangles lit up in odd directions... I'm wondering if it's the way I'm creating the cube, thats the problem. Even though the triangles show up normal, the normals are not and I appear to be using the correct GL calls. I.E GL_LIGHTING, GL_NORMALIZE, etc...

Check out my open source code projects/libraries! My Homepage You may learn something.

I did realize however just now I should be calling GL_BLEND. So now a lot more triangles light up, but it appears the normals are still a bit messed up. =/

Check out my open source code projects/libraries! My Homepage You may learn something.

Well it very well just could be how I'm using glNormalPointer, because I used the down and dirty method to draw a plane with two triangles, and it looked perfectly fine! D: FAIL! If someone can tell me why this could be that would be nice!

I.E How it should look to OpenGL.


//Draw plane...
glBegin(GL_TRIANGLES);
glNormal3f(normals[0], normals[1], normals[2]);
//Draw triangle...
glVertex3f(...);
glNormal3f(normals[3], normals[4], normals[5]);
//Draw another triangle...
glVertex3f(...);

glEnd();
Check out my open source code projects/libraries! My Homepage You may learn something.

I noticed also in your first example, you are calling glTexCoordPointer(2, GL_FLOAT, 0, UV_Coords); without first calling glEnableClientState, and i dont see what glBlend had to do with normals. My guess is some of your triangles are drawn in the wrong order, (clockwise or counterclockwise, i never remember wich one is used by default, but they should all use the same winding order)

This topic is closed to new replies.

Advertisement