Introduction
Greetings and welcome to my article. In this piece, I will discuss a university assignment where we were tasked with creating a game engine from scratch in C++, with the goal of developing a Racing Game. Our game engine was designed to target cross-platform games for modern, high-powered Windows machines using DirectX 12, as well as the PlayStation 5.
Timeline:
This project took a total of 16 weeks, split into 2 blocks of 8 weeks. I worked on it during the first half of my 2nd Year at Breda University of Applied Sciences.
The Team:
The team consisted of five members during the first block of development, including two graphics programmers and three engine programmers. During the second block, two additional team members joined, bringing the total to seven members, consisting of two graphics programmers, two engine programmers, and three gameplay programmers.
For the purposes of this article, I will focus primarily on the graphics features that I developed, as this was my area of responsibility within the team. Additionally, I served as a producer and led most team meetings, oversaw sprint planning, and maintained the GitHub project board and issues.
Please note that all of the videos you see are rendered on a PlayStation 5.
Contributions
Block A:
Play Station 5 Graphics API
In this project, I was responsible for setting up all of the PlayStation 5 rendering aspects. My fellow graphics programmer and I both had no prior experience working with the graphics APIs used in the project, DirectX12 and PS5. To streamline the process, we divided the responsibilities between us, and each of us focused on one API to create all the necessary fundamentals. We then continued to work on the respective console graphics roles, and it made more sense to remain in those roles once established.
As part of the setup, I also developed a camera class which was used until the end of the project, and set up Dear ImGui on PlayStation 5.
GLTF Model Loading
Once we understood how to pass data to the GPU and use it, our next task was loading models that contained more than just vertices and indices. As a team, we decided to use the glTF 2.0 model format because it is often used in game engines. However, there was some confusion about how it worked, so the other graphics programmer and I ended up writing two different formatters for the two rendering APIs. Although not the best approach for the project, it made the learning process better as we worked together and explained the ideas to each other.
I was responsible for implementing several glTF features, including Node Hierarchy, 32-bit Vertices, 32 and 16-bit Indices Loader, Material loading, Image loading, Texture indexing, and a Scene class that held all this information.
It’s important to note that I implemented only what was needed at the time. As a result, I did not support animations or any of the glTF extensions such as lights, clearcoat materials, etc.
Phong Lighting
Not much to say about it, other than I implemented it and we had it as the lighting logic for the first Block of the project.
At this point of the project, we also switched to a different map.
Block B
During the second block, I got to work on more interesting stuff. I and the other graphics programmer had gotten into a rhythm and we worked together very efficiently.
Physically Based Rendering
One of the main tasks I worked on was implementing Physically Based Rendering (PBR), which was a great opportunity for me to learn more about advanced shader algorithms. PBR is essential for most modern racing games and allowed us to improve our game’s graphics quality significantly.
It might be interesting to note, that for most of the project’s development we didn’t have any PBR models because our team was only made up of programmers. During this time we were making arrangements with a Tech Outsource team from the Year above us to create a tool for procedural track generation in Houdini. You can see the exported track from this tool working in the final video in the Conclusion of this article.
ImGui Editor Rework
I spent some time reworking the ImGui Editor to make it more user-friendly and organized. This task was crucial as it allowed us to streamline our workflow and make the editor more intuitive.
2D Ui Elements Rendering
We also needed to implement a 2D UI element rendering system to display all of the elements you typically see in racing games such as the speedometer, tachometer, and progress on the track. This task helped me to understand how to write render passes, which split our rendering pipeline into two parts: 3D and 2D. This implementation made me realize that a graphics API is simply an API that we can use to modify data.
Note: The numbers are rendered from a sprite sheet from numbers between 0 – 9, not from a font renderer. This was due to time limitations.
Skybox Rendering
We used a cubemap approach to add skyboxes to our game. It was yet another render pass with a different shader that happened between the 3D and 2D rendering.
You might be able to see it better in the video above.
Shadow Mapping from Multiple Light Sources
This is the most challenging thing I worked on this block. In our engine, we had 3 types of lights – spot, directional, and point lights. Each car had 2 spot lights at the front from which we wanted to be able to render shadows. I also want to give credit to my other graphics programmer, Andrei Bazzaev, because he went through the process of figuring it out first and then I could recreate his approach in my respective API. This also posed a challenge because some functions were different and I had little explanations since the API falls under NDA.
Unfortunately, I couldn’t get shadows to render from directional lights properly until after the end of the block. I didn’t even attempt to do so for point lights because they require even more shadow maps for each cardinal direction.
Conclusion
In conclusion, our team successfully presented the game you saw in the video above. This was a challenging project that I was passionate about, and I put in many more hours than what was expected of me in the curriculum. Looking back, there are many things I would have done differently, and with the experience gained in project management and technical skills, I would have been able to accomplish much more.
One of the biggest challenges was the API design. It was only towards the end of the project that I fully understood what an API should consist of and how it should be presented to users in a user-friendly way. This affected my ability to give sufficient feedback and direction to my team throughout the project, which is something I will improve on in the future.
Despite these challenges, I am proud of what we accomplished. This project taught me many things through the hardships we faced, which wouldn’t have stuck as well if I hadn’t gone through them. Overall, I am grateful for the experience, and I believe it has prepared me well for future endeavors as a graphics programmer.
Thanks for reading my article! If you’re curious to see more, please check out my Gallery page, which is a collection of interesting videos I’ve collected throughout my programming journey. If you have anything to say, feel free to write to me on Twitter or below in the comments.
Hello.
Good luck 🙂