Oculus SDK Overview
Oculus SDK Overview
SDK Overview
SDK Version 0.2.3
Authors:
Michael Antonov
Nate Mitchell
Andrew Reisse
Lee Cooper
Steve LaValle
Date:
July 3, 2013
2013 Oculus VR, Inc. All rights reserved.
Oculus VR, Inc. 19800 MacArthur Blvd Suite 450 Irvine, CA 92612
Except as otherwise permitted by Oculus VR, Inc., this publication, or parts thereof, may not be reproduced
in any form, by any method, for any purpose. Certain materials included in this publication are reprinted with
the permission of the copyright holder.
All brand names, product names or trademarks belong to their respective holders.
Disclaimer
THIS PUBLICATION AND THE INFORMATION CONTAINED HEREIN IS MADE AVAILABLE BY
OCULUS VR, INC. AS IS. OCULUS VR, INC. DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABIL-
ITY OR FITNESS FOR A PARTICULAR PURPOSE REGARDING THESE MATERIALS.
1
Contents
1 Introduction 5
2 Oculus Rift Hardware Setup 5
2.1 Display Specications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Tracker Specications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Additional Vision Lenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3.1 Changing vision lenses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Screen Distance Adjustment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4.1 Adjusting the screen distance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.5 Control Box Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.5.1 Adjusting brightness and contrast . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.6 Monitor Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3 Oculus Rift SDK Setup 9
3.1 System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.1 Operating systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.2 Minimum system requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3 Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4 Compiler Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.5 Makeles, Projects, and Build Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.5.1 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.5.2 MacOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.5.3 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.6 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Getting Started 12
4.1 OculusWorldDemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1.1 Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1.2 Using OculusWorldDemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Using the SDK Beyond the OculusWorldDemo . . . . . . . . . . . . . . . . . . . . . . . . 14
2
4.2.1 Software developers and integration engineers . . . . . . . . . . . . . . . . . . . . . 14
4.2.2 Artists and game designers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5 LibOVR Integration Tutorial 16
5.1 Outline of Integration Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5.2 Initialization of LibOVR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5.3 Sensor Data and Head Tracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.3.1 Some details on sensor fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3.2 Magnetometer-based yaw error correction . . . . . . . . . . . . . . . . . . . . . . . 21
5.4 User Input Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5 Rendering Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5.1 Rendering stereo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.5.2 Distortion correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.5.3 Distortion scale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.5.4 Distortion and FOV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.5.5 StereoCong utility class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.5.6 Rendering performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6 Optimization 34
6.1 Latency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
A Display Device Management 36
A.1 Display Identication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
A.2 Display Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
A.2.1 Duplicate display mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
A.2.2 Extended display mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
A.2.3 Standalone display mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
A.3 Selecting A Display Device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
A.3.1 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
A.3.2 MacOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
A.4 Rift Display Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
A.4.1 Duplicate mode VSync . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
A.4.2 Extended mode problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3
A.4.3 Observing Rift output on a monitor . . . . . . . . . . . . . . . . . . . . . . . . . . 40
A.4.4 Windows: Direct3D enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
B Chromatic Aberration 42
B.1 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
B.2 Sub-channel aberration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
B.3 Shader implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
C SDK Samples and Gamepad Usage 45
4
1 Introduction
Thanks for downloading the Oculus Software Development Kit (SDK)!
This document will detail how to install, congure, and use the Oculus SDK. The Oculus SDK includes all of
the components that developers need to integrate the Oculus Rift with their game engine or application. The
core of the SDK is made up of source code and binary libraries. The Oculus SDK also includes documentation,
samples, and tools to help developers get started.
This document focuses on the C++ API of the Oculus SDK. Integration with the Unreal Engine 3 (UE3) and
Unity game engine is available as follows:
Unity integration is available as a separate package from the Oculus Developer Center.
Unreal Engine 3 integration is also available as a separate package from the Oculus Developer Center.
You will need a full UE3 license to access the version of Unreal with Oculus integration. If you have a
full UE3 license, you can email [email protected] to be granted download access.
2 Oculus Rift Hardware Setup
In addition to the Oculus SDK, you will also need the hardware provided by the Oculus Rift Development
Kit (DK). The DK includes an Oculus Rift development headset (Rift), control box, required cabling, and
additional pairs of lenses for different vision characteristics.
2.1 Display Specications
7 inch diagonal viewing area
1280 800 resolution (720p). This is split between both eyes, yielding 640 800 per eye.
64mm xed distance between lens centers
60Hz LCD panel
DVI-D Single Link
HDMI 1.3+
USB 2.0 Full Speed+
2.2 Tracker Specications
Up to 1000Hz sampling rate
Three-axis gyroscope, which senses angular velocity
Three-axis magnetometer, which senses magnetic elds
Three-axis accelerometer, which senses accelerations, including gravitational
5
2.3 Additional Vision Lenses
The Rift comes installed with lenses for users with 20/20 or farsighted vision. If your vision is 20/20 or
farsighted, you wont need to change your lenses and you can proceed to Section 2.4.
For nearsighted users, two additional pairs of lenses are included with the kit. Although they may not work
perfectly for all nearsighted users, they should enable most people to use the headset without glasses or
contact lenses.
The medium-depth lenses are for users who are moderately nearsighted. The shortest-depth lenses are for
users who are very nearsighted. We recommend that users experiment with the different lenses to nd the
ones that work best for them.
The lenses are also marked with the letters A, B, and C to aid identication. The recommended lenses
are as follows:
Lenses Appropriate Vision
A 20/20 or farsighted
B Moderately nearsighted
C Very nearsighted
Note: If your eyes have special characteristics such as astigmatism, the provided lenses may not be sufcient
to correct your vision. In this case, we recommend wearing contact lenses or glasses. Note, however, that
using glasses will cut down on your effective eld of view.
2.3.1 Changing vision lenses
Note: Changing the lens may cause dust or debris to get inside the Rift. We strongly recommend changing
the lenses in the cleanest space possible! Do not store the Rift without lenses installed.
To change lenses, rst turn the headset upside down (this is to minimize the amount of dust and debris that
can enter the headset) and gently unscrew the lenses currently attached to the headset. Unscrewing the lenses
doesnt require much pressure; a light touch is most effective. The right lens unscrews clockwise. The left
lens unscrews counterclockwise.
Place the old lenses in a safe place, then take the new lenses and install them the same way you removed
the original pair. Remember to keep your headset upside down during this process. Once the new lenses are
securely in place, youre all set!
After changing the lenses, you may need to adjust the distance of the assembly that holds the screen and
lenses closer or farther away from your face. This is covered next.
2.4 Screen Distance Adjustment
The headset has an adjustment feature that allows you to change the distance of the xture that holds the
screen and lenses from your eyes. This is provided to accommodate different facial characteristics and vision
lenses. For example, if the lenses are too close to your eyes, then you should adjust the xture outward,
moving the lenses and the screen away from your face. You can also use this to provide more room for
eyeglasses.
6
Note: Everyone should take some time to adjust the headset for maximum comfort. While doing so, an
important consideration is that the lenses should be situated as close to your eyes as possible. Remember
that the maximal eld of view occurs when your eyes are as close to the lenses as possible without actually
touching them.
2.4.1 Adjusting the screen distance
There are two screw mechanisms of either side of the headset that can be adjusted using a coin. These screws
control the location of the screen assembly. The setting for the two screw mechanisms should always match
unless youre in the process of adjusting them.
Turn the screw mechanism toward the lenses to bring the assembly closer to the user. Turn the screw
mechanism toward the display to move the assembly farther away from the user. After changing one side,
ensure that the other side is turned to the same setting!
2.5 Control Box Setup
The headset is connected to the control box by a 6ft cable. The control box takes in video, USB, and power,
and sends them out over a single cord to minimize the amount of cabling running to the headset.
1. Connect one end of the video cable (DVI or HDMI) to your
computer and the other end to the control box.
Note: There should only be one video-out cable running to the
control box at a time (DVI or HDMI, not both).
2. Connect one end of the USB cable to your computer and the
other to the control box.
3. Plug the power cord into an outlet and connect the other end to
the control box.
You can power on the DK using the power button on the top of the control box. A blue LED indicates whether
the DK is powered on or off. The Rift screen will only stay on when all three cables are connected.
2.5.1 Adjusting brightness and contrast
The brightness and contrast of the headset can be adjusted using the buttons on the top of the control box.
Looking from the back side:
The leftmost buttons adjust the displays contrast.
The neighboring two adjust the displays brightness.
The rightmost button turns the power on and off.
7
2.6 Monitor Setup
Once the Oculus Rift is connected to your computer, it should be automatically recognized as an additional
monitor and Human Input Device (HID).
The Rift can be set to mirror or extend your current monitor setup using your computers display settings. We
recommend using the Rift as an extended monitor in most cases, but its up you to decide which conguration
works best for you. This is covered in more detail in Appendix A. Regardless of the monitor conguration, is
it currently not possible to see the desktop clearly inside the Rift. This would require stereo rendering and
distortion correction, which is only available while rendering the game scene.
Whether you decide to mirror or extend your desktop, the resolution of the Rift should always be set to
1280 800 (720p).
8
3 Oculus Rift SDK Setup
3.1 System Requirements
3.1.1 Operating systems
The Oculus SDK currently supports MacOS, Windows (Vista, 7, 8) and Linux.
3.1.2 Minimum system requirements
There are no specic computer hardware requirements for the Oculus SDK; however, we recommend that
developers use a computer with a modern graphics card. A good benchmark is to try running Unreal Engine 3
and Unity at 60 frames per second (FPS) with vertical sync and stereo 3D enabled. If this is possible without
dropping frames, then your conguration should be sufcient for Oculus Rift development!
The following components are provided as a guideline:
Windows: Vista, 7, or 8
MacOS: 10.6+
Linux: Ubuntu 12.04 LTS
2.0+ GHz processor
2 GB system RAM
Direct3D10 or OpenGL 3 compatible video card.
Although many lower end and mobile video cards, such as the Intel HD 4000, have the shader and graphics
capabilities to run minimal Rift demos, their rendering throughput may be inadequate for full-scene 60 FPS
VR rendering with stereo and distortion. Developers targeting this hardware will need to be very conscious of
scene geometry because low-latency rendering at 60 FPS is critical for a usable VR experience.
If you are looking for a portable VR workstation, weve found that the Nvidia 650M inside of a MacBook
Pro Retina provides enough graphics power for our demo development.
9
3.2 Installation
The latest version of the Oculus SDK is available at http://developer.oculusvr.com.
The naming convention for the Oculus SDKrelease package is ovr_packagetype_major.minor.build.
For example, the initial build was ovr_lib_0.1.1.zip.
3.3 Directory Structure
The installed Oculus SDK package contains the following subdirectories:
3rdParty Third party SDK components used by samples, such as TinyXml.
Doc SDK Documentation, including this document.
LibOVR Libraries, source code, projects, and makeles for the SDK.
LibOVR/Include Public include header les, including OVR.h. Header les here reference
other headers in LibOVR/Src.
LibOVR/Lib Pre-built libraries for use in your project.
LibOVR/Src Source code and internally referenced headers.
Samples Samples that integrate and leverage the Oculus SDK.
3.4 Compiler Settings
The LibOVR libraries do not require exception handling or RTTI support, thereby allowing your game to
disable these features for efciency.
3.5 Makeles, Projects, and Build Solutions
Developers can rebuild the samples and LibOVR using the projects and solutions in the Samples and
LibOVR/Projects directory.
3.5.1 Windows
The Visual Studio 2010 solution and project les are provided with the SDK:
Samples/LibOVR_Samples_Msvc2010.sln is the main solution that allows you to build and
run all of the samples.
LibOVR/Projects/Win32 contains the project needed to build the LibOVR library itself (for
developers that have access to the full source).
10
3.5.2 MacOS
The included Xcode project Samples/LibOVR_With_Samples.xcodeproj allows you to build and
run all of the samples, and libovr itself. The project is setup to build universal binaries (x86 and x86 64) for
all recent MacOS versions (10.6 and newer).
3.5.3 Linux
A makele is provided in the root folder which allows you to build libovr and the OculusWorldDemo sample.
The code is dependent on the udev and Xinerama runtime components and so before building you must
install the relevant packages. It is also necessary to install a udev/rules.d le in order to set the correct
access permissions for Oculus HID devices. These steps can be performed by executing the provided script
ConfigurePermissionsAndPackages.sh in the root folder of the SDK.
3.6 Terminology
The following terms are crucial in the coming sections:
Interpupillary distance (IPD) The distance between the eye pupils. The default value in the SDK is
64mm which corresponds to the average human distance, but values of
54mm to 72mm are possible.
Field of view (FOV) The full vertical viewing angle used to congure rendering. This is
computed based on the eye distance and display size.
Aspect ratio The ratio of horizontal resolution to vertical resolution. The aspect ratio
for each eye on the Oculus Rift is 0.8.
Multisampling Hardware anti-aliasing mode supported by many video cards.
11
4 Getting Started
Your developer kit is unpacked and plugged in, youve installed the SDK, and you are ready to go. Where is
the best place to begin?
If you havent already, take a moment to adjust the Rift headset so that its comfortable for your head and
eyes. More detailed information about conguring the Rift can be found in Section 2.
Once your hardware is fully congured, the next step is to test the development kit. The SDK comes with a
set of full-source C++ samples designed to help developers get started quickly. These include:
OculusWorldDemo - A visually appealing Tuscany scene with on-screen text and controls.
OculusRoomTiny - A minimal C++ sample showing sensor integration and rendering on the Rift.
SensorBoxTest - A 3D rendered box that demonstrates sensor fusion by tracking and displaying the
rotation of the Rift.
We recommend running the pre-built OculusWorldDemo as a rst-step in exploring the SDK. You can nd a
link to the executable le in the root of the Oculus SDK installation.
4.1 OculusWorldDemo
Figure 1: Screenshot of the OculusWorldDemo application.
4.1.1 Controls
Key or Input Movement Key Function
W, S Move forward, back F1 No stereo, no distortion
A, D Strafe left, right F2 Stereo, no distortion
Mouse move Look left, right F3 Stereo and distortion
Left gamepad stick Move F9 Hardware full-screen (low latency)
Right gamepad stick Turn F11 Windowed full-screen (no blinking)
12
Key(s) Function Key(s) Function
R Reset sensor orientation Insert, Delete Adjust interpupillary distance
G Toggle grid overlay PageUp, PageDown Adjust aspect ratio
Spacebar Toggle debug info overlay [ , ] Adjust eld of view
Esc Cancel full-screen Y, H Adjust k
1
coefcient
-, + Adjust eye height U, J Adjust k
2
coefcient
Z Manual mag calibration/activation P Toggle motion prediction
X Start auto mag calibration N, M Adjust motion prediction
F6 Yaw lines for mag debug C Toggle chromatic aberration
4.1.2 Using OculusWorldDemo
Once youve launched OculusWorldDemo, take a moment to look around using the Rift and double check
that all of the hardware is working properly. You should see an image similar to the screenshot in Figure 1.
Press F9 or F11 to switch rendering to the Oculus Rift.
F9 - Switches to hardware full-screen mode. This will give best possible latency, but may blink
monitors as the operating system changes display settings. If no image shows up in the Rift, then press
F9 again to cycle to the next monitor.
F11 - Instantly switches the rendering window to the Rift portion of the desktop. This mode has higher
latency and no vsync, but is convenient for development.
Note that the Linux version of OculusWorldDemo currently supports only the latter of the two full-screen
modes described above.
If youre having problems (for example no image in the headset, no head tracking, and so on), then see the
developer forums on the Oculus Developer Center. These should help for resolving common issues.
There are a number of interesting things to take note of during your rst trip inside OculusWorldDemo.
First, the level is designed to scale. Thus, everything appears to be roughly the same height as it would
be in the real world. The sizes for everything, including the chairs, tables, doors, and ceiling, are based on
measurements from real world objects. All of the units are measured in meters.
Depending on your actual height, you may feel shorter or taller than normal. The default eye-height of the
player in OculusWorldDemo is 1.78 meters (5ft 10in), but this can be adjusted using the + and - keys.
As you may have already concluded, the scale of the world and the player is critical to an immersive
VR experience. This means that players should be a realistic height, and that art assets should be sized
proportionally. More details on scale can be found in the Oculus Best Practices Guide document. Among
other things, the demo includes simulation of a basic head model, which causes head rotation to introduce
additional displacement proportional to the offset of eyes from the base of the neck. This displacement is
important for improving realism and reducing disorientation.
13
4.2 Using the SDK Beyond the OculusWorldDemo
4.2.1 Software developers and integration engineers
If youre integrating the Oculus SDK into your game engine, we recommend starting by opening the sample
projects (Samples/LibOVR With Samples Msvc2010.sln or
Samples/LibOVR With Samples.xcodeproj), building the projects, and experimenting with the
provided sample code.
OculusRoomTiny is a good place to start because its source code compactly combines all critical features of
the Oculus SDK. It contains logic necessary to initialize LibOVR core, access Oculus devices, implement
head-tracking, sensor fusion, head modeling, stereoscopic 3D rendering, and distortion shaders.
Figure 2: Screenshot of the OculusRoomTiny application.
OculusWorldDemo is a more complex sample. It is intended to be portable and supports many more features
including: windowed/full-screen mode switching, XML 3D model and texture loading, movement collision
detection, adjustable distortion and view key controls, 2D UI text overlays, and so on. This is a good
application to experiment with once you are familiar with Oculus SDK basics.
Beyond experimenting with the provided sample code, you should continue to follow this document. Well
cover important topics including the Oculus kernel, initializing devices, head-tracking, rendering for the Rift,
and minimizing latency.
4.2.2 Artists and game designers
If youre an artist or game designer unfamiliar in C++, we recommend downloading UE3 or Unity along
with the corresponding Oculus integration. You can use our out-of-the-box integrations to begin building
Oculus-based content immediately.
The Unreal Engine 3 Integration Overview document and the Unity Integration Overview document,
available from the Oculus Developer Center, detail the steps required to set up your UE3/Unity plus Oculus
development environment.
We also recommend reading through the Oculus Best Practices Guide, which has tips, suggestions, and
14
research oriented around developing great VR experiences. Topics include control schemes, user interfaces,
cut-scenes, camera features, and gameplay. The Best Practices Guide should be a go-to reference when
designing your Oculus-ready games.
Aside from that, the next step is to get started building your own Oculus-ready games! Thousands of other
developers, like you, are out there building the future of virtual reality gaming. You can reach out to them by
visiting http://developer.oculusvr.com/forums.
15
5 LibOVR Integration Tutorial
If youve made it this far, you are clearly interested in integrating the Rift with your own game engine.
Awesome. We are here to help.
Weve designed the Oculus SDK to be as easy to integrate as possible. This section outlines a basic Oculus
integration into a C++ game engine or application. Well discuss initializing the LibOVR kernel, device
enumeration, head tracking, and rendering for the Rift.
Many of the code samples below are taken directly from the OculusRoomTiny demo source code (available in
Oculus/LibOVR/Samples/OculusRoomTiny). OculusRoomTiny and OculusWorldDemo are great
places to draw sample integration code from when in doubt about a particular system or feature.
5.1 Outline of Integration Tasks
To add Oculus support to a new game, youll need to do the following:
1. Initialize LibOVR.
2. Enumerate Oculus devices, creating HMD device and sensor objects.
3. Integrate head-tracking into your games view and movement code. This involves:
(a) Reading data from the Rifts sensors through the SensorFusion class.
(b) Applying the calculated Rift orientation to the camera view, while combining it with other
controls.
(c) Modifying movement and game play to consider head orientation.
4. Modify game rendering to integrate the HMD, including:
(a) Stereoscopic 3D rendering for each eye.
(b) Correctly computing projection,
fov
, and other parameters based on the HMD settings.
(c) Applying a pixel shader to correct for optical distortion.
5. Customize UI screens to work well inside of the headset.
Well rst take a look at obtaining sensor data because its relatively easy to set up. Well then move on to the
more involved subject of rendering.
5.2 Initialization of LibOVR
We initialize LibOVRs core by calling System::Init, which will congure logging and register a default
memory allocator (that you can override):
#include "OVR.h"
using namespace OVR;
System::Init(Log::ConfigureDefaultLog(LogMask_All));
16
Note that System::Init must be called before any other OVR_Kernel objects are created, and
System::Destroy must be called before program exit for proper cleanup. Another way to initialize the
LibOVR core is to create a System object and let its constructor and destructor take care of initialization
and cleanup, respectively. In the cases of OculusWorldDemo and OculusRoomTiny, the init and destroy calls
are invoked by the OVR_PLATFORM_APP macro.
Once the system has been initialized, we create an instance of OVR::DeviceManager. This allows us
to enumerate detected Oculus devices. All Oculus devices derive from the DeviceBase base class which
provides the following functionality:
1. It supports installable message handlers, which are notied of device events.
2. Device objects are created through DeviceHandle::CreateDevice or more commonly through
DeviceEnumerator<>::CreateDevice.
3. Created devices are reference counted, starting with a RefCount of 1.
4. A devices resources are cleaned up when it is Released, although its handles may survive longer if
referenced.
We use DeviceManager::Create to create a new instance of DeviceManager. Once weve created
the DeviceManager, we can use DeviceManager::EnumerateDevices to enumerate the detected
Oculus devices. In the sample below, we create a new DeviceManager, enumerate available HMDDevice
objects, and store a reference to the rst active HMDDevice that we nd.
Ptr<DeviceManager> pManager;
Ptr<HMDDevice> pHMD;
pManager =
*
DeviceManager::Create();
pHMD =
*
pManager->EnumerateDevices<HMDDevice>().CreateDevice();
We can learn more about a device by using DeviceBase::GetDeviceInfo(DeviceInfo
*
info).
The DeviceInfo structure is used to provide detailed information about a device and its capabilities.
DeviceBase::GetDeviceInfo is a virtual function; therefore, subclasses such as HMDDevice and
SensorDevice can provide subclasses of DeviceInfo with information tailored to their unique proper-
ties.
In the sample below, we read the vertical resolution, horizontal resolution, and screen size from
an HMDDevice using HMDDevice::GetDeviceInfo with an HMDInfo object (subclass of
DeviceInfo).
HMDInfo hmd;
if (pHMD->GetDeviceInfo(&hmd))
{
MonitorName = hmd.DisplayDeviceName;
EyeDistance = hmd.InterpupillaryDistance;
DistortionK[0] = hmd.DistortionK[0];
DistortionK[1] = hmd.DistortionK[1];
DistortionK[2] = hmd.DistortionK[2];
DistortionK[3] = hmd.DistortionK[3];
}
The same technique can be used to learn more about a SensorDevice object. Nowthat we have information
about the HMDDevice, the next step is to set up rendering for the Rift.
17
5.3 Sensor Data and Head Tracking
The Oculus Rift hardware includes a gyroscope, accelerometer, and magnetometer. We combine the informa-
tion from these sensors through a process known as sensor fusion to determine the orientation of the players
head in the real world, and to synchronize the players virtual perspective in real-time.
The Rift orientation is reported as a rotation in a right-handed coordinate system, as illustrated in Figure 3.
This coordinate system uses the following axis denitions:
Y is positive in the up direction.
X is positive to the right.
Z is positive heading backwards.
The rotation is maintained as a unit quaternion, but can also
be reported in yaw-pitch-roll form. Positive rotation is counter-
clockwise (CCW) when looking in the negative direction of each
axis, and the component rotations are:
Pitch is rotation around X, positive when pitching up.
Yaw is rotation around Y , positive when turning left.
Roll is rotation around Z, positive when tilting to the left
in the XY plane.
Figure 3: The Rift coordinate system
To integrate head-tracking, rst we need a SensorDevice object to read from. If we have a reference to
an HMD, we get a reference to the associated sensor using HMDDevice::GetSensor as follows:
Ptr<SensorDevice> pSensor;
pSensor =
*
pHMD->GetSensor();
We can get more information about the sensor using SensorDevice::GetInfo.
The SensorFusion class accumulates sensor notication messages to keep track of orientation. This
involves integrating the gyroscope data and then using the other sensors to correct for drift. SensorFusion
provides the orientation as a quaternion, from which users can obtain a rotation matrix or Euler angles. There
are two ways to receive updates from the SensorFusion class:
1. We can manually pass MessageBodyFrame messages to the OnMessage() function.
2. We can attach SensorFusion to a SensorDevice. This will cause the SensorFusion instance to automati-
cally handle notications from that device.
SensorFusion SFusion;
if (pSensor)
SFusion.AttachToSensor(pSensor);
Once an instance of SensorFusion is attached to a SensorDevice, we can use it to get relevant data from the
Oculus tracker through the following functions:
18
// Obtain the current accumulated orientation.
Quatf GetOrientation() const
// Obtain the last absolute acceleration reading, in m/s2.
Vector3f GetAcceleration() const
// Obtain the last angular velocity reading, in rad/s.
Vector3f GetAngularVelocity() const
In most cases, the most important data coming from SensorFusion will be the orientation quaternion Q,
provided by GetOrientation. Well make use of this to update the virtual view to reect the orientation
of the players head. Well also account for the orientation of the sensor in our rendering pipeline.
// We extract Yaw, Pitch, Roll instead of directly using the orientation
// to allow "additional" yaw manipulation with mouse/controller.
Quatf hmdOrient = SFusion.GetOrientation();
float yaw = 0.0f;
hmdOrient.GetEulerABC<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll);
EyeYaw += (yaw - LastSensorYaw);
LastSensorYaw = yaw;
// NOTE: We can get a matrix from orientation as follows:
Matrix4f hmdMat(hmdOrient);
Developers can also read the raw sensor data directly from the SensorDevice, bypassing SensorFusion
entirely, by using SensorDevice:: SetMessageHandler(MessageHandler
*
handler). The
MessageHandler delegate will receive a MessageBodyFrame every time the tracker sends a data
sample. A MessageBodyFrame instance provides the following data:
Vector3f Acceleration; // Acceleration in m/s2.
Vector3f RotationRate; // Angular velocity in rad/s2.
Vector3f MagneticField; // Magnetic field strength in Gauss.
float Temperature; // Temperature reading on sensor surface, in degrees Celsius.
float TimeDelta; // Time passed since last Body Frame, in seconds.
Over long periods of time, a discrepancy will develop between Q and the true orientation of the Rift. This
problem is called drift error, which described more in Section 5.3.1. Errors in pitch and roll are automatically
reduced by using accelerometer data to estimate the gravity vector. This feature is automatically set by
default, and can be turned off by SetGravityEnabled(false) in the SensorFusion class.
Errors in yaw are reduced by magnetometer data; however, this feature is not enabled by default. For
many games, such as a standard First Person Shooter (FPS), the yaw direction is frequently modied by
the game controller and there is no problem. However, in many other games, the yaw error will need to be
corrected. Suppose, for example, you want to maintain a cockpit directly in front of the player. It should not
unintentionally drift to the side over time.
Basic use of the magnetometer is described here, with more technical details appearing in Section 5.3.2.
The call SetYawCorrectionEnabled(true) to the SensorFusion enables the magnetometer to
be used; however, it must rst be calibrated. OculusWorldDemo contains examples of this usage. The
object MagCal is initialized to maintain magnetometer calibration information. Automatic (or unguided)
calibration is started by pressing the X key, which runs:
MagCal.BeginAutoCalibration(SFusion);
SetAdjustMessage("Starting Auto Mag Calibration");
19
After that, the OnIdle handler contains the following code to update the calibration process and detect its
completion:
if (MagCal.IsAutoCalibrating())
{
MagCal.UpdateAutoCalibration(SFusion);
if (MagCal.IsCalibrated())
{
Vector3f mc = MagCal.GetMagCenter();
SetAdjustMessage("Magnetometer Calibration Complete\nCenter: %f %f %f",mc.x,mc.y,mc.z);
}
SetAdjustMessage("Mag has been successfully calibrated");
}
Calibration may also be performed manually, which guides the player through a sequence of looking
directions. See the method UpdateManualMagCalibration in the OculusWorldDemoApp class
for an example. More details on magnetmeter usage appear in Section 5.3.2.
5.3.1 Some details on sensor fusion
The most important part of sensor fusion is the integration of angular velocity data from the gyroscope. In
each tiny interval of time, a measurement of the angular velocity arrives:
= (
x
,
y
,
z
). (1)
Each component is the rotation rate in radians per second about its corresponding axis. For example, if the
sensor were sitting on the center of a spinning hard drive disk that sits in the XZ plane, then
y
is the rotation
rate about the Y axis, and
x
=
z
= 0.
What happens when the Rift is rotating around some arbitrary axis? Let
=
_
2
x
+
2
y
+
2
z
(2)
be the length of . It turns out that the angular velocity vector beautifully decomposes in the following
useful way:
The current axis of rotation (unit length) is the normalized gyro reading:
1
fov
= 2 arctan
_
VScreenSize
2 EyeToScreenDistance
_
. (4)
We form the projection matrix, P, based on a and
fov
, as
P =
_
_
1
atan(
fov
/2)
0 0 0
0
1
tan(
fov
/2)
0 0
0 0
z
far
znearz
far
z
far
znear
znearz
far
0 0 1 0
_
_
, (5)
25
in which z
near
and z
far
are the standard clipping plane depth coordinates. This common calculation can be
done by the Matrix4f::PerspectiveRH function in the Oculus SDK, the gluPerspective utility
function in OpenGL, or D3DXMatrixPerspectiveFovRH in Direct3D.
The projection center of P as computed above falls in the center of each screen, so we need to modify it to
coincide with the center of the lens instead. Here, lens separation is used instead of the eye separation due to
the properties of the collimated light. Lens center adjustment can be done in nal clip coordinates, computing
the nal left and right projection matrices as illustrated below. Let h denote the absolute value of horizontal
offset to account for lens separation. This can be used in a transformation matrix
H =
_
_
1 0 0 h
0 1 0 0
0 0 1 0
0 0 0 1
_
_
, (6)
which is applied at the end to obtain P
= HP. In the upper right corner of H, the term h appears for the
left-eye case, and h appears for the right eye. For convenience, let
d
lens
= LensSeparationDistance. (7)
The particular horizontal shift in meters is
h
meters
=
HScreenSize
4
d
lens
2
. (8)
In screen coordinates,
h =
4 h
meters
HScreenSize
. (9)
In terms of screen size, this adjustment is signicant: Assuming 64mm interpupillary distance (IPD) and
149.76mm screen size of the 7 Rift, each eye projection center needs to be translated by about 5.44mm
towards the center of the device. This is a critical step for correct stereo rendering.
Assuming that the original non-stereo game view transform V falls at the center between the eyes, the nal
adjustment we have to make is shift that view horizontally to match each eye location:
V
=
_
_
1 0 0 d
ip
/2
0 1 0 0
0 0 1 0
0 0 0 1
_
_
V. (10)
where
d
ip
= InterpupillaryDistance. (11)
It is important that this shift is done by half of the interpupillary distance in world units. Please refer to your
games content or design documents for the conversion to or from real-world units. In Unreal Engine 3, for
example, 2 units = 1 inch. In Unity, 1 unit = 1 meter.
We can now present a more complete example of this stereo setup, as it is implemented inside of the
OVR::Util:: Render::StereoConfig utility class. It covers all of the steps described in this section
with exception of viewport setup.
26
HMDInfo& hmd = ...;
Matrix4f viewCenter = ...;
// Compute Aspect Ratio. Stereo mode cuts width in half.
float aspectRatio = float(hmd.HResolution
*
0.5f) / float(hmd.VResolution);
// Compute Vertical FOV based on distance.
float halfScreenDistance = (hmd.VScreenSize / 2);
float yfov = 2.0f
*
atan(halfScreenDistance/HMD.EyeToScreenDistance);
// Post-projection viewport coordinates range from (-1.0, 1.0), with the
// center of the left viewport falling at (1/4) of horizontal screen size.
// We need to shift this projection center to match with the lens center.
// We compute this shift in physical units (meters) to correct
// for different screen sizes and then rescale to viewport coordinates.
float viewCenter = hmd.HScreenSize
*
0.25f;
float eyeProjectionShift = viewCenter - hmd.LensSeparationDistance
*
0.5f;
float projectionCenterOffset = 4.0f
*
eyeProjectionShift / hmd.HScreenSize;
// Projection matrix for the "center eye", which the left/right matrices are based on.
Matrix4f projCenter = Matrix4f::PerspectiveRH(yfov, aspect, 0.3f, 1000.0f);
Matrix4f projLeft = Matrix4f::Translation(projectionCenterOffset, 0, 0)
*
projCenter;
Matrix4f projRight = Matrix4f::Translation(-projectionCenterOffset, 0, 0)
*
projCenter;
// View transformation translation in world units.
float halfIPD = hmd.InterpupillaryDistance
*
0.5f;
Matrix4f viewLeft = Matrix4f::Translation(halfIPD, 0, 0)
*
viewCenter;
Matrix4f viewRight= Matrix4f::Translation(-halfIPD, 0, 0)
*
viewCenter;
With all of this setup done, you should be able to see the 3D world and converge on it inside of the Rift. The
last, and perhaps more challenging step, will be correcting for distortion due to the lenses.
27
5.5.2 Distortion correction
The lenses in the Rift magnies the image to provide an increased FOV, but this comes at the expense of
creating a pinchsion distortion of the image.
Pincushion Distortion Barrel Distortion
This radially symmetric distortion can be corrected in software by introducing a barrel distortion that cancels
it out. Our distortion models are expressed in terms of the distance r from the center (the axis of symmetry).
It is helpful to think in polar coordinates (r, ) for a point in the image. A distortion model performs the
following transformation:
(r, ) (f(r) r, ), (12)
in which the scaling function is given by the following standard model:
f(r) = k
0
+ k
1
r
2
+ k
2
r
4
+ k
3
r
6
. (13)
In other words, the direction is unharmed, but the distance r is expanded or contracted. The four coefcients
k
0
, . . ., k
3
control the distortion. They are all positive in the case of barrel distortion. With properly chosen
coefcients, the pincushion distortion is canceled off by a barrel distortion.
In OculusWorldDemo, this is implemented by the Direct3D10 pixel shader shown in Figure 7.
This shader is designed to run on a rectangle covering half of the screen, while the input texture spans both
the left and right eyes. The input texture coordinates, passed in as oTexCoord, range from (0,0) for the
top left corner of the Oculus screen, to (1,1) at the bottom right. This means that for the left eye viewport,
oTexCoord will range from (0,0) to (0.5,1). For the right eye it will range from (0.5,0) to (1,1).
The distortion function used by HmdWarp is, however, designed to operate on [-1,1] unit coordinate range,
from which it can compute the radius. This means that there are a number of variables needed to scale and
center the coordinates properly to apply the distortion. These are:
Variable Name Description
ScaleIn Rescale input texture coordinates to [-1,1] unit range, and corrects
aspectratio.
Scale Rescale output (sample) coordinates back to texture range and in-
crease scale so as to support sampling outside of the screen.
HmdWarpParam The array of distortion coefcients (DistortionK[]).
ScreenCenter Texture coordinate for the center of the half-screen texture. This is
used to clamp sampling, preventing pixel leakage from one eye view
to the other.
LensCenter Shifts texture coordinates to center the distortion function around the
center of the lens.
28
Texture2D Texture : register(t0);
SamplerState Linear : register(s0);
float2 LensCenter;
float2 ScreenCenter;
float2 Scale;
float2 ScaleIn;
float4 HmdWarpParam;
// Scales input texture coordinates for distortion.
float2 HmdWarp(float2 in01)
{
float2 theta = (in01 - LensCenter)
*
ScaleIn; // Scales to [-1, 1]
float rSq = theta.x
*
theta.x + theta.y
*
theta.y;
float2 rvector= theta
*
(HmdWarpParam.x + HmdWarpParam.y
*
rSq +
HmdWarpParam.z
*
rSq
*
rSq +
HmdWarpParam.w
*
rSq
*
rSq
*
rSq);
return LensCenter + Scale
*
rvector;
}
float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,
in float2 oTexCoord : TEXCOORD0) : SV_Target
{
float2 tc = HmdWarp(oTexCoord);
if (any(clamp(tc, ScreenCenter-float2(0.25,0.5),
ScreenCenter+float2(0.25, 0.5)) - tc))
return 0;
return Texture.Sample(Linear, tc);
};
Figure 7: Pixel shader code for removing lens distortion.
The nal variable is calculated as
LensCenter = 4
_
_
_
_
HScreenSize
4
LensSeparationDistance
2
HScreenSize
_
_
_
_
= 1 2 LensSeparationDistance / HScreenSize.
(14)
The following diagram illustrates the left eye distortion function coordinate range, shown as a blue rectangle,
as it relates to the left eye viewport coordinates. As you can see, the center of distortion has been shifted to
the right in relation to the screen center to align it with axis through the center of the lense. For the 7 screen
and 64mm lense separation distance, viewport shift is roughly 0.1453 coordinate units. These parameters
may change for future headsets and so this should always be computed dynamically.
29
The diagram also illustrates howsampling coordinates are mapped
by the distortion function. A distortion unit coordinate of (0.5,0.6)
is marked as a green cross; it has a radius of 0.61. In the
example shown, this maps to a sampling radius of 0.68 post-
distortion, illustrated by a red cross. As a result of the distortion
shader, pixels in the rendered image move towards the center of
distortion, or from red to green in the diagram. The amount of
displacement increases the further out we go from the distortion
center.
Hopefully, its clear from this discussion that the barrel distortion pixel shader needs to run as a post-process
on the rendered game scene image. This has several implications:
The original scene rendering will need to be done to a render target.
The scene render target will need to be larger than the nal viewport, to account for the distortion
pulling pixels in towards the center.
The FOV and image scale will need to be adjusted do accommodate for the distortion.
We will now discuss the distortion scale, render target, and FOV adjustments necessary to make the image
look correct inside of the Rift.
5.5.3 Distortion scale
If you run the distortion shader on the original image render target that is the same size as the output screen
you will get an image similar to the following:
Here, the pixels at the edges have been pulled in towards the center, with black being displayed outside,
where no pixel data was available. Although this type of rendering would look acceptable within the Rift,
30
a signicant part of the FOV is lost because large areas of the screen go unused. How would we make the
scene ll up the entire screen?
The simplest solution is to increase the scale of the input texture, controlled by the Scale variable of the
distortion pixel shader discussed earlier. As an example, if we want to increase the perceived input texture
size by 25% we can adjust the sampling coordinate Scale by a factor of (1/1.25) = 0.8. Doing so will have
several effects:
The size of the post-distortion image will increase on screen.
The required rendering FOV will increase.
The quality of the image will degrade due to sub-sampling from the scaled image, resulting in blocky
or blurry pixels around the center of the view.
Since we really dont want the quality to degrade, the size of the source render target can be increased by
the same amount to compensate. For the 1280 800 resolution of the Rift, a 25% scale increase will rquire
rendering a 1600 1000 buffer. Unfortunately, this incurs a 1.56 times increase in the number of pixels in the
source render target. However, we dont need to completely ll the far corners of the screen where the user
cannot see. Trade-offs are evident between the covered eld of view, quality, and rendering performance.
For the 7 Rift, we recommend selecting the radius (distance from image center) so that the image reaches
the left screen edge. For example, to t the left side of the display (1, 0):
s = f(1 LensCenter), (15)
in which s is the scaling factor. This yields maximal horizontal FOV without lling the pixels at the top of
the screen, which are not visible to most users:
For OculusWorldDemo, the actual distortion scale factor is computed inside of the
StereoConfig::updateDistortionOffsetAndScale function by tting the distortion
radius to the left edge of the viewport. The LensCenter is the same as used in the shader above.
5.5.4 Distortion and FOV
With distortion scale and offset properly computed, the FOV needs to be corrected. For this, we need to
examine the geometry of the Rift projection as illustrated in the diagram on the right.
31
The diagram illustrates the effect that a lens introduces into
an otherwise simple calculation of the FOV, assuming that
the user is looking at the screen through the lens. Here, d
species the eye to screen distance and x is half the vertical
screen size (x = VScreenSize/2).
In the absence of the lens, the FOV can be easily computed
based on the distances d and x, as described in Section 5.5.1:
fov
= 2 arctan(x/d)
The lens, however, increases the perceived screen height from 2x to 2x
, in which x
fov
= 2 arctan(x
/d) (16)
in which x
= s VScreenSize/2 and s is the scaling factor from Section 5.5.3 (recall Equation 15).
Note that we compute the eld of view of the distorted render target (RT), which is affected by the distortion
scale s and may not necessarily match the screen edge. Assuming that both the RT and the display have the
same aspect ratios, it is sufcient to adjust the screen size to correctly obtain
fov
.
5.5.5 StereoCong utility class
If setting up the projection and distortion scaling seems like a lot of work, youll be happy to learn that
the Oculus SDK comes with a set of full-source utility classes that do a lot of the work for you. The most
important class for rendering setup is StereoConfig, located inside of the OVR::Util::Render
namespace. This class works as follows:
1. First, create an instance of StereoConfig and initialize it with HMDInfo, viewport, distortion t
location, IPD and other desired settings.
2. StereoConfig computes the rendering scale, distortion offsets, FOV and projection matrices.
3. StereoConfig::GetEyeRenderParams returns the projection matrix and distortion settings
for each eye. These can be used for rendering stereo inside of the game loop.
StereoConfig class is used for rendering setup inside of the OculusRoomTiny and OculusWorldDemo
samples. Heres an example of the initialization you need to get started:
using namespace OVR::Util::Render;
HMDDevice
*
pHMD = ...;
StereoConfig stereo;
HMDInfo hmd;
float renderScale;
// Obtain setup data from the HMD and initialize StereoConfig
32
// for stereo rendering.
pHMD->GetDeviceInfo(&hmd);
stereo.SetFullViewport(Viewport(0,0, Width, Height));
stereo.SetStereoMode(Stereo_LeftRight_Multipass);
stereo.SetHMDInfo(hmd);
stereo.SetDistortionFitPointVP(-1.0f, 0.0f);
renderScale = stereo.GetDistortionScale();
As you can see, after all parameters are initialized, GetDistortionScale computes the rendering scale
that should be applied to the render texture. This is the scale that will maintain one-to-one rendering quality
at the center of the screen while simultaneously scaling the distortion to t its left edge.
Based on this computed state, you can get left and right eye rendering parameters as follows:
StereoEyeParams leftEye = stereo.GetEyeRenderParams(StereoEye_Left);
StereoEyeParams rightEye = stereo.GetEyeRenderParams(StereoEye_Right);
// Left eye rendering parameters
Viewport leftVP = leftEye.VP;
Matrix4f leftProjection = leftEye.Projection;
Matrix4f leftViewAdjust = leftEye.ViewAdjust;
You can use the resulting Viewport and projection matrix directly for rendering the scene. ViewAdjust
should be a post-transform applied after the games view matrix to properly shift the camera for the left or
right eye.
5.5.6 Rendering performance
Aside from changes to the game engines renderer to account for the Rifts optics, there are two other
requirements when rendering for VR:
The game engine should run at least 60 frames per second without dropping frames.
Vertical Sync (vsync) should always be enabled to prevent the player from seeing screen tearing.
These may seem arbitrary, but our experiments have shown them to be important for a good VR experience.
A player can easily tell the difference between 30 FPS and 60 FPS when playing in VR because of the
immersive nature of the game. The brain can suspend disbelief at 60 FPS. At 30 FPS, the world feels choppy.
Vertical sync is also critical. Since the Rift screen covers all of the players view, screen tearing is very
apparent, and causes artifacts that break immersion.
33
6 Optimization
6.1 Latency
Minimizing latency is crucial to immersive VR and low latency head tracking is part of what sets the Rift
apart. We dene latency as the time between movement of the players head, and the updated image being
displayed on the screen. We call this latency loop motion-to-photon latency. The more you can minimize
motion-to-photon latency in your game, the more immersive the experience will be for the player.
Two other important concepts are actual latency and perceived latency.
Actual latency is equivalent to motion-to-photon latency. It is the latency in the system at the hardware and
software level.
Perceived latency is how much latency the player perceives when using the headset. Perceived latency may
be less than actual latency depending on the players movements and by employing certain techniques
in software.
Were always working to reduce actual and perceived latency in our hardware and software pipeline. For
example, in some cases were able to reduce perceived latency by 20ms or more using a software technique
called predictive tracking.
Although 60ms is a widely cited threshold for acceptable VR, at Oculus we believe the threshold for
compelling VR to be below 40ms of latency. Above this value you tend to feel signicantly less immersed in
the environment. Obviously, in an ideal world, the closer we are to 0ms, the better.
For the Rift developer kit, we expect the actual latency to be approximately 30ms to 50ms. This depends
partly on the screen content. For example, a change from black to dark brown may take 5ms but a larger
change in color from black to white may take 20ms.
Stage Event Event Duration Worst Case Total
Start Oculus tracker sends data N/A 0ms
Transit Computer receives tracker data 2ms 2ms
Processing Game engine renders latest frame (60 FPS w/
vysnc)
0 to 16.67ms 19ms
Processing Display controller writes latest frame to LCD (top
to bottom)
16.67ms 36ms
Processing Simultaneously, pixels switching colors 0 to 15ms 51ms
End Latest frame complete; presented to user N/A 51ms
Again, these numbers represent the actual latency assuming a game running at 60 FPS with vsync enabled.
Actual latency will vary depending on the scene being rendered. Perceived latency can be reduced further. As
developers, we want to do everything we can to reduce latency in this pipeline. Techniques for Reducing
Latency:
Run at 60 FPS (remember that vsync should always be enabled for VR).
Minimize swap-chain buffers to a maximum of 2 (the on screen and off screen buffers).
Reduce the amount of rendering work where possible. Multi-pass rendering and complex shaders
34
increase the rendering latency and hence the time between reading the HMD orientation and having
the frame ready to display.
Reduce render command buffer size. By default the driver may buffer several frames of render
commands to batch GPU transfers and smooth out variability in rendering times. This needs to be
minimized. One technique is to make a rendering call that blocks until the current frame is complete.
This can be a block until render queue empty event or a command that reads back a property of the
rendered frame. While blocking, were preventing additional frames from being submitted and hence
buffered in the command queue.
35
A Display Device Management
A.1 Display Identication
Display devices identify themselves and their capabilities using EDID
1
. When the device is plugged into a
PC, the display adapter reads a small packet of data from it. This includes the manufacturer code, device
name, supported display resolutions, and information about video signal timing. When running an OS that
supports multiple monitors, the display is identied and added to a list of active display devices which can be
used to show the desktop or fullscreen applications.
The display within the Oculus Rift interacts with the system in the same way as a typical PC monitor. It too
provides EDID information which identies it as having a manufacturer code of OVR, a model ID of Rift
DK1, and support for several display resolutions including its native 1280 800 at 60Hz.
A.2 Display Conguration
After connecting a Rift to the PC it is possible to modify the display settings through the Windows
Control Panel
2
. For MacOS use the System Preferences Display panel, and for Ubuntu Linux use the
System Settings : Displays control panel.
Figure 8: Screenshot of the Windows Screen Resolution dialog.
Figure 8 shows the Windows Screen Resolution dialog for a PC with the Rift display and a PC monitor
connected. In this conguration there are four modes that can be selected as show in the gure. These are
duplicate mode, extended mode, and standalone mode for either of the displays.
1
Extended Display Identication Data
2
Under Windows 7 this can be accessed via Control Panel : All Control Panel Items : Display : Screen Resolution
36
A.2.1 Duplicate display mode
In duplicate display mode the same portion of the desktop is shown on both displays, and they adopt the
same resolution and orientation settings. The OS attempts to choose a resolution which is supported by both
displays, while favoring the native resolutions described in the EDID information reported by the displays.
Duplicate mode is a potentially viable mode in which to congure the Rift, however it suffers from vsync
issues. See Section A.4 for details.
A.2.2 Extended display mode
In extended mode the displays show different portions of the desktop. The Control Panel can be used to
select the desired resolution and orientation independently for each display. Extended mode suffers from
shortcomings related to the fact that the Rift is not a viable way to interact with the desktop, nevertheless it is
the current recommended conguration option. The shortcomings are discussed in more detail in Section A.4.
A.2.3 Standalone display mode
In standalone mode the desktop is displayed on just one of the plugged in displays. It is possible to congure
the Rift as the sole display, however is becomes impractical due to problems interacting with the desktop.
A.3 Selecting A Display Device
Reading of EDID information from display devices has been found to occasionally be slow and unreliable. In
addition, EDID information may be cached leading to problems with stale data. As a result, display devices
may sometimes become associated with incorrect display names and resolutions, with arbitrary delays before
the information becomes current.
Because of these issues we adopt an approach which attempts to identify the Rift display name among the
attached display devices, however we do not require that it be found for an HMD device to be created using
the API.
If the Rift display device is not detected but the Rift is detected through USB, then we return an empty display
name string. In this case your application could attempt to locate it using additional information, for example
display resolution. The GetDeviceInfo call in the API returns the native resolution of the Rift display. This
can be used to match one of the available display devices.
In general, due to the uncertainty associated with identifying the Rift display device, it may make sense to
incorporate functionality into your application to allow the user to choose the display manually, for example
from a drop-down list of enumerated display devices. One additional root cause of the above scenario, aside
from EDID issues, is that the user failed to plug in the Rift video cable. We recommend appropriate assistance
inside your application to help users troubleshoot an incorrectly connected Rift device.
A.3.1 Windows
The following code outlines how to create an HMD device and get the display device name on Windows:
37
DeviceManager
*
pManager = DeviceManager::Create();
HMDDevice
*
pHMD = pManager->EnumerateDevices<HMDDevice>().CreateDevice();
HMDInfo info;
pHMD->GetDeviceInfo(&HMDInfo);
char
*
displayName = info.DisplayDeviceName;
If there are no EDID issues and we detect the Rift display device successfully, then we return the display
name corresponding to that device, for example \\.\DISPLAY1\Monitor0.
To display the video output on the Rift, the application needs to match the display name determined above
to an object used by the rendering API. Unfortunately, each rendering system used on Windows (OpenGL,
Direct3D 9, Direct3D 10-11) uses a different approach. OpenGL uses the display device name returned in
HMDInfo, while in Direct3D the display name must be matched against a list of displays returned by D3D.
The DesktopX and DesktopY members of HMDInfo can be used to position a window on the Rift if you do
not want to use fullscreen mode.
When using Direct3D 10 or 11, the following code shows how to obtain an IDXGIOutput interface using the
DXGI API:
IDXGIOutput
*
searchForOculusDisplay(char
*
oculusDisplayName)
{
IDXGIFactory
*
pFactory;
CreateDXGIFactory(__uuidof(IDXGIFactory), (void
**
)(&pFactory));
UInt32 adapterIndex = 0;
IDXGIAdapter
*
pAdapter;
// Iterate through adapters.
while (pFactory->EnumAdapters(adapterIndex, &pAdapter) !=
DXGI_ERROR_NOT_FOUND)
{
UInt32 outputIndex = 0;
IDXGIOutput
*
pOutput;
// Iterate through outputs.
while (pAdapter->EnumOutputs(outputIndex, &pOutput) !=
DXGI_ERROR_NOT_FOUND)
{
DXGI_OUTPUT_DESC outDesc;
pOutput->GetDesc(&outDesc);
char
*
outputName = outDesc.DeviceName;
// Try and match the first part of the display name.
// For example an outputName of "\\.\DISPLAY1" might
// correspond to a displayName of "\\.\DISPLAY1\Monitor0".
// If two monitors are setup in duplicate mode then they will
// have the same display part in their display name.
if (strstr(oculusDisplayName, outputName) == oculusDisplayName)
{
return pOutput;
}
}
}
return NULL;
}
38
After youve successfully obtained the IDXGIOutput interface, you can set your Direct3D swap-chain to
render to it in fullscreen mode using the following:
IDXGIOutput
*
pOculusOutputDevice = searchForOculusDisplay(oculusDisplayName);
pSwapChain->SetFullscreenState(TRUE, pOculusOutputDevice);
When using Direct3D 9, you must create the Direct3DDevice with the adapter number corresponding to the
Rift (or other display that you want to output to). This next function shows how to nd the adapter number:
unsigned searchForOculusDisplay(const char
*
oculusDisplayName)
{
for (unsigned Adapter=0; Adapter < Direct3D->GetAdapterCount(); Adapter++)
{
MONITORINFOEX Monitor;
Monitor.cbSize = sizeof(Monitor);
if (::GetMonitorInfo(Direct3D->GetAdapterMonitor(Adapter), &Monitor) && Monitor.szDevice[0])
{
DISPLAY_DEVICE DispDev;
memset(&DispDev, 0, sizeof(DispDev));
DispDev.cb = sizeof(DispDev);
if (::EnumDisplayDevices(Monitor.szDevice, 0, &DispDev, 0))
{
if (strstr(DispDev.DeviceName, oculusDisplayName))
{
return Adapter;
}
}
}
}
return D3DADPATER_DEFAULT;
}
Unfortunately, you must re-create the device to switch fullscreen displays. You can use a fullscreen device
for windowed mode without recreating it (although it still has to be reset).
A.3.2 MacOS
When running on MacOS, the HMDInfo contains a CGDirectDisplayId which you should use to target
the Rift in fullscreen. If the Rift is connected (and detected by libovr), its DisplayId is stored, otherwise
DisplayId contains 0. The following is an example of how to use this to make a Cocoa NSView fullscreen
on the Rift:
DeviceManager
*
pManager = DeviceManager::Create();
HMDDevice
*
pHMD = pManager->EnumerateDevices<HMDDevice>().CreateDevice();
HMDInfo hmdInfo;
pHMD->GetDeviceInfo(&hmdInfo);
CGDirectDisplayId RiftDisplayId = (CGDirectDisplayId) hmdInfo.DisplayId;
NSScreen
*
usescreen = [NSScreen mainScreen];
NSArray
*
screens = [NSScreen screens];
for (int i = 0; i < [screens count]; i++)
{
NSScreen
*
s = (NSScreen
*
)[screens objectAtIndex:i];
CGDirectDisplayID disp = [NSView displayFromScreen:s];
39
if (disp == RiftDisplayId)
usescreen = s;
}
[View enterFullScreenMode:usescreen withOptions:nil];
The DesktopX and DesktopY members of HMDInfo can be used to position a window on the Rift if you do
not want to use fullscreen mode.
A.4 Rift Display Considerations
There are several considerations when it comes to managing the Rift display on a desktop OS.
A.4.1 Duplicate mode VSync
In duplicate monitor mode it is common for the supported video timing information to be different across
the participating monitors, even when displaying the same resolution. When this occurs the video scans on
each display will be out of sync and the software vertical sync mechanism will be associated with only one
of the displays. In other words, swap-chain buffer switches (for example following a glSwapBuffers or
Direct3D Present call) will only occur at the correct time for one of the displays, and tearing will occur
on the other display. In the case of the Rift, tearing is very distracting, and so ideally wed like to force it
to have vertical sync priority. Unfortunately, the ability to do this is not something that weve found to be
currently exposed in system APIs.
A.4.2 Extended mode problems
When extended display mode is used in conjunction with the Rift, the desktop will extend partly onto the
regular monitors and partly onto the Rift. Since the Rift displays different portions of the screen to the left
and right eyes, it is not suited to displaying the desktop in a usable form, and confusion may arise if icons or
windows nd their way onto the portion of the desktop displayed on the Rift.
A.4.3 Observing Rift output on a monitor
Sometimes its desirable to be able to see the same video output on the Rift and on an external monitor. This
can be particularly useful when demonstrating the device to a new user, or during application development.
One way to achieve this is through the use of duplicate monitor mode as described above, however we dont
currently recommend this approach due to the vertical sync priority issue. An alternative approach is through
the use of a DVI or HDMI splitter. These take the video signal coming from the display adapter and duplicate
it such that it can be fed to two or more display devices simultaneously. Unfortunately this can also cause
problems depending on how the EDID data is managed. Specically, with several display devices connected
to a splitter, which EDID information should be reported back to the graphics adapter? Low cost HDMI
splitters have been found to exhibit unpredictable behavior. Typically they pass on EDID information from
one of the attached devices, but exactly how the choice is determined is often unknown. Higher cost devices
may have explicit schemes (for example they report the EDID from the display plugged into output port one),
but these can cost more than the Rift itself! Generally, the use of third party splitters and video switching
40
boxes means that Rift EDID data may not be reliably reported to the OS, and therefore libovr will not be able
to identify the Rift.
A.4.4 Windows: Direct3D enumeration
As described above the nature of the Rift display means that it is not suited to displaying the Windows
desktop. As a result you might be inclined to set standalone mode for your regular monitor to remove the
Rift from the list of devices displaying the Windows desktop. Unfortunately this also causes the device to no
longer be enumerated when querying for output devices during Direct3D setup. As a result, the only viable
option currently is to use the Rift in extended display mode.
41
B Chromatic Aberration
Chromatic aberration is a visual artifact seen when viewing images through lenses. The phenomenon causes
colored fringes to be visible around objects, and is increasingly more apparent as our view shifts away from
the center of the lens. The effect is due to the refractive index of the lens varying for different wavelengths of
light (shorter wavelengths towards the blue end of the spectrum are refracted less than longer wavelengths
towards the red end). Since the image being displayed on the Rift is composed of individual red, green, and
blue pixels,
3
it too is susceptible to the unwanted effects of chromatic aberration. The manifestation, when
looking through the Rift, is that the red, green, and blue components of the image appear to be scaled out
radially, and by differing amounts. Exactly how apparent the effect is depends on the image content and to
what degree users are concentrating on the periphery of the image versus the center.
B.1 Correction
Fortunately, pixel shaders provide the means to signicantly reduce the degree of visible chromatic aberration,
albeit at some additional GPU expense. This is done by pre-transforming the image so that after the lens
causes chromatic aberration, the end result is a more normal looking image. This is exactly analogous to the
way in which we pre-distort the image to cancel out the distortion effects generated by the lens.
B.2 Sub-channel aberration
Although we can reduce the artifacts through the use of a pixel shader, its important to note that this approach
cannot remove them completely for an LCD display panel. This is due to the fact that each color channel is
actually comprised of a range of visible wavelengths, each of which is refracted by a different amount when
viewed through the lens. As a result, although were able to distort the image for each channel to bring the
peak frequencies back into spatial alignment, its not possible to compensate for the aberration that occurs
within a color channel. Typically, when designing optical systems, chromatic aberration across a wide range
wavelengths is managed by carefully combining specic optical elements (in other texts, for example, look
for achromatic doublets).
B.3 Shader implementation
The pixel shader for removing lens distortion was shown in Figure 7. This applies a radial distortion based
on the distance of the display pixel from the center of distortion (the point at which the axis through the
lens intersects the screen). Fortunately, chromatic aberration correction is also dependent on the same radial
distance, and so we can achieve it with minimal modications to the distortion shader.
The approach we employ applies an additional radial scale to the red and blue color channels in addition to
the lens distortion. The scaling function for lens distortion was introduced in (13) and is repeated below:
f(r) = k
0
+ k
1
r
2
+ k
2
r
4
+ k
3
r
6
.
The additional red and blue scaling is expressed as follows:
3
In a typical LCD display, pixel color is achieved using red, green, and blue dyes, which selectively absorb wavelengths from the
back-light.
42
f
R
(r) = f(r)(c
0
+ c
1
r
2
) (17)
f
G
(r) = f(r) (18)
f
B
(r) = f(r)(c
2
+ c
3
r
2
). (19)
The use of a quadratic term provides additional exibility when tuning the function for medium and high
values of r.
The following code shows the resulting pixel shader for correcting both lens distortion and chromatic
aberration:
Texture2D Texture : register(t0);
SamplerState Linear : register(s0);
float2 LensCenter;
float2 ScreenCenter;
float2 Scale;
float2 ScaleIn;
float4 HmdWarpParam;
float4 ChromAbParam;
float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,
in float2 oTexCoord : TEXCOORD0) : SV_Target
{
float2 theta = (oTexCoord - LensCenter)
*
ScaleIn; // Scales to [-1, 1]
float rSq = theta.x
*
theta.x + theta.y
*
theta.y;
float2 theta1 = theta
*
(HmdWarpParam.x + HmdWarpParam.y
*
rSq +
HmdWarpParam.z
*
rSq
*
rSq +
HmdWarpParam.w
*
rSq
*
rSq
*
rSq);
// Detect whether blue texture coordinates are out of range
// since these will scaled out the furthest.
float2 thetaBlue = theta1
*
(ChromAbParam.z + ChromAbParam.w
*
rSq);
float2 tcBlue = LensCenter + Scale
*
thetaBlue;
if (any(clamp(tcBlue, ScreenCenter -float2(0.25,0.5),
ScreenCenter+float2(0.25, 0.5)) - tcBlue))
return 0;
// Now do blue texture lookup.
float blue = Texture.Sample(Linear, tcBlue).b;
// Do green lookup (no scaling).
float2 tcGreen = LensCenter + Scale
*
theta1;
float green = Texture.Sample(Linear, tcGreen).g;
// Do red scale and lookup.
float2 thetaRed = theta1
*
(ChromAbParam.x + ChromAbParam.y
*
rSq);
float2 tcRed = LensCenter + Scale
*
thetaRed;
float red = Texture.Sample(Linear,o tcRed).r;
return float4(red, green, blue, 1);
}
Note the following:
We apply the red and blue scaling after the lens distortion to reduce the amount of shader computation.
We determine the texture coordinates for the blue component rst because this extends farther than the
other color channels and enables us to exit early if the texture coordinates lie outside of the range of
the half-screen.
43
The shader now has to perform three texture lookups instead of one, and only one color component
from each lookup is used.
44
C SDK Samples and Gamepad Usage
Some of the Oculus SDK samples make use of gamepad controllers, for example to move around the virtual
world. This section describes the devices that we currently support and setup instructions. We currently do
not support gamepad controllers in the Linux samples.
Xbox 360 wired controller for Windows
1. Plug the device into a USB port.
2. Windows should recognize the controller and install any necessary drivers.
Logitech F710 Wireless Gamepad
Windows
1. Put the controller into XInput mode by moving the switch on the front of the controller to the X
position.
2. Press a button on the controller so that the green LED next to the Mode button begins to ash.
3. Plug the USB receiver into the PC while the LED is ashing.
4. Windows should recognize the controller and install any necessary drivers.
Mac
1. Put the controller into DirectInput mode by moving the switch on the front of the controller to the D
position.
2. Press a button on the controller so that the green LED next to the Mode button begins to ash.
3. Plug the USB receiver into the PC while the LED is ashing.
4. OSX should recognize the controller and install any necessary drivers.
Sony PlayStation DUALSHOCK3 Controller
Mac
1. Be sure to turn off nearby PS3 consoles.
2. Go to the Bluetooth section of System Preferences....
3. Make sure the On and Discoverable check boxes are checked.
4. Plug the controller into the Mac using the USB cable.
5. Press the PS Button in the middle of the controller for 3 seconds and then remove the USB cable.
6. After removing the cable, the controller should immediately appear in the device list. If a dialog
appears requesting a passcode enter xxxx and press Pair.
45
7. Click on the gear beneath the list of Bluetooth devices and select Add to Favorites.
8. Click on the gear once more and select Update Services.
9. Quickly turn Bluetooth off and then immediately back on again.
10. Press the PS Button on the controller. The controller status should now appear as Connected.
46