~ The Basics of 3D Games ~

* Intro

* What is a 3D object

* How to build 3D objects

* What is a 3D World

* What is a 3D Camera

* Render 3D World on 2D screen using Shaders

* Conclusion


Intro

Hello again! I'm Nick, a software engineer and hobbyst game developer. Recently, I decided to share the experience I've gained over several years of developing video games. I hope you will find it useful!

In this article, I'll cover the basic concepts of creating 3D games. Unlike most 3D engines, such as Unity, Godot or UE, Gamemaker does not have extensive 3D support. To display any 3D object, you need to assemble it yourself and run it through a special shader. But first things first.

What is a 3D object

Important!
Any 3D object is a sequential set of vertices which is called Vertex Buffer. Each vertex includes a certain sequential set of information, such as - a position in the world space (X, Y, Z coordinates), normal vector, texture UV-coordinates, and so on. The rules that specify what must be stored at a vertex are called Vertex Format.

To display any 3D object, all vertices must have at least a position in 3D space - X, Y, and Z coordinates. The X, Y, Z values are represented by floating-point numbers (like 10.5).
For example, let's take four vertices storing coordinates shown in the image below. Using them, we will assemble the simplest square (yes, it can also be in 3D, although it's an ordinary plane).

Vertex explain 1

How to build 3D objects

Great! We have vertices. But to make a full square we still need edges. And here the order in which the vertices are stored in the Vertex Buffer begins to play a role.

Before sending our square to the renderer (GPU), we must tell the computer what primitive it should build - points, straight lines, triangles, or quads. There are others, but we'll limit ourselves to these for now.

After that, the GPU will sequentially go through each vertex in the order in which they are in the vertex buffer and build the required primitive.

To better understand what I'm talking about, let's index the vertices in the order in which they would be in the buffer, and see what the result will look like with different primitives.

Vertex Lines Vertex Trian Vertex Quad

Make a point!
Yes, I specifically skipped the point primitive, because it will render... points. This primitive is used in quite rare cases, for example, to display some particles, build dynamic geometry or in point clouds for 3D mapping.

Note that if we try to construct a triangle, the extra vertex will simply be discarded.

Cool! It would seem that in order to build a square we should use the Quad primitive. But there is a nuance that concerns the GameMaker engine... It still does not support this type of primitive. *facepalm*

So what to do now? The solution is very simple - a square can be built from two triangles. We will have two triangles - with vertex indexes 1, 2, 3 and 1, 3, 4.

But for this we will have to copy vertices 1 and 3 to the vertex buffer. So instead of 4 vertices in the buffer we will have 6.
The final sequence will be as follows - 1, 2, 3, 1, 3, 4.

Make a point!
Someone might ask:
Why did you choose the sequence 1,2,3 and 1,3,4 instead of, for example, 3,1,2 and 4,3,1? After all, they also form the same triangles!
And that's right. However, whether the triangle is formed clockwise or vice versa is very important for optimizing the rendering of 3D models. I will talk about this in another article, which will be released later.

In fact, the triangular primitive is the most common in 3D graphics. From it, you can build an object of almost any complexity. And the difference between 4 and 6 vertices is not great.

But what if we're going to load an object into the game that has 100,000 triangles? A lot of the vertices will just be repeated, taking up extra GPU memory. For this purpose, an another buffer was invented - the Index Buffer.

Important!
Index Buffer is a sequential set of vertex indexes (integers) in the vertex buffer. If you attach it during rendering, the GPU will sequentially take vertices according to this buffer. And there is no need for duplication or sequence of vertices in the vertex buffer.

This seems to be the solution. But developers who use GameMaker engine are again faced with a problem - index buffers are not supported either. *facepalm x2*
And unfortunately, this is something you will have to live with (at least for now). So choose your models wisely, look at how many vertices they contain, and what these vertices store.

What is 3D World

World explain 1
Important!
3D World is a certain space where 3D objects are located. The vertex coordinates are the position of the vertex if the entire 3D object were at coordinate X=0, Y=0, Z=0 in the 3D World! The transformation of each object is specified by translation, rotation and scaling matrices.

Perhaps many people are not familiar with matrices and mathematics in general, so I will try to explain everything as simply as possible. For example, to place a 3D object somewhere in 3D space, it must be moved to the desired position. This is done by shifting the X, Y, Z coordinates of all the vertices of the 3D object.

