2

I am using instanced rendering to draw a large amount of cubes. Now, obviously the maximum amount of visible faces in a cube is 3, which means that in terms of shading, I am doing twice as more works as needed.

Because this is instancing I cannot simply pass only the geometry to be drawn to the shaders. But if in the shaders (I'd imagine this would be in either geometry or tesselation) I can use the normal information to discard a primitive, I can reduce the number of unnecessary operations.

For example if the angle in between the looking direction and the normal to the surface is less than 90 degrees I can assume there is a triangle in front of the current one and thus discard this given primitive.

Is there a way in the pipeline where I can determine if a fragment is needed and discard it accordingly?

Makogan
  • 8,208
  • 7
  • 44
  • 112
  • "*I am using instanced rendering to draw a large amount of cubes.*" Please stop using instanced rendering to draw a large amount of cubes. Indeed, please stop *drawing a large amount of cubes*. If you want to make Minecraft-like graphics, the first thing you should realize is that Minecraft *doesn't draw cubes*. – Nicol Bolas Feb 18 '18 at 03:12
  • I am not going to be drawing cubes soon enough however. Once I have shadow maps working, the next step is to be able to handle varying geometry for each voxel space. Then I need to read some methods on local adaptive subdivision or other methods, to be able to modify the geometry of each voxel space to create a smooth looking surface (A lot of problems are foreseen in this step) Also, I assume that what minecraft does is create a mesh outline of all current cubes loaded? Am I correct? – Makogan Feb 18 '18 at 09:37

1 Answers1

6

Before answering the question, let me first solve your actual problem: use back-face culling. Give the triangles of each cube face a consistent ordering, such that when seen from the front, the ordering of the faces in screen space will be one way, but when seen from the back the ordering will be the other. Which you pick is a convention that's up to you, but pick one and be consistent.

Then use glFrontFace to tell OpenGL which convention is "front" for you, and use glEnable(GL_CULL_FACE) and glCullFace(GL_BACK) to tell OpenGL to cull back-facing triangles.

This culling happens as a part of primitive assembly, so it's long past vertex processing and post-processing.

This is all you really need to do. Oh sure, you're still spending up to half of vertex processing on triangles that won't be seen, but if you want to cull them, you'd still have to spend time figuring out if you want to cull them. And remember: GPUs execute shader invocations in lock-step. Culling triangles/faces/whatever due to conditional operations doesn't always make anything faster, since those invocations will still continue to be executed. Their results will just be thrown away.

Face culling is far more efficient than anything you could code.


Now, let's answer the actual question: what are your options for shader-based culling?

You cannot discard a vertex from a vertex shader. Primitives are based on a specific count of vertices (GL_TRIANGLES means that every group of 3 vertices is a triangle), so how that would work if a vertex is killed is non-functional.

You can use user-defined culling output variables to cull triangles from just the vertex shader. This is analogous to user-defined clipping planes, where your vertex shader provides a special numeric output. For clipping, if the interpolated output is positive, then that part of the triangle is visible; when the interpolated value becomes negative, that part of the triangle becomes non-visible through clipping.

With user-defined culling planes, if any of the vertices for a particular primitive has a negative value for culling, then the entire primitive is culled. This allows one shader invocation to choose to cull whatever primitive it is in by giving a negative culling value.

Tessellation Control Shaders have the ability to discard patches. If the TCS for a patch results in a zero value for any of the outer tessellation levels, then the patch they're working on will be culled by the tessellation primitive generator.

Geometry shaders can of course output no primitives at all.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    You *can* discard geometry. Based on your discard criteria you put all the vertices of some geometry outside clip space if you want them discarded. It might not be common but it's certainly not impossible. [Here's a working example discarding triangles when any one vertex of that triangle is clipped](https://jsgist.org/?src=d8d06ed1419a51876b8b0fe8afaa631a) – gman Feb 01 '21 at 04:09
  • 1
    @gman: "*You can discard geometry.*" ... I know. The answer specifies 3 means to discard geometry. Your suggested mechanism requires manipulating the vertex positions of all vertices associated with a primitive, which can affect any primitives that have to share vertices with that one. So it's pretty sub-optimal, compared to the examples I've provided. – Nicol Bolas Feb 01 '21 at 05:37
  • It's irrelevant that is sub optimal, only that it's possible in response to "*You cannot discard a vertex from a vertex shader*". Effectively you can. – gman Feb 01 '21 at 05:46
  • @gman: I said you cannot discard *a vertex* (note the singular) from a vertex shader. Which is true; your method is discarding *primitives*. Which are (usually) made of multiple vertices. – Nicol Bolas Feb 01 '21 at 05:57
  • The singular part is useful info but it's not relevant. discarding 3 vertices would be impossible if you can discard a vertex since discarding 3 vertices requires discarding 1 vertex 3 times. Besides, you can draw POINTS and effectively discard single vertices as yet another example of discarding a vertex in a vertex shader. – gman Feb 01 '21 at 06:03
  • @gman: "*discarding 3 vertices requires discarding 1 vertex 3 times.*" If you want to redefine "discard a vertex" as "move it out of the clipping area", then you can "discard a vertex". However, that's not what ***I*** mean by the term "discard a vertex". That term would have to do something like "discard a fragment", which is a thing that actually is possible and has a well-understood effect. Whereas moving a vertex off-screen has the effect of... moving it off-screen. Again if you want to call that "discarding", I can't stop you, but I'm not going to pretend they're the same thing. – Nicol Bolas Feb 01 '21 at 06:25
  • The problem is, statements like "You cannot discard a vertex from a vertex shader" lead people to believe there is no solution when there often is. You can choose to consider moving a vertex shader off the screen as not discarding it but functionally [it allows you to discard a vertex](https://jsgist.org/?src=2ee5c4b6d63f326b7825cf5114799cef) – gman Feb 01 '21 at 06:33
  • @gman: You can keep linking to that JS page all you want, but it doesn't change the fact that what you're doing is moving the ***primitive*** offscreen, which discards the ***primitive***. Moving just a vertex off-screen merely distorts any primitives attached to that vertex. – Nicol Bolas Feb 01 '21 at 14:19
  • you're arguing terminology. I'm arguing functionally. Me: "how do I delete a file", You: "You can't delete a file, the correct term is `unlink` which is the name of the function to call" which doesn't actually delete the file, it just removes the link to its data. Same here. Q: "how I discard a vertex". You: "You can't". Me: "I'm functionally discarding vertices here". – gman Feb 01 '21 at 15:11
  • @gman: The question was "geometry, vertices or fragments". The OP clearly thinks that discarding vertices *as separate from geometry* is a thing. And it isn't. That's the point of that part of the answer: I'm helping the OP better understand what they want. You're merely feeding into their misunderstanding. – Nicol Bolas Feb 01 '21 at 16:40
  • 1
    @gman, thank you for the suggestion. Have a instancing situation with particle systems with on/off functionality where I think the approach might be appropriate. Mostly because I haven't found any other ways to conditionally skip the fragment shader. – G. Putnam Feb 15 '23 at 21:42