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

4

Working with Shaders

Welcome to Chapter 4! In the previous two chapters, we created renderers for OpenGL and Vulkan, but we addressed only the application part of the drawing: how to store the data in a simplified model and how to copy the data over to the GPU via a vertex buffer.

The GPU needs to know what to do with the data too. We must tell the graphics card in which format, sizes, and order the data for the vertices arrives, whether and how we would like to transform it, and how to apply colors or textures to the objects we sent.These steps are done in so-called shaders, which are small programs running on the compute units of your graphics card.

In this chapter, we will take a deeper look into the basic functionality of shaders. You will learn more about the way data is sent to the GPU – the vertex data itself, and additional data, such as transformation matrices.

We will also discuss the OpenGL Mathematics (GLM) library, allowing you to use the same data...

Technical requirements

For this chapter, you will need the OpenGL code from Chapter 2 and the Vulkan code from Chapter 3.

We will start with an overview of the shaders themselves: what they are and how they are used.

Shader basics

Today’s GPUs are powerful computing units. While the main job of older graphics cards was just to display the graphics memory (consisting of 2D images of the windows and their contents), the evolution to 3D has shifted some tasks from the CPU to the GPU.

The main “workhorses” in a GPU are the shader units. These are small and simple processing units with a limited instruction set, compared to the system processor. However, they utilize large registers and can operate on more than one data value at once, calculating multiple results in a single step. This is called Single Instruction, Multiple Data (SIMD). You may also have heard terms such as SSE as being included in your CPU (older readers may also remember the predecessors, MMX and 3DNow!). With SIMD, each one of the registers can load more than one value, usually two or four of them. Mathematical operations, such as multiplication or addition, are done on each pair of values in two registers...

GLM, the OpenGL Mathematics library

One important limit when working with OpenGL and Vulkan is that all data must be available in GPU memory so the graphics card can access it directly. We must copy the information about every vertex to the memory of the graphics card, including the vertex position, color, texture coordinates, and more. All or part of this data may be copied in every frame to the graphics card, so the fastest way to copy the data to the GPU memory is a simple memory copy command. In C and C++, this command is called memcpy. The compiler may utilize the best fitting internal method to achieve the data duplication, such as by using the large SIMD registers on a modern CPU.

But a question arises from this transfer: how do we ensure we make a simple copy, without having to touch and adjust every data element?

This may happen if the data is stored in the system memory in a different format compared to the GPU memory. It may differ in element sizes or the alignment...

Vertex data transfer to the GPU

The basic data flow in the graphics pipeline is shown in Figure 2.1 of Chapter 2. The input of the first shader stage is defined by you, the programmer. There are some rules and limits, but mostly it is between “nothing at all” in simple Hello World tutorial code, and extraordinarily complex, structured, and interleaved vertex data from games and 3D applications. As already stated at the start of the GLM, the OpenGL Mathematics library section, all data must reside in the GPU memory to be drawn.

We will copy the vertex data of the 3D models to the GPU by using vertex buffers. These are, like all other buffers, just parts of the GPU memory – in this case, dedicated to storing vertex data in it. Other methods exist, and we will talk about one of them in Chapter 14, where we will use textures as data storage instead of vertex buffers.

The OpenGL renderer from Chapter 2 and the Vulkan renderer from Chapter 3 already contain vertex...

Switching shaders at runtime

In a real 3D renderer, you will need a lot of shader switches. The final picture is often created in many consecutive rendering steps, and the drawing operations need to reflect the tasks to be done in that single step. The vertex data sent to the GPU may be the same among many of the shaders, as changes in the data structures would require expensive transformations or lead to duplicate data storage with another set of attributes. But in many cases, the rest of the shader code itself will be completely different, depending on the input to that shader and the expected output.

Adding new shaders is done in only a couple of steps for the OpenGL renderer, so we will walk through the changes here. The Vulkan renderer needs different adjustments, and we will only check the broad steps.

The full example code can be found in the chapter04 | 01_opengl_shader_switch folder.

Creating a new set of shaders

The first step is to have multiple shaders available...

Sending additional data to the GPU

The data sent from the vertex buffer has one important drawback: it changes for every vertex. If we need some sort of values to remain constant during a frame, such as a transformation matrix, we must take a different approach. Using the CPU for this has already been ruled out as it would be too expensive and time-consuming, so we will utilize the GPU for this task.

Both OpenGL and Vulkan have a special type of buffer for this use case: the uniform buffer.

Using uniform buffers to upload constant data

Uniform buffers have two important properties:

  • They are shared among all shaders on the graphics card
  • They are read-only inside the shaders

Any data, such as the aforementioned transformation matrices, needs to be uploaded only once per frame before the drawing starts. In the shader code, the data can be referenced like local variables, but in contrast to vertex positions, colors, and so on, it is the same for every vertex...

Summary

In this chapter, you learned about some of the methods to move data from your application to the graphics card so that shaders can use it and draw the triangles on the screen. While the data inside the vertex buffers changes for every vertex, uniform buffers and push constants enable you to upload tiny amounts of constant data to the shaders. This enables you to offload the transformation work (such as scaling, translating, or perspective changes) to the shader units on your graphics card. By using GLM for the vertex data, you avoid expensive transformations during the process of copying the vertex data to the GPU.

In Chapter 5, we will add some fancy UI elements to the rendering window. This will allow us to have critical data at hand, such as the frame rate or various timings inside the application. By using buttons, we are also able to switch options on and off using the mouse. For many operations, using a mouse is more convenient compared to remembering all the keys...

Practical sessions

As in the previous chapters, here are some additional suggestions on what to do after you finish reading this chapter:

  • Add more vertices to the mockup model object, and create a fully textured, six-faced cube. Check for the face orientation of the single triangles, as they may be in the wrong direction and create holes in the cube. Also, watch out for the proper orientation of the texture (i.e., add some text to the texture and try to have it readable on all six faces). This will add a lot of duplicated code, though, but in Part 3 of the book, we will load real models from files.
  • Duplicate the model after creating a cube and try to transform and concatenate the vertex data to draw all models at once. You could create three or four cubes, translate them to some place in 3D space, and rotate them around their center. Check out the differences between local rotation and translation, and global rotation and translation, and observe the results if you change...

Additional resources

For further reading, please check the following links:

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