Week In Development 6

The past few weeks have been fairly busy. I haven’t been writing tutorials lately for one of two reasons:

  1. I worry about the usability of a monolithic set of tutorials that expects you follow in a particular order.
  2. I have been particularly busy with class lately.

Instead what I plan on doing is finishing off the tutorials with the OpenGL Frame Profiler, and then continuing the handle additions to the code-base in terms of new subdirectories so that – if I do find that I want to write a tutorial on one of the subjects – I have a simple application to reference.

I could just reference specific points in terms of commits, but the reason I decided not to do this is because I wanted to be able to fix past tutorials if problems arose.

I think finishing it off at the Frame Profiler is more than fair. You have an OpenGL Context, it’s more of a question of how you use OpenGL at that point – maybe I’ll talk about other interesting talking points as they come up.

Work Done

Last update we saw that we could draw basic geometry provided in the source code. Since then, I have added support for loading obj files using a custom runtime-efficient double-buffered file reader to generate half edge meshes (knowledge taken from my experience writing an interpreted scripting language).

OBJ HEM Parser

Times of the parser are very promising. A blank obj parser which simply parses and doesn’t do anything with the data can parse the Stanford Dragon in 0.13 seconds. Generating the half edge mesh is a little more complicated – so we see the time jump to ~1 seconds. And depending on if we want face normal generation or averaged vertex normals, generating the OpenGL buffers from this information can take 1~2 seconds extra. (There may be inefficiencies there, I have not profiled for them.)

Simpler models – like the Stanford Bunny – are practically instant. Even in debug mode. Half edge meshes are complete – meaning the boundary edges are linked properly. Face normals are calculated using the One-Ring Traversal. Vertex normals are calculated using several One-Ring Traversals on each adjacent face to a vertex by swiveling the outgoing half edge (twin->next) until we wrap around to the starting edge.

Provided are just some sampled load times for obj files using the parser:

OBJ Load Times (Best of 3)

Averaged Normals

Stanford Dragon (437k vertices, 871k faces)
Create Half Edge Mesh: 0.863s
Create OpenGL Buffers: 1.502s

Stanford Bunny (35k vertices, 69k faces)
Create Half Edge Mesh: 0.067s
Create OpenGL Buffers: 0.147s

Teapot (3k vertices, 6k faces)
Create Half Edge Mesh: 0.005s
Create OpenGL Buffers: 0.090s

Sphere (362 vertices, 720 faces)
Create Half Edge Mesh: 0.001s
Create OpenGL Buffers: 0.001s

Face Normals

Stanford Dragon (437k vertices, 871k faces)
Create Half Edge Mesh: 1.063s
Create OpenGL Buffers: 0.100s

Stanford Bunny (35k vertices, 69k faces)
Create Half Edge Mesh: 0.081s
Create OpenGL Buffers: 0.009s

Teapot (3k vertices, 6k faces)
Create Half Edge Mesh: 0.007s
Create OpenGL Buffers: ~0.000s

Sphere (362 vertices, 720 faces)
Create Half Edge Mesh: 0.001s
Create OpenGL Buffers: ~0.000s


Another new development is my involvement in the Open Source community with Qt5+. If all goes according to plan, I will be providing some of the new headers for the Qt project. Specifically the two I have on my radar are QOpenGLFunctions_ES3_0, and QOpenGLFunctions_ES3_1. I’ve needed these abstractions for my project currently, and instead of keeping the implementation all to myself, I plan on adding it as a feature in an upcoming version of Qt – pending approval of course.

This will be good for me to get some more real world experience as a developer, and for developers who are following in my footsteps using the new OpenGL abstractions as provided by Qt. I hope to continue making contributions, but official involvement will depend solely on my availability when I start my job in a few months.

For now though, these additions should be simple enough. And it’s all I’m promising for now – until I know more.

Current Status

What I currently have doesn’t look very pretty – but it’s efficient and well-coded (if I do say so myself). Not perfect, but definitely not a hack-job. Soon we will be getting to making things look pretty. Right now I’m simply loading an obj, calculating it’s normals, drawing the absolute value of the normals for each attribute. It looks something like this:

Averaged Normals on Stanford Dragon

Averaged Normals on Stanford Dragon

As you can see from the title, this is my 14th major change. It’s the last visual change made to the project thus far. In total, I’m currently on version 19. Version 19 supports the following additions:

  • QOpenGLFunctions_ES3_0 sample class – just practicing for when I actually add the implementation to the final Qt class.
  • Uniform Buffer Objects are being used to share uniforms between shaders. I have supplied a OpenGLUniformBufferObject class to assist with this.
  • An OpenGLSLParser class which is used solely for finding #include directives and expanding in-line. This uses common technology as the OBJ Parser, so it’s very efficient.
  • Instanced Rendering using glDrawArraysInstanced() and glVertexAttribDivisor(). Not very useful right now, but it will be when we start having more complicated scenes.

Looking towards the future of this project, we will see the following changes:

  • G-Buffer calculated in place of drawing directly to the screen for more efficient lighting calculations. (I don’t want to do this the inefficient way, e.g. fragment light loop)
  • Addition of (at minimum) multiple point lights of different color in a scene, animated and moving around the scene with many more static instanced objects.
  • Exponential shadow mapping, to introduce quality and shadow casting to the scene on all objects.

