So lets say we have a cube which we want each face to have different normals. The cube consists of 6 vertices. Indices works per vertex. Hence if I wanted to have each vertex have a specific normal, I would have to duplicate the vertex per normal, and effectively not use any indices. But then if I want to do GPU skinning, I would eventually skin multiple vertices multiple times. Is there a way around this?
The cube consists of 6 vertices.
No, it has 8 positions.
A “vertex” is a unique combination of attributes (in a shader world, these are user-defined). A position is only part of a vertex. If your need normals, then the normal would typically also be a vertex attribute.
Hence if I wanted to have each vertex have a specific normal, I would have to duplicate the vertex per normal, and effectively not use any indices.
Only if you’re rendering with quads. Each of the 4 vertices for the 4 faces represents two triangles. Which means 6 indices. So yes, you’d still be using indices.
But then if I want to do GPU skinning, I would eventually skin multiple vertices multiple times.
1: Skinning is rarely used on cubes. Most surfaces you want to skin are smooth, meaning that unique combinations of positions and normals (and texture coordinates and so forth) are frequently used by multiple triangles. So there would be plenty of vertex reuse.
2: Skinning operates on both the position and the normal. So any vertex reuse would necessarily mean pairs of positions and their associated normals. If you have a position with multiple normals (and therefore a non-smooth part of the surface), then you have two different vertices. Even if you could index positions and normals separately, you’d still need two separate vertex shader invocations, since VS’s can’t compute positions separately from normals.
Is there a way around this?
You live with it, just like everyone else.
Haha okay thx
A cube has 8 vertices (in the mathematical sense; see Alfonse’ reply for the situation regarding OpenGL) and 6 faces.
First, if flat-shading is enabled with glShadeModel(GL_FLAT) (for legacy OpenGL) or by using the “flat” interpolation qualifier on the variable containing the normal (for modern OpenGL), the normal is taken from one of the vertices of each face (the “provoking” vertex, typically the last vertex); the normals associated with the other vertices are ignored.
So if you create a cube using 6 quads, you just need to ensure that none of the 8 vertices is the provoking vertex for more than one face (with 8 vertices for 6 faces, this is clearly possible). If you use 12 triangles, it’s still possible, as you still only have 6 distinct normals. Where a vertex is the provoking vertex of multiple triangles, they must share the same normal.
For arbitrary triangle meshes, face normals typically outnumber vertex positions by a factor of roughly 2:1, so you can’t avoid having more vertices than vertex positions. But you can avoid requiring 3 distinct vertices per face (I typically end up with ~2.2 vertices per face for flat shading).
Alternatively, you could omit the normals from the attribute arrays and calculate them in a geometry shader. Or you could store them in a uniform array or texture which is indexed using gl_PrimitiveID. Both of these approaches have a performance cost, though.