3D graphics, a beginners mind.
In this tutorial I would like to show how 3d graphics is done today, why it's important, and how it will change the way you see 3d graphics applications.
To better understand, we'll end up creating a 3d engine with Python.
I expect you to be familiar with Python, if you understand what
class means you're probably qualified in this department.
I also expect you to understand what the terms fov (field of view), vertex, mesh..etc. mean.
But 3d graphics is hard!
No, it's not. This is an awesome area of programming you'll be able to show to your friends, there will be math(s) involved, so strap in, but it'll all be explained. If there are aspects you do not understand, simply copy my solution.
To start, let's go through the basic building blocks.
There's more going on under the hood, this cube is made up of two things, vertexes and triangles.
Vertexes are essentially points in 3d space. Look around your room, and imagine a speck of dust, a single point in 3d space.
Triangles are, well just triangles, normal 2d flat triangles. However their three points are connected to specific vertexes.
On the above image of a cube, you can see there are eight points, these are the points which make up the cube.
In memory, these points each have a 3d coordinate: X, Y, Z axis. however when we go to render the cube, we map each 3d coordinate to 2d screen space. And the way we do that is surprisingly simple.
As you can see, a triangle is now connected to three of the points. Do this 12(*) times and you'll get a cube.
*: A cube is made up of 6 faces, however to make a face with a triangle, you must use two triangles, so it ends up being 12 triangles.
Enough "fundamentals", more coding!
Alright, now that we understand the basic structure for rendering 3d shapes. Let's get more technical. We'll be doing this in
Python (with Turtle).
First, we import Turtle, I will assume you already know how to use Turtle and are familiar with it's functionality. In short, it's just a graphics library aimed at kids learning how to code with graphics, and making flowers and all sorts of things... Except we'll be going much further than flowers.
Next we need to store our object data. We need to store all our vertexes and triangles.
For now, we only have one triangle connected to the first three points.
Our basic main loop
We want to simulate a normal graphics library with turtle.
Our program will follow this structure:
Alright, now we need to somehow map these 3d vertex coordinates to 2d screen coordinates.
To do this, let's use the Perspective Formula. Before we dive into the details of what exactly this formula does, let's start with an observation.
Place an object in front of you, for instance a cup. As you move away, the cup shrinks; now this is all very obvious, but it is an essential property of 3d space we must consider.
When we're creating a 3d engine, what we're doing is simulating this observation. When we move away from our objects, that is - the Z axis, we're essentially converging the X and Y axis toward zero.
So what is this "formula"?
Where x, y, z are vertex coordinates.
We can simplify this to:
So let's add
FOV at the top of the file:
Drawing the points
Let's iterate through each vertex:
But where are our four other points from before? The ones behind?
The issue is we're inside the cube, we need to move the camera out.
Alright, I won't go into the camera in this tutorial, you can look at my repl at the bottom to see how to properly implement a 3d engine, but we're taking baby steps here.
When we think of moving the camera, we think of the camera object moving, simple right? Well that's not easy to implement in a rasterized renderer. However what's easier is to move the world around it. Think about it, either you can move the camera, or move the world; it's the same effect.
As it turns out, it's a lot easier to offset the vertex positions than somehow change the perspective formula to equate the position; it would be a whole lot more complex.
So quickly solve this, let's move the camera out:
And adjust the
FOV to say,
To draw triangles, consider this code. By this point you should be able to understand it:
To rotate our object, we'll be using the Rotation Matrix. It sounds scary, right?
If you're familiar with linear algebra, you should already know this, but the rotation matrix is commonly defined as:
0 as theta
I won't go into detail of the matrix. If you're unfamiliar, feel free to either research or copy & paste.
To implement this, we'll first need the
Let's make a function to rotate:
Then let's place this before we do our perspective formula calculations:
Let's make the rest of the triangles:
Let's initialize a counter at the start of the file:
and increment this at the end of every loop:
And replace our rotation function:
It's rotating, awesome!
To rotate on the X, Y and Z axis:
Before you read, I recommend you do read through the above, I know it's easier to just skip down to the bottom for the solutions.
However, if you're here after reading through the above, feel free to post
Full read in the comments as a token of my respect, and feel free to copy this code =)
If you want to see an expanded and better written version: https://repl.it/@CoolqB/3D-Engine
If there's demand I will perhaps dive into shading, lighting, culling, clipping and even texturing.
If you've got any questions, fire away in the comments.
anytime i make an update using this, it always gives me the same error "Unindent does not match any outer indentation level". Just by adding one line that prints something in the console throws me that error...
Heres my code:
This is awesome! I finally understand how to make a 3D engine.
Also, full read.
One thing I'd like if you'd update though, it would be extremely annoying having to manually write all of the triangles, maybe you could make a function that automatically generates a list of those triangles, so I can just do
TRIANGLES = genTris(VERTICES)?
Also, the plural form of
vertices, just saying ;)