“As Cinder, crusader of the Torch-Bearers, venture into the corrupted swamp, Epthael. Blaze a path through the foul creatures of the swamp to reclaim it from Pythos’ grasp, as you, and your faith, grow stronger.”
About Spite
Spite: Ashes of Epthael was the fifth game project at TGA. The school gave us the assignment to make a game based on Diablo III and it had to be called Spite. This was also our groups first game we made together and the first game where we had to use our own engine, LAMP Engine. Which came with a lot of freedom in what we could do, and naturally, a lot of issues, both unforeseen and completely expected.
LAMP Engine was built of the back of the graphics engine I built during the graphics programming course. I spent the majority of my time implementing and adjusting the graphics engine to the games and the teams needs. While also implementing the latest things from the graphics programming course. After two weeks this is what we had.
Shadows
One of the hardest things to get right for the game was shadows, especially the directional light shadows. In the video above you can see the result of the shadow mapping implementation. While it looks fine in the image it has two major issues. Issue 1: To have any reasonable quality to the shadows the shadow map needs to be very big. Issue 2: When the directional light or the camera are moved it causes a “shimmering” effect on the edge of the shadows.
To solve theses issues I decided to go with a technique called cascading shadow maps. It’s a technique where the camera frustum is split up into multiple frustums based on distance. For each of these frustums get their own shadow map. I decided to split our camera frustum in two, giving the nearest frustum a third of the cameras far plane. I will explain why later.
In the pixel shader we sample one of the two shadow maps based on the pixels distance from the camera. The closer shadow map covers less of the game world and but are of a much higher quality, while the other shadow map covers much more area, but because it’s only used on places that are farther away from the camera where the quality doesn’t matter as much. This way we can have a lower resolution on the shadow map but still get shadows that look good.
In the video above I’ve colored the non shadowed parts depending on what shadow map it’s sampling from. Red being the higher quality and green the lower quality.
But looking at the green section while the camera is moving you can see the second issue clearly. The edges of the shadows are constantly changing, this is “shimmering” and gave me quite the headache.
There are two things that are causing this shimmering. One, the shadow map texels don’t line up with the pixels the camera renders. The way I solved this is by snapping the position of the directional light to match with the grid. That way the texels match up with the pixels. Doing this lessens the shimmering effect. But it’s still there.
Two, while the shadow map is now in lock step with the pixels. The size of the light frustum depends on the angle of the camera to the direction of the light.
As I turn the camera you can see how the shadow map seems to grow and shrink. This change in size is what’s causing the last of the shimmering. By choosing the longest line in the frustum as the size and making sure the shadow map always uses that size it will eliminate the last of the shimmering.
Finally here we have the full implementation. No more shimmering.
Even after finishing the implementation it was a balancing act between how many cascades too use and where to split the camera frustum. I ended up going with two cascades and the split being at the first third of the frustum. Spite: Ashes of Ephtael has a fixed camera that follows the player, and when playing there is a clear foreground and background that sit very similarly everywhere. Using two cascades let me use one for the foreground and one for the background and not have to render more unnecessarily. Putting the split at a third of the frustum happend to work really well for having the two frustums hit the fore/background.
As part of our tools course I had to build a model viewer (MV 1.0). It was my first time using the Dear ImGui library and I ended up making a lot of mistakes. It was clunky, unintuitive and had a good few design choices that made no sense.
To redeem myself and as the final project for the tools course I rewrote the model viewer (MV 2.0). Focusing on features that the rest of Behemoth wanted and trying to make it more user friendly.
Important Features Added
Undo/Redo: A natural feature that user expect to exist, which MV 1.0 did not have. Multiple objects: MV 1.0 could only show one model at a time, making it difficult to compare and see changes that had been made. Detachable Viewport: MV 1.0 rendered the scene to the entire window and ImGui would render over it. Causing large parts of the scene to be hidden.
Undo / Redo
One of the most annoying things with MV 1.0 was that it did not support undo and redo. It made it incredibly frustrating to work with. It was also the main reason I wanted to make MV 2.0. I started learning and using the command pattern to implement undo and redo and struggled a lot with how I was going to combine it with ImGui to create the commands. After figuring it out it came to the annoying part, which was rewriting almost all of my code to use commands instead of manipulating the data directly.
I asked some of the Behemoth members to give feedback and suggest changes they wanted. One reoccurring thing was that they wanted an undo history. Personally I don’t like an undo history and I never use them which is why I didn’t make one, but after hearing that it was a feature that people wanted it was put onto the list and added in the next update.
Multiple Objects
One of the biggest requests that came was to be able to have multiple objects. Different disciplines wanted it for different reasons, but it all boiled down to be able to see two versions of an asset next to each other, whether that be animations, textures, models or shaders. Having multiple object wasn’t an issue, the problem came from having a way for the user to select an object. To do this I implemented object picking. When rendering each object I write it’s object ID to a texture, when the user clicks to select an object that pixel of the texture is read and the ID is used to select the object in the model viewer.
But one problem came up when I implemented the detachable viewport
Detachable Viewport
To avoid the ImGui windows from covering the scene I implemented a detachable viewport that itself is an ImGui window. Now it could be moved around and dock it just like all the other windows. But two problems came up. The rendered image would match it’s aspect ratio to the ImGui window which looked horrible and was awful to use. I ended up just forcing the image to always be in a 16:9 ratio. But the larger issue came from object picking. To read a pixel from the texture a UV coordinate is needed. But I could not just convert the mouse position to a UV coordinate as the viewport was now smaller and also offset. Should be a simple fix, simply transform the mouse position by the ImGui window position and the size of the image shown. Which is correct that it is that simple, I struggled a lot with getting the right offset because I had misunderstood how ImGui handles it’s window and element positions.
Conclusion
Because MV 1.0 was my first time using the Dear ImGui library I had no idea how to actually use it, but I had learnt a lot and used that to create MV 2.0. A lot of work went into remaking the different windows and making them less cluttered and more user friendly. I had a lot of feedback sessions where the group tried out the model viewer. Even after I had “finished” it feedback and bugs kept coming and I had to fix it. I learnt a lot from making MV 2.0, especially about making a tool for somebody else. Before I had only made tool that I used myself which made it easy to just make something without putting much thought into it because I know how it works. But making a tool that was meant to be used by a variety of disciplines taught me a lot about how to think from a users perspective rather than from a developer perspective.
Spite: Ashes of Eptael
“As Cinder, crusader of the Torch-Bearers, venture into the corrupted swamp, Epthael. Blaze a path through the foul creatures of the swamp to reclaim it from Pythos’ grasp, as you, and your faith, grow stronger.”
About Spite
Spite: Ashes of Epthael was the fifth game project at TGA. The school gave us the assignment to make a game based on Diablo III and it had to be called Spite. This was also our groups first game we made together and the first game where we had to use our own engine, LAMP Engine. Which came with a lot of freedom in what we could do, and naturally, a lot of issues, both unforeseen and completely expected.
LAMP Engine was built of the back of the graphics engine I built during the graphics programming course. I spent the majority of my time implementing and adjusting the graphics engine to the games and the teams needs. While also implementing the latest things from the graphics programming course. After two weeks this is what we had.
Shadows
One of the hardest things to get right for the game was shadows, especially the directional light shadows. In the video above you can see the result of the shadow mapping implementation. While it looks fine in the image it has two major issues. Issue 1: To have any reasonable quality to the shadows the shadow map needs to be very big. Issue 2: When the directional light or the camera are moved it causes a “shimmering” effect on the edge of the shadows.
To solve theses issues I decided to go with a technique called cascading shadow maps. It’s a technique where the camera frustum is split up into multiple frustums based on distance. For each of these frustums get their own shadow map. I decided to split our camera frustum in two, giving the nearest frustum a third of the cameras far plane. I will explain why later.
In the pixel shader we sample one of the two shadow maps based on the pixels distance from the camera. The closer shadow map covers less of the game world and but are of a much higher quality, while the other shadow map covers much more area, but because it’s only used on places that are farther away from the camera where the quality doesn’t matter as much. This way we can have a lower resolution on the shadow map but still get shadows that look good.
In the video above I’ve colored the non shadowed parts depending on what shadow map it’s sampling from. Red being the higher quality and green the lower quality.
But looking at the green section while the camera is moving you can see the second issue clearly. The edges of the shadows are constantly changing, this is “shimmering” and gave me quite the headache.
There are two things that are causing this shimmering. One, the shadow map texels don’t line up with the pixels the camera renders. The way I solved this is by snapping the position of the directional light to match with the grid. That way the texels match up with the pixels. Doing this lessens the shimmering effect. But it’s still there.
Two, while the shadow map is now in lock step with the pixels. The size of the light frustum depends on the angle of the camera to the direction of the light.
As I turn the camera you can see how the shadow map seems to grow and shrink. This change in size is what’s causing the last of the shimmering. By choosing the longest line in the frustum as the size and making sure the shadow map always uses that size it will eliminate the last of the shimmering.
Finally here we have the full implementation. No more shimmering.
Even after finishing the implementation it was a balancing act between how many cascades too use and where to split the camera frustum. I ended up going with two cascades and the split being at the first third of the frustum. Spite: Ashes of Ephtael has a fixed camera that follows the player, and when playing there is a clear foreground and background that sit very similarly everywhere. Using two cascades let me use one for the foreground and one for the background and not have to render more unnecessarily. Putting the split at a third of the frustum happend to work really well for having the two frustums hit the fore/background.
As part of our tools course I had to build a model viewer (MV 1.0). It was my first time using the Dear ImGui library and I ended up making a lot of mistakes. It was clunky, unintuitive and had a good few design choices that made no sense.
To redeem myself and as the final project for the tools course I rewrote the model viewer (MV 2.0). Focusing on features that the rest of Behemoth wanted and trying to make it more user friendly.
Important Features Added
Undo/Redo: A natural feature that user expect to exist, which MV 1.0 did not have. Multiple objects: MV 1.0 could only show one model at a time, making it difficult to compare and see changes that had been made. Detachable Viewport: MV 1.0 rendered the scene to the entire window and ImGui would render over it. Causing large parts of the scene to be hidden.
Undo / Redo
One of the most annoying things with MV 1.0 was that it did not support undo and redo. It made it incredibly frustrating to work with. It was also the main reason I wanted to make MV 2.0. I started learning and using the command pattern to implement undo and redo and struggled a lot with how I was going to combine it with ImGui to create the commands. After figuring it out it came to the annoying part, which was rewriting almost all of my code to use commands instead of manipulating the data directly.
I asked some of the Behemoth members to give feedback and suggest changes they wanted. One reoccurring thing was that they wanted an undo history. Personally I don’t like an undo history and I never use them which is why I didn’t make one, but after hearing that it was a feature that people wanted it was put onto the list and added in the next update.
Multiple Objects
One of the biggest requests that came was to be able to have multiple objects. Different disciplines wanted it for different reasons, but it all boiled down to be able to see two versions of an asset next to each other, whether that be animations, textures, models or shaders. Having multiple object wasn’t an issue, the problem came from having a way for the user to select an object. To do this I implemented object picking. When rendering each object I write it’s object ID to a texture, when the user clicks to select an object that pixel of the texture is read and the ID is used to select the object in the model viewer.
But one problem came up when I implemented the detachable viewport
Detachable Viewport
To avoid the ImGui windows from covering the scene I implemented a detachable viewport that itself is an ImGui window. Now it could be moved around and dock it just like all the other windows. But two problems came up. The rendered image would match it’s aspect ratio to the ImGui window which looked horrible and was awful to use. I ended up just forcing the image to always be in a 16:9 ratio. But the larger issue came from object picking. To read a pixel from the texture a UV coordinate is needed. But I could not just convert the mouse position to a UV coordinate as the viewport was now smaller and also offset. Should be a simple fix, simply transform the mouse position by the ImGui window position and the size of the image shown. Which is correct that it is that simple, I struggled a lot with getting the right offset because I had misunderstood how ImGui handles it’s window and element positions.
Conclusion
Because MV 1.0 was my first time using the Dear ImGui library I had no idea how to actually use it, but I had learnt a lot and used that to create MV 2.0. A lot of work went into remaking the different windows and making them less cluttered and more user friendly. I had a lot of feedback sessions where the group tried out the model viewer. Even after I had “finished” it feedback and bugs kept coming and I had to fix it. I learnt a lot from making MV 2.0, especially about making a tool for somebody else. Before I had only made tool that I used myself which made it easy to just make something without putting much thought into it because I know how it works. But making a tool that was meant to be used by a variety of disciplines taught me a lot about how to think from a users perspective rather than from a developer perspective.