Indexed Drawing - Is it always useless when every face should have its own normal? (e.g. Flat shading)

Started by
8 comments, last by Matias Goldberg 9 years, 6 months ago

I was looking at implementing lighting on an indexed cube, and was trying to figure out what to do re: the normals. I already knew you can only use 1 index per draw call, so you can't just have every vertex and every normal defined once, and then I read this Q&A for more info...

http://stackoverflow.com/questions/2954349/when-should-i-use-indexed-arrays-of-opengl-vertices

Does that mean then that for any case where you want every face to have its own shading, you can't really use indices at all and have to define every separate instance of a vertex-normal pair? So if you wanted to have old-fashioned flat shading (e.g. Frontier Elite II) you couldn't get any benefit from using indices?

When I think about it, and how you have to use the same indices for the normals as the vertices, it seems like you'd have to specify as much data as if you just didn't use indices in the first place.

Or is there some way of using them for a more minor gain?

I suppose that either way, it's probably not a huge issue since any model using flat shading isn't likely to have a lot of vertices (it's meant to look polygonal after all) so the performance gain from indices would be less important anyway... or so I imagine.

Advertisement

Indices are always useful for concatenating primitives.

Take the example of a model that may be composed of multiple triangle strips and/or fans. To draw that model in a single draw call you can use indices to concatenate those strips and/or fans, then just issue a single glDrawElements call.

It's an absolute fallacy that the sole purpose of indices is to reduce data size, but understandable that people focus on that.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Do you mean like using glPrimitiveRestartIndex()? So, if I'm right, you'd still have to specify just as many vertices/normals as many times as if you didn't use indices, but the single draw call would be more efficient as well as shorter to type?

Think of a vertex as a package that contains 3 components: position + normal + texcoord.

The fact that two vertex 'packages' happen to have the same position is irrelevant if the other two components differ. It's only when all three are the same that you consider the 'package' to be the same.

Models done in a "polygonal style" may very well have lots of faces that have more than three corners. It's easy to imagine spaceship or station models having lots of four-sided faces and it's not a big stretch to come up with designs with more complex flat surfaces. Still, if the models are simple and few enough, it really might not matter much at all.

Do you mean like using glPrimitiveRestartIndex()? So, if I'm right, you'd still have to specify just as many vertices/normals as many times as if you didn't use indices, but the single draw call would be more efficient as well as shorter to type?

Yes, but even so, primitive restart isn't actually needed. E.g a triangle fan can be specified with indices 0, 1, 2, 0, 2, 3, 0, 3, 4, etc. On some hardware that may be faster than primitive restart, despite using more storage, because restarting a primitive may be an expensive operation.

And yes, fewer draw calls are almost always more efficient as each draw call has a fixed CPU overhead irrespective of how many primitives you draw.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I'm not sure if what you mean is for example a cube where every face normal points to a very different direction but because you reuse the vertex it means you can't but in this particular case the normal is averaged between all faces that share this vertex. If that's not what you mean then please dismiss my post.
In the specific case of flat shading, there's other solutions, such as setting the interpolation mode of the normal variable to flat/no-interpolate, or to not have any per-vertex normal data at all but calculate the surface normal in the pixel shader using the derivative of the position.

I'm not sure if what you mean is for example a cube where every face normal points to a very different direction but because you reuse the vertex it means you can't but in this particular case the normal is averaged between all faces that share this vertex. If that's not what you mean then please dismiss my post.

Yeah, that's basically what I mean. A shape where you don't want to create the illusion of a smooth surface between faces.

In the specific case of flat shading, there's other solutions, such as setting the interpolation mode of the normal variable to flat/no-interpolate, or to not have any per-vertex normal data at all but calculate the surface normal in the pixel shader using the derivative of the position.

Interesting... yes, I've seen the flat modifier before (I think I may have even messed around with it to see if I could create a flat shaded effect) but I'd forgotten about it.

Hmmm... though does that actually help in this case, where we want to avoid adjoining faces being given the same shading through their common edge, or does it just ensure the shading is constant across a single face?

I'll have to look into that a bit more.

But in conclusion...

If I want to give every face a unique normal, indices won't help with that, right?

i.e. Indices won't reduce the amount of data I have to upload to the GPU in terms of position and normal data if I want to avoid the smooth surface effect discussed in the Shared vs Separate section here:

http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/

Here I am in GameDev.Net linking again to A cube may be drawn with 8, 24, or 36 vertices in the same week.

As you can see in the example from the blog, even a fully flat shaded cube needs 24 vertices, not 36 (12 triangles * 3 vertices per tri).

If you're going to render your cube with 36 vertices, then sure; use non-indexed draws; it's going to waste less space than indexed draws.

But just because you need flat shading doesn't mean you need to duplicate every single vertex. There are still going to be vertices that can be shared. Hence 24 vertices.

There's also another issue that needs to be taken into account: If you mix (interleave) indexed tris with non-indexed draw calls too much, you're going to add driver overhead that won't go unnoticed.

This topic is closed to new replies.

Advertisement