12
\$\begingroup\$

I have been using OpenGL for a while and have read a large number of tutorials. Aside from the fact that a lot of them still use the fixed pipeline, they usually throw all the initialisation, state changes and drawing in one source file. This is fine for the limited scope of a tutorial, but I’m having a hard time working out how to scale it up to a full game.

How do you split your usage of OpenGL across files? Conceptually, I can see the benefits of having, say, a rendering class that purely renders stuff to screen, but how would stuff like shaders and lights work? Should I have separate classes for things like lights and shaders?

\$\endgroup\$
10
  • 1
    \$\begingroup\$ There are open source games out there as well as larger tutorials that have more code. Check some of them out and see how the code is organized. \$\endgroup\$
    – House
    Commented Mar 18, 2012 at 0:37
  • \$\begingroup\$ Are you a beginner when it comes to rendering engines? \$\endgroup\$
    – Samaursa
    Commented Mar 18, 2012 at 2:07
  • \$\begingroup\$ This question cannot be reasonably answered without some narrowing of the scope. Just what are you trying to build? What kind of game, what kind of graphics does it have, etc? \$\endgroup\$ Commented Mar 18, 2012 at 7:00
  • 1
    \$\begingroup\$ @Sullivan: "I thought that these kind of concepts would apply to almost any game." They don't. If you ask me to make Tetris, I'm not going to bother making a big wrapper layer or something around OpenGL. Just use it directly. If you ask me to make something like Mass Effect, then we're going to need multiple layers of abstraction around our rendering system. Throwing the Unreal engine at Tetris is using a shotgun to hunt flies; it makes the task so much harder than just hand coding it directly. You should only build as big and complex of a system as you need, and no bigger. \$\endgroup\$ Commented Mar 18, 2012 at 17:51
  • 1
    \$\begingroup\$ @Sullivan: The only complexity your question implies is where you talk about splitting across multiple files. Which is something I would do even in Tetris. And there are many levels of complexity between Tetris and the Unreal engine. So again: which one are you asking about? \$\endgroup\$ Commented Mar 18, 2012 at 20:48

4 Answers 4

6
\$\begingroup\$

I think OO OpenGL is not that necessary. It is different when you talk about shader, model, etc class.

Basically you would do game/engine initialization first (and other things). Then you would load textures, models and shaders to RAM (if needed) and Buffer Objects, and upload/compile shaders. After that, you, in your data structure or class of shader, model, have int IDs of shaders, model and texture buffer objects.

I think most engines have engine components and everyone of them has certain interfaces. All engines I have looked into have some component such as Renderer or SceneManager or both (depends on complexity of game/engine). Than you can have OpenGLRenderer class and/or DXRenderer which implement Renderer interface. Then if you have SceneManager and Renderer you can do some of the following things:

  • Traverse SceneManager and send each object to Renderer for rendering
  • Encapsulate upper case in some SceneManager method
  • Send SceneManager to Renderer so it handles it

Renderer would probably call object's draw function which would call draw function of each mesh that is composed of and mesh would bind texture object, bind shader, call OpenGL draw function and then unuse shaders, texture and object data buffer objects.

NOTE: this is just example, you should study SceneManager in more detail and analyze your use case to see what is best implementation option

You would of course, have other components of engine, such as MemoryManager, ResourceLoader etc, which would take care of both video and RAM memory usage, so they could load/unload certain models/shaders/textures as needed. Concepts for this include memory caching, memory mapping etc etc. There are lot of details and concepts about each components.

Take a look at more detailed description of other game engines, there are a lot of them and their documentation is pretty much available.

But yes, classes make life easier; you should totally use them and remember about encapsulation, inheritance, interfaces, and more cool stuff.

\$\endgroup\$
5
  • \$\begingroup\$ This is the answer I was kind of looking for. However, when you say “... load textures, models and shaders to RAM (if needed) and Buffer Objects, and upload/compile shaders...” brings me back to my original question; should I use objects to encapsulate things like models and shaders? \$\endgroup\$
    – Sullivan
    Commented Mar 18, 2012 at 10:14
  • \$\begingroup\$ object is instance of a class, and 'you should totally use them'. \$\endgroup\$
    – edin-m
    Commented Mar 18, 2012 at 12:03
  • \$\begingroup\$ Sorry, I meant to ask ‘how should I use objects’. My bad. \$\endgroup\$
    – Sullivan
    Commented Mar 18, 2012 at 12:48
  • \$\begingroup\$ Well, it depends of your class design. Some basic example would be intuitive, something like: object3d (class) has meshes (class); each mesh has material (class); each material has texture (can be class, or just id) and shader (class). But as you read about each component you will see there are different but equal approaches to making SceneManager, Renderer, MemoryManager (all of these can be single class or multiple of them, inherited etc etc). Dozens of books are written on this topic (game engine architecture), and to answer question in detail one could write one. \$\endgroup\$
    – edin-m
    Commented Mar 18, 2012 at 14:47
  • \$\begingroup\$ I think that this is the best answer I’m going to get. Thank you for your help :) \$\endgroup\$
    – Sullivan
    Commented Mar 20, 2012 at 9:42
2
\$\begingroup\$

OpenGL has some 'Object' concepts in it already.

For example anything with an id can be through of as an object (There are also things specifically named 'Objects'). Buffers, Textures, Vertex Buffer Objects, Vertex Array Objects, Frame Buffer Objects and so on. With a little work you can wrap classes around them. It also give you a easy way to fall back to old deprecated OpenGL functions if your context doesn't support the extensions. For example a VertexBufferObject could fall back to using glBegin(), glVertex3f(), and so on.

