Skip to content

the-akze/rendering_engine_3d_from_scratch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

3D Rendering Engine from Scratch

Made by Akshat S

Demo:

demo image demo animation

Used C++ and linear algebra to implement meshes, linear transformations, rasterization, a custom math library (vectors, matrices), a camera system, and lighting to render 3D geometry. I developed this from scratch, start to finish, to deeply learn how 3D rendering engines work and practice low-level programming where optimizing memory and efficiency was important.

The 3D rendering engine’s process of rendering can be broken down into 3 parts: data, transformations, and rasterization. The data part involves how the data is stored, such as vertices, objects, the world which contains the objects, and the camera from which the scene is rendered. The transformations part consists of using linear algebra to figure out based on the stored data where on the screen the objects should be drawn. Finally, the rasterization consists of using the transformation’s data of the screen-space positions to determine what color should show up at each pixel on the screen.

Before explaining each of these steps, it is important to understand a few terms. The first terms include “screen-space,” “object-space,” and other terms that end with “space”. This simply means a position stored relative to said subject. For example, world space means the position relative to the world, and screen-space means the position on the screen.

To store the data, we have a world which contains 3D objects, and every object has an associated position, rotation, scale, and mesh. The mesh defines the actual shape of the object, that is, the position of vertices in object-space and the associated vertex groups that define faces, which are stored in groups of 3 to form triangles. The camera is stored similar to an object except it has a field of view attribute and no associated mesh.

Next, the engine uses linear algebra, namely matrix multiplication, to perform transformations of meshes first from object-space to world-space, world-space to camera-space, and finally camera-space to clip-space. Clip space is a space where all objects that are to be rendered onto the screen are inside a unit-sized cube at the center of the coordinate system, and are smaller as they get further along the z-axis. To help visualize clip-space, if the clip-space meshes were viewed from an orthogonal camera at the origin, it would look the same as the world-space objects viewed from a perspective camera at the camera’s position. In simple terms, clip-space meshes are stored in such as way that when rasterizing (drawing the pixels), only offset and scaling of the coordinates on the x and y axes needs to be done to figure out where on the screen to draw.

Finally, the rasterization algorithm takes place. For each triangle in the scene, the engine loops through all pixels of a bounding box of the triangle and uses a triangle function to determine if the pixel is inside the triangle. Then, it uses barycentric coordinates to figure out the color and depth at that pixel. The depth is used to determine if a pixel should be drawn or if there is already something in front of it, and if the pixel should be drawn, the color is written to that pixel in the buffer. The buffer is an array of every pixel’s color, which is used at the end of all the rasterization loops to output the final frame.

All these steps are performed repeatedly to create an animated render.

About

a 3d rendering engine made from scratch

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors