0
votes

could anyone please help me calculating vertex normals in OpenGL? I am loading an obj file and adding Gouraud shading by calculating vertex normals without using glNormal3f or glLight functions.. I have declared functions like operators, crossproduct, innerproduct,and etc.. I have understood that in order to get vertex normals, I first need to calculate surface normal aka normal vector with crossproduct.. and also since I am loading an obj file.. and I am placing the three points of Faces of the obj file in id1,id2,id3 something like that

I would be grateful if anyone can help me writing codes or give me a guideline how to start the codes. please ... thanks..

its to draw

FACE cur_face = cube.face[i];
        glColor3f(cube.vertex_color[cur_face.id1].x,cube.vertex_color[cur_face.id1].y,cube.vertex_color[cur_face.id1].z);
        glVertex3f(cube.vertex[cur_face.id1].x,cube.vertex[cur_face.id1].y,cube.vertex[cur_face.id1].z);
        glColor3f(cube.vertex_color[cur_face.id2].x,cube.vertex_color[cur_face.id2].y,cube.vertex_color[cur_face.id2].z);
        glVertex3f(cube.vertex[cur_face.id2].x,cube.vertex[cur_face.id2].y,cube.vertex[cur_face.id2].z);
        glColor3f(cube.vertex_color[cur_face.id3].x,cube.vertex_color[cur_face.id3].y,cube.vertex_color[cur_face.id3].z);
        glVertex3f(cube.vertex[cur_face.id3].x,cube.vertex[cur_face.id3].y,cube.vertex[cur_face.id3].z);
    }

This is the equation for color calculation

VECTOR kd;
VECTOR ks;
kd=VECTOR(0.8, 0.8, 0.8);
ks=VECTOR(1.0, 0.0, 0.0);
double inner =  kd.InnerProduct(ks);

int i, j;
for(i=0;i<cube.vertex.size();i++)
{
    VECTOR n = cube.vertex_normal[i];
    VECTOR l = VECTOR(100,100,0) - cube.vertex[i];
    VECTOR v = VECTOR(0,0,1) - cube.vertex[i];
    float xl = n.InnerProduct(l)/n.Magnitude();
    VECTOR x = (n * (1.0/ n.Magnitude())) * xl;
    VECTOR r = x - (l-x);

    VECTOR color = kd * (n.InnerProduct(l)) + ks * pow((v.InnerProduct(r)),10);
    cube.vertex_color[i] = color;
3
For the surface normal you may use normalized((B-A)x(C-A)) for every ABC combination you draw. For the vertex normal I have no idea what you want to compute. The vertex normals are the same as the surface ones for the edgy shapes. But for the smooth shapes this can be pretty much anything, in most cases you could find all the surface normals for a vertex and use the normalized average of the surface normals. It is impossible to say which to use as it is ambiguous for some cases (for instance drawing a disco ball would need surface normals, ball would not but both have the same position data)Matic Oblak
I wrote codes for surface normal..!!chloe
based on what I learned, normal vertex is the average of surface normalschloe
@MaticOblak An obj file usually contains V, F, and VNchloe
It may be an average of surface normals but as I said not for the edgy parts. For instance a box (cube) will need to use surface normals and each corner vertex then actually has 3 normals (one for each surface). Anyway if you chose you will be creating vertex normals by using an average of surface normals that's fine. But where is the issue in that? What exactly is it you need help with? Finding a vector average?Matic Oblak

3 Answers

0
votes

It seems all you need to implement is the function to get the average vector from N vectors. This is one of the ways to do it:

struct Vector3f {
    float x, y, z;
};
typedef struct Vector3f Vector3f;

Vector3f averageVector(Vector3f *vectors, int count) {
    Vector3f toReturn;
    toReturn.x = .0f;
    toReturn.y = .0f;
    toReturn.z = .0f;

    // sum all the vectors
    for(int i=0; i<count; i++) {
        Vector3f toAdd = vectors[i];
        toReturn.x += toAdd.x;
        toReturn.y += toAdd.y;
        toReturn.z += toAdd.z;
    }
    // divide with number of vectors
    // TODO: check (count == 0)
    float scale = 1.0f/count;
    toReturn.x *= scale;
    toReturn.y *= scale;
    toReturn.z *= scale;

    return toReturn;
}

I am sure you can port that to your C++ class. The result should then be normalized unless the length iz zero.

Find all surface normals for every vertex you have. Then use the averageVector and normalize the result to get the smooth normals you are looking for.

Still as already mentioned you should know that this is not appropriate for edged parts of the shape. In those cases you should use the surface vectors directly. You would probably be able to solve most of such cases by simply ignoring a surface normal(s) that are too different from the others. Extremely edgy shapes like cube for instance will be impossible with this procedure. What you would get for instance is:

{
1.0f, .0f, .0f,
.0f, 1.0f, .0f,
.0f, .0f, 1.0f
}

With the normalized average of {.58f, .58f, .58f}. The result would pretty much be an extremely low resolution sphere rather then a cube.

0
votes

*This answer is for triangular mesh and can be extended to poly mesh as well.

tempVertices stores list of all vertices.

vertexIndices stores details of faces(triangles) of the mesh in a vector (in a flat manner).

    std::vector<glm::vec3> v_normal;

    // initialize vertex normals to 0
    for (int i = 0; i != tempVertices.size(); i++)
    {
        v_normal.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
    }

    // For each face calculate normals and append to the corresponding vertices of the face
    for (unsigned int i = 0; i < vertexIndices.size(); i += 3)
    {
        //vi v(i+1) v(i+2) are the three faces of a triangle
        glm::vec3 A = tempVertices[vertexIndices[i] - 1];
        glm::vec3 B = tempVertices[vertexIndices[i + 1] - 1];
        glm::vec3 C = tempVertices[vertexIndices[i + 2] - 1];
        glm::vec3 AB = B - A;
        glm::vec3 AC = C - A;
        glm::vec3 ABxAC = glm::cross(AB, AC);
        v_normal[vertexIndices[i] - 1] += ABxAC;
        v_normal[vertexIndices[i + 1] - 1] += ABxAC;
        v_normal[vertexIndices[i + 2] - 1] += ABxAC;
    }

Now normalize each v_normal and use. Note that the number of vertex normals is equal to the number of vertices of the mesh.

0
votes

This code works fine on my machine

glm::vec3 computeFaceNormal(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3) {
    // Uses p2 as a new origin for p1,p3
    auto a = p3 - p2;
    auto b = p1 - p2;
    // Compute the cross product a X b to get the face normal
    return glm::normalize(glm::cross(a, b));
}

void Mesh::calculateNormals() {
    this->normals = std::vector<glm::vec3>(this->vertices.size());
    // For each face calculate normals and append it
    // to the corresponding vertices of the face
    for (unsigned int i = 0; i < this->indices.size(); i += 3) {
        glm::vec3 A = this->vertices[this->indices[i]];
        glm::vec3 B = this->vertices[this->indices[i + 1LL]];
        glm::vec3 C = this->vertices[this->indices[i + 2LL]];
        glm::vec3 normal = computeFaceNormal(A, B, C);
        this->normals[this->indices[i]] += normal;
        this->normals[this->indices[i + 1LL]] += normal;
        this->normals[this->indices[i + 2LL]] += normal;
    }
    // Normalize each normal
    for (unsigned int i = 0; i < this->normals.size(); i++)
        this->normals[i] = glm::normalize(this->normals[i]);
}