Falling Down the Rabbit Math Hole
Like most of my personal projects, I fell into this math hole with a few (not so) simple items on my agenda. They eventually looked something like this:
- Learn OpenGL.
- Make a VR sculpting application.
- Write this using only C++ and small helper libraries. No big frameworks.
- Implement a signed distance renderer.
But I’m getting ahead of myself a bit. In the beginning, I discovered signed distance fields while doing research for a shader I was trying to write in Unreal Engine. I saw a bunch of cool examples, and wanted to dive right in and figure out how all this stuff worked! Unfortunately, all the examples I was looking at were written in GLSL. Learning a new complex subject and trying to translate that into Unreal’s shader system was more than I wanted to chew off. So, I opened up Unity for the first time, and started coding (that’s simpler, right?).
As it turns out, this WAS simpler. After learning the basics of how modern game engines were organized in Unreal, picking up a little Unity was a breeze. Coming from python/c++ background, C# was very straight forward as well, especially with the great IDE integration with Visual Studio Code. I really enjoyed learning C#. I wish this language had more adoption in the fields I normally work in.
To get started, I read a few signed distance field ray marching tutorials specifically for Unity. For more information, I inspected a number of shadertoy shaders. Then I dug in deeper, and got the whole thing to render in VR. My backwards approach to learning unity gave me a good laugh at the time. I started with learning the shader system, then how to integrate my shaders with C#/SteamVR, and only at that point did I learn how to spawn an object.
Wow, this was a lot of fun! I slapped some physics on there because playing around in zero-g is pure joy in VR. Look at all those things zooming around. I think this demo looks really impressive. Package it up and ship it, right? No, there was one fatal flaw that prevented me from taking this any further: there was no headroom for improvement. My GTX 1080, which was close to the best video card on the market, could only make 10 objects before it started dropping frames. Barf-land here we come. And since ray marching renderers are a brute force approach, anti-aliasing the results would come at a very serious cost. You can already see aliasing problems in the video. Throwing balls around is fun, but if I wanted to write a sculpting application, I was going to need to make more than 10 cubes.
At this point, I (naively) decided that Unity wasn’t giving me much other than a glorified shell to write cg shaders in. I wasn’t sure how much the overhead of Unity itself, and the C# language was slowing things down. I didn’t think it was much, but I wanted to find out. I decided to ditch Unity and write my own engine from the ground up. As it turns out, Unity was doing a lot of work for me! Scheduling, garbage collection, polygons, oh my! But without writing it myself, I would have never known! A month or so later, I had almost the same demo up and running in my own application.
Subjectively, I felt like I did eek out a tiny bit more performance over Unity, but I didn’t do any hard benchmarks because it wasn’t a significant gain. It was still a great learning experience, and now I had my own application to use as a test bed for further work. Which I did with vigor! Here’s an early test I did, trying to ray casting through a voxel field before dropping to ray marching once the ray was close to something.
There were still a few bugs I needed to work out! I love how visually intense bugs can get when writing renderers. One incorrect sign somewhere and suddenly, you’re seeing stars. I think I did get this approach working, but there weren’t any performance gains (though, there were a number of items that I now know where wrong with my methodology, so I may look at this again). I needed a radical change. Something completely different than ray marching. Luckily for me, that’s exactly what Media Molecule claimed to have done. I just needed to follow them further down this math hole… right?