There are a few ways you might need to move away from the traditional OpenGL concepts, for example you probably want to store metadata about the buffers in the buffer objects. For example if the buffer stores vertices. What is the format of the vertices (ie position, normals, texcoords and so on). What primitives it uses (GL_TRIANGLES, GL_TRIANGLESTRIP, etc...), size information (how many floats are stored, how many triangles they represent, etc...). Just to make it easy to plug them into the draw arrays commands.

I recommend you look at OGLplus. It's C++ bindings for OpenGL.

Also glxx, that's only for extension loading though.

In addition to wrapping the OpenGL API, you should look at making a slightly higher level one build on top of it.

For example a material manager class that is responsible for all your shaders, loading and using them. Also it would be responsible for transferring properties to them. That way you can just call: materials.usePhong(); material.setTexture(sometexture); material.setColor(). This allows fore more flexibility since you can use newer things like shared uniform buffer objects to just have 1 big buffer containing all the properties your shaders use in 1 block but if its not supported you an fall back to uploading to each shader program. You can have 1 big monolithic shader and swap between different shader models using uniform routines if it's supported or you can fall back to using a bunch of different small shaders.

You can also look at expending on from the GLSL specs for writing your shader code. For example the #include would be incredibly useful and very easy to implement in your shader loading code (there is also an ARB extension for it). You can also generate your code on the fly based on what extensions are supported, for example use a shared uniform object or fall back to using normal uniforms.

Finally you will want a higher level rendering pipeline API that does things like scene graphs, special effects (blur, glow), things that require multiple rendering passes like shadows, lighting and such. And then on top of that a game API that has nothing to do with the graphics API but just deals with objects in a world.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ "I recommend you look at OGLplus. It's C++ bindings for OpenGL." I would recommend against that. I love RAII, but it's entirely inappropriate for most OpenGL objects. OpenGL objects are associated with a global construct: the OpenGL context. So you would be unable to create these C++ objects until you had a context, and you would be unable to delete these objects if the context has already been destroyed. Also, it gives the illusion that you can modify objects without changing global state. This is a lie (unless you are using EXT_DSA). \$\endgroup\$ Commented Mar 18, 2012 at 7:03
  • \$\begingroup\$ @NicolBolas: Could I not check to see if there is a context and only initialize then? Or perhaps only allow managers to create the objects which require an OpenGL context? --- btw, great set of tutorials (link in your profile)! Somehow I never came across them when searching for OpenGL/Graphics. \$\endgroup\$
    – Samaursa
    Commented Oct 19, 2012 at 3:18
  • \$\begingroup\$ @Samaursa: To what end? If you have a manager for OpenGL objects, then... you don't really need to have objects take care of themselves; the manager can do it for them. And if you only initialize them when a context exists, what happens if you try to use an object that was not properly initialized? It just creates frailty in your codebase. Nor does it change anything I said about the ability to modify these objects without touching global state. \$\endgroup\$ Commented Oct 19, 2012 at 3:21
  • \$\begingroup\$ @NicolBolas: "you would be unable to create these C++ objects until you had a context"... is that any different than using bare OpenGL constructs? In my eyes, the oglplus::Context class makes this dependency highly visible - would that be a problem? I believe it will help new OpenGL users to avoid a lot of problems. \$\endgroup\$
    – xtofl
    Commented Nov 23, 2012 at 13:08
1
\$\begingroup\$

In modern OpenGL you can almost totally seperate rendered object from each other, using different vaos and shader programs. And even the implementation of one object can be separated into many abstraction layers.

For example, if you want to implement a terrain, you could define a TerrainMesh whose constructor creates the vertices and indices for the terrain, and sets them into arraybuffers, and - if you give it an attribute position - it shaderplumbs your data. It should also know how to render it, and it should take care to revert all the context changes it has done to setup the rendering. This class itself should know nothing about the shader program that is going to render it, and nor should it know anything about any other objects in the scene. Above this class you could define a Terrain, which is aware of the shader code, and its job is to create the connection between the shader and TerrainMesh. This should mean getting attribute and uniform positions and loading in textures, and things like that. This class should not know anything about how the terrain is implemented, what LoD algorithm it uses, it is just responsible for shading the terrain. Above this, you could define the non-OpenGL functionality like behivour and collision-detection and such.

Coming to the point, even though OpenGL is designed to be used low-level, you can still build independent layers of abstraction, that let you scale up to apllications with a size of an Unreal game. But the number of layers you want / need really depends on the size of the application you want.

But don't lie to yourself about this size, don't try to mimic Unity's object model in a 10k line application, the result will be a complete disaster. Build the layers gradually, only increase the number of abstraction layers, when it's needed.

\$\endgroup\$
0
\$\begingroup\$

ioDoom3 is probably a great starting point, as you can kind of rely on Carmack to follow great coding practice. Also I believe he doesn't use megatexturing in Doom3, so it's relatively straight forward as a render pipe.

\$\endgroup\$
2
  • 6
    \$\begingroup\$ "as you can kind of rely on Carmack to follow great coding practice" Oh man, that's a good one. Carmack follows great C++ coding practice. That's a riot! Oh... you weren't kidding. Um... have you ever actually looked at his code? He's a C programmer and that's how he thinks. Which is fine for C, but the question is about C++. \$\endgroup\$ Commented Mar 18, 2012 at 7:06
  • \$\begingroup\$ I come from a C background so his code makes a lot of sense to me :P and yes, spent a bit of time working on quake based games. \$\endgroup\$
    – chrisvarnz
    Commented Mar 18, 2012 at 16:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .