Welcome to Thomas Chernaik's Portfolio

About Me

Fourth year Computer Science student at the University of Leeds, specializing in high-performance graphics and games engineering, with a focus on real-time rendering systems and high-performance C/C++ programming. Developed a deferred & forward PBR rendering system using Vulkan, as well as geometric mesh repair/simplification tools. Engineered a GPU-native Gaussian splatting rasteriser from scratch, utilizing compute shaders and parallel sorting to render 2M+ splats interactively on integrated hardware. Passionate about advancing real-time graphics and solving problems to create new, prettier, high-performance solutions.

Vulkan renderer

As part of my advanced rendering module, I implemented a renderer in Vulkan and C++, with various features. First, I implemented a basic renderer, which could render a single triangle. Simply rendering one triangle with Vulkan involves using lots of different components of the Vulkan API. Once I had that one triangle, I could the progress to rendering a scene (Sun temple), and allowing the camera to move about with standard wasd controls. I then implemented PBR shading and alpha masking for the leaves on the trees, as well as a few simple debug views such as a depth view and a mip map view. I then implemented a version which used a naive deferred pipeline, before moving on to add shadow mapping, a post process bloom effect using a separable gaussian filter, and normal mapping. I also implemented a couple more debug views - overshading and mesh density. The hardest part of this project, apart from wrangling with Vulkan synchronisation problems, was the shadow mapping. Implementing a shadowmap for a single light was ok, but I wanted the ability to have many lights which cast shadows, and managing all the resources while keeping everything synchronised was a challenge, but not one that I couldn't overcome. I had a lot of fun making this and I hope to come back to it and add more features in the future.


Source code available on request.

Gaussian Splatting Rasteriser

Gaussian splatting for radiance field rendering is a modern technique for rendering scenes, with benefits including the ability to convert from directly from a real video to a splatting scene, and alias-free rendering. I first read the paper on this technique in 2023, and decided to implement a renderer in C++ and OpenGL for my final year project at university. In order to acheive remotely real-time performance, the entire scene is rendered in a series of compute shaders, set up in a pipeline of my design. I implemented a sorting pass, which required a GPU-based radix sort, as well as a pass to project the splats, and a pass to render the splats. You can read my full report, which includes a literature review, a description of the implementation, and a discussion of the results, here. My implementation was able to render a scene with 100,000 splats at >30fps on AMD integrated graphics, and I achieved a first class grade for this project.


You can find the source code here.

Dodecahedra of doom

This project involved two main parts. The first was to implement a skeletal animation system, taking bvh files, and using them to animate a running skeleton. This involved taking the animation data, using it to pose the skeleton correctly, and updating the pose used as the character runs, and blending between animations when the character starts and stops.
The second part was to implement a simple physics engine, which could handle collisions between spheres at first, then moving on to a more complex (and spinnable) mesh of a dodecahedron.


Source code available on request.

Mesh processing toolkit

For a university project, I implemented a mesh processing toolkit in C++.
There were several features:
  • Conversion from triangle soup to indexed face
  • Conversion from indexed face to directed edge
  • Manifold testing for meshes, including robust triangle-triangle intersection
  • Topological analysis using Euler's formula
  • Mesh repair
  • Mesh simplification using curvature analysis
It was a fun coursework, with a few interesting challenges. The first challenge is making a triangle-triangle intersection test that is robust for this purpose. General methods are for real time applications (such as physics), and don't have suitable precision for this. Exploring different techniques to make it more robust was very interesting. The second challenge was trying to get the mesh repair to work for more complex cases. There are cases where it seems obvious to a human how a mesh should be repaired, but it is difficult to describe this repair as a general purpose algorithm. It was fun coming up with ways to overcome this. I received 98% for this coursework.


You can find the source code here.

NEE Pathtracer

This project involved implementing, first a raytracer, then a monte carlo pathtracer in C++. It was a fun project, and I implemented the following features for it:
  • Ray-triangle intersection
  • PBR phong shading model
  • Monte carlo path tracing
  • Refraction
  • Reflection
  • Soft shadows
  • Anti aliasing
  • Importance sampling using next event estimation
  • Fresnel



Source code available on request.

Rocket Demo

The second coursework for my computer graphics module was to create a rocket demo in OpenGL, where the rocket could be triggered to launch. This was a group project, and I was responsible for the base rendering, particle system, UI system, and animation system. The particle system was implemented using batch rendering, and the particles were rendered as untextured quads. I achieved performance of over 60fps with 2,000,000 particles by optimising the particle system to have as little computation as possible. The paths of each particle were pre-calculated, and the particles were rendered in a single draw call, with the location of each particle updated in the vertex shader with some simple interpolation of the path, based on the current time. The UI system was implemented using 2 custom classes I made, one for the text and one for the buttons. They both had negligible performance impact, and were implemented such that it was easy to add new buttons and text. The animation system was implemented by defining a mathematical function for the rocket to follow, and then interpolating the rocket's position and rotation based on the current time. This made it easy to reset and restart the rocket whenever wanted. The base rendering uses standard OpenGL techniques, such as VAOs, VBOs, and shaders, and a custom shader for each model, which allowed for real time lighting. The rocket demo was a great success, and I achieved a mark of 90% for this coursework.


Source code available on request.

Scribbles

Six second scribbles is a great card game which you should get to play with your family and friends. Unfortunately, my friends are living abroad this year, so I decided to make a web version of the game so we could play together. I used the python Flask library for this, as I was familiar with it from more than one past project. I used the flask-socketio library to handle the websockets, which I had not used before. I used the javascript library socket.io to handle the websockets on the client side. I used bootstrap to make the website look nice. I wanted the server to be able to handle multiple games at once, so I implemented a lobby system. One player would create a lobby, and then other players could join via a code. The players could then vote to start the game, which was communicated via websockets. There were many cards to type up from the card game, so I made a little website interface to type up the cards, which would then be saved to a file. In a game, players would make a drawing, which would be sent to the server, and then sent to the other players. The other players would then have to guess what the drawing was. I stored the images using a custom class I made which implemented pickling, and cleaned up the images after a certain time, and when a new round started. I deployed this project to azure, but it is no longer on there, as my student account expired. I have a private deployment I can use with friends, but I won't link here.


You can find the source code here.

Chess

This was a project I did a while ago to improve my C++ skills. I wanted to make a project that would test my knowledge, and hopefully provide some learning experiences. I also wanted to make a game on a lower level than I had before, so I decided to make a chess game. I had used the QT library before for a university project, but I wanted to work at a lower level, so I decided to use the SDL2 library. I used the SDL2 library to create a window, and to handle input. I used the SDL2_image library to load images for the chess pieces. I implemented the game logic using some OO principles. It was in my mind while I was making it that I would want to make a chess AI at some point, so I made sure to keep the game logic, input handling, and rendering separate. I also had a way to store game states, so I could look ahead easily. Memory had to be dynamically allocated for the game states, so I used smart pointers to make sure that memory was freed when it was no longer needed. I have done several C projects, especially at university, so I was very familiar with using raw pointers to manage memory, but I wanted to use smart pointers to practice using them. I also implemented a feature to save and load from a file in the Forsyth–Edwards Notation (FEN) format, which is a standard format for chess states. I used this feature to test the game logic. I found that there was no need to implement checkmate, as the game logic I had implemented already handled it, as player where unable to make moves that would put them in checkmate, so when checkmate happened, a player would be unable to make a move. I implemented a feature to highlight the squares that a piece could move to, which was very useful for testing the game logic. In the future, I will make a chess AI using the minimax algorithm, and I will use the game logic I have already implemented.


You can find the source code here.

We suck at deliveries

I competed in Ludum Dare 53, my first game jam. The theme was delivery, and there was a game I had wanted to make for a while which could be made as a delivery game. It was inspired by the car in the level select menu in the overcooked games, and came out very nicely. I made the game with one other person, and I contributed a lot of the gameplay code for this project, as well as some of the 3D art. I modeled the van and the pallets in Blender. I programmed a lightweight custom physics engine that allowed us to calculate collisions our van took with the environment without dealing with Unity's built-in physics engine. I also programmed the package distribution system. This involved keeping track of "collectors" and "spawners", where the collectors are houses the player must deliver packages to, and the spawners are the shops where the packages spawn in. For each package spawned, there must be a house which expects a package. Additionally, we had to keep track of packages that hadn't been delivered in a certain amount of time, so we could call the player out. All of the magic numbers involved here were put onto nice easily adjustable public variables, so we could easily tweak the gameplay, and speed up gameplay for what little testing we had time for.


You can find the ludum dare submission here, and you can play the game jam version of the game here.

Placeholder Image

Flashcards

I took part in the great uni hack in late 2022. This was a great experience, where we created a full stack application in 24 hours. I worked with two other people, and we created a flashcard application, which would generate flashcards from your notes. About 8 months later, Quizlet released a feature which did exactly this, and we are looking to release a full version of our application soon. We used the python library flask for the backend, and wrote the frontend using node.js. This was before ChatGPT came out, so we used an LLM called Cohere to generate the flashcards. I worked on the backend, and implemented some of the database functionality, as well as some of the endpoints. I also worked with the LLM, and implemented some of the code to generate the flashcards. I am still currently working on this project, and we have changed some of our infrastructure, such as moving to nosql from SQLite, and moving to use chatgpt. My work currently has been on improving response times on the backend, and researching implementing a subscription system.


You can find the Hackathon version of the source code here.

Procedural Grass

For this project I wanted to make grass similar to how grass works in some AAA games, using real geometry for each blade. There are two stages to the grass generation. The first stage is in a compute shader which calculates a location and rotation for each blade. Then a stage in the surface shader calculates sway, lighting, colour, and if the blade is flattened by an object. The final result is optimized enough that it runs at over 100 fps with 500000 visible blades, which I am very happy with.


You can find the full source code here

FABRIK

Following the procedural animation project, I decided to create my own FABRIK implementation for unity, having initially used another implementation. I then tested the FABRIK implementation with a simple procedurally animated cat.


You can find the full source code here

Procedural Animation

To further my interest in procedural animation I decided to design a project which would develop the skillset. This took the form of a procedurally animated "tentacle monster" which can move around with convincing animation. I calculate new feet positions randomly in the direction of velocity so that a minimum number of tentacles are always in spawn. When feet reach a certain distance from the centre, they despawn, leading to new ones spawing. In order to have the tentacles convincingly spawn and despawn, I made a script to shrink the mesh. This is in the form of a compute shader which clips the mesh down to the required size. By the end of my project I had produced a monster-character with convincing animation that can be player controlled.


You can find the full source code here

Peer to Peer multiplayer

I set myself the challenge of designing a simple multiplayer system, as multiplayer is part of many of the biggest modern games. I wanted to use a udp peer to peer system, as this would require me to write code to communicate over the internet, but wouldn't require usage of external hosting. In the end, I successfully completed a peer to peer system in unity, as you can see in the video before where the two clients communicate across the local network. The black cube is controlled by the left client and the white cube is controlled by the right client


You can find the full source code here

Slime mould simulation

I began this project after watching this youtube video in which a similar project is made. This project was where I learned how to use HLSL and compute shaders for the first time. There are two main steps: A step in which the direction of each spore is calculated, and a step in which the trails of the spores diffuse. Once I implemented these, I added the ability to influence the patterns of the spores with an image.


You can find the full source code here

Contact Information

Email: thomas.chernaik@gmail.com

Phone: +447486409343

LinkedIn: linkedin/thomas-chernaik-611aa7226/

GitHub: github/thomas-chernaik