Hello VR friends! Today I am going to give you a few quick tips for preparing a VR ready scene in Unity. I will also show you how quick and easy it is to prepare that scene for use in VR using my core VR development package called PuppetJump. First, let’s go over a few things to make sure the scene, and the 3D models in it, are in good shape to be used in VR.
Check your polygons!
For this demo, I am using a classroom scene I downloaded from the Unity Asset store. When using a prebuilt scene like this, the first thing I like to do is get a quick idea of what the polygon count is looking like. I can that do that by going to the Scene view and set Shaded Wireframe as my Shader Mode. Looking at this, I can usually tell pretty quickly if the artist who created this scene did so with real-time rendering considerations in mind. I usually take just a few minutes to cruise around the scene and inspect all the objects including the walls, floor and ceiling to see if I can spot any areas with an unnecessary amount of geometry. If all the geometry is nice, clean and has generally low polygon counts, I know this scene is on the right track to being VR ready. (We’ll check our polygon counts more accurately once we have the PuppetJump rig in our scene using the Game Stats.)
Bake those lights!
The next thing I want to take a look at is how the lighting in the scene is set up. I can see that this scene has been setup with two Reflection Probes, three Directional Lights, four Area Lights and three Point Lights. If I examine each of the lights, I can see that the three Directional Lights are all set to Realtime Mode and the remaining lights are all set to Baked Mode. For VR, it’s good practice to only have one Realtime light active in view. By changing two of the Directional Lights to Baked Mode, and leaving only one set to Realtime, I know I am greatly increasing my chances of achieving an acceptable VR frame rate of about 90 frames per second. After changing these lights to Baked we will have to re-bake our lightmaps but before we do that let’s do a couple of more things.
If it ain’t going to move, make it Static!
Before we bake our lighting, we want to make sure that all objects that don’t move at runtime are labeled Static in the Inspector. Static objects will be included in our lightmaps during baking which will make them render nice, pretty and fast at runtime because their appearance has been pre-calculated by all the baked lights in the scene. Static objects can also be passed over by some other Unity runtime calculations which may improve the performance of your application. Remember, we’re always looking to hit 90 FPS!
In some cases, it might be useful to organize your Hierarchy so that you can easily select all Static or all Dynamic objects. I did that here because it helped me setup my lighting and it will also help me setup PuppetJump interactions.
Light Probes
Since we need to keep the number of dynamic lights in our scene at a bare minimum, Static and Dynamic objects will look different at runtime. This is because there’s not nearly the same amount of lighting calculations contributing to the look of Dynamic objects. Although it’s improving all the time, computers are still just not fast enough to process all the lighting calculations needed to match baked and real-time renders. Especially not at 90 FPS or higher!
An great analogy here is to look at how classic Disney animated films look. The backgrounds, or anything that is Static in the scenes, are often paintings. These paintings use complicated lighting scenarios that include subtle shading and shadowing. They take an enormous amount of time for an artist to complete but once done they can be used, as they are, for many many frames of the film. Dynamic objects on the other hand, like the characters in a scene, are often rendered in flat colors with no shadowing or shading. This is because that drawing will only be seen for one frame of the film and it will require many many different drawings to make that character appear to move. If each one frame drawing was shaded with the care and detail of the background painting, classically animated films would take forever to make! (Not that they don’t already)
Imagine these two types of art taking place in the rendering of each frame of your game or application. Think of Static objects in your scene as the background painting. The detailed shading and shadowing might take a great deal of time to get right, but the work will pay off in the end because it is will be Baked, stored and ready to use over and over at runtime. Since Dynamic objects potentially change every frame, it’s ok if their appearance is slightly less detailed. This will save the artists inside your computer from working too hard!
So whether it’s animated films or real-time computer programs, striking a delicate balance between appearance, efficiency and performance must always be the goal.
So how do we deal with combination of baked lighting and real-time lighting so that our Static and Dynamic objects are rendered with a similar appearance while maintaining a high performance frame rate? To handle that efficiency we must use Light Probes. By placing Light Probes in our scene, we are able to use portions of the pre-calculated baked light data and project that onto our Dynamic objects. It’s important to have Light Probes particularly in areas of the scene where the lightning situation changes, like at an entrance to a cave or something like that. In this scene however, the lighting is pretty consistent throughout, so I want to just make sure I have a few scattered around.
Our scene is prepared, we can go ahead and bake our lights!
Summary
These practices are good for any real-time game or application but they are essential for VR. There are lot’s of good references out there for effective lighting techniques. One of my favorites can be found here. Additional information on performance targets for acceptable numbers for polygon counts and draw calls to achieve acceptable VR framerates can be found here.
PuppetJump Setup
With our scene ready to go, I’m going to show how quickly I can get this scene working with physics driven VR using PuppetJump. First, I will import the PuppetJump package. Once the package is imported, I am going to PuppetJump -> Prefabs folder and drag the PuppetJumpManager prefab into my scene. PuppetJump works with both SteamVR and Oculus devices and can auto-detect which device is connect if you click the Auto Type checkbox on the PuppetJumpManger. I know I am going to be using Oculus today so I am going to just set the Device Type to OVR.
Next I am going to back to the PuppetJump -> Prefabs folder and drag the PuppetRigVR prefab into my scene. Normally, we could go ahead and delete the Main Camera game object from our scene because the PuppetRigVR contains the VR camera we will be using. However, PuppetJump has a unique feature that allows it to switch to a third person camera view which can be used to alleviate motion sickness. So I am going to select that Main Camera game object and place high in the back corner of the room, some place where I can see the entire space. Once I like the position I can go ahead and remove the Camera component from this object. All I really need here is the transform. I will rename this game object “Third Person Camera” then drag it into the PuppetRigVR -> Views -> Third Person View variable on the PuppetRigVR game object in my scene.
With the PuppetJump rig in place, now I need to prepare the objects in my scene for interaction. First I’m going to select all the Static objects in my scene and add a Mesh Collider component to them. Then I’m going to find the floor and add a Scripts -> PuppetJump.Utils -> TeleportLocation component. This will tell the PuppetJump rig that the floor is an object available to teleport on. Next I’m going to select all the Dynamic objects in my scene. I am going to add a Box Collider component and a Rigidbody component to all of them. Some of these objects will require more a more complex set of colliders but for now a Box Collider will do. I will also want to come back and adjust the mass on the Rigidbodies later. Since PuppetJump is at it’s core a physics driven VR system, we’ll want to adjust the weight of various objects so it feels more real and can deliver a connected immersive experience. With a Box Collider and a Rigidbody added to all Dynamic objects I will add the Scripts -> PuppetJump.Objs -> Grabbable component to all of them. For now, we can leave all the Grabbable default settings except let’s click the Is Throwable checkbox to make all the objects throwable. Admit it, we all love to throw things in VR! And let’s also say check Grab Hides Puppet Hand Display so that when we grab any of these objects the hand, or controller, object will not be visible.
That’s it! We should be able to push Play and see our physics driven world come to life!
For more on what a physics driven VR world is check out this post.
Thanks and see you next time!
Leave a Reply
You must be logged in to post a comment.