If a vertex shader output / fragment shader input has the flat
qualifier, the value from the triangle’s last vertex is used for all fragments; the values from the other two vertices are ignored.
Your cylinder has 2N+2 vertex positions but only N+2 normals, so it’s entirely possible to use a single vertex array with 2N+2 vertices.
For the top and bottom faces, the centre vertex should be the last vertex of each triangle, so all triangles will use that normal.
For the vertical faces, use one of the vertices which is shared by both triangles to hold the normal.
vec3 position[2*N+2];
vec3 normal[2*N+2];
uint16_t indices[12*N];
void make_cylinder(float r, float z0, float z1)
{
position[2*N+0] = vec3(0,0,z0); // top centre
position[2*N+1] = vec3(0,0,z1); // bottom centre
normal[2*N+0] = vec3(0,0,-1); // top centre
normal[2*N+1] = vec3(0,0,1); // bottom centre
for (int i = 0; i < N; i++)
{
float angle = i*2*M_PI/N;
float x = r*cos(angle);
float y = r*sin(angle);
position[2*i+0] = vec3(x, y, z0);
position[2*i+1] = vec3(x, y, z1);
float angle2 = angle + M_PI/N; // extra half step
float nx = cos(angle2);
float ny = sin(angle2);
normal[2*i+0] = vec3(nx, ny, 0);
// normal[2*i+1] isn't used
int j = (i+1)%N; // index of next face
// outer faces
// lower-right triangle
indices[6*i+0] = 2*j+0; // lower-right
indices[6*i+1] = 2*j+1; // upper-right
indices[6*i+2] = 2*i+0; // lower-left; this has the normal
// upper-left triangle
indices[6*i+3] = 2*j+1; // upper-right
indices[6*i+4] = 2*i+1; // upper-left
indices[6*i+5] = 2*i+0; // lower-left; this has the normal
// bottom face
indices[6*N+3*i+1] = 2*j+0;
indices[6*N+3*i+0] = 2*i+0;
indices[6*N+3*i+2] = 2*N+0; // centre; this has the normal
// top face
indices[9*N+3*i+0] = 2*i+1;
indices[9*N+3*i+1] = 2*j+1;
indices[9*N+3*i+2] = 2*N+1; // centre; this has the normal
}
}
In general, it’s often necessary to have more vertices than vertex positions in order to accommodate all of the normals. Typical triangle meshes have roughly twice as many faces as vertices, meaning that you need to increase the number of vertices to match the number of faces; possibly more, as ensuring that each triangle has at least one unshared vertex is a hard problem for arbitrary triangle meshes. But you don’t need three distinct, unshared vertices per face.
In this case, the number of normals is reduced significantly as all of the triangles on the top and bottom faces share a common normal and both triangles of each edge face share a common normal. So you only have N+2 normals for 4N faces and 2N+2 vertices.