![](https://github.com/h3nry-d1az/hngin/raw/main/assets/hngin-logo.png?raw=true)
An attempt to implement a 3D rendering engine based on mere intuition and my little knowledge of linear algebra.
ʜɴɢɪɴ is an attempt to implement a three-dimensional rendering engine from scratch, based mainly on my intuition, my limited knowledge of linear algebra and some research and information gathering that I have carried out in order to achieve certain functionalities.
As can be inferred from the current state of the project, it should be treated for the moment as a proof of concept rather than a stable and secure product, since there are still many functionalities to be implemented that are necessary in any rendering engine (see the to-do section), as well as some possible bugs to cover, improve the code in many aspects and write documentation for all the features and operation of the software (however, for a greater understanding of this aspect one can go to the intuition behind the engine section, or read my less rigorous, expository essay about three-dimensional rendering and its underlying algorithmics); it is also necessary to mention the detail that this program is written entirely in Python, which makes it relatively slow in comparison, although somewhat easier to read.
However, and as a result of the relatively important progress that I have been achieving in this project, I have decided to make it public and freely accessible in order to facilitate its use and to be able to receive suggestions, feedback and contributions from interested users.
Section table of contents
First, one has to clone the code repository on its local machine using git, to do this, use the following command:
git clone https://github.com/h3nry-d1az/hngin.git
Once the repository is cloned, proceed to move to the recently cloned directory using the command shown below:
cd hngin/
In order to run the demo program, you must have the pygame library installed, which is responsible for managing two-dimensional graphics. If you do not have the aforementioned module installed on your system, you can obtain it with the following command:
pip install pygame
For more information, one can go to the official website of the said library.
Once the previous dependencies have been installed, one can finally run the engine demo program. To do this, first go to the root directory of this project and run the following command in your terminal:
python ./main.py
Which should cause three windows to be displayed on the screen, a rectangular one (640x480), centered on the screen (approximately) and two smaller ones located right next to it, which processes the engine configurations.
The aforementioned windows should look as shown below:
demonstration.mp4
To compile the source code of the ʜɴɢɪɴ demo program to an executable binary, you will require the PyInstaller package on your machine, you can get it by running the following command:
pip install pyinstaller
Again, for more information you can go to the project website. Once you have the dependencies installed on your device, proceed to execute the command shown below to produce the desired binary:
pyinstaller --onefile --noconsole ./main.py
Precompiled binaries for Windows (from version v0.0.2 onwards) will be provided in the releases section.
Even though enough features have been implemented within the engine to have been able to make the decision to make it public, most of the functionalities that I would like to see covered and that I think would be feasible are not yet implemented. Therefore, below is the roadmap designed for the project (although without any date, it is more than likely that not all of them will end up being fulfilled):
- Achieve orthogonal projection.
- Achieve projection based on focal length and linear interpolation.
- Importing .obj files (vertices).
- Importing .obj files (faces).
- Fix visual glitches.
- Implement camera rotations.
- Implement a settings bar.
- Change the use of edges for faces.
- Optimize code and improve execution speed.
- Add solid colors to the models' faces.
- Implement the painter's algorithm for rendering order.
- Implement algorithm for direct lighting.
- Add support for textures in models.
(Almost) all progress on this project has been recorded and saved to the YouTube playlist "Motor 3D ʜɴɢɪɴ". Any contribution that helps carry out the above goals will always be welcome.
Warning
The following article is written regarding version v0.0.1 of the program and, therefore, all the concepts explained here are valid for the aforementioned version, since they may have undergone changes. To view the code in such version, go here.
Note
For a less rigorous explanation of the functioning and algorithmics underlying three-dimensional rendering, that at the same time requires of a mathematical background much more accessible to the average user, see my expository essay "An Exploration of 3D Computer Graphics and Rendering Techniques", in which these concepts are discussed more closely.
Section table of contents
The idea behind ʜɴɢɪɴ lies in, first, modeling three-dimensional objects through mathematical structures with which we can then operate (to, for example, move or rotate the said objects), and manipulate this three-dimensional space to project it into a two-dimensional one, since we can draw this on the screen easily using the pygame library mentioned above.
By its own definition, every vertex of a three-dimensional object consists of three coordinates:
As mentioned in resources [1] and [2], three-dimensional objects (at least those in .obj format) are also composed of faces (triangles), however, we can simplify these as three edges that join three points, then our job is reduced to modeling these edges. Given
Once one has modeled the two fundamental concepts of three-dimensional objects (for the moment, since to date I do not know how to draw faces with solid colors nor work with normals) in a three-dimensional space, we will have to find a way to project it. so that we can see it on the screen, which is a two-dimensional surface.
The first approach that one can take when facing this problem is to simply delete the
This procedure is called orthogonal projection [3], and although the intuition behind it is simple, the result, although clearly not perfect (I mean, the lack of depth and perspective is evident), is a valid starting point.
Another more accurate approach that we can take to this problem lies in the field of optics, and I must say that my knowledge in this branch of physics is constrained only to the limited research that I have done for the purpose of carrying out this project, so I quote verbatim the explanation behind the method I ended up using: "Let's assume you stand in front of a window, looking out. If you stand in the center of the window, looking out through the center of the window, then we can treat the center of your eye (more precisely, the center of the lens in the pupil of your dominant eye) the origin in 3D coordinates ... Let's say one of the 3D coordinates of an interesting detail, [any given point], are
To try to summarize and simplify this paragraph, we compare our screen with a window through which we see the scene, and through linear interpolation we can work and show that a point
Since the window is our screen, we can delete the
And the resulting two-dimensional vector
One must keep in mind that any point with a
From this section on, when we refer to a vertex (vector) and its transformations, it must be normalized. Once the operations have been carried out, we can obtain the original vector again by adding the camera position vector to it:
The most trivial (although valid, and the one I ended up using) approach one can take when implementing camera rotations in a three-dimensional space is to simply rotate the rest of the stage in the opposite direction, that is, rotate the camera to the left involves rotating the scene to the right, or rotating it up involves rotating it down.
This, however, raises two not so obvious questions: first, how to rotate a three-dimensional vector properly (as we will see later, in what order to apply the rotation matrices), and also, that the aforementioned 3D space consists of three axes, that is, six possible directions of rotation, while (ideally, and as it works in most systems) we only have four keys, two for vertical movement and another two for horizontal movement.
We will begin by resolving the first of the said questions. The space
As it is possible to infer, we need to rotate each vector around each of the three axes (with a rotation angle probably different for each of them), which implies applying each of these three matrices consecutively, or what is the same, apply the composition (product) of the three, that is, an even larger matrix.
![the-world-if-meme](https://private-user-images.githubusercontent.com/61124185/273376003-f3ddfeee-928d-4d08-8af8-44c3097f9045.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjE0OTg0ODEsIm5iZiI6MTcyMTQ5ODE4MSwicGF0aCI6Ii82MTEyNDE4NS8yNzMzNzYwMDMtZjNkZGZlZWUtOTI4ZC00ZDA4LThhZjgtNDRjMzA5N2Y5MDQ1LmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MjAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzIwVDE3NTYyMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTEzZWQ1Y2I5ODk1MzE0OWI3NzNhY2M0YzkyZTI1NDU3Yzg3MzE3ZjRjNmJiY2RhYWFmZjI0ZjE0YTJhNmUzYTYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.mwlB9A9-EnU-8VaeFhehXjbTB5Kt5BaEQ615XGEUHAY)
The problem comes, however, that the product of these matrices is not commutative, that is, the order in which we apply these rotations matters, then rotate 27º around the
However, none of these methods worked, giving me wrong perspectives of the elements on the screen, which was because the rotation around the
bad-rotation.mp4
Thinking about it, if we first rotate around the
The solution? Get rid of the rotation around the
good-rotation.mp4
In general, for a vertex
Which, computed, has the following appearance:
And finally projected on screen:
Once we can rotate the camera properly, we have to simplify the necessary controls to only depend on four keys instead of six, that is, two that control the vertical rotation and two that control the horizontal one, instead of two for each axis.
The horizontal rotation is simple, because it remains the same as the rotation around the
Vertical rotation, however, is more complicated, since it is necessary to rotate around the
For now, we can create a special angle called
Now, when
Finally, to avoid graphical errors, we will keep the
- Guidance to write a parser for .Obj and mtl file, http://web.cse.ohio-state.edu/~shen.94/581/Site/Lab3_files/Labhelp_Obj_parser.htm. Accessed 3 October 2023.
- CS 418 - Parsing OBJ Files, 17 April 2023, https://cs418.cs.illinois.edu/website/text/obj.html. Accessed 3 October 2023.
- Margalit, Dan, and Joseph Rabinoff. Orthogonal Projection, https://textbooks.math.gatech.edu/ila/projections.html. Accessed 4 October 2023.
- “3D projection on a 2D plane ( weak maths ressources ).” Mathematics Stack Exchange, 1 June 2017, https://math.stackexchange.com/questions/2305792/3d-projection-on-a-2d-plane-weak-maths-ressources. Accessed 4 October 2023.
- “Focal Length of a Lens.” HyperPhysics Concepts, http://hyperphysics.phy-astr.gsu.edu/hbase/geoopt/foclen.html. Accessed 4 October 2023.
- Euclid's Postulates, https://people.math.harvard.edu/~ctm/home/text/class/harvard/113/97/html/euclid.html. Accessed 4 October 2023.
- 3DRotations, http://motion.pratt.duke.edu/RoboticSystems/3DRotations.html. Accessed 6 October 2023.
- “Rotation matrix.” Wikipedia, https://en.wikipedia.org/wiki/Rotation_matrix. Accessed 6 October 2023.
- “How to Make a Three-Dimensional Project.” Scratch Wiki, 2 July 2023, https://en.scratch-wiki.info/wiki/How_to_Make_a_Three-Dimensional_Project#Rotation. Accessed 6 October 2023.
- Plein, Dominic. “Extrinsic & intrinsic rotation: Do I multiply from right or left?” Dominic Plein, 2 May 2022, https://dominicplein.medium.com/extrinsic-intrinsic-rotation-do-i-multiply-from-right-or-left-357c38c1abfd. Accessed 6 October 2023.
MIT License
Copyright (c) 2023 Henry Díaz Bordón
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This project is dedicated mainly to my parents, who, despite being very far away, are still with me; but also to my host family, to whom I couldn't be more grateful for all the effort they are making.