Reader small image

You're reading from  C++ Game Animation Programming - Second Edition

Product typeBook
Published inDec 2023
Reading LevelN/a
PublisherPackt
ISBN-139781803246529
Edition2nd Edition
Languages
Tools
Concepts
Right arrow
Authors (2):
Michael Dunsky
Michael Dunsky
author image
Michael Dunsky

Michael Dunsky is an educated electronics technician, game developer, and console porting programmer with more than 20 years of programming experience. He started at the age of 14 with BASIC, adding on his way Assembly language, C, C++, Java, Python, VHDL, OpenGL, GLSL, and Vulkan to his portfolio. During his career, he also gained extensive knowledge in virtual machines, server operation, infrastructure automation, and other DevOps topics. Michael holds a Master of Science degree in Computer Science from the FernUniversität in Hagen, focused on computer graphics, parallel programming and software systems.
Read more about Michael Dunsky

Gabor Szauer
Gabor Szauer
author image
Gabor Szauer

Gabor Szauer has been making games since 2010. He graduated from Full Sail University in 2010 with a bachelor's degree in game development. Gabor maintains an active Twitter presence, and maintains a programming-oriented game development blog. Gabor's previously published books are Game Physics Programming Cookbook and Lua Quick Start Guide, both published by Packt.
Read more about Gabor Szauer

View More author details
Right arrow

14

Creating Instanced Crowds

Welcome to Chapter 14! In the previous chapter, we explored the tech side of inverse kinematics. Using inverse kinematics, constrained movement of models can be made more natural-looking, such as climbing stairs or holding artifacts in their hands.

In this chapter, we will add more virtual people to our virtual world. We’ll start with a brief overview of the right way to add multiple instances of the glTF model, as naive duplication raises a lot of problems.

Next, we’ll split the model class into two parts, one for the shared part of the model data and the other one for the individual data of every instance on the screen. Moving the instance data to a separate class allows full control of every single model instance we draw on the screen.

Then, we’ll extend the code to allow more than one model type and look at a GPU feature to let the graphics card do even more work while drawing instances. At the end of the chapter, we...

Technical requirements

To follow along with this chapter, you will need the OpenGL and Vulkan renderer code from Chapter 12, plus the parent node changes in the GltfNode class in Chapter 13.

Splitting the model class into two parts

Right now, the code is made to show only a single glTF model. The options to show the model or the skeleton, the drawing settings, and the animation properties were created to support one and only one model on the screen. To render multiple models, we must adjust the application code.

In a naive solution, we would simply loop over a vector of glTF models and do all the preparation and drawing steps for every model. This way of drawing models works, but the loading and data extraction phases will take a lot of time and waste space in the main memory, as we need to add vertex data and animation clips to every single model.

To achieve proper instancing, we will split the model class into two separate classes. The original GltfModel class will keep the shared data for all instances, and a new GltfInstance class will maintain the variable per-instance data.

The full code for this section is available in the chapter14 folder, in the 01_opengl_instances...

Rendering instances of different models

First, we need a small extension of the GltfInstance class, a getter for the saved glTF model. The declaration in the GltfInstance.h file and the implementation in the GltfInstance.cpp file in the model folder are trivial, so we can skip a listing.

More interesting are the changes to the OGLRenderer class. Change the declaration of the private member variable storing the glTF model in the OGLRenderer.h file in the opengl folder to std::vector:

    std::vector<std::shared_ptr<GltfModel>> mGltfModels{};

Also, add two new vectors to store the pointers to the instances using joint matrices or dual quaternions:

    std::vector<std::shared_ptr<GltfInstance>>
      mGltfMatrixInstances{};
    std::vector<std::shared_ptr<GltfInstance>>
      mGltfDQInstances{};

Due to the possible mixing...

Using GPU instancing to reduce data transfers

By using so-called instanced drawing, the graphics card duplicates the vertex data for the model instances by itself. All we must tell the GPU is the location of the vertex or index buffer data to use, and the number of instances to create.

The normal call telling OpenGL to start drawing looks like this:

