dark regions in some parts of the mesh

Started by
7 comments, last by tolaris 18 years, 10 months ago
Hi friends, I'm new in OpenGL. I loaded a .3ds model in my OpenGL code. There is no texture mapped on the model (only material color). The model contains thin triangles in some regions. The problem is that these regions are appearing darker... (as if I had applied a super smooth on the surface). In 3ds max the model doesn't show these dark regions. It seems a small shading problem... but I'm sure the normals were correctly calculated. Is there some OpenGL setting I should do to avoid that? Thanks in advance.
Advertisement
OK friends, here is an image about the problem. The left model was exported as a 3ds file and it was rendered using OpenGL (notice the dark regions). The right model was exported as an x-file and it was rendered using Direct3D (no shading problem here).

models.jpg
Most likely its a case of shoddy normals. Are you using normals supplied by the 3ds model? If not, make sure you're generating "good" normals. and finnally, show us your rendering code (more importantly your setup before you render)

cheers
-Dan
When General Patton died after World War 2 he went to the gates of Heaven to talk to St. Peter. The first thing he asked is if there were any Marines in heaven. St. Peter told him no, Marines are too rowdy for heaven. He then asked why Patton wanted to know. Patton told him he was sick of the Marines overshadowing the Army because they did more with less and were all hard-core sons of bitches. St. Peter reassured him there were no Marines so Patton went into Heaven. As he was checking out his new home he rounded a corner and saw someone in Marine Dress Blues. He ran back to St. Peter and yelled "You lied to me! There are Marines in heaven!" St. Peter said "Who him? That's just God. He wishes he were a Marine."
Hi Ademan555, the rendering code is pratically the same than 3DS Loader shown in Game Tutorials site.

LFace face;for (int k=0; k < GetMesh->GetFaceCount(); k++){      face = GetMesh->GetFace(k);      glBegin(GL_TRIANGLES);        glNormal3f(face.normals[0].x, face.normals[0].y, face.normals[0].z);        glVertex3f(face.vertices[0].x, face.vertices[0].y, face.vertices[0].z);                glNormal3f(face.normals[1].x, face.normals[1].y, face.normals[1].z);        glVertex3f(face.vertices[1].x, face.vertices[1].y, face.vertices[1].z);        glNormal3f(face.normals[2].x, face.normals[2].y, face.normals[2].z);        glVertex3f(face.vertices[2].x, face.vertices[2].y, face.vertices[2].z);      glEnd();}


LFace is a structure containing vertices and normals data:
struct LVector3{    float x;    float y;    float z;};struct LFace{    LVector3 vertices[3];    LVector3 normals[3];};


The function to calculate the normals is:
void LMesh::CalcNormals(){    LVector3 vertex;    LVector3 normal;    int i, k, j;    if (m_vertexCount <= 0)        return;    m_normalCount = m_vertexCount;    m_normals = (LVector3*) malloc(m_vertexCount*sizeof(LVector3));    for (i=0; i<m_vertexCount; i++)    {        normal.x = 0.0f;        normal.y = 0.0f;        normal.z = 0.0f;        vertex = m_vertices;        // find all vertices with the same coords        for (k=0; k<m_vertexCount; k++)        {            if ((fabs(vertex.x - m_vertices[k].x) < 0.0000001f) &&                (fabs(vertex.y - m_vertices[k].y) < 0.0000001f) &&                (fabs(vertex.z - m_vertices[k].z) < 0.0000001f))            {                for (j=0; j<m_triangleCount; j++)                {                    if ((m_triangles[j].a == (unsigned int)k) ||                        (m_triangles[j].b == (unsigned int)k) ||                        (m_triangles[j].c == (unsigned int)k))                    {                        LVector3 a, b, n;                        a = SubtractVectors(m_vertices[m_triangles[j].b], m_vertices[m_triangles[j].a]);                        b = SubtractVectors(m_vertices[m_triangles[j].b], m_vertices[m_triangles[j].c]);                        n = CrossProduct(b, a);                        n = NormalizeVector(n);                        normal = AddVectors(normal, n);                    }                }            }        }        m_normals = NormalizeVector(normal);    }}
It looks like you're doing a basic vertex normal by averaging all incident face normals. This is fine for smooth meshes (eg. heightmaps) but for an object like this you actually want two discrete normals for certain verts (like those around the edges of the holes). Best option is to actually export and load those normals from your editor.

Alternativly, you need to add the concept of a 'crease angle'. This basically means if two incident normals are too dissimilar (ie. you think theres a crease in the mesh) you duplicate the vert and only use the normals that are similar. This is fairly standard so google should give you a suitable algorithm.
Quote:Original post by OrangyTang
Alternativly, you need to add the concept of a 'crease angle'. This basically means if two incident normals are too dissimilar (ie. you think theres a crease in the mesh) you duplicate the vert and only use the normals that are similar. This is fairly standard so google should give you a suitable algorithm.

This seems to be the case to me on the screenshot, the shading goes weird because the normals are smoothing faces which are pretty much at straight angle to one another, and consequently the renderer is trying to interpolate between 'in light' and 'in shadow' along what's really a single, flat polygon.

checking acos( dot_product( face_1_normal, face_2_normal )) if it's below defined threshold angle (in radians) would generally work from what i can see.

On side note, testing every vertex against every vertex to see if they share position isn't very fast... implementing some kind of hash_multimap which keeps references to the vertices, with vertex position being the map key... might speed things up a lot, the testing for each vertex would be then done against greatly reduced sub-range of the whole set.
It looks like you used a boolean object in creating that model in 3d studio's, that can have some weird effects on meshes.

Besides that, try just calculating normals per face and rendering them that way instead of calculating per vertex.

It will give you more of the look you have on the .x file rather than the smoothed look you have in the 3ds file.
Thanks folks,

I solved the problem by importing the normals from an x-file to my OpenGL code (I wrote a code to parse the x-file to OpenGL functions). I decided to use an x-file (instead of a .3ds file) because I am already familiarized with this file format.

Another reason why I decided to import normals instead of calculating them in the code is: Usually, it is more flexible to change the smooth level in the mesh editor (3ds max) than in the code.

Thank you very much again for the help.
Quote:Original post by Mari_p
Another reason why I decided to import normals instead of calculating them in the code is: Usually, it is more flexible to change the smooth level in the mesh editor (3ds max) than in the code.

True about the flexibility... that's why ideally for 3ds format you'd be reading the smoothing groups information stored in the file:
** Subchunks of 0x4000 - Object description Block* Subchunks of 0x4100 - Triangular Polygon List(..)0x4150: Face Smoothing Group chunk.stores: unsigned int * number of faces.bits of the int indicate enabled smooth groups for this particular face.

... then during the check of vertices, instead of dot product of face normals, you do bitwise AND check: face_1_smoothgroup & face_2_smoothgroup ... if the result is non-zero it means faces share at least one smoothing group, and the normals for points they share should be smoothed.

The angle test on the other hand is good for other formats which make use of it, Lightwave files for example.

[edit] just for the heck of it, this thread gave me excuse to do small test how many vertex checks are done with different methods... the results for classic teapot with 5894 vertices (after unwelding):

* straight comparison (each vertex with each vertex): 34,739,236 tests
* 'smart' straight comparison (tested vertex and the target share their normals at once, vertices which already did it are skipped): 17,366,671 tests
* hash map, straight comparison: 118,354 tests
* 'smart' hash map, similar to method 2: 56,230 tests

from nearly 35 mil to 56 k... wish it's always possible to reduce workload like that :s

[Edited by - tolaris on June 12, 2005 10:22:35 AM]

This topic is closed to new replies.

Advertisement