Sponsored By
Sergio Hidalgo, Blogger

September 3, 2014

7 Min Read
Game Developer logo in a gray background | Game Developer

Context

Dreadhalls is a VR horror dungeon crawler for the Oculus Rift currently in development, made in Unity. You can see a trailer below:

 

The development of Dreadhalls was already well underway when I first learnt of Gear VR and considered supporting the new platform as well. Because the game originally targeted PC / Mac only, it didn't need to meet the same kind of strict performance requirements that would be necessary to make it run in a mobile device. Enforcing those requirements mid-development seemed like a very challenging task.

However, the device turned out to be more powerful than I first imagined, and we managed to make a port that delivers the same core experience that you get in the PC version. I have to thank the Oculus team for their help in this, since their performance recommendations and SDK made it possible in the first place.

Here are some of the details of the porting process:

Shadows & Lighting

Mobile GPUs are more powerful than what I expected, but they are still far from being able to consistently render at 60FPS in stereo, with dynamic shadows and per-pixel lighting. Removing these was a hard, but necessary choice to make.

To compensate for this, I played with other graphical settings, such as ambient lighting, fog, and the intensity and range of the remaining lights. These were all tied together into an "ambience area" system that allowed me to define different settings to be used in different areas. Being able to define these "ambience settings" in relation to each other, and preview the final results in realtime was key in recreating the same feel and tone from the original version, with a more limited array of tools.

Occlusion Culling

I already had an occlusion culling system in place for the PC version, which greatly enhanced performance, since the small rooms and corridors of Dreadhalls are ideal for it. There was room to improve, though. Since originally it was quite permissive, it resulted in some chunks being drawn even when not really visible.

Removing the shadows also helped with this, since now chunks casting those didn't need to be taken into account anymore, which simplified the code and reduced the amount of total geometry drawn.

Polygon Reduction

The recommended polygon target of 50k was way below what the PC version was using. While this was more of a general guideline than a strict limit, it was clear geometry needed to be reduced in order to fit in the device.

This could have been a very painful task, if not by Simplygon. For most of the props and objects in the game, Simplygon did an excellent job at creating good quality versions of them at a lower poly count.

For the main environment pieces, like the walls or ceilings, automated tools weren't enough, since these meshes had to fit perfectly with each other to create the enveloping rooms and corridors. In these cases, Dreadhalls' environment artist Joseph Battyanyi had to create the low-res versions himself.

Atlases

Reducing the number of drawcalls was also fundamental in ensuring the game could run smoothly. To do so, it was necessary to group the different materials into one, and ensure efficient render call batching.

Since Dreadhalls' maps are procedurally generated, most Unity atlas solutions didn't work out of the box, so I ended up coding my own. I settled down on having one atlas for each architectural style (since most of them aren't active at the same time in the same map), and a bunch of them for all the other props and objects. This was a good balance between performance and flexibility.

However, I soon ran into trouble. Since we hadn't any atlasing system when the models were first made, some of them used tiling textures that would break when in an atlas. Fixing those would involve moving most of the calculations to pixel shaders, along with some extra steps to fix the derivative discontinuities doing the process there would cause, which would imply a performance hit.

Eventually I figured out an alternative way of making it work. Just tile the texture by 2x when creating the atlas, and remap tiling polygons to ensure they lie within the bounds of the now bigger texture. If a polygon was too big to fit, it would be splitted. Since not all meshes needed this, the extra wasted space was not too high.

Overall, the solution worked, and the new atlases, combined with static batching and other optimizations managed to reduce the drawcalls by nearly an order of magnitude.

Draw order and transparent elements

Mobile devices tend to struggle when too much overdraw is required. Thankfully there aren't many transparent materials in Dreadhalls, so that wasn't too much of a problem. But I did want to reduce overdraw, since it was an easy fix that could give some extra performance.

In Unity, you'll most likely have to choose between batching and overdraw reduction. Ideally, you'd want to draw front to back, to reduce the number of fragments that are actually rendered, but static batching reduces your control over how the environment is rendered.

As long as you have different materials, however, you can still retain some control. Since the atlas used for the architectural elements (walls, etc...) was different than that of the props, I could control their relative order, making sure the walls would render first, discarding the fragments of the props behind them.

Doing this was as simple as adding an offset to the render queue in the shader, such as: "Queue"="Geometry+10"

CPU Optimizations

During the porting process I learnt some bad habits in Unity can bite you back really hard when working on smaller devices. Not removing the empty Update() functions Unity adds by default to every script is one of them. You should also remove any active wait (where you increase a timer counter and check it every frame) and use co-routines intead.

In the case of Dreadhalls, there was a system that needed a heavier reworking: the cell visibility system that checks which parts of the room are actually visible to the player. This is different than the occlusion culling, since this one is used for gameplay purposes only and has a higher fidelity, using physical checks to detect when a prop or a moving object is in the way.

Of course, these cells only did these checks when they weren't culled out. But turns out having lots of objects checking a boolean in their Update is still a slow process, if only because of all the function calls. The solution here was to remove the Updates and have a central manager that updated itself only those cells that needed updating.

This removed most extra checks and function calls, and also allowed for scheduling the cell's updates, since a few extra frames of delay could be easily ignored for most gameplay purposes, but it really enhanced performance.

Lastly, reducing garbage allocation is always an important optimization, but more so for mobile VR. Unity's profiler was very helpful here, and most times you can easily get rid of most of the allocated garbage in your game. I'd also suggest force calling the collector at those times when it won't be noticed by the player, such as right after a level load, so that it has less chance of activating during play.

User Interface

There isn't too much user interface in Dreadhalls, since there is no HUD and only a few textual prompts and simple menus here and there, but it was still a problem since during a large part of the development process most of the UI solutions for Unity didn't work in the VR device.

Rather than delaying development waiting for a fix, we implemented a custom UI solution based on 3d meshes (quads and TextMeshes), which worked flawlessly. This was somewhat costly, but as a plus the UI is now completely integrated into the 3D world, with nice small details such as the buttons changing their depth when selected.

Conclusions

Overall, I'm very happy with the results, and I believe we managed in porting the Dreadhalls experience into Gear VR. We were luckly in that the game features mostly small space with objects close to the player and heavy occlusion, which is ideal for this case.

Making a VR app for a mobile platform requires you to be very conscious of what you're doing, though it's perfectly doable, and the results are surely worth the effort!

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like