Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting Started with WebVR Contents Creation for Oculus Quest

Getting Started with WebVR Contents Creation for Oculus Quest

Slide deck of tutorial seminar which is going to be held 23 June for WebVR contents creation for Oculus Quest.

TakashiYoshinaga

June 21, 2019
Tweet

More Decks by TakashiYoshinaga

Other Decks in Technology

Transcript

  1. Getting Started with WebVR
    Contents Creation for Oculus Quest

    View full-size slide

  2. Please Download Sample Data
    http://arfukuoka.lolipop.jp/quest/Sample.zip

    View full-size slide

  3. What’s A-Frame
     JS library to create VR apps which work on web browser.
     3D objects can be placed by writing HTML tags.
     VR apps work on web browser. (ex. Chrome, Firefox)
     Also available on WinMR, HTC Vive and Oculus Quest.

    View full-size slide

  4. Experience
    Access to the official site of A-Frame
    (https://aframe.io/)

    View full-size slide

  5. Experience
    Sample demos are shown on the left column.
    サンプル

    View full-size slide

  6. Experience
    Let’s see the most primitive sample Hello WebVR.
    Hello WebVR

    View full-size slide

  7. Experience
    See around VR environment by mouse/key controll.
    Click inside the screen
    Rotation:drag & move a mouse
    Left/Right:[←][→] key
    Forward/Back:[↑][↓] key

    View full-size slide

  8. Another Example
    360°Images also work with A-Frame easily.
    360°Image
    RICOH Theta

    View full-size slide

  9. Of course you can play on Oculus Quest

    View full-size slide

  10. The Goal of This Tutorial
    https://youtu.be/J-AQpvOawJ0

    View full-size slide

  11. Let’s Experience on Oculus Quest!
    https://quest-demo.glitch.me

    View full-size slide

  12. You can See Also...
    Primitive sample of getting input from
    controllers
    https://github.com/TakashiYoshinaga/Oculus-
    Quest-Input-Sample
    Source code of the goal of today’s tutorial.
    https://github.com/TakashiYoshinaga/Oculus-
    Quest-Interaction-Sample

    View full-size slide

  13. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  14. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  15. Please log in your account of Glitch
    https://glitch.com/

    View full-size slide

  16. Getting Sample Code
    Click the link shown as GET STARTED
    GET STARTED

    View full-size slide

  17. Remixing the Sample Code
    Description of Hello WebVR
    Hello WebVR

    View full-size slide

  18. Remixing the Sample Code
    Click remix the starter example on Glitch
    Click

    View full-size slide

  19. Remixing the Sample Code
    Click “Remix your own” button shown on the
    right bottom of preview image.
    Click

    View full-size slide

  20. Click index.html to view the sample code.
    Click

    View full-size slide

  21. Explanation of the Sample Code


    Hello, WebVR! - A-Frame

    <br/>



    Description of 3D contents are written here.



     A-Frame library is imported between
     Description of 3D objects are written between

    View full-size slide

  22. Explanation of the Sample Code





    color="#FFC65D">
    width="4" height="4" color="#7BC8A4">

     Primitive objects are defined as a-xxx
    https://aframe.io/docs/0.9.0/primitives
    /a-box.html (ex. Detail of a-box)

    View full-size slide

  23. Run
    ①Show
    ②Next to The Code

    View full-size slide

  24. How to Modify Objects?
    You can change parameters of components.
    For example...
     position: x y z are used to modify the position of a CG.
    (0 1.25 -5)

    View full-size slide

  25. How to Modify Objects?
    Examples of components
     position:x y z are used to modify the position.
     rotation:x y z means rotation of each axis.
     color:color code are used to define the color.
     There are some parameters for each primitive object.
    X
    Z
    Y
    (0 1.25 -5) 【Others】
    radius
    width
    height
    depth
    src (image file)
    原点

    View full-size slide






  26. color="#FFC65D">
    width="4" height="4" color="#7BC8A4">

    Let’s Try to Edit Components
    #AAAAAA
    position="-1 0.5 -2" rotation="0 0 0"
    position="0 1.25 -4" radius="0.7"
    position="1 0.75 -2 height="0.7"
    position="0 0 0"
    width="10" height="10"

    View full-size slide

  27. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  28. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  29. Applying Texture Image

    View full-size slide

  30. Applying Texture Image
    ①assets
    ②Add Asset → Upload

    View full-size slide

  31. Applying Texture Image
    ①back.png
    ②Open

    View full-size slide

  32. Applying Texture Image
    Click

    View full-size slide

  33. Applying Texture Image
    ①Click
    ②Click out of the dialog

    View full-size slide

  34. Applying Texture Image
    index.html

    View full-size slide

  35. Applying Texture Image





    color="#FFC65D">
    width="10" height="10" color="#7BC8A4" shadow>

     Replace the a-sphere’s material to src instead color.
    To be continued to the next page...
    Delete color

    View full-size slide

  36. Applying Texture Image





    color="#FFC65D">
    width="10" height="10" src="URL" shadow>
    Paste the URL
    Add src
     Change the a-sphere’s material to src instead color.
     Paste the URL of the texture image on the right side of src.

    View full-size slide

  37. Duplication of Textured Plane





    color="#FFC65D">
    width="10" height="10" src="URL" shadow>
    width="10" height="10" src=“URL" shadow>

    Copy & Paste

    View full-size slide

  38. Duplication of Textured Plane
    width="10" height="10" src="URL">
    width="10" height="10" src="URL">
    width="10" height="5" src="URL">
    width="10" height="5" src="URL">
    width="10" height="5" src="URL">
    width="10" height="5" src="URL">
    Make 4 walls with the same procedure.

    View full-size slide

  39. Modify URL of Your VR Content
    Click Text
    Modify a name

    View full-size slide

  40. Modify URL of Your VR Content
    https://XXXX-XXXX.glitch.me

    View full-size slide

  41. Test on Oculus Quest
    You will be immersed in the VR space you created.

    View full-size slide

  42. Adding Controller Entity
    width="10" height="10" src="URL" shadow>
    width="10" height="10" src="URL" shadow>
    width="10" height="5" src="URL" shadow>
    width="10" height="5" src="URL" shadow>
    width="10" height="5" src="URL" shadow>
    width="10" height="5" src="URL" shadow>


    enables us to define original entity
    instead pre-defined primitive entities.

    View full-size slide

  43. Before Test on Oculus Quest...
    Reload!

    View full-size slide

  44. Test on Oculus Quest
    Oculus Touch’s controller, not Quest’s, will appear.

    View full-size slide

  45. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  46. Today’s Tutorial
    Step1: Learning basic usage of A-Frame
    Step2: Making the simple VR environment.
    Step3: Writing script to achieve interaction.

    View full-size slide

  47. Definition of Input Behavior
    width="10" height="10" src="URL">
    width="10" height="10" src="URL">
    width="10" height="10" src="URL">
    width="10" height="10" src="URL">


    Add input-listen component to describe behavior
    when input of controller is received.
    From next page, source code of input-listen is explained.

    View full-size slide

  48. Definition of Input Behavior
    <br/>AFRAME.registerComponent('input-listen', {<br/>init:function () { //Initialization<br/>} ,<br/>tick: function () { //Update<br/>}<br/>});<br/>

    color="#4CC3D9" shadow>




    1.txt

    View full-size slide

  49. Definition of Input Behavior
    init:function () { //Initialization
    //Called when x button begin to be pressed (for Left Hand)
    this.el.addEventListener('xbuttondown', function (e) {
    /*Do something*/
    });
    //Called when x button is released (for Left Hand)
    this.el.addEventListener('xbuttonup', function (e) {
    });
    //Called when grip button begin to be pressed (for Both Hand)
    this.el.addEventListener('gripdown', function (e) {
    });
    //Called when grip button released. (for Both Hand)
    this.el.addEventListener('gripup', function (e) {
    });
    } , 2.txt

    View full-size slide

  50. Visualization of Input Info with Text


    height="5" src="URL" shadow>

    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">





    3.txt

    View full-size slide

  51. Visualization of Input Info with Text
    const txt = document.getElementById("txt");
    //Called when x button begin to be pressed (for Left Hand)
    this.el.addEventListener('xbuttondown', function (e) {
    txt.setAttribute("value", "x pressed");
    });
    //Called when x button is released (for Left Hand)
    this.el.addEventListener('xbuttonup', function (e) {
    txt.setAttribute("value", "x released");
    });
    //Called when grip button begin to be pressed (for Both Hand)
    this.el.addEventListener('gripdown', function (e) {
    txt.setAttribute("value", "grip pressed");
    });
    //Called when grip button released. (for Both Hand)
    this.el.addEventListener('gripup', function (e) {
    txt.setAttribute("value", "grip released");
    }); 4.txt

    View full-size slide

  52. Before the Test on Oculus Quest
    Reload!!

    View full-size slide

  53. Test on Oculus Quest
    Each action of grip or a-button will appear.

    View full-size slide

  54. Reference
    https://aframe.io/docs/0.9.0/components/oculus-touch-controls.html
    ①Oculus-touch-controls
    ②Events
    Info of events are explained in the official reference in detail.

    View full-size slide

  55. Duplication of the Project
    Remix Project
    I recommend to preserve this code as the primitive project.
    Click the content name

    View full-size slide

  56. Modification of URL of the New Content
    Click the content name
    Replace the name as you like

    View full-size slide

  57. Delete Lines of Visualization of Input
    const txt = document.getElementById("txt");
    //Called when x button begin to be pressed (for Left Hand)
    this.el.addEventListener('xbuttondown', function (e) {
    txt.setAttribute("value", "x pressed");
    });
    //Called when x button is released (for Left Hand)
    this.el.addEventListener('xbuttonup', function (e) {
    txt.setAttribute("value", "x released");
    });
    //Called when grip button begin to be pressed (for Both Hand)
    this.el.addEventListener('gripdown', function (e) {
    txt.setAttribute("value", "grip pressed");
    });
    //Called when grip button released. (for Both Hand)
    this.el.addEventListener('gripup', function (e) {
    txt.setAttribute("value", "grip released");
    });

    View full-size slide

  58. Enabling Teleportation to the Content



    Hello, WebVR! • A-Frame




    5.txt

    View full-size slide

  59. Enabling Teleportation to the Content




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">







    Append camera and controller as children of

    to get them to be allowed jumping together to th destination.

    View full-size slide

  60. Enabling Teleportation to the Content




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    laser-controls="hand: left" input-listen>



    Next, teleportation function will be added to left-hand controller.

    View full-size slide

  61. Enabling Teleportation to the Content




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    laser-controls="hand: left" input-listen>



    Add teleport-controls component to left-hand controller.
    6.txt

    View full-size slide

  62. Supplementation 1




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    laser-controls="hand: left" input-listen>



    It means to allow jumpping #CameraRig and its children from #head

    View full-size slide

  63. Supplementation 2




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    laser-controls="hand: left" input-listen>


    teleportstart is called to start pointing destination.
    teleportend is called to jump to pointed position.

    View full-size slide

  64. Enabling Teleportation to the Content
    init:function () {
    const txt = document.getElementById("txt");
    ////Called when x button begin to be pressed
    this.el.addEventListener('xbuttondown', function (e) {
    //Start to point destination
    this.emit('teleportstart');
    });
    // //Called when x button is released
    this.el.addEventListener('xbuttonup', function (e) {
    //Jump to pointed position.
    this.emit('teleportend');
    });
    /*Following code is omitted in this slide.*/
    }

    View full-size slide

  65. Check the URL Before Starting Test
    Remember
    the project name
    https://XXX-XXX.glitch.me

    View full-size slide

  66. Test on Oculus Quest
    Teleportation will be available.

    View full-size slide

  67. Text Step: Manipulation
    raycaster
    Checking
    intersection
    intersect
    Gripdown
    Manipulate!

    View full-size slide

  68. Adding Raycaster Component




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    laser-controls="hand: left" input-listen>
    laser-controls="hand: right" input-listen>


    Find controllers

    View full-size slide

  69. Adding Raycaster Component




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend“
    raycaster="objects: .collidable; far:1.2;"
    laser-controls="hand: left" input-listen>
    laser-controls="hand: right" input-listen>
    Raycaster will interact with collidable entity.(see next page)
    It just works when distance between controller & object is in 1.2m

    View full-size slide

  70. Adding Raycaster Component




    scale="0.5 0.5 0.5" align="center" color="#FFFFFF">


    "cameraRig: #cameraRig; teleportOrigin:#head;
    startEvents: teleportstart; endEvents: teleportend"
    raycaster="objects: .collidable; far:1.2;"
    laser-controls="hand: left" input-listen>
    laser-controls="hand: right" input-listen>
    Raycaster will interact with collidable entity.(see next page)
    It just works when distance between controller & object is in 1.2m

    View full-size slide

  71. Setting Entities as Collidable Object

    color="#4CC3D9" shadow>
    color="#EF2D5E" shadow>
    height="0.6" color="#FFC65D" shadow>
    src="URL" shadow>

    Checking intersection
    with raycaster is ON
    Floor and walls are
    not checked intersection

    View full-size slide

  72. Condition of Manipulation (1/2)
    init:function () {
    const txt = document.getElementById("txt");
    this.el.grip=false;
    this.el.addEventListener('xbuttondown', function (e) {
    this.emit('teleportstart');
    });
    this.el.addEventListener('xbuttonup', function (e) {
    this.emit('teleportend');
    });
    this.el.addEventListener('gripdown', function (e) {
    this.grip=true;
    });
    this.el.addEventListener('gripup', function (e) {
    this.grip=false;
    });
    } ,
    Holding grip is pressed or not.

    View full-size slide

  73. Condition of Manipulation (2/2)
    init:function () {
    /*Source code is omitted in this slide.*/
    this.el.addEventListener('gripup', function (e) {
    this.grip=false;
    });
    //called when raycaster intersected with something
    this.el.addEventListener('raycaster-intersection', function (e) {
    //Holding 1st object of detected entity as selected object.
    this.selectedObj = e.detail.els[0];
    });
    //called when raycaster intersection is cleared.
    this.el.addEventListener('raycaster-intersection-cleared', function (e) {
    //Reset selected object.
    this.selectedObj = null;
    });
    } ,
    Checking raycaster is
    intersected with CG or not
    7.txt

    View full-size slide

  74. Manipulation
    init:function () {
    /* Omitted in this slide */
    this.el.addEventListener('raycaster-intersection', function (e) {
    this.selectedObj = event.detail.els[0];
    });
    this.el.addEventListener('raycaster-intersection-cleared', function (e) {
    this.selectedObj = null;
    });
    },
    //Update
    tick: function () {
    if (!this.el.selectedObj) { return; }
    if (!this.el.grip) { return; }
    //Make selected object to follow the tip of raycaster when
    //raycaster intersected object and grip is pressed.
    }

    View full-size slide

  75. Manipulation
    //update
    tick: function () {
    if (!this.el.selectedObj) { return; }
    if (this.el.grip == false) { return; }
    //Getting direction of raycaster attached on this controller.
    var ray = this.el.getAttribute("raycaster").direction;
    //Calculate 1.2m forward position relative to controller
    var p = new THREE.Vector3(ray.x, ray.y, ray.z);
    p.normalize();
    p.multiplyScalar(1.2);
    //Convert local position to world position.
    this.el.object3D.localToWorld(p);
    //put the selected object on the tip of the raycaster
    this.el.selectedObj.object3D.position.set(p.x, p.y, p.z);
    } 8.txt

    View full-size slide

  76. Test on Oculus Quest
    3D objects will become manipulatable

    View full-size slide

  77. Reference of raycaster
    https://github.com/aframevr/aframe/blob/master/docs/co
    mponents/raycaster.md
    You can read more detail of raycaster below.

    View full-size slide

  78. Next Step:Shooting Bullet(ball)
    Press Trigger

    View full-size slide

  79. Import Library of Physics


    Hello, WebVR! • A-Frame






    All script is omitted in this slide.
    background="color: #AAAAAA">
    /*Omitted*/


    9.txt
    physics="gravity: 0; restitution: 0.9;"

    View full-size slide

  80. Adding Event of Trigger Down
    init:function () {
    /*Source code is omitted in this slide.*/
    //Raycaster intersected with something.
    this.el.addEventListener('raycaster-intersection', function (e) {
    this.selectedObj = event.detail.els[0];
    });
    //Raycaster intersection is finished.
    this.el.addEventListener('raycaster-intersection-cleared', function (e) {
    this.selectedObj = null;
    });
    //Calles when the trigger button begin to be pressed.
    this.el.addEventListener('triggerdown', function (e) {
    });
    }

    View full-size slide

  81. Instantiation of Ball
    this.el.addEventListener('triggerdown', function (e) {
    //Getting the current position of the controller.
    var point = this.object3D.getWorldPosition();
    //Instantiation of ball entity.
    var ball = document.createElement('a-sphere');
    ball.setAttribute('class', 'ball');
    ball.setAttribute('scale', '0.2 0.2 0.2');
    ball.setAttribute('position', point);
    //Adding dynamic-body and its property to enable physics.
    ball.setAttribute('dynamic-body', 'shape: sphere; sphereRadius:0.2; ');
    //Generate ball entity in a-scene
    var scene = document.querySelector('a-scene');
    scene.appendChild(ball);
    });
    10.txt

    View full-size slide

  82. Test on Oculus Quest
    Ball will appear on the hand position but not be shot yet.

    View full-size slide

  83. Calculation of Shooting Vector
    this.el.addEventListener('triggerdown', function (e) {
    /*Source code is omitted in this slide.*/
    //Generating ball entity in a-scene
    var scene = document.querySelector('a-scene');
    scene.appendChild(ball);
    //Getting direction of raycaster component of the controller.
    var dir = this.getAttribute("raycaster").direction;
    //Setting direction and magnitude of shoot vector.
    var force = new THREE.Vector3();
    force.set(dir.x, dir.y, dir.z);
    force.multiplyScalar(2000);
    //Declare and set force vecter.
    ball.force = this.object3D.localToWorld(force);
    });
    11.txt

    View full-size slide

  84. Shooting Bullet
    this.el.addEventListener('triggerdown', function (e) {
    /*Source code is omitted in this slide.*/
    ball.force = this.object3D.localToWorld(force);
    //Shoot after finishing to load physics on ball
    ball.addEventListener('body-loaded', function (e) {
    //Getting position of ball
    var p = this.object3D.position;
    //Add force
    var f = this.force;
    this.body.applyForce(
    new CANNON.Vec3(f.x, f.y, f.z), new CANNON.Vec3(p.x, p.y, p.z)
    );
    });
    });
    12.txt

    View full-size slide

  85. Test on Oculus Quest
    Balls are shot but through the wall.

    View full-size slide

  86. Enabling Collision Detection
    background="color: #AAAAAA">









    --omitted--
    box, sphere, cylinder, planeにstatic-bodyを追加

    View full-size slide

  87. Remove Balls by Pressing A-Button
    init:function () {
    /*Source code is omitted in this slide.*/
    //called when trigger button is started to be pressed
    this.el.addEventListener('triggerdown', function (e) {
    /*Source code is omitted in this slide.*/
    });
    //Called when pressing a-button is began.
    this.el.addEventListener('abuttondown', function (e) {
    //Obtain all entity of being ball class
    var els = document.querySelectorAll('.ball');
    //Remove each balls from a-scene.
    for (var i = 0; i < els.length; i++) {
    els[i].parentNode.removeChild(els[i]);
    }
    });
    } ,
    13.txt

    View full-size slide