I disagree with JasonPh's answer. The result from his approach is face normal---the normal perpendicular to the face of i-i1-i2.
To calculate vertex normal, you need to calculate face normal first. The vertex normal is usually the average of the normals of faces adjacent to this vertex. Alternatively, a weighted average of these face normals can be used.
In order to traverse the faces adjacent to this vertex, half-edge data structure (http://www.sccg.sk/~samuelcik/dgs/half_edge.pdf) are usually adopted. It not only helps find the faces adjacent to a vertex, but also helps answer other questions more easily
- Which faces use this vertex?
- Which edges use this vertex?
- Which faces border this edge?
- Which edges border this face?
- Which faces are adjacent to this face?
- ...
The structure is like
struct HE_edge {
HE_vert* vert; // vertex at the end of the half-edge
HE_edge* pair; // oppositely oriented half-edge
HE_face* face; // the incident face
HE_edge* prev; // previous half-edge around the face
HE_edge* next; // next half-edge around the face
};
struct HE_vert {
float x, y, z; // the vertex coordinates
HE_edge* edge; // one of the half-edges emanating from the vertex
};
struct HE_face {
HE_edge* edge; // one of the half-edges bordering the face
};
With this structure, the procedure is basically (just a code to help understand, not the most effective one):
for each vertex v:
// initialize
v->normal = vec3(0,0,0);
//iterate through all v's neighbouring face
neighbouring_edge = v->edge;
v->normal += calcFaceNormal(neighbouring_edge->face);
//find the next edge emanating from vertex v
neighbouring_edge = neighbouring_edge->pair->next
while neighbouring_edge!= v->edge:
v->normal += calcFaceNormal(neighbouring_edge->face);
neighbouring_edge = neighbouring_edge->pair->next;
v->normal = normalize(v->normal);
where the calcFaceNormal function can be (assuming triangle mesh):
calcFaceNormal(myface):
v1 = face->edge->vert;
v2 = face->edge->next->vert;
v3 = face->edge->prev->vert;
e1 = v2-v1;
e2 = v3-v1;
return corss_product(e1,e2);