how to conditional render different mesh LODs

hi,

lets say i have a mesh in different levels of details (each being a separate mesh / drawcall). to save performance, i first want to render a simple box around the mesh to check for the number of samples passed. depending on the samples passed, i want openGL to select a suitable LOD of that mesh. (a mesh has about 4…5 LODs, the lowest LOD has just a hand full of faces / vertices [maybe 100], the highest LOD about up to about 50K faces)

the only way to do that (that i’m aware of) is without “conditional rendering”, but to use a kind of “double-buffered query object”. in frame X, i render the box around the mesh, capture the samples in query X. next frame, i use result of query X to determine the LOD to draw, and draw the box again, capturing the samples passed into query X+1.

is there another way with “conditional rendering” of so ?
if not, is it a “good” approach if i have a couple of hundreds of meshes in my scene ?

thanks for any advice !

You might be interested in this.

What you’re trying to do looks interesting. Mainly if you use the occlusion queries for managing occlusion at the same time. Otherwise, you’ll slow things down as it is a 2 passes algorithm, which then might not be as efficient. Other things to take into account is the popping effect that will probably happen since you’ll switch from a model with N faces to the ‘same’ model but with only M faces where M << N. You’ll then have to see if that happen, and if so, if this is acceptable for you. To prevent this, algorithm as continuous LOD or adaptative LODs exist. One of them is called ROAM but is known to be old and not to provide any real benefits anymore (so avoid this latter one).

[QUOTE=john_connor;1287846]lets say i have a mesh in different levels of details (each being a separate mesh / drawcall) …
i want openGL to select a suitable LOD of that mesh. …
i have a couple of hundreds of meshes in my scene …[/QUOTE]

Ok, so lots of models, each model has multiple mesh LODs, each mesh LOD is a draw call. And you want to do LOD selection based on some “screen size” heuristic.

to save performance, i first want to render a simple box around the mesh to check for the number of samples passed. depending on the samples passed, i want openGL to select a suitable LOD of that mesh.

You could use this as a LOD selection heuristic. However, think about a long thin feature like a table top (with no legs or thin legs). When you’re looking top-down it hits a lot of samples. When you’re looking near edge-on, it hardly hits any samples. Do you really want to render a different and much lower-resolution LOD for that? That’s when you need the edge profile detail.

I suspect you may be happier with a LOD selection heuristic based on the size of the model’s bounding sphere on-the-screen (in the view frustum), or based on a distance-to-model-center heuristic. Both are super-cheap to evaluate in a shader, and the former has the benefit of dynamically adapting to field-of-view (frustum) changes. With either of these, you don’t need to rasterize the model to be able to tell which LOD you want to use.

If you think one of those LOD selection criterias (or something similar) might work for you, you can do the model culling and LOD selection on the GPU using a geometry shader in a first pass (this builds a list of instances). And immediately after that, you can launch one instanced draw call per unique LOD mesh (e.g. 5) and in doing so render all of your models (e.g. 100+), each one rendering with the correct LOD.

This all happens on the GPU side so it’s very fast, and in the end you end up rendering a lot of models with very few draw calls.

For more on this, read this thread (including the linked tutorial pages):

thank you both for your answers !!

[QUOTE=Dark Photon;1287850]Ok, so lots of models, each model has multiple mesh LODs, each mesh LOD is a draw call. And you want to do LOD selection based on some “screen size” heuristic.

You could use this as a LOD selection heuristic. However, think about a long thin feature like a table top (with no legs or thin legs). When you’re looking top-down it hits a lot of samples. When you’re looking near edge-on, it hardly hits any samples. Do you really want to render a different and much lower-resolution LOD for that? That’s when you need the edge profile detail.

I suspect you may be happier with a LOD selection heuristic based on the size of the model’s bounding sphere on-the-screen (in the view frustum), or based on a distance-to-model-center heuristic. Both are super-cheap to evaluate in a shader, and the former has the benefit of dynamically adapting to field-of-view (frustum) changes. With either of these, you don’t need to rasterize the model to be able to tell which LOD you want to use.

If you think one of those LOD selection criterias (or something similar) might work for you, you can do the model culling and LOD selection on the GPU using a geometry shader in a first pass (this builds a list of instances). And immediately after that, you can launch one instanced draw call per unique LOD mesh (e.g. 5) and in doing so render all of your models (e.g. 100+), each one rendering with the correct LOD.

This all happens on the GPU side so it’s very fast, and in the end you end up rendering a lot of models with very few draw calls.

For more on this, read this thread (including the linked tutorial pages):

“instance shader” … “model culling” … sounds very interesting :wink:
something like:

pass 1: build instances
for each object
determine model type, add transformation matrix to temp array

put all temp arrays of model instances into array buffer (or uniform buffer)

for each model type
use compute shader or transform feedback to determine if model center is in view frustum, feedback if so, and determine LOD dependent on the “depth” value = (MVP * vec4(0, 0, 0, 1)).z / w

pass 2:
for each model type
for each mesh
for each LOD
drawinstanced() (maybe indirect if i’m getting it right)

… right ?! i’ll try that approach … thanks again !

Something like that. Re indirect, yes (it’s an instanced indirect draw call).

However, the way you wrote this prompts a question: when you say you have “hundreds of meshes in my scene”, do you mean hundreds of instances of a small set of unique meshes? Or hundreds of “unique” meshes?

Where this approach is going to help you most is with many mesh instances drawn from a small set of unique meshes. I was assuming this before, but it occurs to me that perhaps I shouldn’t have been. For lots of mesh instances, this approach yields very few draw calls and state changes.

However, if all of your meshes are unique, then this isn’t going to help you so much. In fact it’s going to be overkill.

What prompted this question is your “for each mesh” loop in pass 2. If here “mesh” is a mesh instance, then no; this loop should not be here. The instanced draw covers rendering multiple instances of the same mesh LOD.

[QUOTE=Dark Photon;1287871]However, the way you wrote this prompts a question: when you say you have “hundreds of meshes in my scene”, do you mean hundreds of instances of a small set of unique meshes? Or hundreds of “unique” meshes?

However, if all of your meshes are unique, then this isn’t going to help you so much. In fact it’s going to be overkill.[/QUOTE]

no, not “unique”, consider a space sim:
there are several ships in the scene (up to hundred+ maybe), each ship has a “hull”, a set of guns, thats it essencially (maybe 1 particle emitter). each hull has about 4…5 LODs, and each gun about 3 LODs or so. many ships are of the same type, that means the same meshes, and different model types can use similar gun types.

std::vector<...> meshes = .. all possible meshes in the scene

pass 2:
for (auto& mesh : meshes) {
  for (auto& lod: mesh.LODs) {
    apply material
    drawinstanced(... lod.drawcall ..., lod.instancecount)
  }
}

so i’ll definitely get several instances (up to max hundreds maybe) per mesh, spread over a hand full LODs per mesh. never heared about “model culling” / “instance shader” before, the idea is great (i tried that about a year ago, but very simplified on the cpu: if (modelcenter behind camera) then cull)