5
votes

I am trying to render 3D prisms in LWJGL OpenGL with flat shading. For example, I have a cube indexed as following:

enter image description here

I only have 8 vertices in the vertex buffer, which I have indexed as above. Is there any way to implement flat normal shading on the cube such as below? I don't want to rewrite my vertex and index buffers to include duplicate vertices if possible.

1
When you say "flat shading", what exactly do you mean by that? Do you mean that you want to compute a face normal which is constant across the triangle's surface, or do you actually want to compute lighting in the VS? Or something else. "I don't want to rewrite my vertex and index buffers to include duplicate vertices if possible." Why not? It'd make things a whole lot easier. - Nicol Bolas
I've already implemented vertices and indices for a generalized nth dimensional 3D prism, so I'd prefer not to go back and rewrite all of that. When I say flat shading, I just mean creating something like the cube pictured above. I can already calculate the face normal for each triangle, but I don't know how to format them in the normals array. - Sibh
So you know how to write a diffuse shading shader, is your question, how you can send data to the GPU so that normals correspond to the right triangle, when you don't have an IBO? - jackw11111
@Rabbid76 I am afraid you're slightly wrong in this case as for n-dimensional objects the math is not implemented in shaders natively and if any kind of projection into 3D occurs you can no longer compute normals from the projected faces without thge loss of information ... Of coarse in case the whole ND mesh is passed to shaders it might be possible but it would most likely not be passed in GLSL native way so geometry shader would be also useless. - Spektre
@Sibh take a look at these: 4D rendering techniques and simple ND rendering engine for some ideas. However as I mentioned in the previous comment if you want correct Flat shading you need to compute and pass ND normals instead of 3D ones and adjust the lighting dot product to ND !!! The examples use just simple 3D normals so they lack the dimensions above 3D ... Its the same as you would project 3D object on a plane and shade it whole with single color so you would not see the edges ... - Spektre

1 Answers

5
votes

If you don't need any other attributes (e.g. texture coordinates), then there is an option to create a cube mesh with face normal vectors, by 8 vertices only. Use the flat Interpolation qualifier for the normal vector.

Vertex shader:

flat out vec3 surfaceNormal;

Fragment sahder:

flat out vec3 surfaceNormal;

When the flat qualifier is used, then the output of the vertex shader will not be interpolated. The value given to the fragment shader is one of the attributes associated to one vertex of the primitive, the Provoking vertex.
For a GL_TRINANGLE primitive this is either the last or the first vertex. That can be chosen by glProvokingVertex.

Choose the first vertex:

glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);

For the order of the points of your cube mesh (image in the question)

  front      back

  1     3    7     5
   +---+      +---+
   |   |      |   |
   +---+      +---+
  0     2    6     4

you have to setup the following vertex coordinates and normal vectors:

//  x   y   z   nx, ny, nz
    -1, -1, -1,   0, -1,  0,  // 0, nv front
    -1, -1,  1,   0,  0,  1,  // 1, nv top
     1, -1, -1,   0,  0,  0,  // 2
     1, -1,  1,   1,  0,  0,  // 3, nv right
     1,  1, -1,   0,  1,  0,  // 4, nv back
     1,  1,  1,   0,  0,  0,  // 5
    -1,  1, -1,   0,  0, -1,  // 6, nv bottom
    -1,  1,  1,  -1,  0,  0,  // 7, nv left 

Define the indices in that way, that the vertices 7, 3, 0, 4, 6, 1 are the first vertex for both triangles of the left, right, front, back, bottom and top of the cube:

0, 2, 3,   0, 3, 1, // front
4, 6, 7,   4, 7, 5, // back
3, 2, 4,   3, 4, 5, // right
7, 6, 0,   7, 0, 1, // left
6, 4, 2,   6, 2, 0, // bottom 
1, 3, 5,   1, 5, 7  // top

Draw 12 triangle primitives. e.g:

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);