6
\$\begingroup\$

I'm trying to implement flat shading for non-planar polygons (using a single normal vector per polygon) based on buffer textures, but I can't seem to figure out how to set up things correctly. The approach is as follows —

  • I render n-gons using GL_TRIANGLE_FAN. Everything is stored index-based, so I use glDrawElements(). To know where one n-gon ends and the next one starts, I use the primitive restart approach.

  • In the fragment shader, one has access to gl_PrimitiveID. In the rendering process, each n-gon is split into (n-2) triangles, each triangle has a unique gl_PrimitiveID.

  • Each triangle can be linked back to its original n-gon as follows. Let's consider a mesh consisting of a pentagon, a triangle and a quad (in that order). The data stored in the first buffer texture is 0,0,0,1,2,2. The pentagon (index 0) is rendered as three triangles, the triangle (index 1) is of course rendered as a single triangle, and the quad (index 2) is rendered as two triangles. Using gl_PrimitiveID, the n-gon index can be obtained.

  • Finally, there is a second buffer texture which contains one normal vector per original n-gon. The indices in the other buffer texture (i.e., 0, 1 and 2 in the example) can be used to obtain the right normal vectors for each triangle.

Ok, to create the buffer textures, I use

GLuint faceIndexBuffer, faceIndexTexture, faceNormalBuffer, faceNormalTexture;

glGenBuffers(1, &faceIndexBuffer);
glBindBuffer(GL_TEXTURE_BUFFER, faceIndexBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(unsigned int)*FaceIDs.size(), FaceIDs.data(), GL_DYNAMIC_DRAW);

glGenTextures(1, &faceIndexTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, faceIndexTexture);
// Unsigned integers (uint)
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, faceIndexBuffer);

glGenBuffers(1, &faceNormalBuffer);
glBindBuffer(GL_TEXTURE_BUFFER, faceNormalBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(QVector3D)*FaceNorms.size(), FaceNorms.data(), GL_DYNAMIC_DRAW);

glGenTextures(1, &faceNormalTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, faceNormalTexture);
// RGB floats (vec3)
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, faceNormalBuffer);

where

QVector<unsigned int> FaceIDs;
QVector<QVector3D> FaceNorms;

Neither FaceIDs nor FaceNorms contains any data when the above code is run. To update the actual data, I use

glBindBuffer(GL_TEXTURE_BUFFER, faceIndexBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(unsigned int)*FaceIDs.size(), FaceIDs.data(), GL_DYNAMIC_DRAW);

glBindBuffer(GL_TEXTURE_BUFFER, faceNormalBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(QVector3D)*FaceNorms.size(), FaceNorms.data(), GL_DYNAMIC_DRAW);

The other data (vertex information, i.e. coordinates and normals, and indices) are stored as VBOs and IBOs, wrapped in a VAO.

Relevant part of the fragment shader —

layout(binding = 0) uniform samplerBuffer primIdToFaceId;
layout(binding = 1) uniform samplerBuffer faceNormals;

int faceIndex = int(texelFetch(primIdToFaceId, gl_PrimitiveID).x);
vec3 norm = texelFetch(faceNormals, faceIndex).xyz;

Note that I use bindings in the shader, so I shouldn't have to use e.g.

uFaceIDs = glGetUniformLocation(ShaderProg->programId(), "primIdToFaceId");
glUniform1i(uFaceIDs, 0);
uFaceNorms = glGetUniformLocation(ShaderProg->programId(), "faceNormals");
glUniform1i(uFaceNorms, 1);

Unfortunately, something is apparently not set up in the correct way, because I don't obtain the right normals. This is the relevant logging information (Buffer 4 corresponds to the first TBO containing indices, Buffer 5 to the second one, containing normals) —

→ Log: QOpenGLDebugMessage("APISource", 131185, "Buffer detailed info: Buffer object 4 (bound to GL_TEXTURE_BUFFER, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.", "NotificationSeverity", "OtherType")
→ Log: QOpenGLDebugMessage("APISource", 131185, "Buffer detailed info: Buffer object 5 (bound to GL_TEXTURE_BUFFER, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.", "NotificationSeverity", "OtherType")

when creating/updating data, and

→ Log: QOpenGLDebugMessage("APISource", 131185, "Buffer detailed info: Buffer object 4 (bound to NONE, usage hint is GL_DYNAMIC_DRAW) will use VIDEO memory as the source for buffer object operations.", "NotificationSeverity", "OtherType")

when rendering. This last line here indicates what might be wrong, the first TBO is not bound anymore? What am I overlooking?

\$\endgroup\$
4
\$\begingroup\$

Fixed it! One of the TBOs is filled with unsigned integers, therefore usamplerBuffer has to be used instead of samplerBuffer

layout(binding = 0) uniform usamplerBuffer primIdToFaceId;
layout(binding = 1) uniform samplerBuffer faceNormals;

Some debugging and results. Using

fColor = vec4(0.0, mod(gl_PrimitiveID,256)/255, 0.0, 1.0);

in the fragment shader results in

enter image description here

Note that in this setting, each triangle in an original n-gon has its own colour —

enter image description here

Using

fColor = vec4(0.0, mod(faceIndex,256)/255, 0.0, 1.0);

results in

enter image description here

Now all triangles in an n-gon share the same colour. Finally, using the code from the original post, we get the intended flat shading effect —

enter image description here

[Edit] I still get the

→ Log: QOpenGLDebugMessage("APISource", 131185, "Buffer detailed info: Buffer object 4 (bound to NONE, usage hint is GL_DYNAMIC_DRAW) will use VIDEO memory as the source for buffer object operations.", "NotificationSeverity", "OtherType")

message, though it appears to be harmless.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.