In simple terms, it is just a matter of summation. The scaling and rotating operations are a little more complex to calculate, but can still be reduced to a combination of sequential additions and multiplications of numbers.

That's why the great mathematicians of the past came up with such a thing as matrices. And to perform any transformation of a coordinate, it is enough to multiply it by the necessary matrix. Sounds much simpler, doesn't it?

For those who still have no idea what a matrix is, it is a set of numbers arranged in a certain way in a certain container (array). Yeah, many resources present them as a two-dimensional table with rows and columns, but in GameMaker matrices are stored as a one-dimensional array.

We will talk about what numbers are in matrices and how to create them next time, since this is a rather broad topic. And don't worry too much if this seems difficult to you. In the engine itself, all the matrices are in fact created by calling a very simple single function.

Great. We have vertices from the Vertex Buffer, and translation (T), rotation (R) and scaling (S) matrices. It is clear that we need to multiply. But in what order?

Any combination of the order S * R * T gives a valid transformation matrix. However, it is pretty common to first scale the object, then rotate it, then translate it. Matrix multiplication is written right-to-left, so we will have the following formula:

Important!
New vertex location = T * R * S * Vertex position

If you do not do it in that order, then a non-uniform scaling will be affected by the previous rotation, making your object look skewed. And the rotation will be affected by the translation, making the final position of your object very different from what the value of the translation would make you expect.

What is 3D Camera

Now we have a concept of what 3D Objects are and how they will be located in the 3D World of your future super-cool game. But to see our objects and our world, we need an Observer. In videogames, the Camera plays that role. It's usually placed inside or near the game character's head, so it feels like we are playing him. But what is a camera from a developer's point of view?

Important!
3D Camera is a combination of two matrices - View matrix and Projection matrix.
The View matrix determines a position of the observer and where it is looking.
The Projection matrix determines how the world that the observer sees will be displayed.

To create a View Matrix, we need to know the following:

The Up vector is needed so that our image of the world is not being upside down. Usually this vector is directed straight into the sky of our game world.

To create a Projection matrix, we need to know the following:

The projection Width and Height are usually equal to the dimensions of the game window/screen.

The Near and Far Clipping Distances are distances between the camera position and imaginary planes that perpendicular to the Forward (look) vector of the camera. Anything closer to the camera than the near clipping distance isn't displayed (it's too close), and anything further away from the camera than the far clipping distance isn't displayed either (it's too far away).

There are two types of projections: orthographic and perspective.

Ortho Projection Orthographic projection (from Disgaea 2)

Perspective Projection Perspective projection (from Anno: Mutationem)

In Orthographic projection there is no visible depth effect: all objects appear as if they are at the same distance from the camera.
In Perspective projection the size of objects depends on the distance to the camera: the further away, the smaller they are. This projection is similar to our vision, or the lens of a real camera.

Also for perspective projection the angle of view is important. The larger it is, the more objects will fit into the camera view. For this purpose, games often use the so-called FOV.

Perspective explain
Make a point!
In general, the Near and Far Clipping Distances are enough to define the perspective projection matrix, but it is much more convenient to define it through the FOV angle and the aspect ratio of these planes (e.g. Far Distance / Near Distance).

Render 3D World on 2D screen using Shaders

Shaders. For me, and probably for many others, the mere mention of them made me break out in a cold sweat. That's why people came up with a technology called Shader Graph, but alas, Gamemaker doesn't have it.

From my experience, I can say that you can only learn to write shaders in practice. Which we will do in the next article. So now I'll briefly explain how shaders connect our game world and the actual game window that the user will see.

Important!
Shaders are programs running on the GPU to which we pass data of our 3D objects and the Camera, resulting in a 2D image that is saved to the framebuffer.
The framebuffer is the frame that will be displayed on the player's screen.

Conclusion

Perspective explain

In this article I went over the basic theory of creating 3D games, sometimes touching on the features of the GameMaker engine.

In the next article, I will explain the practical part - I will show how to implement our first 3D game, with code examples.

In Progress

Thank you for reading!

If you liked the article, you can share it and/or write your impressions and wishes to me personally in the guest book.

@Nick_Nishort

Go to home page