Getting Started in VR with JS
Getting Started in VR with JS
The Dream of the 90s is Alive!
#empirejs2015
MARRY ME, ALICIA!
GO SARNIA BEES!
20 years later …
Why would this time be any
different?
VR SHE WROTE
JavaScript FTW
WebVR allows us to work in JS “native” browser
environment.
What’s a headset?
WOAH!
your app renders
1. delivers position data to
2. surface to display
stereoscopic image
.js
A device that gives positional
data & a surface to draw on?
Sounds awful like a smart phone!
google.com/get/cardboard/
I’ll be giving these out
Come see me in the Q&A lounge after this and
throughout the conference.
Forgiveness
please …
I AM A (DEMO) GOD HERE
Demos available at c5vr.com
And also in the Q&A Lounge after.
Download ALL THE THINGS!
Drivers and SDK from developer.oculus.com
Firefox WebVR Browser from mozvr.com/downloads
Chromium WebVR Browser from bit.ly/1DPjgDQ
What is WebVR?
What it's not!
Not a Virtual Reality DOM
Not a WebGL replacement
WebGL is a framework to build your own 3D graphics
rendering pipeline.
Math is HARD
Computing per pixel transform and coloring takes a lot
of work.
Use three.js
Provides a higher level abstraction that is easier to work
with.
c5vr.com/no_vr.html
no_vr.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>No VR</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<style>
body {
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/no_vr.js"></script>
</body>
</html>
no_vr.html
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/no_vr.js"></script>
</body>
</html>
no_vr.js - Create a Renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.3, 10000
);
var light = new THREE.PointLight(0xffffff, 1.0, 0);
light.position.set(0,0,0);
scene.add(light);
var bitGeometry = new THREE.DodecahedronGeometry(0.5);
no_vr.js - Let Your Scene be Seen
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.3, 10000
);
var light = new THREE.PointLight(0xffffff, 1.0, 0);
light.position.set(0,0,0);
scene.add(light);
var bitGeometry = new THREE.DodecahedronGeometry(0.5);
no_vr.js - Let there be light!
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.3, 10000
);
var light = new THREE.PointLight(0xffffff, 1.0, 0);
light.position.set(0,0,0);
scene.add(light);
var bitGeometry = new THREE.DodecahedronGeometry(0.5);
no_vr.js - Have Something to See
var light = new THREE.PointLight(0xffffff, 1.0, 0);
light.position.set(0,0,0);
scene.add(light);
var bitGeometry = new THREE.DodecahedronGeometry(0.5);
var bitMaterial = new THREE.MeshLambertMaterial({
color: 0x00ffff, shading: THREE.FlatShading
});
var bit = new THREE.Mesh(bitGeometry, bitMaterial);
bit.position.z = -2;
scene.add(bit);
var planeGeometry =
new THREE.PlaneBufferGeometry(1000, 1000, 1000);
var planeMaterial = new THREE.MeshPhongMaterial({
no_vr.js - Get Animated
scene.add(plane);
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
no_vr.js - Get Animated
scene.add(plane);
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
no_vr.js - Get Animated
scene.add(plane);
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
no_vr.js - Get Animated
scene.add(plane);
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
no_vr.js - Get Animated
scene.add(plane);
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
renderer.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
Apply some
WebVR and …
c5vr.com/basic_vr.html
What is WebVR?
WebVR is a device interface
Detect and poll Head Mounted Displays (HMDs) and
other position reporting devices.
navigator.getVRDevices()
Through callback or promise returns a list of VR devices
of two types …
PositionSensorVRDevice
Information about position and orientation
HMDVRDevice
Informs you have a surface to render on and
information about the eyes.
Math is HARD
Translating all this is difficult, but luckily three.js comes
to the rescue.
Nearly all WebVR demos use
VRControls and VREffect
Found in three.js examples alongside other great
utilities.
github.com/mrdoob/three.js/tree/master/examples/js
basic_vr.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Basic VR</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<style>
body {
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/basic_vr.js"></script>
</body>
</html>
basic_vr.html
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/basic_vr.js"></script>
</body>
</html>
basic_vr.js - Introduce VRControls
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.3, 10000
);
var controls = new THREE.VRControls(camera);
var effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
var light = new THREE.PointLight(0xffffff, 1.0, 15);
light.position.set(0,0,0);
VRControls
Handles PositionSensorDevice and manipulates the
camera orientation.
VRControl.js - Initialization
function gotVRDevices( devices )
devices = filterInvalidDevices( devices );
for ( var i = 0; i < devices.length; i ++ ) {
if ( devices[ i ] instanceof PositionSensorVRDevice ) {
vrInputs.push( devices[ i ] );
}
}
if ( onError ) onError( 'HMD not available' );
}
if ( navigator.getVRDevices ) {
navigator.getVRDevices().then( gotVRDevices );
}
basic_vr.js - Introduce VREffect
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.3, 10000
);
var controls = new THREE.VRControls(camera);
var effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);
var light = new THREE.PointLight(0xffffff, 1.0, 15);
light.position.set(0,0,0);
VREffect
Handles HMDVRDevice and uses it to render a
stereoscopic view
VREffect.js - Initialization
function gotVRDevices( devices ) {
for ( var i = 0; i < devices.length; i ++ ) {
if ( devices[ i ] instanceof HMDVRDevice ) {
///…
}
if ( vrHMD === undefined ) {
if ( onError ) onError( 'HMD not available' );
}
}
VREffect.js - Remember the "..."
var eyeParamsL = vrHMD.getEyeParameters( 'left' );
var eyeParamsR = vrHMD.getEyeParameters( 'right' );
eyeTranslationL = eyeParamsL.eyeTranslation;
eyeTranslationR = eyeParamsR.eyeTranslation;
eyeFOVL = eyeParamsL.recommendedFieldOfView;
eyeFOVR = eyeParamsR.recommendedFieldOfView;
basic_vr.js - Go Full Screen
var vrMode = false;
function enterVR() {
effect.setFullScreen(true);
vrMode = true;
}
function exitVR() {
effect.setFullScreen(false);
vrMode = false;
};
function toggleVR() {
if (!vrMode) {
enterVR();
VREffect - Full Screen VR display
this.setFullScreen = function ( boolean ) {
if ( vrHMD === undefined ) return;
if ( isFullscreen === boolean ) return;
if ( canvas.mozRequestFullScreen ) {
canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
} else if ( canvas.webkitRequestFullscreen ) {
canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
}
};
basic_vr.js - Let Reality Take Control
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
controls.update();
effect.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
VRControls.js - Get State from VR Device
this.update = function () {
for ( var i = 0; i < vrInputs.length; i ++ ) {
var vrInput = vrInputs[ i ];
var state = vrInput.getState();
if ( state.orientation !== null ) {
object.quaternion.copy( state.orientation );
}
if ( state.position !== null ) {
object.position.copy( state.position )
.multiplyScalar( scope.scale );
}
}
};
basic_vr.js - The Full VR Effect
function animate(time) {
bit.position.x = Math.sin(time/1000) * 2;
bit.position.y = Math.sin(time/2000);
bit.rotation.y += 0.01;
bit.rotation.z += 0.01;
controls.update();
effect.render(scene, camera);
requestAnimationFrame( animate );
}
animate();
Two Cameras for the price of
ONE!
It creates a camera for each eye, shifts them from the
main camera location based on HMDVRDevice eye
information, then renders the view for each!
Confession
This doesn’t work on mobile
browsers!
WebVR is still in development.
webvr-polyfill
Supplies Mobile device gyroscope as
PositionSensorVRDevice, screen as HMDDevice.
github.com/borismus/webvr-polyfill
webvr-boilerplate
A very basic skeleton much like you’ve seen here today.
github.com/borismus/webvr-boilerplate
basic_vr_mobile.html - Drop It In
<!DOCTYPE html>
<html lang="en">
<head>
<title>Basic VR Mobile</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<style>
body {
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/webvr-polyfill.js"></script>
<script src="js/basic_vr.js"></script>
</body>
</html>
basic_vr_mobile.html - Drop It In
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/webvr-polyfill.js"></script>
<script src="js/basic_vr.js"></script>
</body>
</html>
I know what
you’re thinking …
Simulator Sickness is Real …
Need to hit 60-90 fps.
Motion data already laggy.
Additional disadvantage of being in browser.
More graphics you push the harder this gets.
Consider 360 Video
Spherically map videos
around each camera/eye
Again, math is HARD!
See three.js vr_video or eleVR-Web-Player for example.
Interaction
All the HTML5 Things
Keyboard, mouse, gamepad …
Leap Motion is Strongly
Embracing WebVR
Works directly with three.js
leapmotion.com/product/vr
c5vr.com/leap_motion.html
leap_motion.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Leap Motion VR</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<style>
body {
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/webvr-polyfill.js"></script>
<script src="//js.leapmotion.com/leap-0.6.3.min.js"></script>
<script src="//js.leapmotion.com/leap-plugins-0.1.9.min.js"></script>
<script src="//js.leapmotion.com/leap.rigged-hand-0.1.7.min.js"></script>
<script src="js/leap_motion.js"></script>
</body>
</html>
leap_motion.html
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/VRControls.js"></script>
<script src="js/VREffect.js"></script>
<script src="js/webvr-polyfill.js"></script>
<script src="//js.leapmotion.com/leap-0.6.3.min.js"></script>
<script src="//js.leapmotion.com/leap-plugins-0.1.9.min.js"></
script>
<script src="//js.leapmotion.com/leap.rigged-hand-0.1.7.min.js"
script>
<script src="js/leap_motion.js"></script>
</body>
</html>
leap_motion.js
Leap.loop();
Leap.loopController.use('transform', {
vr: true,
effectiveParent: camera
});
Leap.loopController.use('riggedHand', {
parent: scene,
renderer: renderer,
materialOptions: {
emissive: new THREE.Color(0x00aaaa)
}
});
animate();
Confession 2.0
This doesn’t work on mobile*
* without hacks
One interaction
to rule them all
Gaze
Where you are looking can trigger changes, from
environment to storyline.
c5vr.com/raycast_vr.html
raycast.js - Gaze
var raycaster = new THREE.Raycaster();
var center = new THREE.Vector2();
function gaze() {
if(!audioPlaying) {
raycaster.setFromCamera(center, camera);
var intersects = raycaster.intersectObjects([bit]);
if (intersects.length > 0) {
audioPlaying = true;
if (Math.random() < 0.5) {
switchBitTo(1);
yes.play();
} else {
switchBitTo(0);
no.play();
Responsive VR
For “simple” devices, it’s a roller coaster ride.
More complex, more interactions.
Math is HARD
Collision detection, physics, etc.
Unity publishes to multiple
platforms
Plugins for VR available. Support directly baked in 5.1
#pragma strict
var center : GameObject;
var axis : Vector3;
function Start () {
}
function Update () {
transform.RotateAround(
center.transform.position, axis,30 * Time.deltaTime
);
}
Orbit.js
Cores.js
function beingLookedAt() {
var hit : RaycastHit;
var ray = new Ray(Camera.main.transform.position,
Camera.main.transform.forward);
return GetComponent
.<Collider>()
.Raycast(ray, hit, Mathf.Infinity);
}
Unity now exports to WebGL
An idea …
Build a WebVR Unity plugin
Bridges when publishing to WebGL.
Design once, distribute everywhere, run anywhere.
NOW YOU’RE PLAYING WITH POWER
Gotchas
Again, Simulator Sickness is
Real …
It’ll get better, but start with easy motions
3D Modelling is Hard
Spent most of my time on it.
Develop interactions first!
Then find a friend.
The ground is constantly
changing
Beta Software running on Beta Hardware.
Things break constantly.
So that’s it …
Except for one thing …?
Why would this time be any
different?
Answer: Because of you …
Thanks!
e: rudy@carbonfive.com
t: @rudy

Getting Started in VR with JS

  • 1.
  • 2.
    Getting Started inVR with JS The Dream of the 90s is Alive! #empirejs2015
  • 7.
  • 8.
  • 14.
  • 18.
    Why would thistime be any different?
  • 19.
  • 20.
    JavaScript FTW WebVR allowsus to work in JS “native” browser environment.
  • 24.
    What’s a headset? WOAH! yourapp renders 1. delivers position data to 2. surface to display stereoscopic image .js
  • 25.
    A device thatgives positional data & a surface to draw on? Sounds awful like a smart phone!
  • 27.
  • 28.
    I’ll be givingthese out Come see me in the Q&A lounge after this and throughout the conference.
  • 29.
  • 30.
    I AM A(DEMO) GOD HERE
  • 31.
    Demos available atc5vr.com And also in the Q&A Lounge after.
  • 32.
    Download ALL THETHINGS! Drivers and SDK from developer.oculus.com Firefox WebVR Browser from mozvr.com/downloads Chromium WebVR Browser from bit.ly/1DPjgDQ
  • 33.
  • 34.
  • 35.
    Not a VirtualReality DOM
  • 39.
    Not a WebGLreplacement WebGL is a framework to build your own 3D graphics rendering pipeline.
  • 40.
    Math is HARD Computingper pixel transform and coloring takes a lot of work.
  • 42.
    Use three.js Provides ahigher level abstraction that is easier to work with.
  • 43.
  • 44.
    no_vr.html <!DOCTYPE html> <html lang="en"> <head> <title>NoVR</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <style> body { background-color: #000; color: #fff; margin: 0px; padding: 0; overflow: hidden; } </style> </head> <body> <script src="js/three.js"></script> <script src="js/no_vr.js"></script> </body> </html>
  • 45.
  • 46.
    no_vr.js - Createa Renderer var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.3, 10000 ); var light = new THREE.PointLight(0xffffff, 1.0, 0); light.position.set(0,0,0); scene.add(light); var bitGeometry = new THREE.DodecahedronGeometry(0.5);
  • 47.
    no_vr.js - LetYour Scene be Seen var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.3, 10000 ); var light = new THREE.PointLight(0xffffff, 1.0, 0); light.position.set(0,0,0); scene.add(light); var bitGeometry = new THREE.DodecahedronGeometry(0.5);
  • 48.
    no_vr.js - Letthere be light! var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.3, 10000 ); var light = new THREE.PointLight(0xffffff, 1.0, 0); light.position.set(0,0,0); scene.add(light); var bitGeometry = new THREE.DodecahedronGeometry(0.5);
  • 49.
    no_vr.js - HaveSomething to See var light = new THREE.PointLight(0xffffff, 1.0, 0); light.position.set(0,0,0); scene.add(light); var bitGeometry = new THREE.DodecahedronGeometry(0.5); var bitMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff, shading: THREE.FlatShading }); var bit = new THREE.Mesh(bitGeometry, bitMaterial); bit.position.z = -2; scene.add(bit); var planeGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 1000); var planeMaterial = new THREE.MeshPhongMaterial({
  • 50.
    no_vr.js - GetAnimated scene.add(plane); function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; renderer.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 51.
    no_vr.js - GetAnimated scene.add(plane); function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; renderer.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 52.
    no_vr.js - GetAnimated scene.add(plane); function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; renderer.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 53.
    no_vr.js - GetAnimated scene.add(plane); function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; renderer.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 54.
    no_vr.js - GetAnimated scene.add(plane); function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; renderer.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 55.
  • 56.
  • 57.
  • 59.
    WebVR is adevice interface Detect and poll Head Mounted Displays (HMDs) and other position reporting devices.
  • 60.
    navigator.getVRDevices() Through callback orpromise returns a list of VR devices of two types …
  • 61.
  • 62.
    HMDVRDevice Informs you havea surface to render on and information about the eyes.
  • 63.
    Math is HARD Translatingall this is difficult, but luckily three.js comes to the rescue.
  • 64.
    Nearly all WebVRdemos use VRControls and VREffect Found in three.js examples alongside other great utilities. github.com/mrdoob/three.js/tree/master/examples/js
  • 65.
    basic_vr.html <!DOCTYPE html> <html lang="en"> <head> <title>BasicVR</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <style> body { background-color: #000; color: #fff; margin: 0px; padding: 0; overflow: hidden; } </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <script src="js/VREffect.js"></script> <script src="js/basic_vr.js"></script> </body> </html>
  • 66.
    basic_vr.html </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <scriptsrc="js/VREffect.js"></script> <script src="js/basic_vr.js"></script> </body> </html>
  • 67.
    basic_vr.js - IntroduceVRControls var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.3, 10000 ); var controls = new THREE.VRControls(camera); var effect = new THREE.VREffect(renderer); effect.setSize(window.innerWidth, window.innerHeight); var light = new THREE.PointLight(0xffffff, 1.0, 15); light.position.set(0,0,0);
  • 68.
    VRControls Handles PositionSensorDevice andmanipulates the camera orientation.
  • 69.
    VRControl.js - Initialization functiongotVRDevices( devices ) devices = filterInvalidDevices( devices ); for ( var i = 0; i < devices.length; i ++ ) { if ( devices[ i ] instanceof PositionSensorVRDevice ) { vrInputs.push( devices[ i ] ); } } if ( onError ) onError( 'HMD not available' ); } if ( navigator.getVRDevices ) { navigator.getVRDevices().then( gotVRDevices ); }
  • 70.
    basic_vr.js - IntroduceVREffect var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.3, 10000 ); var controls = new THREE.VRControls(camera); var effect = new THREE.VREffect(renderer); effect.setSize(window.innerWidth, window.innerHeight); var light = new THREE.PointLight(0xffffff, 1.0, 15); light.position.set(0,0,0);
  • 71.
    VREffect Handles HMDVRDevice anduses it to render a stereoscopic view
  • 72.
    VREffect.js - Initialization functiongotVRDevices( devices ) { for ( var i = 0; i < devices.length; i ++ ) { if ( devices[ i ] instanceof HMDVRDevice ) { ///… } if ( vrHMD === undefined ) { if ( onError ) onError( 'HMD not available' ); } }
  • 73.
    VREffect.js - Rememberthe "..." var eyeParamsL = vrHMD.getEyeParameters( 'left' ); var eyeParamsR = vrHMD.getEyeParameters( 'right' ); eyeTranslationL = eyeParamsL.eyeTranslation; eyeTranslationR = eyeParamsR.eyeTranslation; eyeFOVL = eyeParamsL.recommendedFieldOfView; eyeFOVR = eyeParamsR.recommendedFieldOfView;
  • 74.
    basic_vr.js - GoFull Screen var vrMode = false; function enterVR() { effect.setFullScreen(true); vrMode = true; } function exitVR() { effect.setFullScreen(false); vrMode = false; }; function toggleVR() { if (!vrMode) { enterVR();
  • 75.
    VREffect - FullScreen VR display this.setFullScreen = function ( boolean ) { if ( vrHMD === undefined ) return; if ( isFullscreen === boolean ) return; if ( canvas.mozRequestFullScreen ) { canvas.mozRequestFullScreen( { vrDisplay: vrHMD } ); } else if ( canvas.webkitRequestFullscreen ) { canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } ); } };
  • 76.
    basic_vr.js - LetReality Take Control function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; controls.update(); effect.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 77.
    VRControls.js - GetState from VR Device this.update = function () { for ( var i = 0; i < vrInputs.length; i ++ ) { var vrInput = vrInputs[ i ]; var state = vrInput.getState(); if ( state.orientation !== null ) { object.quaternion.copy( state.orientation ); } if ( state.position !== null ) { object.position.copy( state.position ) .multiplyScalar( scope.scale ); } } };
  • 78.
    basic_vr.js - TheFull VR Effect function animate(time) { bit.position.x = Math.sin(time/1000) * 2; bit.position.y = Math.sin(time/2000); bit.rotation.y += 0.01; bit.rotation.z += 0.01; controls.update(); effect.render(scene, camera); requestAnimationFrame( animate ); } animate();
  • 79.
    Two Cameras forthe price of ONE! It creates a camera for each eye, shifts them from the main camera location based on HMDVRDevice eye information, then renders the view for each!
  • 80.
  • 81.
    This doesn’t workon mobile browsers! WebVR is still in development.
  • 83.
    webvr-polyfill Supplies Mobile devicegyroscope as PositionSensorVRDevice, screen as HMDDevice. github.com/borismus/webvr-polyfill
  • 84.
    webvr-boilerplate A very basicskeleton much like you’ve seen here today. github.com/borismus/webvr-boilerplate
  • 85.
    basic_vr_mobile.html - DropIt In <!DOCTYPE html> <html lang="en"> <head> <title>Basic VR Mobile</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <style> body { background-color: #000; color: #fff; margin: 0px; padding: 0; overflow: hidden; } </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <script src="js/VREffect.js"></script> <script src="js/webvr-polyfill.js"></script> <script src="js/basic_vr.js"></script> </body> </html>
  • 86.
    basic_vr_mobile.html - DropIt In </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <script src="js/VREffect.js"></script> <script src="js/webvr-polyfill.js"></script> <script src="js/basic_vr.js"></script> </body> </html>
  • 87.
  • 90.
    Simulator Sickness isReal … Need to hit 60-90 fps. Motion data already laggy. Additional disadvantage of being in browser. More graphics you push the harder this gets.
  • 91.
  • 92.
    Spherically map videos aroundeach camera/eye Again, math is HARD! See three.js vr_video or eleVR-Web-Player for example.
  • 93.
  • 94.
    All the HTML5Things Keyboard, mouse, gamepad …
  • 96.
    Leap Motion isStrongly Embracing WebVR Works directly with three.js leapmotion.com/product/vr
  • 97.
  • 98.
    leap_motion.html <!DOCTYPE html> <html lang="en"> <head> <title>LeapMotion VR</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <style> body { background-color: #000; color: #fff; margin: 0px; padding: 0; overflow: hidden; } </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <script src="js/VREffect.js"></script> <script src="js/webvr-polyfill.js"></script> <script src="//js.leapmotion.com/leap-0.6.3.min.js"></script> <script src="//js.leapmotion.com/leap-plugins-0.1.9.min.js"></script> <script src="//js.leapmotion.com/leap.rigged-hand-0.1.7.min.js"></script> <script src="js/leap_motion.js"></script> </body> </html>
  • 99.
    leap_motion.html </style> </head> <body> <script src="js/three.js"></script> <script src="js/VRControls.js"></script> <scriptsrc="js/VREffect.js"></script> <script src="js/webvr-polyfill.js"></script> <script src="//js.leapmotion.com/leap-0.6.3.min.js"></script> <script src="//js.leapmotion.com/leap-plugins-0.1.9.min.js"></ script> <script src="//js.leapmotion.com/leap.rigged-hand-0.1.7.min.js" script> <script src="js/leap_motion.js"></script> </body> </html>
  • 100.
    leap_motion.js Leap.loop(); Leap.loopController.use('transform', { vr: true, effectiveParent:camera }); Leap.loopController.use('riggedHand', { parent: scene, renderer: renderer, materialOptions: { emissive: new THREE.Color(0x00aaaa) } }); animate();
  • 101.
  • 102.
    This doesn’t workon mobile* * without hacks
  • 103.
  • 104.
    Gaze Where you arelooking can trigger changes, from environment to storyline.
  • 106.
  • 107.
    raycast.js - Gaze varraycaster = new THREE.Raycaster(); var center = new THREE.Vector2(); function gaze() { if(!audioPlaying) { raycaster.setFromCamera(center, camera); var intersects = raycaster.intersectObjects([bit]); if (intersects.length > 0) { audioPlaying = true; if (Math.random() < 0.5) { switchBitTo(1); yes.play(); } else { switchBitTo(0); no.play();
  • 108.
    Responsive VR For “simple”devices, it’s a roller coaster ride. More complex, more interactions.
  • 109.
    Math is HARD Collisiondetection, physics, etc.
  • 111.
    Unity publishes tomultiple platforms Plugins for VR available. Support directly baked in 5.1
  • 114.
    #pragma strict var center: GameObject; var axis : Vector3; function Start () { } function Update () { transform.RotateAround( center.transform.position, axis,30 * Time.deltaTime ); } Orbit.js
  • 115.
    Cores.js function beingLookedAt() { varhit : RaycastHit; var ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward); return GetComponent .<Collider>() .Raycast(ray, hit, Mathf.Infinity); }
  • 116.
    Unity now exportsto WebGL An idea …
  • 117.
    Build a WebVRUnity plugin Bridges when publishing to WebGL. Design once, distribute everywhere, run anywhere. NOW YOU’RE PLAYING WITH POWER
  • 118.
  • 119.
    Again, Simulator Sicknessis Real … It’ll get better, but start with easy motions
  • 120.
    3D Modelling isHard Spent most of my time on it. Develop interactions first! Then find a friend.
  • 121.
    The ground isconstantly changing Beta Software running on Beta Hardware. Things break constantly.
  • 122.
  • 124.
    Except for onething …?
  • 125.
    Why would thistime be any different?
  • 126.
  • 127.