2

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?

  • This has come up in similar form many times. See for example: http://stackoverflow.com/questions/29718501/c-obj-parser-issue-with-parsing-or-drawing-using-opengl or http://stackoverflow.com/questions/23349080/opengl-index-buffers-difficulties. – Reto Koradi Jun 24 '15 at 14:51
  • But one of them uses fixed function and other uses draw array method I think. Im using element method so should I leave the wavefront file? Im also studying raytraceing. Do you have a suggestion for a model file type for use in my code? Thank you. –  Jun 25 '15 at 19:46
  • You can use the algorithm in @RetoKoradi 's [answer here](http://stackoverflow.com/questions/23349080/opengl-index-buffers-difficulties) if you want to use glDrawElements. Indexing logic works the same for fixed function and programmable pipeline so that won't be an issue. – samgak Jun 25 '15 at 22:43

1 Answers1

0

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.

samgak
  • 23,944
  • 4
  • 60
  • 82
  • Thank you samgak. I'm also inserted ni and ti index into indices and rendered but same thing.. I want to use element method for efficient. I have some obj file that uses same v, t, n index (such f 1/1/1 2/2/2 3/3/3.......) and rendered correctly but other files have different normal, texcoord size . What should I do? (Sorry for my English:) I think i understand code better than english) –  Jun 25 '15 at 19:15
  • You have to use a format with the same indices e.g. 1/1/1 2/2/2 3/3/3 because OpenGL doesn't support separate indexing for position/tex/normal. It's less memory efficient than .obj file but you can't do anything about that because it is an OpenGL limitation. So you either need to use a file format with the indices in that format or convert it at load time or in a pre-processing step. – samgak Jun 25 '15 at 22:47