0
votes

I have a obj file which stores data this way:

v value1 value2 value3  
f value1 value2 value3

First I calculate a normal for the face and then assign for each vertex of that face:

for(int i = 0; i < verticesInd.size(); i+=3)
{
    glm::vec3 normal = glm::normalize(glm::cross(glm::vec3(vertices[verticesInd[i + 1]]) - glm::vec3(vertices[verticesInd[i]]), glm::vec3(vertices[verticesInd[i + 2]]) - glm::vec3(vertices[verticesInd[i]])));
    out_Normals[i] = normal;
    out_Normals[i + 1] = normal;
    out_Normals[i + 2] = normal;
}

For achieve a flat shading I can duplicate vertices:

for(int i = 0; i < verticesInd.size(); i++)
{
    out_Vertices.push_back(vertices[verticesInd[i]]);
} 

and then draw the object using glDrawArrays:

glDrawArrays(GL_TRIANGLES, 0, out_Vertices.size());

For achieve a smooth shading I need to average normals for each vertex but I have no idea how to find adjacent faces.

Edit1: I didn't notice a single s parameter before f:

v value1 value2 value3
s 1  
f value1 value2 value3

Edit2: Normals averaging

glm::vec3 tNormal;
for(int i = 0; i < vertices.size(); i++)
{
    for(int  j = 0; j < verticesInd.size(); j++)
    {
        if(verticesInd[j] == i)
        {
            tNormal += faceNormals[j / 3];
        }
    }
    aNormals.push_back(glm::normalize(tNormal));
    tNormal = glm::vec3(0,0,0);
}

Edit 3 Face normals:

for(int i = 0; i < verticesInd.size(); i+=3)
{
    glm::vec3 normal = glm::normalize(glm::cross(glm::vec3(vertices[verticesInd[i + 1]]) - glm::vec3(vertices[verticesInd[i]]), glm::vec3(vertices[verticesInd[i + 2]]) - glm::vec3(vertices[verticesInd[i]])));
    faceNormals.push_back(normal);
}
3

3 Answers

1
votes

In most object formats adjacent faces should be sharing vertices. Finding the smooth shaded normal at a vertex should just then be a question of averaging the normal of any face which uses that vertex.

I suggest that you create an additional new array the same size as your existing vertex array.

Iterate over each face, and for each vertex index, add that vertice's face normal to the new array.

At the end of the process, normalise the result normal vectors and then use that instead of the previously computed face normals.

If I understand your data structures correctly, it would look something like this:

glm::vec3 aNormals[];   // one for each vertex - use the appropriate constructor

for (int i = 0; i < verticesInd.size(); ++i) {
    int f = i / 3;                 // which face is this index part of (3 per face?)
    int v = verticesInd[i];        // which vertex number is being used
    aNormals[v] += faceNormals[f]; // add the face normal to this vertex
}

// now normalise aNormals
0
votes

I have no idea how to find adjacent faces.

Add a list of faces to your temporary, in-OBJ-loader vertex storage. As you're processing the face lines add the new face to the face list of each vertex it references.

That way you can spin over all the vertexes at the end, look up the face(s) it belonged to, grab the face normals, and average them.

If your OBJ doesn't have such nice baked-in connectivity information (and there's no requirement that it has to) then you'll have to do a nearest-neighbor search on each vertex to find vertexes (and corresponding faces) that are near it. Use your favorite spatial index to speed those sorts of queries up.

0
votes

Follow this guy https://www.youtube.com/watch?v=MRD_zN0SWh0&feature=plcp on per fragment lighting. It contains the same equations for finding the normals on a per vertex basis as far as I know.

You would need a list of faces. If you've ever seen a wavefront obj. They contain multiple triangle indices 4,7,2 1,2,3. Each 3 numbers representing a face. With a normal Mesh each point can only be used 3 times. If you find each group that has a 3 you can find each face. Find their corresponding normal values and then average.

Alnitak has valid info too. You have a list of vertices, then list them off as groups of 3 so that you can reuse vertices(share) as face data.