I hope to add more than this before my next check-in, but really this is the minimum I want to accomplish.


Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 thoughts on “Week In Development

  • Dan

    Is it likely your QOpenGLFunctions_ES3_1 will be in Qt 5.5? I’ll be pretty excited if that’s the case. Additionally, will the ES3.1 context work on the desktop, or must you switch the context manually?

    • Trent Post author

      Hey Dan,

      Actually I’m glad someone inquired, I wasn’t sure if I was going to move forward with committing a patch to Qt for the ES3 headers. It’s a little more work than I had envisioned, so I was planning on just keeping the headers in my own project here. Now that I know there is interest, I will look into it once again. (So I’m not quite sure, depends on their release schedule and all that)

      ES is for Embedded Systems, so it’s far more likely that if you support ES3, you don’t really support the equivalent desktop GL. There really isn’t a need to switch, I’ve found what works best is to create a common OpenGLFunctions class which derives off the relevant desktop GL functions (primary), and then if that doesn’t work, the ESGL functions (secondary). If neither of these are available at compile time, you likely don’t have the proper hardware to use the GL functionality you want.

      If both are supported, I try to make a desktop GL context first, if that fails, then an ESGL context. If that fails, display an error message because the user doesn’t support the proper minimum GL which I’ve designed for. I’ve never run into this case myself, but it’s good to program for it.

      When programming modern GL, it’s always worthwhile to know what features you’re using and where they align with the ES features, especially if you want something to work across both desktop and mobile.

      Look forward to more blog posts about the ES3_0 and ES3_1 inclusion, I’ll have to send some emails and get on their code review systems to make the patch.

      • Dan

        Hey Trent,

        Thanks for the reply. I’m currently targeting OpenGL 4.1 core (lowest common denominator of Windows/Ubuntu/Mac unfortunately). So yeah, I’ll just have to make sure to only use features available with both GL ES 3.1 and GL 4.1.

        The one problem is I have a dependency on tessellation shaders, which is only an extension on a few Android GPUs out there. I’ve just got a Galaxy S6 here, and while the GPU is supposed to support tessellation, I’m not seeing any of the required extensions in the OpenGL extensions viewer. Qt’s OpenGLExtensions module doesn’t even seem to support some of the ones needed. Have you any idea how to approach tessellation with OpenGL in Qt? Anyway, looking forward to whatever you end up doing with your ES3_1 functions even if they don’t get in Qt 5.5.

        • Dan

          Sorry if that last question was unclear. I’m currently using tessellation with Qt on the desktop, but am wondering how it would work with your ES3_1 functions if possible. The trick is, it seems to be more of a thing with QOpenGLShaderProgram (being able to compile/link the tessellation shaders), so maybe it is more involved than just loading up a few extensions.

          • Trent Post author

            I would say that since an ES 3.1 addition is not guaranteed, don’t count on it. Even if 3.1 GLES headers are added, I don’t know if the Qt team would mandate that all possible 3.1 extensions be added to the list of extension structures. (If they added support for this extension, why not all other extensions?)

            The good news is that even without the 3.1 headers and extension structures, these things can still be accomplished by any programmer in the traditional OpenGL way. It may not be ideal because we’re not using a provided interface any more (less convenient), but I’ve found that I’ve had to do that anyways for many aspects of my own application (either the interface Qt decided upon wasn’t up-to-snuff, or the interface simply wasn’t there). (Hint: Look into setting your minimum Android NDK to a version which requires GLES 3.1, then have some common header in your project which will include the gles31.h header – BAM! GLES capabilities!)

            The cleanest solution in your case is to add the classes you need that Qt doesn’t provide (as I did with OpenGLFunctions_ES_3_1, and the like), and make them as “Qt” as possible if you want to continue to use a nice interface. I simply remove the “Q” when making my own classes. See that OpenGLFramebufferObject and OpenGLUniformBufferObject are examples of both of these cases. I only implemented what I needed – but I kept the interfaces similar to Qt OpenGL interfaces.

            Honestly though, if I were in your shoes, I would first ask “is tesselation necessary for the end user?”. If it just provides a higher level of detail, I would first do the dumb solution of: Desktop = Tesselation, GO!
            Embedded Systems = Tesselation, NO!
            And then if you find that you even need to add the tesselation capabilities to ES, simply add the classes/structures required, and do the little runtime check for hasExtension() and make the ES version utilize the extensions. Even if it’s possible to do tesselation, if it’s not necessary I might still make it toggleable for the end-user in case their machine chugs a little when rendering your game or app or whatever it is utilizing tesselation. (Though it’s hardware accelerated, it’s still work – is it work every user will want to do?)

            If it’s just 100% necessary, think about either making it not necessary (since not all 3.1 hardware is required to support it) – or think about just not supporting OpenGL ES (does your application need to be on mobile?).

            If it’s 100% necessary, first look into whether or not you can even mandate users must support the extension from the Google Play Store. I would not want users who don’t support it download your app, even for it to gracefully fail and have a pop-up stating “You don’t support the required XYZ extensions, your device cannot run this application.” I have no experience with this, so I can’t really speak about it. If this is possible, then look into the custom extension support as mentioned above.

            Qt doesn’t do anything special, it’s just an abstraction of OpenGL. You might need to fiddle with your own classes, but as long as you’re clean about it you can still “extend” the API yourself with your own custom classes. Might not be the answer you were looking for, but I hope this helped a little.

            If there are any developments on the 3.1 ES API I will make a post about it, though. ;)

  • Dan

    Great insight, Trent. After taking a look at your QtOpenGL project (guess I didn’t look hard enough before to find it), it all makes much more sense how to achieve this. Even if you don’t get the ES31 functions in official Qt. The reason for using tessellation on all platforms is really just for ease of coding (not implementing a fallback version for devices without tessellation). So I’ll find a way to gracefully blacklist users without the proper hardware.

    Once Vulkan becomes a thing, I’ll probably end up converting over to that which should put this whole dilemma away. It will probably take a while for any fancy Qt classes to be made around the new API, but I’m guessing there will be a way render from Vulkan into a QSGNode texture or something.