1. 简介
A-Frame是一个用来构建虚拟现实(VR)应用的网页开发框架。由WebVR的发起人Mozilla VR 团队所开发,是当下用来开发WebVR内容主流技术方案。
使用时,一般需要导入两个库,一个aframe.js是基本组件,一个aframe-ar.js是用来做AR的,放在script标签中,而需要显示的内容则放在a-scene中。
这里是资源清单:https://github.com/aframevr/awesome-aframe/
下面是一些常用的资源网址:
https://codepen.io/pen/tour/welcome/start 前端在线编辑器
https://www.flickr.com/search/?text=equirectangular 全景图
https://www.toptal.com/designers/subtlepatterns/ 材质
2. 基本数据结构
A-Frame基于three.js框架,并且使用了实体-组件-系统(entity-component-system)(ECS)架构。ECS架构是三维游戏中常见且理想的设计模式, 遵循组合模式要好于继承和层次结构的设计原则。下面是一些将不同组件组合成不同类型的实体的抽象示例:
2.1 实体-组件
A-Frame拥有代表ECS每个方面的API:
- 实体(Entities) 对应的是<a-entity> 元素和原型。
- 组件(Components) 通过 <a-entity>的HTML属性来表示。底层实现上, 组件是包含模式(schema)、生命周期处理器和方法的对象。组件通过AFRAME.registerComponent (name, definition)API来注册。
- 系统(Systems) 通过 <a-scene>的HTML属性来表示。系统在定义上和组件类似,系统通过 AFRAME.registerSystem (name, definition) API来注册。
我们创建 <a-entity> 并将组件附加为其HTML属性。大多数组件具有多个属性,这些属性用类似于 HTMLElement.style CSS的语法来表示。此语法采用分号形式来隔离属性及其属性值,并使用一个分号来分隔不同的属性定义:
<a-entity ${componentName}="${propertyName1}: ${propertyValue1}; ${propertyName2:}: ${propertyValue2}">
举例如下,我们有一个 <a-entity>,添加了 geometry,material,light, and position 组件,使用了各种属性和属性值:
<a-entity geometry="primitive: sphere; radius: 1.5"
light="type: point; color: white; intensity: 2"
material="color: white; shader: flat; src: glow.jpg"
position="0 0 -5"></a-entity>
2.2 原语
A-Frame提供一些简单易用的标签元素如a-box和 a-sky,这些被称之为 原语(primitives),实际上是实体-组件模式的封装,使其对于初学者容易使用,如下图:
下面来测试一个物理效果和一个特殊的海洋组件,注意设置了static-body和dynamic-body两项:
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@v6.1.0/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/aframe-physics-system@1.4.0/dist/aframe-physics-system.min.js"></script>
</head>
<body>
<a-scene physics>
<a-box position="-1 4 -3" rotation="0 45 0" color="#4CC3D9" dynamic-body></a-box>
<a-plane position="0 -0.5 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
<a-ocean color="aqua" depth="100" width="100" static-body></a-ocean>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
2.3 A-frame extras
这里面有一些有用的控制器、模型加载器、组件等等,参考这里:https://github.com/donmccurdy/aframe-extras
下面是常用资源:
- A-Frame注册表(Registry):https://aframe.io/aframe-registry
- npm搜索:https://www.npmjs.com/search?q=aframe-component
- awesome-aframe:https://github.com/aframevr/awesome-aframe#components
- A-Painter:https://github.com/aframevr/a-painter/
- A-Blast:https://github.com/aframevr/a-blast/
- Architect:https://github.com/aframevr/architect/
下面来介绍一下怎么使用,比如说粒子系统组件:
https://www.npmjs.com/package/aframe-particle-system-component
我们使用unpkg工具,有两种方式:
1)https://unpkg.com/@/
如果我们想要组件脚本的最新版本, 我们可以去除 version 参数:
2)https://unpkg.com//
找到其中的链接:
然后引用链接即可:
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-particle-system-component@1.0.9/dist/aframe-particle-system-component.min.js"></script>
</head>
<body>
<a-scene>
<a-entity particle-system="preset: snow" position="0 0 -10"></a-entity>
</a-scene>
</body>
</html>
下面是优化版本:
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-animation-component@3.2.1/dist/aframe-animation-component.min.js"></script>
<script src="https://unpkg.com/aframe-particle-system-component@1.0.x/dist/aframe-particle-system-component.min.js"></script>
<script src="https://unpkg.com/aframe-extras.ocean@%5E3.5.x/dist/aframe-extras.ocean.min.js"></script>
<script src="https://unpkg.com/aframe-gradient-sky@1.0.4/dist/gradientsky.min.js"></script>
</head>
<body>
<a-scene>
<a-entity id="rain" particle-system="preset: rain; color: #24CAFF; particleCount: 5000"></a-entity>
<a-entity id="sphere" geometry="primitive: sphere"
material="color: #EFEFEF; shader: flat"
position="0 0.15 -5"
light="type: point; intensity: 5"
animation="property: position; easing: easeInOutQuad; dir: alternate; dur: 1000; to: 0 -0.10 -5; loop: true"></a-entity>
<a-entity id="ocean" ocean="density: 20; width: 50; depth: 50; speed: 4"
material="color: #9CE3F9; opacity: 0.75; metalness: 0; roughness: 1"
rotation="-90 0 0"></a-entity>
<a-entity id="sky" geometry="primitive: sphere; radius: 5000"
material="shader: gradient; topColor: 235 235 245; bottomColor: 185 185 210"
scale="-1 1 1"></a-entity>
<a-entity id="light" light="type: ambient; color: #888"></a-entity>
</a-scene>
</body>
</html>
测试一下效果
from openvino.inference_engine import IENetwork, IECore
import cv2
path = 'instance-segmentation-security-1025.xml'
model_xml = path+".xml"
model_bin = path+".bin"
ie = IECore()
net = IENetwork(model=model_xml, weights=model_bin)
input_blob,out_blob,net.batch_size = next(iter(net.inputs)),next(iter(net.outputs)),1
n, c, h, w = net.inputs[input_blob].shape
exec_net = ie.load_network(network=net, device_name='CPU')
x = cv2.imread('lbl.jpeg')
x = cv2.resize(x,(480,480)).transpose([2, 0, 1])
output = exec_net.infer(inputs={input_blob:x })[out_blob]
exec_net.infer(inputs={input_blob:x })
2.4 Aframe和js交互
最好是使用组件封装代码。作为最简单的一个例子,注册一个 console.log 组件:
AFRAME.registerComponent('log', {
schema: {type: 'string'},
init: function () {
var stringToLog = this.data;
console.log(stringToLog);
}
});
然后在HTML中使用该组件:
<a-scene log="Hello, Scene!">
<a-box log="Hello, Box!"></a-box>
</a-scene>
首先是使用.querySelector() 用来查询单个元素。
var sceneEl = document.querySelector('a-scene');
如果元素有ID,我们可以使用ID选择器(也就是 #),元素ID通常在页面中是唯一的。通过查询选择器,我们可以将查询范围限制在任何范围内:
var sceneEl = document.querySelector('a-scene');
console.log(sceneEl.querySelector('#redBox'));
// <a-box id="redBox" class="clickable" color="red"></a-box>
.querySelectorAll() 用来查询多个元素。比如我们可以按标签名称来查询元素:
console.log(sceneEl.querySelectorAll('a-box'));
// [
// <a-box id="redBox" class="clickable" color="red"></a-box>,
// <a-box color="green"></a-box>
// ]
我们可以按类名class来查询元素(也就是 .<CLASS_NAME>)。比如获取所有带 clickable class的元素:
console.log(sceneEl.querySelectorAll('.clickable'));
// [
// <a-box id="redBox" class="clickable" color="red"></a-box>
// <a-sphere class="clickable" color="blue"></a-sphere>
// ]
我们可以查询包含某属性(某组件)(也就是 [<ATTRIBUTE_NAME>])的元素。比如获取所有包含light组件的实体:
console.log(sceneEl.querySelectorAll('[light]'));
// [
// <a-entity light="type: ambient"></a-entity>
// <a-entity light="type: directional"></a-entity>
// ]
对于 .querySelectorAll() 返回的多个元素, 我们可以使用 for 循环来遍历它。下面的代码使用通配符 * 查询所有元素并遍历之:
var els = sceneEl.querySelectorAll('*');
for (var i = 0; i < els.length; i++) {
console.log(els[i]);
}
代码 document.createElement 将创建一个空的实体:
var el = document.createElement('a-entity');
但是,这个实体不会被初始化或成为场景的一部分,直到我们把它添加到场景中。
使用 .appendChild(element) 接口来添加实体到DOM中。对于添加元素到场景中,具体而言我们获得场景(scene)对象,创建实体,然后通过前述方法添加到场景中:
var sceneEl = document.querySelector('a-scene');
var entityEl = document.createElement('a-entity');
// Do `.setAttribute()`s to initialize the entity.
sceneEl.appendChild(entityEl);
注意 .appendChild() 在浏览器中是一个异步(asynchronous)操作。在DOM添加实体完成之前,我们不能对之进行实际的操作(比如调用 .getAttribute() 方法来获取属性)。如果我们需要查询一个刚刚添加的实体的属性,我们可以侦听该实体的 加载完成(loaded) 事件,或者把代码逻辑放在A-Frame组件里面,这样一旦实体准备好了就可以被执行:
var sceneEl = document.querySelector('a-scene');
AFRAME.registerComponent('do-something-once-loaded', {
init: function () {
// This will be called after the entity has properly attached and loaded.
console.log('I am ready'!);
}
});
var entityEl = document.createElement('a-entity');
entityEl.setAttribute('do-something-once-loaded', '');
sceneEl.appendChild(entityEl);
要从DOM也即从场景中删除实体,我们可以从父元素调用 .removeChild(element) 方法。如果我们有一个实体,我们需要访问其父元素 (parentNode) 来删除它。
entityEl.parentNode.removeChild(entityEl);
A-Frame 元素提供如下方法来发射自定义事件:.emit(eventName, eventDetail, bubbles)。例如,假设我们在构建一组物理组件,并希望实体在发生碰撞时发出信号:
entityEl.emit('physicscollided', {collidingEntity: anotherEntityEl}, false);
然后代码的其他部分可以等待并侦听这个事件并执行代码来响应。我们可以通过事件处理方法的第二个参数来传递事件细节信息和数据。我们还可以指定事件是否冒泡,也就是说其父实体是否也将发出事件。所以代码的其他部分就是注册一个事件侦听器。
和普通HTML元素类似,我们可以使用 .addEventListener(eventName, function) 来注册一个事件侦听器。当eventName所对应的事件发生时,function函数将被调用并处理事件。例如,继续前一个物理系统的例子,当发生碰撞事件时:
entityEl.addEventListener('physicscollided', function (event) {
console.log('Entity collided with', event.detail.collidingEntity);
});
当实体发出 physicscollided 事件时,碰撞处理函数将被调用。在事件对象中,我们有事件详细信息,其中包含传递的事件数据和信息。
3. 模型与动画
3.1 加载模型
使用a-collada-model 等标签。下面这段代码成功将纸片人加入网页:
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-extras.ocean@%5E3.5.x/dist/aframe-extras.ocean.min.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<a-asset-item id="boxModel" src="http://fst-sample.oss-cn-hangzhou.aliyuncs.com/algorithm/model.dae"></a-asset-item>
</a-assets>
<a-collada-model src="#boxModel" scale="0.5 0.5 0.5" rotation="0 90 0" position="0 -0.5 -4" ></a-collada-model>
<a-entity id="ocean" ocean="density: 20; width: 50; depth: 50; speed: 4"
material="color: #9CE3F9; opacity: 0.75; metalness: 0; roughness: 1"
rotation="-90 0 0"></a-entity>
<a-plane position="0 -0.5 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
模型动画使用animation-mixer 组件,
3.2 GIF shader
这里是例子:https://github.com/mayognaise/aframe-gif-shader/blob/master/example.gif
下面是示例代码:
<head>
<title>My A-Frame Scene</title>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://jslab.pro/demo/vr/aframe-gif-shader.js"></script>
</head>
<body>
<a-scene>
<a-plane position="0 -0.5 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
<a-entity geometry="primitive:plane;width:2;height:2;" position="0 1 -2" material="shader:gif;src:url(banana.gif);transparent:true;" gif="" rotation="" scale="" visible=""></a-entity>
</a-entity>
</a-scene>
</body>
banana.gif放在同目录下,并且要起server,不然会出现域问题不允许访问。
如果你用的是mac,可以直接把文件放在/Library/WebServer/Documents下(自带的apache服务器),然后使用http:localhost访问,出现如下跳舞的香蕉图:
3.3 ar.js
AR的核心要素是要与拍摄到的实景进行交互,ar.js主要有3个功能:图像跟踪、marker跟踪、基于位置。
项目地址为:https://github.com/AR-js-org/AR.js
文档地址为:https://ar-js-org.github.io/AR.js-Docs/
AR.js with Image Tracking + Location Based AR:
- AFRAME version: https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js
- three.js version: https://raw.githack.com/AR-js-org/AR.js/master/three.js/build/ar-nft.js
AR.js with Marker Tracking + Location Based AR:
- AFRAME version: https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js
- three.js version: https://raw.githack.com/AR-js-org/AR.js/master/three.js/build/ar.js
<html>
<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
<script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
<body style="margin : 0px; overflow: hidden;">
<a-scene embedded arjs>
<a-marker preset="hiro">
<a-box position="0 0 0" rotation="0 0 0" color="#4CC3D9"></a-box>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
</body>
</html>
效果如下:
一些想法
AR偶像,最简单的想法是使用gif动画。基于场景互动,有点难哦。
聊天机器人,自拍与互动。基于人脸。
AR弹出人体:识别纸上画的动漫人物,然后弹出来。