Calculating smooth normals on seperate faces

I’ve been trying to achieve smooth normals on a generated terrain mesh (tile grid style) to give off smooth lighting, however I have only be able to achieve flat shaded lighting and I believe this might be to do with the fact all the faces are separated (though some vertex’s may have the same position).

I’ve looked around for a similar question, the closest would be: this question however I’m not sure about how to go about implementing his solution of taking a percentage.

Currently I have this code which is based on the typical way to calculate smooth normals – but only achieves flat shading in my case.

    // Reset Normals
    for (int i = 0; i < this->vertexCount; i++) {
        this->vertices[i].normal = glm::vec3(0.0f);
    }

    // For each face
    for (int i = 0; i < totalVerts; i += 3) {

        auto index0 = indices ? this->indices[i] : i;
        auto index1 = indices ? this->indices[i + 1] : i + 1;
        auto index2 = indices ? this->indices[i + 2] : i + 2;

        auto vertex0 = this->vertices[index0].position;
        auto vertex1 = this->vertices[index1].position;
        auto vertex2 = this->vertices[index2].position;

        auto normal = glm::cross(vertex1 - vertex0, vertex2 - vertex0);

        this->vertices[index0].normal += normal;
        this->vertices[index1].normal += normal;
        this->vertices[index2].normal += normal;

    }

    // Normalize
    for (int i = 0; i < this->vertexCount; i++) {
        this->vertices[i].normal = glm::normalize(this->vertices[i].normal);
    }

How would I go about changing this to smooth between surrounding faces? (As for why each face is separate, it’s because some may have verts with specific properties which cannot be shared with other faces)

Answer

For your reference, a working code to get the face normals, vertices, and vertex normals is:

void get_vertices_and_normals_from_triangles(vector<triangle> &t, vector<vec3> &fn, vector<vec3> &v, vector<vec3> &vn)
{

// Face normals
fn.clear();

// Vertices
v.clear();

// Vertex normals
vn.clear();

if(0 == t.size())
    return;

cout << "Triangles: " << t.size() << endl;

cout << "Welding vertices" << endl;

// Insert unique vertices into set.
set<indexed_vertex_3> vertex_set;

for(vector<triangle>::const_iterator i = t.begin(); i != t.end(); i++)
{
    vertex_set.insert(i->vertex[0]);
    vertex_set.insert(i->vertex[1]);
    vertex_set.insert(i->vertex[2]);
}

cout << "Vertices: " << vertex_set.size() << endl;

cout << "Generating vertex indices" << endl;

vector<indexed_vertex_3> vv;

// Add indices to the vertices.
for(set<indexed_vertex_3>::const_iterator i = vertex_set.begin(); i != vertex_set.end(); i++)
{
    size_t index = vv.size();
    vv.push_back(*i);
    vv[index].index = index;
}

for (size_t i = 0; i < vv.size(); i++)
{
    vec3 vv_element(vv[i].x, vv[i].y, vv[i].z);
    v.push_back(vv_element);
}

vertex_set.clear();

// Re-insert modifies vertices into set.
for(vector<indexed_vertex_3>::const_iterator i = vv.begin(); i != vv.end(); i++)
    vertex_set.insert(*i);

cout << "Assigning vertex indices to triangles" << endl;

// Find the three vertices for each triangle, by index.
set<indexed_vertex_3>::iterator find_iter;

for(vector<triangle>::iterator i = t.begin(); i != t.end(); i++)
{
    find_iter = vertex_set.find(i->vertex[0]);
    i->vertex[0].index = find_iter->index;

    find_iter = vertex_set.find(i->vertex[1]);
    i->vertex[1].index = find_iter->index;

    find_iter = vertex_set.find(i->vertex[2]);
    i->vertex[2].index = find_iter->index;
}

vertex_set.clear();

cout << "Calculating normals" << endl;
fn.resize(t.size());
vn.resize(v.size());

for(size_t i = 0; i < t.size(); i++)
{
    vec3 v0;// = t[i].vertex[1] - t[i].vertex[0];
    v0.x = t[i].vertex[1].x - t[i].vertex[0].x;
    v0.y = t[i].vertex[1].y - t[i].vertex[0].y;
    v0.z = t[i].vertex[1].z - t[i].vertex[0].z;

    vec3 v1;// = t[i].vertex[2] - t[i].vertex[0];
    v1.x = t[i].vertex[2].x - t[i].vertex[0].x;
    v1.y = t[i].vertex[2].y - t[i].vertex[0].y;
    v1.z = t[i].vertex[2].z - t[i].vertex[0].z;

    fn[i] = cross(v0, v1);
    fn[i] = normalize(fn[i]);

    vn[t[i].vertex[0].index] = vn[t[i].vertex[0].index] + fn[i];
    vn[t[i].vertex[1].index] = vn[t[i].vertex[1].index] + fn[i];
    vn[t[i].vertex[2].index] = vn[t[i].vertex[2].index] + fn[i];
}

for (size_t i = 0; i < vn.size(); i++)
    vn[i] = normalize(vn[i]);
}

