Translation|Build a WebVR version of My World using A-Frame


I'm Kevin Ngo, an employee of Mozilla VR Team The web virtual reality developer and A-Frame s core developers. Today, we're going to look at how to use A-Frame to build an example WebVR version of My World that is spatially-tracked (room-scale) enough to run on HTC Vive, Oculus Rift, Samsung GearVR, Google Cardboard, desktop devices, and mobile devices. This example is based on an A-Frame and uses only 11 HTML elements!

A-Frame

A few years ago, Mozilla invented and developed the WebVR —— A at Create immersion in the browser VR experiential JavaScript API —— and post it at An experimental version of the Firefox In the browser。 thereafter,WebVR obtained Google、Microsoft、Samsung with and Oculus Extensive support from other companies such as。 now at,WebVR moreover at Embedded in just a few months at distribution Firefox In the browser, and was set because of Default on!

because of What will be born WebVR?Web because of VR Brings openness; at Web upper, The content is not under the control of the administrator, The user is not shut down either at Tall walled garden(walled garden) in。Web too because of VR Brings connectivity; at Web upper, We were able to at Weaving in and out of the world —— Just like when we click on a hyperlink at Page see shuttle like。 along with WebGL maturity of the and (various things) such as Web Assembly harmony Service Workers Presentation of norms,WebVR It's ready.。

Mozilla VR Team The A-Frame framework was created to throw light on the WebVR ecosystem, giving developers the ability to build 3D and VR worlds.

A-Frame Official Website Home

A-Frame is a virtual reality experience built web framework, It is based on HTML harmony Entity Component Paradigm(the Entity-Component pattern). HTML is the most understandable of all computer languages, which makes it possible for anyone to Get Started Quickly A-Frame. Here is a complete 3D and VR scene built using HTML that runs on any VR platform such as desktop and mobile devices.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<a-scene>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  <a-sky color="#ECECEC"></a-sky>
</a-scene>

Open in CodePen

That's it! Use only the One line of HTML (, include:canvas、 scenario、 renderer、 rendering cycle、 The camera is based on and raycaster。 then (afterwards), we This can be done by Use the Add Sub elemental the way to because of Adding objects to a scene。 No construction required It's just a simple, copy-and-paste HTML file.

We can also dynamically query and manipulate the HTML of an A-Frame, just as we can with the standardized JavaScript harmony DOM APIs (e.g. querySelector, getAttribute, addEventListener, setAttribute) like that.

 // Use `querySelector` to query the scene image.
var sceneEl = document.querySelector('a-scene');
var boxEl = sceneEl.querySelector('a-box');

 // Use `getAttribute` to get the entity's data.
console.log(box.getAttribute('position'));
// >> {x: -1, y: 0.5, z: -3}

 // Use `addEventListener` to listen for events.
box.addEventListener('click', function () {
   // Use `setAttribute` to modify the attribute.
  box.setAttribute('color', 'red');
});

moreover, reason because of These are just HTML harmony JavaScript, therefore A-Frame harmony Many of the existing frameworks harmony Good library compatibility:

Compatible with d3, Vue, React, Redux, jQuery, Angular

Although the HTML for A-Frame looks relatively simple, the API for A-Frame is far more powerful than a simple 3D declaration. A-Frame is a Entity Component System (ECS) Framework ECS is a popular pattern in game development, and it's worth noting that ECS is also used by the Unity engine. Concepts include:

  • In the scene, all objects are Entities (entities) , the empty object itself can do nothing, similar to the empty <div> . A-Frame uses HTML elements to represent entities in the DOM.
  • Next, we insert in the entity Components to provide the appearance、 go because of harmony functionalities。 at A-Frame in, Component is registered at JavaScript in, And can be used for anything。 They are available with the full three.js harmony DOM APIs。 After component registration, Can be attached at HTML in essence。

The strength of ECS is its composability; we can mix and match these reusable components to build more complex 3D objects. A-Frame takes it to the next level by declaring these components and making them part of the DOM, as we'll see later in the My World example.

Example skeleton

current at Come follow our examples . We will Build a basic VR Stereoscopic pixel maker(voxel builder), It is mainly used to support location tracking(positional tracking) harmony Tracking Controller(tracked controllers) spatial tracking of the VR installations( for example HTC Vive and Oculus Rift + Touch)。

We'll start with the HTML skeleton. If you want a quick overview (full 11 lines of HTML), the Click here to view the source code on GitHub

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<body>
  <a-scene>
  </a-scene>
</body>

Add Ground

<a-plane> harmony <a-circle> Both are often used as Add Ground graph element of, But we'll use <a-cylinder> to better work with the controller to complete the lighting calculations. The radius of the cylinder (cylinder) is 30 meters, and the sky we'll add later will match this radius value. Note that the units in the A-Frame are meters to match the units in the real world returned by the WebVR API.

