# Same specular lighting on both tris in a quad (for “low-poly” “flat shading” look)?

Here's an example of the flat shaded squares I want. But my squares are made of two triangles, and I'm getting flat shading of those triangles.

In openGLES 3.1 (android), I'm turning off interpolation in the fragment shader, and using the provoking vertex (or using the centroid/average), to get the same "low-poly" lighting throughout a tri. The specular calculation is done in the vertex shader, so it's only done once.

I have a flat sea, a mesh of squares (two triangles per quad), with a specular reflection of the sun. But instead of each square being lit the same, the two triangles forming a square get different specular values.

I worked out it's because the tris are at different locations. Although the sun's rays are parallel (with the same angle of incidence at all locations), the angle between each tri and the viewer is slightly different, depending on where the tri is relative to the viewer.

This diagram (Intro to Computer Graphics, David J. Eck) only shows the light from one point, for observers at different locations, but you can see that light from different points would produce a shifted cone, having a similar effect on an observer at one place.

A brute force solution would be to store a special "location" for each vertex of each tri, and set "location" to the same value for each of the 4 verices of the two tris forming the square.

But that's inefficient, and I'm using a STRIP for the mesh. I don't think this brute force solution work, because any per vertex data would be used by the 4 squares sharing that vertex, wouldn't they? Or can you have per tri data?

EDIT I currently have per vertex normals - used in the specular calc - and only the normal of the provoking vertex is used for a tri. This works out as per tri, since only one tri is provoked by each vertex. I could give the same "location" to both tris in a quad this way. But it's still brute force - is there a better way?

Or, can you tell which tri of a quad you're in, so you can decide what it's "location" should be?

It seems so simple, but doesn't fit with openGL ES's approach... is there a way to do it? Note it's openGL ES 3.1 (mobile).

If I understand you correctly, the problem is that the provoking vertex isn't the same vertex between the two triangles in a quad, due to using triangle strips. You could switch to using an indexed triangle list, and set up indices in such a way that the provoking vertex is the same in each quad. For instance, if one quad is vertices {0, 1, 2, 3} (CCW) then you might write {0, 1, 2, 3, 0, 2} in your index buffer so that 2 is the provoking vertex for both triangles. This should still be pretty efficient.

Another possibility (more involved, but more flexible) is to use the built-in gl_VertexID variable in your vertex shader to figure out which quad you're in (eg int quadID = gl_VertexID / 4), and use an appropriate shading location for that quad—the centroid of its vertices, for instance. However, for this to work, I think you would have to "un-share" the vertices so that each quad has its own 4 unique vertices (the two triangles in a quad can still share). So you would have to use primitive restarting, or again switch to indexed non-strip draws. You could also have per-quad data in an SSBO that you manually fetch using your computed quad index.

• Thanks, that's really clever! I think it almost doubles the number of 16bit indices, but isn't there some caching advantage to indexing too? TIL ES 3.1 has gl_VertexID and primitive restart (thought degenerate tris was the only way)... pity there's no gl_PrimitiveID. I see it's more flexible, but I think same memory overhead as the provoking-trick. – hyperpallium Dec 19 '17 at 4:25
• @hyperpallium Yeah, more indices. They're usually a pretty small memory overhead compared to vertex data, but YMMV. As for cache, a well optimized indexed mesh can be as good or better than strips for general meshes, but I'm not sure if that's true for pure grid meshes. – Nathan Reed Dec 19 '17 at 5:41
• I can see it's much less than vertex data; but better to have no extra at all (if possible...). I probably shouldn't worry too much over efficiency at this stage - it seems a bit hard to predict. BTW I was thinking the way to cache-optimize a (plane) mesh is to split the rows, making it short enough so the vertices shared with the above tris are still in cache when needed (I don't think there's benefit to it being any shorter; and a small detriment with losing the horizontally shared vertices where the row is split.). – hyperpallium Dec 19 '17 at 6:24
• @hyperpallium You might be interested in this article: Optimal grid rendering is not optimal – Nathan Reed Dec 19 '17 at 6:40
• Spot on. Definitely best to not worry about optimizing yet! I wonder if different on mobile GPUs, which probably do fewer clever things under the hood(?) – hyperpallium Dec 19 '17 at 6:57

The easiest fix would be to do your light calculations in the fragment shader rather than per-vertex—you’ll avoid the discontinuity you’re currently seeing and your lighting will appear more natural (while still fitting the “low-poly” aesthetic). If that’s not an option, then you’re probably stuck with passing in each quad’s center along with the normals you’re already supplying.

• Thanks. If I understand correctly, this will give a gradually changing specular highlight across the tri (because each pixel is in a slightly different location). The discontinuity between tris will vanish, because adjacent pixels in the different tris are very close in location. Is that what you mean? [BTW I've gotten a similar effect by letting the specular result from the vertex shader interpolate in the fragment shader]. I agree this is more natural - but I want uniform shading across the quad (so the discontinuity is between quads). A bit like the flashing facets of a jewel. – hyperpallium Dec 18 '17 at 23:16
• a common way to get that discontinuity is to interpolate position but not interpolate normals. maybe this works better for you – Sebastián Mestre Dec 20 '17 at 2:32

You are probably mixing thing up. As I understood from you explanation, you are trying to do Phong shading and you want flat shading.

Your problem might be happening because you are calculating a specular component that depends on the view/camera direction.

It would help if you could add pictures explaining what is happening and what you want to happen.

• Thanks. Yes, "flat shading" as here. And yes, the diffuse + specular reflection of Phong shading, but without the interpolation (TIL the interpolation is also called "Phong shading"). I believe the specular component increases the angle between the angle of reflection and the viewing direction decreases (brightest when aligned).... specular highlights move as you move your head. I needed a diagram too! I'll look for an online diagram tool (do you know of one?) – hyperpallium Dec 19 '17 at 3:12
• typo, should be: "specular component increases as the angle between reflected ray and the direction of the viewer decreases" – hyperpallium Dec 19 '17 at 4:33