void glDrawElements(drawMode, indexCount,
 componentType, indices);

For the drawing mode, we are using GL_TRIANGLES to draw triangles, defined by groups of three vertices. The index count is the number of entries in the index buffer. If some vertices are shared between triangles, the number of index entries may be lower than the overall number of vertices. Depending on the amount of index entries, the component type could be a byte, a short integer with 16 bits, or an integer with 32 bits. As we are using an index buffer to store the index elements, the last parameter is nullptr.

The call to tell OpenGL to create more than one...

Textures are not just for pictures

In the previous chapters, we used two different methods to upload larger amounts of arbitrary data to the GPU: in Chapter 4, we added uniform buffers, and in Chapter 9, shader storage buffers were introduced. The push constants for Vulkan are not added to this list because of the limited size of only 128 bytes.

Uniform buffer objects, abbreviated to UBOs, were introduced in OpenGL 3.1. UBOs can contain data shared across all shaders, ideal for uploading central data such as matrices or light parameters. But alas, the minimum guaranteed size of uniform buffers is only 64 KB, a limit one could reach quickly on complex virtual scenes.

Also introduced in OpenGL 3.1 were texture buffer objects, or for short, TBOs. Technically, a TBO is closely related to a texture, but it is not backed by an image like a real texture. Instead, a separate buffer is bound to the texture unit, and every texel of that texture can be read by its position. The value is...

Summary

In this chapter, we upgraded our renderer from showing only a single model to rendering a larger crowd of models.

First, we split the GltfModel class into two parts, adding a new GltfInstance class for the instance-specific variables and methods. This split enabled us to enhance the renderer to draw many instances of the same model. Next, we upgraded the renderer to draw instances of different models on the screen.

Then, we used the code of the first example with the split of the model and the instance class as the basis and added GPU-side instancing to the code to offload the drawing of the instances to the graphics card. Lastly, we explored TBO as an alternative way to transfer data to the GPU.

In the next chapter, we look deeper under the hood of the created application. We had to add an optimization in this chapter, but there is much more to explore and check on the CPU and GPU sides to make the application even faster.

Practical sessions

You may try out the following exercises to get a deeper insight into rendering multiple instances of glTF models:

  • Enable the dynamic addition of new instances. While the addition of a new instance to the std::vector array is easy, the buffer sizes require more attention. You need to check for a sufficient size and re-create or adjust the GPU buffers.
  • Add more than one model per instance on the screen when using GPU-instanced rendering. You could calculate the joint matrices and dual quaternions normally but add multiple GltfInstance models with the same buffer data while altering the world position and rotation values. This addition would create a much larger crowd with the same amount of CPU load. Think of thousands or tens of thousands of models jumping on the screen. Due to the spacing between the models sharing the animation clip and animation replay speed, the crowd will still look random.
  • Medium difficulty: Add both the non-instanced and instanced...

Additional resources

lock icon
The rest of the chapter is locked
You have been reading a chapter from
C++ Game Animation Programming - Second Edition
Published in: Dec 2023Publisher: PacktISBN-13: 9781803246529
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at AU $19.99/month. Cancel anytime

Authors (2)

author image
Michael Dunsky

Michael Dunsky is an educated electronics technician, game developer, and console porting programmer with more than 20 years of programming experience. He started at the age of 14 with BASIC, adding on his way Assembly language, C, C++, Java, Python, VHDL, OpenGL, GLSL, and Vulkan to his portfolio. During his career, he also gained extensive knowledge in virtual machines, server operation, infrastructure automation, and other DevOps topics. Michael holds a Master of Science degree in Computer Science from the FernUniversität in Hagen, focused on computer graphics, parallel programming and software systems.
Read more about Michael Dunsky

author image
Gabor Szauer

Gabor Szauer has been making games since 2010. He graduated from Full Sail University in 2010 with a bachelor's degree in game development. Gabor maintains an active Twitter presence, and maintains a programming-oriented game development blog. Gabor's previously published books are Game Physics Programming Cookbook and Lua Quick Start Guide, both published by Packt.
Read more about Gabor Szauer