The code to stuff the vertex data into a vector is as follows. Note that the unwelded vertices of the triangles are reconstructed in the following calls to vertex_data.push_back(v0.x);, etc.

void draw_mesh(void)
{
glUseProgram(render.get_program());


glUniformMatrix4fv(uniforms.render.proj_matrix, 1, GL_FALSE, &main_camera.projection_mat[0][0]);
glUniformMatrix4fv(uniforms.render.mv_matrix, 1, GL_FALSE, &main_camera.view_mat[0][0]);
glUniform1f(uniforms.render.shading_level, 1.0f);

vector<float> vertex_data;

for (size_t i = 0; i < triangles.size(); i++)
{
    vec3 colour(0.0f, 0.8f, 1.0f);

    size_t v0_index = triangles[i].vertex[0].index;
    size_t v1_index = triangles[i].vertex[1].index;
    size_t v2_index = triangles[i].vertex[2].index;

    vec3 v0_fn(vertex_normals[v0_index].x, vertex_normals[v0_index].y, vertex_normals[v0_index].z);
    vec3 v1_fn(vertex_normals[v1_index].x, vertex_normals[v1_index].y, vertex_normals[v1_index].z);
    vec3 v2_fn(vertex_normals[v2_index].x, vertex_normals[v2_index].y, vertex_normals[v2_index].z);

    vec3 v0(triangles[i].vertex[0].x, triangles[i].vertex[0].y, triangles[i].vertex[0].z);
    vec3 v1(triangles[i].vertex[1].x, triangles[i].vertex[1].y, triangles[i].vertex[1].z);
    vec3 v2(triangles[i].vertex[2].x, triangles[i].vertex[2].y, triangles[i].vertex[2].z);

    vertex_data.push_back(v0.x);
    vertex_data.push_back(v0.y);
    vertex_data.push_back(v0.z);
    vertex_data.push_back(v0_fn.x);
    vertex_data.push_back(v0_fn.y);
    vertex_data.push_back(v0_fn.z);
    vertex_data.push_back(colour.x);
    vertex_data.push_back(colour.y);
    vertex_data.push_back(colour.z);

    vertex_data.push_back(v1.x);
    vertex_data.push_back(v1.y);
    vertex_data.push_back(v1.z);
    vertex_data.push_back(v1_fn.x);
    vertex_data.push_back(v1_fn.y);
    vertex_data.push_back(v1_fn.z);
    vertex_data.push_back(colour.x);
    vertex_data.push_back(colour.y);
    vertex_data.push_back(colour.z);

    vertex_data.push_back(v2.x);
    vertex_data.push_back(v2.y);
    vertex_data.push_back(v2.z);
    vertex_data.push_back(v2_fn.x);
    vertex_data.push_back(v2_fn.y);
    vertex_data.push_back(v2_fn.z);
    vertex_data.push_back(colour.x);
    vertex_data.push_back(colour.y);
    vertex_data.push_back(colour.z);
}


GLuint components_per_vertex = 9;
const GLuint components_per_normal = 3;
GLuint components_per_position = 3;
const GLuint components_per_colour = 3;

GLuint triangle_buffer;

glGenBuffers(1, &triangle_buffer);

GLuint num_vertices = static_cast<GLuint>(vertex_data.size()) / components_per_vertex;

glBindBuffer(GL_ARRAY_BUFFER, triangle_buffer);
glBufferData(GL_ARRAY_BUFFER, vertex_data.size() * sizeof(GLfloat), &vertex_data[0], GL_DYNAMIC_DRAW);

glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "position"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "position"),
    components_per_position,
    GL_FLOAT,
    GL_FALSE,
    components_per_vertex * sizeof(GLfloat),
    NULL);

glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "normal"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "normal"),
    components_per_normal,
    GL_FLOAT,
    GL_TRUE,
    components_per_vertex * sizeof(GLfloat),
    (const GLvoid*)(components_per_position * sizeof(GLfloat)));

glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "colour"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "colour"),
    components_per_colour,
    GL_FLOAT,
    GL_TRUE,
    components_per_vertex * sizeof(GLfloat),
    (const GLvoid*)(components_per_normal * sizeof(GLfloat) + components_per_position * sizeof(GLfloat)));

glDrawArrays(GL_TRIANGLES, 0, num_vertices);

glDeleteBuffers(1, &triangle_buffer);
}

I’m not sure if you’re getting notifications of my edits.