又一次看到在网上看到一个HTML5的VR街道的应用,印象比较深刻,觉得还挺有意思的。正好最近项目间隙,找点事情搞搞,所以找到一个WebVR的网页开发框架A-Frame。以下内容整理来自于踏得网的A-Frame中文教程。
简介
虚拟现实(VR)是一种技术,使用头显设备产生逼真的图像,声音和其他感觉,使得用户进入身临其境的虚拟环境。VR允许我们创建无界的世界,人们可以使用手来控制虚拟世界中的行走和互动,仿佛他们被时空传送到另一个地方。如果说互联网打破了时空界限,那么虚拟实现将打破虚拟和现实的界限。
A-Frame是一款用来构建虚拟现实(VR)的网页开发框架,属于当下流行的开发WebVR解决方案。A-Frame是完全开源的框架,核心思想来自three.js,提供声明式、可拓展、组件化的编程结构,并且支持主流VR头显。
个人理解:通过A-Frame框架,将3D、VR、AR设计中的元素进行标签化,类似html里面的标签或者元素。常见到到标签有:
- a-scene(屏幕)
- a-entity(实体)
- a-assets(资源)
- a-light(光照)
- a-animation(动画)
- a-camera(相机)
- a-cursor(光标)
- a-sound(声音)
- a-text(文字)
有些常用的实体标签进行封装,比如正方体、圆柱体、球之类。
- a-sky(天空/背景)
- a-plane(地面)
- a-box(方体)
- a-sphere(球)
- a-cylinder(圆柱)
有标签,就有标签的属性了。常见的属性值有:
- position(定位:x,y,z轴值)
- rotation(旋转:x,y,z轴角度)
- geometry(几何数据)
- material(表面材质)
- scale(缩放)
- particle-system(粒子系统)
- color(颜色)
- radius(半径:圆的会用)
- height(高)
- width(宽)
然后就是各种类型的属性值了。开发的时候就是写各种元素根据需要向屏幕中进行堆叠就可以了。是不是跟你平时做的html开发没啥区别。
后面根据你项目需要可以自己写组件,根据特殊的需求,可以引入各种各样写好的插件,满足你的天马行空的想象。
举个栗子
就拿上面提到的元素标签来几段代码,做个效果。
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<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>
</body>
</html>
复制代码
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/[email protected]/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 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
复制代码
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/aframe-animation-component.min.js"></script>
<script src="https://unpkg.com/[email protected]/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/[email protected]/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>
复制代码
如何使用
自行运行上述代码试试。真正项目运行当然就需要web服务器了,前面引入JS,中间不断堆叠需要的实体、特效、交互、动画就可以了。不需要编译,直接预览就可以。
<!-- Production Version, Minified -->
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<!-- Development Version, Uncompressed with Source Maps -->
<script src="https://aframe.io/releases/0.5.0/aframe.js"></script>
复制代码
遇到一个小问题,本地html文件引入同文件资源,居然还产生跨域问题。
设备和平台支持
A-Frame支持的通用平台包括:
- 桌面电脑上的虚拟现实和头戴设备
- 移动设备上的虚拟现实和头戴设备
- 平面桌面设备(也就是普通电脑显示器、鼠标和键盘)
- 智能手机(比如magic window)
一些其他的平台包括:
- 增强现实(AR)头戴设备(比如HoloLens, Windows Mixed Reality)。
- 移动设备上的增强现实(AR) (比如magic window)
- AltSpaceVR,通过本地SDK
当然前端开发要关心支持那些浏览器了,webvr.rocks维护最新列表。目前
- Firefox Nightly (Firefox 55)
- Chromium的实验版本
- Chrome for Android (Daydream)
- Oculus Carmel (GearVR)
- Samsung Internet (GearVR)
- Microsoft Edge
A-Frame尝试通过WebVR polyfill来支持那些未实现WebVR接口的现代浏览器,但是由于不是官方支持,所以体验可能会不佳:
- Safari for iOS
- Chrome for Android
- Firefox for iOS
- Samsung Internet
- UC Browser
对于平面的3D渲染,A-Frame支持所有实现了WebGL接口的现代浏览器:
- Firefox
- Chrome
- Safari
总之,万恶的IE
Primitives
踏得网翻译是原语,百度翻译是基元。实际就是个语法糖,把一些常用的实体进行封装。方便理解学习,降低开发难度,本身也是实体。
比如 实际是 并且a-box增加属性,映射到a-entity的具体属性值中。
<a-box color="red" width="3"></a-box>
//效果跟下方相同
<a-entity geometry="primitive: box; width: 3" material="color: red"></a-entity>
复制代码
这种语法糖可以通过AFRAME.registerPrimitive(name, definition)进行注册,方法也很简单。举例:
AFRAME.registerPrimitive('a-ocean', {
defaultComponents: {
ocean: {},
rotation: {x: -90, y: 0, z: 0}
},
mappings: {
width: 'ocean.width',
depth: 'ocean.depth',
density: 'ocean.density',
color: 'ocean.color',
opacity: 'ocean.opacity'
}
});
复制代码
其中,name是对应生成的新实体的名字,definition是新实体需要的参数对象。definition参数对象中包含defaultComponents和mappings,defaultComponents是新实体的默认参数,比如先指定下几何图形等等,mappings是对外暴露的属性值及在默认对象中的对应关系。
有没有一种面向对象开发中,a-entity是基础类,注册一个子类,继承父类,对外暴露特定接口的概念。
ECS架构
实体-组件-系统(entity-component-system),ECS架构是三维游戏中常见且理想的设计模式,Unity就是ECS架构的游戏引擎。A-Frame是基于three.js,并且使用了ECS架构。
ECS的基本定义包括:
-
实体(Entities)是容器对象,用来包含组件。实体是场景中所有对象的基础。没有附加组件的实体不会渲染任何东西,类似于空的div标签。对应的是 元素和原型
-
组件(Components)是可重用的模块或数据容器,可以依附于实体以提供外观、行为和/或 功能。组件就像即插即用的对象。所有的逻辑都是通过组件实现,并通过混合、匹配和配置组件来定义不同类型的对象。通过的HTML属性来表示。底层实现上, 组件是包含模式(schema)、生命周期处理器和方法的对象。组件通过AFRAME.registerComponent (name, definition)API来注册。
-
系统(Systems) 为组件类提供全局范围、管理和服务。系统通常是可选的,但我们可以使用它们 来分离逻辑和数据;系统处理逻辑,组件充当数据。通过的HTML属性来表示。系统在定义上和组件类似,系统通过AFRAME.registerSystem (name, definition) API来注册。
对应aframe框架,类比HTML结构理解,就是 systems是html/body标签,Entities是div标签,Component是div标签上的style属性。
组件上的语法属性值和style的样式一样,可以添加很多。
<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>
复制代码
JavaScript, Events 和 DOM APIs
把元素堆叠起来是显示效果,当然我们还需要控制场景中的元素为我们所用。这里还是进行的DOM操作,
查询遍历方法
querySelector()和querySelectorAll(),借鉴Jquery。
<html>
<a-scene>
<a-box id="redBox" class="clickable" color="red"></a-box>
<a-sphere class="clickable" color="blue"></a-sphere>
<a-box color="green"></a-box>
<a-entity light="type: ambient"></a-entity>
<a-entity light="type: directional"></a-entity>
</a-scene>
</html>
复制代码
声明对象可以使用下面一些方式进行声明。
var sceneEl = document.querySelector('a-scene'); //指定一个对象
var redBox = sceneEl.querySelector('#redBox'); //使用ID值
var boxs = sceneEl.querySelectorAll('a-box'); //选择一类实体
var btns = sceneEl.querySelectorAll('.clickable'); //使用class值
var lights = sceneEl.querySelectorAll('[light]'); //使用属性值
var els = sceneEl.querySelectorAll('*'); //使用通配符
复制代码
对象属性操作
后续整个理解其实和正常的操作dom,使用的一样的方法和接口。
- createElement(创建对象)
- appendChild(追加对象)
- removeChild(删除对象)
- setAttribute(设置属性)
- removeAttribute(删除属性)
事件和侦听器
和浏览器事件相同,增加的事件可以通过.emit向外发射,.addEventListener进行侦听,.removeEventListener删除侦听。举例,空间里面两个对象碰到了就叫一声(emit),空间里面管理员听到了就会去处理。addEventListener就是增加管理员,里面代码是听到叫声要操作的事情。
entityEl.emit('physicscollided', {collidingEntity: anotherEntityEl}, false); //碰了就叫一声
function collisionHandler (event) {
console.log('Entity collided with', event.detail.collidingEntity);
}); //要做的时事情
entityEl.addEventListener('physicscollided', collisionHandler); //放侦听器处理
entityEl.removeEventListener('physicscollided', collisionHandler); //删除侦听器
复制代码
父子关系与坐标转换
A-Frame中元素也存在父子关系的嵌套,并且父子元素之间的空间关系也是相互影响的。
<a-scene>
<a-box>
<a-sphere></a-sphere>
<a-light></a-light>
</a-box>
<a-entity id="foo" position="1 2 3">
<a-entity id="bar" position="2 3 4"></a-entity>
</a-entity>
</a-scene>
复制代码
其中id为bar的实体的实际空间坐标就变成了(3, 5, 7)。最外层坐标叫世界坐标,元素里面的坐标是相对于父坐标的坐标。
可视化查看器和开发工具
A-Frame查看器是一个用来检测和修改场景元素的可视化工具。使用 + + i组合快捷键来打开/关闭查看器,使用该工具,我们可以:
- 使用句柄和助手拖动、旋转和缩放实体
- 使用小部件(widgets)调整实体的组件及其属性
- 立即看到更改结果,而无需在代码和浏览器之间来回切换
查看器类似于浏览器的DOM查看器,但面向的是3D上下文和A-Frame。
生命周期
- init 初始化组件时调用一次。用于设置初始状态和实例化变量。
- update 在组件初始化和任何组件属性更新(例如,通过setAttribute)修改属性)时被调用来更新该实体。
- remove 在组件被从实体中删除时(比如,通过removeAttribute)或者当实体从场景中分离时被调用。用于撤消以前所有的实体修改。
- tick 在每个场景渲染循环被调用。用于连续的改变或检查。
- play 每当场景或实体播放来添加任意背景或动态行为时被调用。组件初始化时也会被调用一次。用于启动或恢复动态行为。
- pause 每当场景或实体暂停来删除任意背景或动态行为时被调用。当组件从实体中移除或实体脱离场景时也会被调用。用于暂停动态行为。
- updateSchema 当组件的任意属性更新时被调用。可用来动态修改模式(schema)。
文末
本文算是一个初期的入门理解,更加深入的理解肯定要实战中去增强理解,好多问题只有在多次实战运用之后才会又更深入的了解。而且像这种3D内容的开发,更多的其实是空间设计能力,这个才是重点。