• Advertisement
Sign in to follow this  

OpenGL Managing scene's (level's) geometry - how to do if efficiently

This topic is 1676 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey, im working on engine for future game and i stumbled on a problem. I'm not sure how to efficiently manage (mostly store and send to GPU) data of my level geometry.

Game will be with Top-Down camera (3D), so visibile area will be reduced, also most of objects will be pretty low-poly since nobody needs extra sharp(/smooth) models if u barely can see some details on it. ;) Problem is that i'm not sure how to store it, (update if needed* - though geometry is static, just what is visible) and send to GPU.
I've come up with some ideas:

1. Most basic - load everything right in one VBO (as models are very low-poly it won't have too much data as you might think?).
I think that though vertex shadders will work on every vertex, fragment shaders will work only on visible fragments and "discard" others automatic, right?

+ simple to write
+ dont have to bind multiple VAOs/VBOs/textures every frame
+ don't have to send new data every frame to GPU (using CPU time) and compute it!
- lot of data is on VBO (though size won't change during game) and will go through Vertex Shader (i think not by F.S. - right?)
- it seem's not so optimized?
- everything must be in one texture atlas which limits possibilities

2. Load every distinct object at startup of level, and then every frame create new VBO with new mesh, that would be made from objects that are currently visible at scene (modified with transformation matrix so that it all works ok).

+ it sounds as its an optimization ;p (but after some thought i'm not so sure)
+ i manage what i send to draw, so i could do some work (like LOD, though not in that kind of game, becouse all models are usally at same distance from camera...) so it's not actually "pro" per se...
+ i bind one VAO/VBO after creating that whole scene's mesh and dont change it for frame.
+ only visible vertexes (objects, somevertex might be little out of course) are sent to GPU
- loooooot of work on CPU, which may result in: GPU waits for CPU data ..... CPU waits for rendering by GPU ... and again GPU waits...
- looks much more complicated to build efficiently (at least as much as can)
- everything visible must be in one texture atlas...

3. Split level on smaller areas like 2D-tiles, each tile contains every object's data that is (at least partial) in this tile. Just not too small tiles, so that per visible scene there would be 3x3 or 4x4 tiles max. (or even 2x2?).

+ sound pretty fast (only needed data, not many VAO/VBO binds per frame, VAO/VBO would be already set-up at loading-time so no computing time needed per frame, only draw call).
+ somehow more flexible with textures, becouse i need current texture only for small tile which can be only couple meshes.
- seems hard to code it efficient, some problems (look below)
- some meshe's that would be on more than one tile would have to be doubled or splitted (not easy job i guess to do it right and fast! doubled seems better somehow)
- lot of pre-processing so as long as #1 pre-processing or even longer.

4. Render each object separate. If it's visible multiple times, render it using some kind of mechanism to do it efficiently like instancing (or something else? suggestons? i dont know instancing yet, but i heard of it).

+ normally it may be common way to render scenes (when meshes have many triangles its ok, becouse switching vbo doesnt cost so much then)
+ easiest to implement
+ easy to manage scene
+ easy to create a scene (just add description of mesh - pos/rotate/scale and which object it is just to add it).
+ i could create VAOs/VBOs earlier so no computing time needed on CPU side every frame
+ only visible models would be rendered
- lot of switches between every objects (maybe not that much, but when meshes are low-poly, i guess switching would take long time compared to rendering)
- it doesn't seems too optimized.

As i said, mostly i'd like to balance between complexity and performance, since i'm not working on some top-level, AAA project, just smaller project that may be very playable, may not ;)

PS. Could someone write in some order or with "weights" how much OpenGL actions cost (about)? Like: single draw call, bind VBO, bind VAO, bind texture, etc... so i'd knew what to watch for :)

Share this post


Link to post
Share on other sites
Advertisement
fragment shaders will work only on visible fragments and "discard" others automatic, right?

Not necessarily, deferred rendering is meant to address this issue.  Some other behind the scenes optimizations require some thought on your part.  Depth sorting for one...

 

If you load 1 big VBO then you can still access them individually by using the models indices.  I would not do this.  Accessing a component still requires additional draw calls if you want to change anything.  If you combine this with the headache and nuisance of adding and subtracting and hopping through indices then it may not be worth the extra few percentage points of performance increase.  Some implementations may slow down, who knows?  Messing up the index management can crash a computer. You won't know all your particular  issues until you try what you've built on every single machine that you can get your hands on.

 

Even most of those silly little handheld phones can handle at least several dozen VBO draw calls before they start complaining too much, some of them will do 100 or more per frame, easily.  This is combined with changing shaders and textures for many of those models.

 

Depending on your setup:

(i)You can use a distance check to disable whatever is off screen.  if(modelPosX > -5.0 && modelPosX < 5.0){displayArea_1();} helps a lot to manage top view and side view games.  Also if you isolate collision + animation within these screen-sized chunks then you can get really carried away for every discrete area.

(ii)If, when you change rooms it's only vertically or horizontally, then you will never have to display more than two areas at a time which gives you lots of flexibility in how many draw calls, texture and shader swaps can take place. 

 

You could pack all the floor tiles for a room into one VBO, I think this would be practical and easy and safe.  Same for trees, rocks, bushes.   Now for the tiles, rocks, trees, and bushes you would have only 4 VBO's to switch between.  Even a wrist watch running Java could handle that much.

 

Pay attention to optimization but don't become so hung up on it that you set yourself behind several years worrying about it.  Stability is far more important.  Finishing something is also good.

 

p.s. You mentioned something about creating the required VBO's every frame.  I would say, avoid that.  Even changing them per-frame can stall things, I imagine creating them per-frame will be far worse. My imagination can't be stretched far enough for me to come up with a good reason to do this.

Edited by Josh Petrie

Share this post


Link to post
Share on other sites
You can use a distance check to disable whatever is off screen.  if(modelPosX > -5.0 && modelPosX < 5.0){displayArea_1();} helps a lot to manage top view and side view games.  Also if you isolate collision + animation within these screen-sized chunks then you can get really carried away for every discrete area.

But then i guess u mean that every mesh has its own VBO, right?
 

 

when you change rooms it's only vertically or horizontally, then you will never have to display more than two areas at a time which gives you lots of flexibility in how many draw calls, texture and shader swaps can take place.

I plan for it to be outdoor top-down shooter. Something similiar to Left 4 Dead, but top-down, now for PC and maybe in the future for Android(/iOS). Test pic (a lot to do yet): http://i.stack.imgur.com/XC0ga.png S(it had some debugging on so actually rendering time is not even half now)

I thought about making group of objects but it doesnt help me too much, if i put - in example - all trees in one VBO...
 

 

p.s. You mentioned something about creating the required VBO's every frame.  I would say, avoid that.  Even changing them per-frame can stall things, I imagine creating them per-frame will be far worse. My imagination can't be stretched far enough for me to come up with a good reason to do this.

I guess i'll have to update my shadow-map generation so that i'm not creating new texture every frame (for each light) but use same texture and just update it... Luckily its only couple lines...

In the end i still dont know how should i keep it. Group it by position in bigger VBOs and use only 2-4 VBOs per frame looks best, right? But the question is - should i keep it in VRAM or RAM and just send it if i need it? 2nd option would allow me to create very big maps since it holds very little data in vram (and ram is usally much bigger), but seems slower...

Btw. as i asked before - how slow is binding VAOs, binding VBOs, binding Texture, drawing 100 triangles, drawing 100k triangles, drawing 500k triangles, turning of depth test, something else that could be important here too, etc. ? I'd like to know what should i avoid and how hard should i optimize things to use as few as possible. 

Edited by RippeR37

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By LifeArtist
      Good Evening,
      I want to make a 2D game which involves displaying some debug information. Especially for collision, enemy sights and so on ...
      First of I was thinking about all those shapes which I need will need for debugging purposes: circles, rectangles, lines, polygons.
      I am really stucked right now because of the fundamental question:
      Where do I store my vertices positions for each line (object)? Currently I am not using a model matrix because I am using orthographic projection and set the final position within the VBO. That means that if I add a new line I would have to expand the "points" array and re-upload (recall glBufferData) it every time. The other method would be to use a model matrix and a fixed vbo for a line but it would be also messy to exactly create a line from (0,0) to (100,20) calculating the rotation and scale to make it fit.
      If I proceed with option 1 "updating the array each frame" I was thinking of having 4 draw calls every frame for the lines vao, polygons vao and so on. 
      In addition to that I am planning to use some sort of ECS based architecture. So the other question would be:
      Should I treat those debug objects as entities/components?
      For me it would make sense to treat them as entities but that's creates a new issue with the previous array approach because it would have for example a transform and render component. A special render component for debug objects (no texture etc) ... For me the transform component is also just a matrix but how would I then define a line?
      Treating them as components would'nt be a good idea in my eyes because then I would always need an entity. Well entity is just an id !? So maybe its a component?
      Regards,
      LifeArtist
    • By QQemka
      Hello. I am coding a small thingy in my spare time. All i want to achieve is to load a heightmap (as the lowest possible walking terrain), some static meshes (elements of the environment) and a dynamic character (meaning i can move, collide with heightmap/static meshes and hold a varying item in a hand ). Got a bunch of questions, or rather problems i can't find solution to myself. Nearly all are deal with graphics/gpu, not the coding part. My c++ is on high enough level.
      Let's go:
      Heightmap - i obviously want it to be textured, size is hardcoded to 256x256 squares. I can't have one huge texture stretched over entire terrain cause every pixel would be enormous. Thats why i decided to use 2 specified textures. First will be a tileset consisting of 16 square tiles (u v range from 0 to 0.25 for first tile and so on) and second a 256x256 buffer with 0-15 value representing index of the tile from tileset for every heigtmap square. Problem is, how do i blend the edges nicely and make some computationally cheap changes so its not obvious there are only 16 tiles? Is it possible to generate such terrain with some existing program?
      Collisions - i want to use bounding sphere and aabb. But should i store them for a model or entity instance? Meaning i have 20 same trees spawned using the same tree model, but every entity got its own transformation (position, scale etc). Storing collision component per instance grats faster access + is precalculated and transformed (takes additional memory, but who cares?), so i stick with this, right? What should i do if object is dynamically rotated? The aabb is no longer aligned and calculating per vertex min/max everytime object rotates/scales is pretty expensive, right?
      Drawing aabb - problem similar to above (storing aabb data per instance or model). This time in my opinion per model is enough since every instance also does not have own vertex buffer but uses the shared one (so 20 trees share reference to one tree model). So rendering aabb is about taking the model's aabb, transforming with instance matrix and voila. What about aabb vertex buffer (this is more of a cosmetic question, just curious, bumped onto it in time of writing this). Is it better to make it as 8 points and index buffer (12 lines), or only 2 vertices with min/max x/y/z and having the shaders dynamically generate 6 other vertices and draw the box? Or maybe there should be just ONE 1x1x1 cube box template moved/scaled per entity?
      What if one model got a diffuse texture and a normal map, and other has only diffuse? Should i pass some bool flag to shader with that info, or just assume that my game supports only diffuse maps without fancy stuff?
      There were several more but i forgot/solved them at time of writing
      Thanks in advance
    • By RenanRR
      Hi All,
      I'm reading the tutorials from learnOpengl site (nice site) and I'm having a question on the camera (https://learnopengl.com/Getting-started/Camera).
      I always saw the camera being manipulated with the lookat, but in tutorial I saw the camera being changed through the MVP arrays, which do not seem to be camera, but rather the scene that changes:
      Vertex Shader:
      #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0f); TexCoord = vec2(aTexCoord.x, aTexCoord.y); } then, the matrix manipulated:
      ..... glm::mat4 projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); ourShader.setMat4("projection", projection); .... glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); ourShader.setMat4("view", view); .... model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); ourShader.setMat4("model", model);  
      So, some doubts:
      - Why use it like that?
      - Is it okay to manipulate the camera that way?
      -in this way, are not the vertex's positions that changes instead of the camera?
      - I need to pass MVP to all shaders of object in my scenes ?
       
      What it seems, is that the camera stands still and the scenery that changes...
      it's right?
       
       
      Thank you
       
    • By dpadam450
      Sampling a floating point texture where the alpha channel holds 4-bytes of packed data into the float. I don't know how to cast the raw memory to treat it as an integer so I can perform bit-shifting operations.

      int rgbValue = int(textureSample.w);//4 bytes of data packed as color
      // algorithm might not be correct and endianness might need switching.
      vec3 extractedData = vec3(  rgbValue & 0xFF000000,  (rgbValue << 8) & 0xFF000000, (rgbValue << 16) & 0xFF000000);
      extractedData /= 255.0f;
    • By Devashish Khandelwal
      While writing a simple renderer using OpenGL, I faced an issue with the glGetUniformLocation function. For some reason, the location is coming to be -1.
      Anyone has any idea .. what should I do?
  • Advertisement