Search code examples
openglglsllightingnormalswavefront

OpenGL: Strange normal rendering


I have a issue with OpenGL normals. I'm rendering the dragon model, but I have some weird normal patterns.

Here is screenshot from my render: render_screen

this is my buffer creation methods:

glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    if (has_position) {
        glGenBuffers(1, &vertex_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kVertexArray);
        glVertexAttribPointer(kVertexArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_normal) {
        glGenBuffers(1, &normal_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
        glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float), normals.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kNormalArray);
        glVertexAttribPointer(kNormalArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_tex_coord) {
        glGenBuffers(1, &tex_coord_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, tex_coord_buffer);
        glBufferData(GL_ARRAY_BUFFER, tex_coords.size() * 3 * sizeof(float), tex_coords.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kTexCoordArray);
        glVertexAttribPointer(kTexCoordArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_index) {
        glGenBuffers(1, &index_buffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 3 * sizeof(unsigned short), indices.data(), GL_STATIC_DRAW);
        glBindVertexArray(0);
    }

and draw with: glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));

Wavefront obj loader method:

bool Loadfs() {
    CVector3<float> vertex;
    CVector3<unsigned short> vindex, tindex, nindex;

    while(file_stream->offset_ < file_stream->size_) {
        if (file_stream->buffer_[file_stream->offset_] == '#') {
            char comment[512] = {0};
            file_stream->ReadLine(comment); // check return code -> "its local variable return address"
            file_stream->SkipLine();
        }
        else if (file_stream->buffer_[file_stream->offset_] == 'v') {
            file_stream->offset_++;

            if (file_stream->buffer_[file_stream->offset_] == ' ') {
                has_position = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                vertices.push_back(vertex);
            }
            else if (file_stream->buffer_[file_stream->offset_] == 'n') {
                has_normal = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                normals.push_back(vertex);
            }
            else if (file_stream->buffer_[file_stream->offset_] == 't') {
                has_tex_coord = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                tex_coords.push_back(vertex);
            }
        }
        else if (file_stream->buffer_[file_stream->offset_] == 'f') {
            has_index = true;
            file_stream->offset_++;
            file_stream->SkipWhiteSpace();

            //
            if (has_position) {
                vindex.x = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.x = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.x = file_stream->ReadShort();
            }
            file_stream->SkipWhile(' ');

            //
            if (has_position) {
                vindex.y = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.y = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.y = file_stream->ReadShort();
            }
            file_stream->SkipWhile(' ');

            //
            if (has_position) {
                vindex.z = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.z = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.z = file_stream->ReadShort();
            }
            vi.push_back(--vindex);
            ti.push_back(--tindex);
            ni.push_back(--nindex);
            //indices.push_back(--vindex);
        }
        else file_stream->SkipLine();
    }


    indices.insert(indices.end(), vi.begin(), vi.end());
    return true;
}

Here vs:glsl main methods:

position_ = MV * vec4(vVertex, 1.0);
normal_ = normalize(N * vNormal);
texture_ = vTexture;
//shadow_ = S * M * vec4(vVertex, 1.0);

gl_Position = MVP * vec4(vVertex, 1.0);

and fs.glsl main methods:

vec3 rgb = vec3(1.0, 1.0, 1.0);
rgb = PhongShade(g_light, g_material, position_, normal_);
_frag_color = vec4(rgb, 1.0);

//_frag_color = texel * vec4(ambient + diffuse + specular, 1.0);

Anyone got any thoughts?


Solution

  • The problem is that OpenGL uses a single index buffer to index the vertex position, texture coordinates and normal buffers, using the same index for each (3 indices for a triangle), whereas the Wavefront obj format allows each face to specify separate indices for the vertex position, texture coordinate and normal independently (9 indices in total for a triangle).

    It's not clear from your code but most likely you are not actually using the ti and ni index arrays that you are reading in, but are creating your index_buffer from vi, so the vertex indices are being used to index into normal_buffer and tex_coord_buffer, giving the strange results.

    To fix this issue you need to create buffers of vertex coordinates, tex coordinates and normal coordinates with an entry for each unique combination of vertex position index, tex coord index and normal index that is used in a face definition in the obj file, then make your index buffer index into these arrays such that a particular face vertex has the same index for each. Ideally this would be done as a pre-processing step instead of at load time.

    Alternatively, abandon indexed rendering with glDrawElements and use glDrawArrays instead. Write out one entry to each of the arrays for each face vertex, based on the face vertex indices for the position, tex coord and normal, and then render that. This is less efficient however.

    Another solution (if it's possible) is to export the obj file in such a way that each face vertex uses the same indices for position, tex coord and normal.