VBO + octree + shaders?

Hello all!

I’m can’t figure out how to sort this problem out. :confused:

> I have to use an frustum-culled octree structure to render my geometry, BUT I have to use vertex arrays or VBO’s (of course). How should I store data to make this possible and efficient?

> I’m using several shaders to render my scene. I don’t want to perform unneeded switches as it’s killing performance. Right now I’m storing data according to the shader to use, and render all faces of one specific shader at the same time; and so on for all shaders. How could I combine my shaders with the octree and VBO?

Thanks a lot for your help, it’ll be greatly appreciated!

HT

For storing data, it usually depends on the size of the scene. If its a small scene (around 60k triangles), I believe a single VBO batch is good enough. You can bind the scene VBO before starting the drawing of any node. If you have a bigger scene, you can break the VBOS per octree leaf node and when you are about to draw a leaf node, first bind its VBO and then draw. So you will have geometery and associated VBO per leaf node.

For the shading tree, you can have a shader list per octree node. Each shader object of the list should have a texture/material list. Each object of the material list should have a list of faces (for the node that use the material).

So efectively you will

  • Frustum cull your octree nodes
  • Sort your visible nodes front to back
  • Bind the VBO of the current leaf node
  • For each shader in the leaf node shaderlist, bind the shader
  • For each material in the shader material list, bind the material
  • glDrawElement… for drawing the faces of the leaf node using this material

Hope you get the idea.

Fastian

Hi, thanks for your feedback

I get the idea but I’m still concerned about the frequent shader switch:


for each node
bind VBO
for each shader
glDrawElement(faces using this shader)
end for
end for

It means if I have, say, 6 shaders and 25 visible nodes, I’ll have to switch my active shader 150 times per frame, which is not acceptable.

I think I’ll have to do this: one VBO per leaf AND per shader type, then loop through my octree and find what to render, and put what to render in a sorted batch list:


for each node
for each shader
add VBO ref to list
end for
end for

sort list by shader

for each list item
bind list item VBO
glDrawElements(list item)
end for

Do you find this acceptable?

HT

hmmm… Again that actually depends on your geometry type. If your node size is reasonably big and you have an indoor scene, you probably won’t be rendering more than 2-3 visible nodes per frame (as the rest will be occluded). The shader switch for these would be limited and you won’t need to switch so many VBOs per frame. Again depending on the size of data, if some of the VBOs are in system memory, switching between them could cause a major stall.

Maybe I am not getting what you are trying to do here. Do you mean you sort everything based on shader, then sort everything based on distance. If that is the case, that is OK as long as there is no issue of VBO data coming from system memory.

As you said, it depends on the geometry type. I’m intending to render a large outdoor scene.

The guess now is to know what’s worst: frequent shader change OR frequent VBO binding… I think I’ll have to test different node sizes for the octree.

HT

try this…

  1. create materials (shaders) list. each shader should have array of batches
  2. create octree. each node should have list of batches sorted by material ID
batch 0: material_ID, primitive type, start index,  count
batch 1: material_ID, primitive type, start index, count
batch 2: material_ID, primitive type, start index, count
...
  1. create one big vertex VBO (holding 1M vertices in VBO is not problem on todays hw)
  2. create VBO with all triangles sorted by nodes and sorted by batches…
    node 0(b0,b1,b2…)
    node 1(b0,b1,b2,…)

Rendering:

  1. frustum cull your octree nodes
  2. for each visible node notify materials with batches in node, based on material ID in batch
  3. for each notifyed material, enable it’s shaders and render batches (you can use glMultiDrawElements)

To speedup rendering:

  1. render zfill
  2. render axis aligned bbox (from nodes) with occlusion queries.
  3. remove occluded nodes from list of visible nodes

If you need to spread dataset on several VBO’s you can add VBO id in each node in each batch. Materials should have separate list if batches for each VBO id.
Now… you have bunch of materials and few VBO’s. So… render loop can be:

for each VBO_ID in VBO set
{
 bind VBO(VBO_ID) & pointers
 for each material in materials set 
    render batches(VBO_ID)
}

or 

for each material in materials set 
{
  for each VBO_ID in VBO set
  {
     bind VBO(VBO_ID) & pointers
     render batches(VBO_ID)
  }
}

One of this two render loops are faster… you have to test it…

yooyo

So you mean, using this technique, that I have to notify all visible material with batches, then AFTER each material visibility has been processed, I loop through all materials (preferabily sorted I guess) and only switch once per material type? I think that’s what I understood.

> in each octree node: shader (material) list with faces (batch referencing big VBO)
> frustum cull and everything
> put each visible face into render-list (sorted by shader/material)
> draw render-list

I think this should minimize VBO binding and shader change. Further feedback will greatly be appreciated :wink:

HT

Yes… To clarify few things… You will have a bunch of materials. In some frame, only some materials will be used. This materials will be notify by octree frustum culling.

Take a look in glMultiDrawElements spec to figure how to send several batches to render using single draw call.

My suggestion is to create array of pairs (uint offset, int count) in each material. When engine notify material, it will simple add new batch (new pair) in array.

yooyo

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.