This project implements a 3D renderer in pure CPU side pygame. It allows the user to quickly prototype 3D scenes and build simple 3D games while maintaining the simplicity provided by the 2D pygame library.
This is an example of a game made using this library. The example folder holds this game's code.
- Scene Class: The scene class manages all game objects. It allows the user to build a hierarchy system by providing the add_group and add_child methods. Additionally, this class handles obj file imports and rendering of the game objects.
- Camera Class: This class does the main 3D perspective projection and screen clipping math. It provides the user with methods to manipulate its position and rotation.
- Group Class: The group class acts as the main game object used to build a hierarchy system. It holds and manages faces and other group classes and allows rotation of the entire group around one pivot point.
- Face Class: Provides utility functions for a single 3D face and handles back face culling.
- Utility Functions: Include rotation, color and screen coordinate utilities.
- Documentation: A full documentation file is provided for ease of use.
The core part of the engine is the camera. The camera does all the perspective projection calculations that transform a 3D point in world space to a 2D point on the camera's screen space. First, the camera world coordinates are subtracted from the vertex's world coordinates to shift the origin to the camera center. Second, the vertex coordinate is rotated around the origin (camera) using the pre-computed sine and cosine values. This is done for two vertices of a face at a time to allow clipping points on the near plane. If one vertex is visible and the other is behind the near plane or vice versa, linear interpolation is used to find the point on the near plane. Third, the translated, rotated and near plane clipped points are divided by the Z coordinate (depth) and multiplied by the view factor, making them screen coordinates. This process is repeated for every vertex in the given face until the 3D face is translated into a 2D shape. For performance reasons, pygame.gfxdraw is used for drawing 2D shapes but this introduces a problem if the shape is partially offscreen. The vertices of the shape that are offscreen are simply clamped to the nearest screen coordinate causing distortions at the edge of the screen. To fix this a clip 2D polygon function was added to the camera class. This function implements the Sutherland-Hodgman polygon clipping algorithm and clips the projected shape to be within the screen.
View Projection Matrix: Instead of pre-computing sine and cosine values, a faster way to do 3D projection is to use a view matrix. This is how modern graphics engines handle 3D projections in GPU shaders.
Quaternions: Using quaternions to rotate objects would avoid gimbal lock which is present in the current rotation system.
- Clone this repository or download the code.
- Run
example.pyfor the the 3D rotating cube or runexample/main.pyfor Airplane Dash game demo
