Mathematics for Game Development and Graphics Programming - Study Note

Created on: 21 Jun 23 20:02 +0700 by Son Nguyen Hoang in English

A study note created after a month self-studying Mathematics. The book's author is Penny De Byl, a well-known lecturer and teacher on Unity Development.

For a month, I have been trying to study Math for Game Development (mostly Graphics Programming) from a book written by Penny De Byl. The book quality is average at best, but at least it provides basic information on Math and other stuff. The tutorials in the book were mostly done in Python. Here is my quick study note to summarize what I learned from the book. First, I will make a short review for the given content:

  • The first chapters are good, while the last chapters were a bit harsh. I cannot compile my code in the last tutorials, even though I tried to use the code supplied by the official GitHub repository.
  • The explanations & discussion on OpenGL need to be more detailed.
  • While using PyGame, most features of the engine are not used.

img1
Book Thumbnail

This book is just an introductory so don’t expect deep analysis and explanations. There is a lot of code/snippet that comes up from now with very little elaboration. The math stuff is good actually, I took these math lessons as opportunities to relearn old concepts.

Below is the short study note from the book. The order of content is listed based on my ordering, which is entirely different from the listing order of the book itself. In the below section, I skipped several parts, such as:

  • Math on Triangles, sin, cos, tan, … . This basic math is no trouble to me.
  • Vectors
  • Line equation
  • Plane equation
  • Normal vector for the plane

Also, I skipped the part on Quaternion, as this topic is very advanced and required its own research & deep understanding.

Basic Functions of PyGame:

The books introduce standard API from the PyGame library/engine:

  • Making a game window and game window size.
  • Rendering a point, set color for any points.
  • Running the main game loop.
  • Getting input from the console (button key, mouse click)
  • Handle in-game clock, and fps capping. These API are mostly standard so that nothing to be discussed here.
Small but great algorithms

Have you ever wondered how a straight line be rendered on the window screen? Given the equation of a line is ax + by + c = 0; What strategy will you apply to draw this line on the screen?

The naive solution would be to make a loop from two points x0 to x1; for each x value, you calculate the corresponding y the plot the point to the screen. This solution has a big issue: It does not guarantee the displayed points are next to each other. If y = 100x; then from x0 = 0 to x1 = 1, the two values of y will be 0 and 100. If these values are plotted on the graph, they will create a big gap between the two.

To solve this problem, the solution is Bresenham’s line algorithm. The idea would be instead of making a for loop from x0 to x1, we create a while loop that loops from x0 to x1. For each iteration, select a pixel on the screen that is 1-pixel away from the previous pixel (originally x0, y0). The way we choose this pixel is that the exact coordinate of the point must be the one that is closest to the pixel we choose.

I solved the algorithm & prove it by hand. The only trouble is that I couldn’t understand the best, optimal solution provided in the paper of Zingl, Alois (2012), A Rasterizing Algorithm for Drawing Curves. Although the algorithm is fascinating, the explanation the author is very lacking & confusing as well.

A similar problem when applying the naïve solution also exists when drawing a circle. For a circle, we more or less follow the same principle: choose the closest pixel that is nearest to the actual point. However, for a circle, as the shape of a circle is very symmetric, we can draw one octant of the circle and then apply the same calculated point for other octants, albeit modify a bit (for example, the pixel chosen for the octant of 45 degrees to 90 degrees can be copied for the octant of 90 degrees to 135 degrees, with the x value is opposite).

The “Bresenham’s Algorithm” for drawing a circle is easier to prove by hand than the line algorithm.

img0
Image from Zingl, Alois

How to Render Object?

Some keywords:

  • The data of any 3d object is a collection of vertices – or points. From these points we create triangles, from the triangles (or polygons), we render the mesh. So basically, we need vertex and triangles to render the mesh.
  • From the object, we have local coordinates, these are then computed in World Space to have World Coordinates. From Coordinates, we project it to the screen space. Screen space is the actual place where the human eyes observe the mesh.
  • Placing texture to object using OpenGL functions.

One important factor to consider is the cameras, two types of cameras: perspective and orthogonal (or parallel). Real human eyes have frustum that make parallel lines toward the eyes not exactly paralleled (but collided with the eye position). Vice versa in a parallel camera everything is exactly parallel. Another important factor is the** Field of View** – the areas that cameras can see (appearing as a curve), of course, there are vertical fields of view & horizontal fields of view.

The concept is culling is also important here. We don’t need to render the mesh as detailed as the original model, because often, there is a part of the model that could not be seen/observed from the eyes. From a normal vector, we can perform culling to reduce efficiency.

img2
Image from the book

Game Engine Basics?

The book illustrates a great concept of a game engine with a components system, similar to Unity Engine. We have an Object class. Each instance of this class has a list of Components. Also, each of them has an Update function that will be called frame by frame.

The component list will be reused extensively to contain standard components such as Transform, Mesh, … In the Objec.Update() function, for each frame, we loop through this list and then perform a function based on the component type. For example, we can use isInstance(component, Transform)(to check for component type) then component.update_position() to update position from Transform.update_position().

Also, to make a simple GUI, we store two object lists, one for 3d objects, and one for 2d objects. The 3ds is rendered first, then the 2ds on top of the perspective camera.

Matrix, and why we should care about it?
  • From my definition, the matrix is a collection of data. Since this is a collection of data, computing on the matrix is more efficient than calculating normal data. One example could be calculating new position, rotation, and scaling of Transform by combining all of these data into one matrix.

  • Matrices are also effective in rendering pipelines. Matrix is used extensively to render objects, mostly for coordinate conversion, from the polygon to model view, to projection, clip, viewport, and screen. Basically, from the original matrix of transform, this matrix is transformed to coordinate in world space, to projection then the screen (after clipping redundant pixels). Without a matrix, computing will be very annoying and troubling, as there are a lot of data to be projected: location, rotation, scale, … and also many coordinate systems to be calculated.

    img2
    Image from the book

On Shader

This section’s code cannot be compiled on my machine. Although I pasted the error log to the internet, it seemed like no solution could fix my scenario. Thus, I couldn’t run the code and only focus on the theory of the part.

The basic difference between GPU and CPU is that the former on specialized in parallel processing, while the CPU is aimed to do sequence processing. In theory, you can do sequence processing on GPU but not every program can run efficiently on GPU as they must be parallelable. GPU processes each vertex in parallel thus processing for many vertices is computed at the same time.

The order of shader integration is:

  1. Write shader Code.
  2. Compile shader code.
  3. Create a program for shader code.
  4. This program is used from material, which binds the shader program into an object.

Variable from CPU can be imported (or loaded) into shader: This can be super-helpful such as when you want to do shading effect (light effect). Developers can import “normal vector” of the plane into the shader and then calculate the shading value. Extra reading: Diffuse Light & Phong Shader.

On Realism Rendering

Realism rendering implies a set of techniques to simulate the rendering to be as close to real life as possible. This was done due to understanding the physics behind the light. Some important rules/keywords/laws when rendering realism are:

  • Energy Conservation Law: The total amount of light that enters a surface will be equal to the plus amount of light that had been absorbed and retracted from that surface.
  • The Inverse Rule: The brightness of light is inversely proportional to the square of the distance.
  • Bidirectional Reflectance Distribution Function (BRDF): A function that presents how the surface reflects the light. This is an advanced topic. Many functions have been invented to solve this problem.

BRDF relies on other functions such as:

  • Distribution function

  • Fresnel effect

  • Geometric attenuator factor

    img2
    Image from the book

These are all notes I think to be important takeaways from the book. Hope it can help someone and also me-in-the-future as well.

All Images in this article is collected from the book & internet.

Back To Top