The texture of the ground is deployed in https://cdn.aframe.io/a-painter/images/floor.jpg . We add the texture to the project and use that texture to make a flat cylindrical solid.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<a-scene>
  <a-cylinder id="ground" src="https://cdn.aframe.io/a-painter/images/floor.jpg" radius="32" height="0.1"></a-cylinder>
</a-scene>

Open in CodePen

Pre-loaded resources

pass (a bill or inspection) src The URL resource specified by the attribute will be loaded at runtime.

Since network requests can negatively affect the performance of rendering, we can preload Textures to ensure that no rendering is done until the resource has been downloaded, preload This can be done by Resource management system (asset management system) to finish.

We will <a-assets> put in <a-scene> in, connects resources( For example, the picture、 Video、 models and sound etc.) put in <a-assets> in and point the resource to our entity via a selector (e.g. #myTexture).

Let's move the ground texture to <a-assets> in which the use of <img> element to preload it.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<a-scene>
  <a-assets>
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
  </a-assets>

  <a-cylinder id="ground" src="#groundTexture" radius="32" height="0.1"></a-cylinder>
</a-scene>

Open in CodePen

Add Background

Let's use <a-sky> elemental because of <a-scene> Add a 360° background.<a-sky> It's a giant 3D sphere with material pasted on the inside. Just like a regular picture.<a-sky> This can be done by src Attribute accepts image address。 eventual We will You can use a line HTML Code to implement immersive 360° pictures。 Later you can also at Flickr spherical projection image pool (requires FQ) Select some 360° images in the exercise.

We can add a normal color background (e.g. <a-sky color="#333"></a-sky> ) or gradual change , but this time let's add a background texture image. The image was deployed in https://cdn.aframe.io/a-painter/images/sky.jpg . The image we're using is one that applies to a hemisphere, so first we need to take the sphere we just used theta-length="90" The ball is truncated horizontally into a hemisphere, plus we set the radius of the ball to 30 meters to match the ground.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<a-scene>
  <a-assets>
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
    <img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
  </a-assets>
  
  <a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>

  <a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>
</a-scene>

Open in CodePen

Adding somatotropin

In our VR application, voxels are written similarly <a-box>, but will add some custom A-Frame assemblies。 But let's start with a general idea Entity-component paradigm, Come see like <a-box> How such a figure element is synthesized.

In this section, we'll take a closer look at the implementation of several A-Frame components. In practice, we often use components through HTML that has already been written by A-Frame community developers, rather than building them from scratch.

Entity-component paradigm

Every object in the A-Frame scene is <a-entity> , which by itself can do nothing, is like an empty <div> the same as . We will assemblies( don't! harmony Web Components perhaps React Components confusion) Insert entities to give them appearance、 go because of harmony logic (loanword)。

For a box, We will. because of its configuration and increase A-Frame underlying Geometric components harmony Material components . Components are represented using HTML attributes, and component attributes are represented by default using a CSS-like style representation. Here is a <a-box> The disassembly of the base component of the writeup can be seen <a-box> Several components are in fact wrapped.

<a-box color="red" depth="0.5" height="0.5" shader="flat" width="0.5"></a-box>

<a-entity geometry="primitive: box; depth: 0.5; height: 0.5; width 0.5"
          material="color: red; shader: standard"></a-entity>

The advantage of using components is that they are composable. We can construct a variety of objects by mixing and matching a bunch of existing components.

at 3D under development, The types of objects we might build out at quantities harmony Infinite in complexity, So we need an easy、 brand new、 Non-traditional inherited object definition methods。 together with 2D web compare, We are no longer stuck using a handful of fixed HTML elemental and nesting them at Very deep in the hierarchy。

Random color components

The components in an A-Frame are defined by JavaScript, and they can be used with the full three.js and DOM APIs, they can do anything. All objects are defined by a bundle of components.

current at Putting the model just described into practice, pass (a bill or inspection) Write a A-Frame assemblies, because of Our boxes are set in random colors。 assemblies pass (a bill or inspection) AFRAME.registerComponent registration, We can define schema( Data for components) with and Life cycle approach( Logic of components)。 as far as sth is concerned Random color components, We don't need to set schema, reason because of It cannot be configured。 But we will define a init handler function that is called when the component is first attached to its entity.

AFRAME.registerComponent('random-color', {
  init: function () {
    // ...
  }
});

For the random color component, our intent is to set random colors for its attached entities. In the component's methods, you can use the this.el Access entity references.

To change the color using JavaScript, we use the .setAttribute() to set the color properties of the material component. A-Frame introduces only a few APIs, most of which are consistent with the native web development API. Click here to learn more about how to use JavaScript and DOM API in A-Frame

We also need to be material component is added to the list of pre-initialized components to ensure that the material is not material component is overwritten.

AFRAME.registerComponent('random-color', {
  dependencies: ['material'],

  init: function () {
     // Set the color property of the material component to a random color
    this.el.setAttribute('material', 'color', getRandomColor());
  }
});

function getRandomColor() {
  const letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++ ) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

After the component has been registered, we can Using HTML directly to link the component。A-Frame All of the code in the framework is a response to the HTML extensions, And these extensions can be used for other objects harmony Other scenes。 What's great is that, A developer can write a way to add a physical to an object elemental components of, People who use this component won't even notice JavaScript at He added this physics to his scenes elemental!

attention back to the box entity just now, will random-color do because of HTML attribute is inserted into the random-color components . We will Component Saving because of an JS documents, then (afterwards) at The scene code referenced it before:

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>

<a-scene>
  <a-assets>
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
    <img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
  </a-assets>
  
  <a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>

  <a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
  
  <!--  Random color box -->
  <a-entity geometry="primitive: box; depth: 0.5; height: 0.5; width 0.5"
            material="shader: standard"
            position="0 0.5 -2"
            random-color></a-entity>
</a-scene>

Open in CodePen

Components can be inserted into any entity, but do not need to create or extend classes as they would in a traditional inheritance pattern. If we want to make a difference in something like <a-shpere> perhaps <a-obj-model> Add-ons in, just add them!

<!--  at Reused on other entities and attached Random color components -->
<a-sphere random-color></a-sphere>
<a-obj-model src="model.obj" random-color></a-obj-model>

If we want to share this component with others, that's fine. We can be there A-Frame Warehouse Get many convenient components in the A-Frame ecosystem, similar to Unity's Asset Store. If we develop applications using components, then we should ensure that our code is modular and reusable internally!

Aligning components

We will use snap component to align the boxes to the grid to avoid them overlapping. We won't dive into how the component is implemented, but you can take a look at the The source code for the snap component(20 lines of JavaScript code).

Attach the snap component to the box entity so that the box is aligned every half meter, while using offset to center the box.

<a-entity
   geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
   material="shader: standard"
   random-color
   snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-entity>

Now we have a box entity consisting of a bundle of components that can be used to describe all the voxels (bricks) in our scene.

Mixins

We can create mixin to define a collection of reusable components.

with the use of <a-entity> Adding an object to a scene is different, we use <a-mixin> to create reusable voxels, using them as if they were pre-defined entities.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/snap.js"></script>

<a-scene>
  <a-assets>
    <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
    <img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
    <a-mixin id="voxel"
       geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
       material="shader: standard"
       random-color
       snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-mixin>
  </a-assets>

  <a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>

  <a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
  
  <a-entity mixin="voxel" position="-1 0 -2"></a-entity>
  <a-entity mixin="voxel" position="0 0 -2"></a-entity>
  <a-entity mixin="voxel" position="0 1 -2">
    <a-animation attribute="rotation" to="0 360 0" repeat="indefinite"></a-animation>
  </a-entity>
  <a-entity mixin="voxel" position="1 0 -2"></a-entity>
</a-scene>

Open in CodePen

We then added several voxels using mixin.

<a-entity mixin="voxel" position="-1 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 1 -2">
  <a-animation attribute="rotation" to="0 360 0" repeat="indefinite"></a-animation>
</a-entity>
<a-entity mixin="voxel" position="1 0 -2"></a-entity>

next, We will pass (a bill or inspection) Use a tracking controller to dynamically create soymines based on user interaction。 Let's start adding a hand to the program。

Add hand controller

increase HTC Vive perhaps Oculus Touch The tracking controller is very simple:

<!-- Vive -->
<a-entity vive-controls="hand: left"></a-entity>
<a-entity vive-controls="hand: right"></a-entity>

<!-- Rift -->
<a-entity oculus-touch-controls="hand: left"></a-entity>
<a-entity oculus-touch-controls="hand: right"></a-entity>

We will use the abstract hand-controls components to be compatible at the same time Vive harmony Rift control, It provides a basic hand model。 The left hand is responsible for moving the position, The right hand is responsible for placing the bricks。

<a-entity id="teleHand" hand-controls="left"></a-entity>
<a-entity id="blockHand" hand-controls="right"></a-entity>

because of Add transient functionality to your left hand

We will because of The left hand increases the ability to move instantaneously, When holding down the left-handed controller button, An arc is displayed from the controller, When you let go., Teleport to a position at the end of the arc。 at before then, We've written our own implementation of a random color A-Frame assemblies。

But it's also possible to use open source components already in the community and then use them directly via HTML!

For transients, there's one from @fernandojsg Transient Control Assembly. Following the README, we use the <script> tag introduction teleport-controls component and attach it to the controller entity.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>

<!-- ... -->

<a-entity id="teleHand" hand-controls="left" teleport-controls></a-entity>
<a-entity id="blockHand" hand-controls="right"></a-entity>

Let's then configure teleport-controls component that will transiently move the type Set to arc. By default.teleport-controls 's teleportation will only occur on the ground, but we can also specify collisionEntities Allow transient movement to bricks via selector harmony On the ground. These properties are teleport-controls Part of the API created by the component.

<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>

That's it! With just a script tag and an HTML attribute, we can transientize. at A-Frame Warehouse More cool components can be found in.

Adding voxel generator functionality to the right hand

In 2D applications, objects have the ability to handle clicks built in, whereas in WebVR objects don't have that ability and need to provide it ourselves. Fortunately, A-Frame has many components that handle interaction. The method used in VR for cursor click-like scenes is to use a raycaster, which shoots a laser and returns the object the laser hit. Then we listen to the interaction events and look at the raycaster to get the hit point information.

A-Frame provides a point-of-gaze based cursor (note: like the collimator in FPS games) that can be used to click on the object being gazed at, but there are also available Controller cursor assembly to fire a laser based on the position of the VR tracking controller, just like just using teleport-controls component, we pass the script tag to the controller-cursor component is introduced and then attached to the entity. This time it's the right hand's turn.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>
<script src="https://unpkg.com/aframe-controller-cursor-component@0.2.x/dist/aframe-controller-cursor-component.min.js"></script>

<!-- ... -->

<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>
<a-entity id="blockHand" hand-controls="right" controller-cursor></a-entity>

Now when we press the button on the tracking controller, thecontroller-cursor component will trigger both the controller and the interactive entity's click Event. A-Frame also offers features such as mouseenter and mouseleave Events like this. Events contain detailed information about user interactions.

This gives us the ability to click, but we also have to write some logic that handles generating bricks in response to the click event. You can use event listeners and document.createElement To complete.

document.querySelector('#blockHand').addEventListener(`click`, function (evt) {
   // Create a brick entity
  var newVoxelEl = document.createElement('a-entity');

   // Use mixin to turn it into a voxel
  newVoxelEl.setAttribute('mixin', 'voxel');

   // Use the hit point data to set the brick position.
   // The `snap` component described above is part of the mixin and will align the bricks to the nearest half meter
  newVoxelEl.setAttribute('position', evt.detail.intersection.point);

   // Use `appendChild` to add to the scene
  this.appendChild(newVoxelEl);
});

To handle needs like creating entities at hit points in a generalized way, we create intersection-spawn assemblies, This component accepts any event harmony Configuration of the property list。 We won't discuss its implementation in detail, But you can at GitHub Check out this simple one intersection-spawn Source code of the component . We will intersection-spawn The ability to attach to the right hand.

<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>

Now when we click, we can generate body quality!

Add support for mobile and desktop devices

We learned how to build a custom type of object (e.g., a hand controller with click functionality and brick generation on click) by combining components. One of the benefits of components is that they can be reused in different contexts. We will intersection-spawn component and the point-of-view-based cursor Components can be combined to generate bricks in mobile and desktop devices without changing the components at all.

<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>

<a-camera>
  <a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>
</a-camera>

give it a try

View the source on GitHub

Our VR voxel builder was eventually implemented using 11 HTML elements. We can preview it on desktop or mobile devices. On desktop devices, we can generate bricks by dragging and tapping; on mobile devices, we can pan the device and tap the screen to generate bricks.

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>
<script src="https://unpkg.com/aframe-controller-cursor-component@0.2.x/dist/aframe-controller-cursor-component.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/snap.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/intersection-spawn.js"></script>

<body>
  <a-scene>
    <a-assets>
      <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
      <img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
      <a-mixin id="voxel"
         geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
         material="shader: standard"
         random-color
         snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"
      ></a-mixin>
    </a-assets>

    <a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>

    <a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>

    <!-- Hands. -->
    <a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>
    <a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>

    <!-- Camera. -->
    <a-camera>
      <a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>
    </a-camera>
  </a-scene>
</body>

Open in CodePen

If you have a VR headset (e.g. HTC Vive, Oculus Rift + Touch), then look for a WebVR-enabled browsers and open the example.

If you want to watch what VR is like using a desktop or mobile device. You can view recorded VR motion capture and gesture demos


Recommended>>
1、Can Ford a late starter in driverless catch up with Waymo
2、Artificial blood vessels to be expected for kidney failure patients
3、The twentyfifth Rudong County law contest to win phone fees winner announcement
4、Make the writing block more up to par with the requirements
5、SQL Scramble ends with a bang

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号