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?
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
andni
index arrays that you are reading in, but are creating yourindex_buffer
fromvi
, so the vertex indices are being used to index intonormal_buffer
andtex_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 useglDrawArrays
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.