Tuesday, May 5, 2015

Wrapping Up!

It's the end of the semester, and so the end of the time we allotted to work on Sphericles. A lot has changed regarding its inner workings and performance since the last post: lower poly spheres, enhancements to LOD, less updates, etc. In the end it became what we set out to make, but like with all engineering there's tradeoffs - in this post I hope to go over all of them and what they've taught us.
But first, it's time to present it to the world!

Sphericles

Sphericles is an audiovisual experiment where you can fly around a space full of user-created objects hunting for "Sphericles", an object that is different from the rest. We've made it as simple as possible to add objects, and the entire thing is done through the editor, giving the user total creative control. We've made tutorials on how to make objects, and we'd be honored if people played with it/made things. NOTE: It requires you to install Unreal 4, which is fortunately free now!

Click these to learn more and see it in action!

INTRODUCTION

HOW TO ADD OBJECTS

GITHUB PAGE


Wrap Ups:


Design

From a design standpoint, it's *there*. We set out to make an interactive audiovisual experience where you fly around, see interesting user-created objects, and try to find the "one" object known as Sphericles.
The only thing I'm not pleased with is that I'd have liked to have more objects, but weaker computers could not run it. Also it was a stretch but I'd have liked to implement a editing tool to simplify the creation and distribution process.

Technology

Working with Unreal 4 was a lot of fun. I'd used it before, so I was pretty comfortable. It was Amier's first time with it, but he caught on extremely quickly and started churning out content. We programmed the entire project in the Blueprint interface, a visual-flow-chart system. Through this we implemented the player, the gamemode, and most importantly the Caplet concept, allowing us to make creative objects out of spheres by dynamically generating between them, and made it fast despite using only the Blueprints.

Sound

Sounds were also interesting. Due to a hard limit (maybe not a hard limit, but it would stop playing sounds) in UE4, we had to limit how many sounds were playing at once. We did this by using a hard-coded distance-check. If the player is within that distance play the sound, and use an attenuation set at run-time to set the volume lower the farther the player is. If the framerate of the user wasn't high enough, the sounds would choke as well. So to keep the creative capabilities robust this demanded more performance improvements. Smart pre-caching of certain values saved some time here.

Caplets

Implementing the objects was an interesting task as well. Each object was comprised of a caplet collection, which contained a list of the edges (which contained vertices that were the spheres). Each frame a tube would be generated between each edge, filling out the object. The math of this is discussed in a slightly unoptimized fashion in previous blog posts.

In order to allow the objects to move, make noise, grow, shrink, and be active we implemented a series of arrays for each sphere that contained locations to move to, sizes to grow/shrink to, and change color. Each frame we interpolate between these and update the outward position/color/size of the sphere. We managed to make it very fast by modulating through the array instead of using a ton of branching to check if our index is past the end, and keeping things relative to the parent object.

Optimizations

Computationally that was expensive, so we came up with tons of interesting ways to limit the amount of "updating" we did in a frame. We limited it by first checking if the object was on-screen, and then by updating less if the object was farther away. We had to remove the hyper-accurate form of on-screen checking, where we update the spheres that were on screen even if other parts of the caplet collection were off screen. However, this took numSpheres number of visibility tests, for which the overhead was not worth it. The LOD-updating algorithm was also not good enough, since it was a CPU optimization it didn't change the performance much on older and less powerful hardware. The image quality hit it took also wasn't worth the meager boosts it gave.

In the end the most important optimizations we made were pre-caching EVERYTHING used more than a couple times, implementing LOD for the amount of triangles in each tube, the visibility checks, and lowering the amount of polygons in the spheres. The spheres we'd been using up until the last week were UE4's default spheres, which had 700-900 triangles! I hopped into blender and made a much simpler UV sphere and integrated it. It's lighting is much more poor, but at least the program can run fast enough on old PCs to even have lighting and anti-aliasing with a reasonable frame-rate.

So you're probably wondering why it's so slow in the first place, to require all these cut-corners and optimizations in 2015 - as were we. We pulled up the profiler and started recording data. We saw most of our time was spent in stalls and generically marked overhead times - and we realized it was the Blueprint interface.
We learned that the Blueprint interface compiles to byte-code which is run on a virtual machine. One of the UE4 engineers said he Blueprint interface executes instructions up to 10 times slower. We were pretty crushed to learn we essentially programmed our entire project in a slightly slower javascript, but at the same time impressed we got so much out of it.
If we could start over we'd implement it all in C++.


Cool Things

We implemented Caplets into Unreal 4, and allowed the objects to move around do interesting things. We also implemented a global scaling factor to scale all the objects to be bigger, as well as passive rotations with a hack-y solution for what I think was gimbal lock. The object's sound fade in and out, and their sound is altered in a custom way to make the experience of flying around more responsive to what you are actually looking at vs. what is near you. Adding objects is also super simple!

The craziest thing to me is that we accomplished everything here in spite of using the Blueprint interface. When we got the first caplet collection object working, drawing tubes, changing color, sizes, and sounds, the framerate was 15 frames per second, for only one object.
Now it can run hundreds and get good framerates with lighting/anti-aliasing on mid-range computers from 2010.


Regrets 

We should have used C++ instead of the Blueprint interface. It was hip, cool, and alluring, so we fell for it. It's a shame, because we had already implemented the entire project and couldn't afford to do a full re-write a week before the "end" of the project. You know what they say though, you can never trust an engineer who hasn't had to deal with a poor initial design choice.

We think an editor would have made the project much more usable. It would allow us to package it and distribute it as a single file instead of distributing the project and requiring users to download UE4. However omitting it saved time and allowed us to spend time making the Blueprint implementation fast enough to be respectable.

We also should have been a little more active in the blog. We changed and rewrote entire systems so often that a blog post made on optimizations would become immediately obsolete.



Anyway, thanks for going on this journey with us, download it off Github and add some objects! Send me your changes through Github and I may add your custom object to the project as a whole!

No comments:

Post a Comment