构建一个基础场景
要构建一个基础的A-Frame场景,你需要具备基本的HTML知识。你将学习如何通过原语(primitives)来添加3D实体(比如对象),应用3D空间变换,使用图片作为纹理贴图,使用动画和事件添加交互性,通过调整光照和添加背景来改变外观,以及使用A-Frame实体-组件生态系统来添加文本。
考虑到A-Frame的读者可能没有3D或VR(乃至编程)经验,我们打算一步步来讲解这些内容。
下面是我们需要最终完成的作品场景,一块漂浮的天外陨石,我们可以先预览下:
<!-- -->![](https://imgs.xnip.cn/cj/docimg/665/449f0675-3de6-427a-8f5b-2b804fb89d1a.jpg)
![](https://imgs.xnip.cn/cj/docimg/665/be0d9544-e2bb-477e-851e-3b299b76bffe.jpg)
![](https://imgs.xnip.cn/cj/docimg/665/37a65abe-9b01-4385-ac7a-9bea5400b8a7.jpg)
我们可以通过按下ctrl+alt+i组合键来打开检测器,来查看场景中的元素。
基础HTML代码
我们以最低要求的HTML结构来开始:
我们通过在 <head>
中添加script标签来包含A-Frame框架, 指向一个CDN缓存的A-Frame构建版本。这个必须放在 <a-scene>
声明之前,否则 <a-scene>
将不起作用。
接下来我们在<body>
中声明一个 <a-scene>
标签, <a-scene>
是场景容器,用来包含所有实体。 <a-scene>
处理所有3D所需要的设置:建立WebGL上下文,画布,相机,光照,渲染器,渲染循环以及现成可用的设备平台支持如:HTC Vive, Oculus Rift, Samsung GearVR, 以及智能手机(Google Cardboard)。 <a-scene>
帮我们做了很多事情!
添加一个实体
在 <a-scene>
里面,我们通过原语 <a-box>
来添加一个实体。我们可以把 <a-box>
当作普通HTML元素一样使用,定义标签,通过属性来定制它。还有一些其他的原语包括: <a-cylinder>
, <a-plane>
, or <a-sphere>
.
这里我们定义颜色属性(color),更多 <a-box>
的属性请查阅 <a-box>
文档(比如: 宽度(width)
, 高度(height)
, 长度(depth)
).
Image by Ruben Mueller from vrjump.de
补充说明一下,原语(primitives)是A-Frame所使用的HTML元素扩展,用来封装底层的实体-构件组合。使用起来很方便,但我们要意识到 <a-box>
的内部实现是带geometry和material的一个<a-entity>
:
<a-entity geometry="primitive: box" material="color: red"></a-entity> |
不过,因为缺省的相机(camera)和盒子(box)都在原点 0 0 0
,我们将看不到盒子,除非我们移动它的位置,这可以通过使用 position组件来变换3D空间中的盒子。
3D实体变换
让我们先看看3D空间。A-Frame使用右手坐标系统。默认的摄像机方向:正x轴延伸。 右(right), 正y轴延伸 上(up), 正z轴延伸 前(out)对着我们的屏幕:
Image from what-when-how.com
A-Frame的距离单位是米(meters),因为 WebVR API以米为单位返回姿势数据。在设计VR场景时,考虑我们创建的实体的真实世界大小是很重要的。一个height="10"
的盒子在我们电脑屏幕上看起来很普通的,但是在VR里将显示成真实的大小,那会是一个很高的盒子。
A-Frame的旋转单位是角度(degrees),虽然它会在three.js内部转换为弧度。要确定旋转的正方向, 请使用右手规则(right-hand rule)。 把大拇指指向正轴的方向,我们手指绕的方向就是旋转的正方向。
要平移,旋转和缩放盒子,我们可以改变 位置(position), 旋转(rotation, 和 缩放(scale)组件。让我们首先应用旋转和缩放组件:
<a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box></a-scene> |
上面的代码示例将旋转我们的盒子并放大到两倍。
父子变换
A-Frame HTML代表一个 场景图(scene graph)。在场景图中,实体可以有一个单亲和多个孩子。子实体继承其父代的变换(即位置、旋转和缩放)。
例如,盒子有一个球的子对象:
<a-scene> <a-box position="0 2 0" rotation="0 45 45" scale="2 4 2"> <a-sphere position="1 0 3"></a-sphere> </a-box></a-scene> |
如果我们计算球体的世界位置,它将是1 2 3
, 通过将球体的父位置与自己的位置组合而成。同样,对于旋转和缩放,球体将继承盒子的旋转和刻度。球体也会随其父盒子一样旋转和伸展。如果盒子改变了它的位置、旋转或刻度,它将立即适用于球体并影响球体。
如果我们把一个圆柱体作为一个子对象添加到我们的球体,那么圆柱体的变换将同时受到球体和盒子的影响。在底层three.js中,这是通过变换矩阵相乘来实现的。幸运的是,我们不必考虑这个问题!
把我们的盒子放在照相机前面
现在让我们把盒子放到一个对于相机可见的位置上去。我们可以使用位置组件把盒子沿负z轴方向往后退5米,然后往上移动2米,避免跟地面接触:
<a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box></a-scene> |
现在我们就能看到这个盒子了:
缺省控制
对于平板显示器(即膝上型电脑、台式机),默认的控制方式是通过点击-拖动鼠标来四下观望以及通过WASD
或方向键来四处移动。在手机上,我们可以通过旋转摄像机来四下观望。虽然A-Frame是为WebVR量身定做,这个默认的控制方案在没有头戴设备的情况下也可以使用。
通过点击护目镜进入VR模式,当然你首先得连接一个VR头戴设备(比如Oculus Rift, HTC Vive),我们可以身临其境地体验场景。如果空间尺度(room-scale)可用,我们还可以在场景中四处行走。
给场景添加一个背景
只需要一行简单的HTML代码,我们就可以添加一个身临其境的背景: <a-sky>
是一个围绕着场景的天空背景。 <a-sky>
,它是一种应用于球体内部的材料,可以是普通颜色,360°图像,或360°视频。例如,添加一个深灰色的背景代码如下:
<a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky></a-scene> |
应用一个图片纹理
确保建立一个本地web服务器,以便能正常加载纹理贴图。
我们可以使用src
属性来给这个box应用图片、视频或者<canvas>
纹理,就好像普通的<img>
元素一样。我们得去掉color="red"
,这样避免和纹理产生混合效果。默认材料颜色是white
,所以去掉颜色属性就可以了。
<a-scene> <a-box src="https://i.imgur.com/mYmmbrp.jpg" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky></a-scene> |
现在我们将看到一个使用远程图片为纹理的盒子。
使用资源管理系统
不过,出于性能考虑我们推荐使用资源管理系统。资产管理系统使浏览器缓存资源更容易(例如,图像,视频,模型),并且A-Frame框架将确保所有的资源都在渲染之前被获取到。
如果我们在资源管理系统里面定义一个<img>
,three.js就无需在底层再创建一个<img>
。在A-Frame中自行创建 <img>
也给了我们更多的控制,让我们在多个实体上重用纹理。同时必要时A-Frame还能自动设置crossOrigin
以及其他一些属性。
要将资源管理系统用于图像纹理:
- 添加
<a-assets>
到场景中。 - 将纹理定义为
<a-assets>
下面的<img>
。 - 给
<img>
一个HTML ID (e.g.,id="boxTexture"
)。 - 以DOM选择器格式使用ID来引用资源(
src="#boxTexture"
)。
<a-scene> <a-assets> <img src="https://i.imgur.com/mYmmbrp.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky></a-scene> |
添加一张背景图
回到<a-sky>
,我们可以使用一张图片纹理来获取一个360°背景图,使用src
而不是color
:
<a-scene> <a-assets> <img src="https://i.imgur.com/mYmmbrp.jpg"> <img src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky src="#skyTexture"></a-sky></a-scene> |
添加地面
要添加一个地面,我们可以使用 <a-plane>
。默认情况下,a-plane是平行于XY轴的。要让它与地面平行,我们需要将它和XZ轴确定的平面平行。我们可以通过旋转负90°来完成,在x轴上旋转。
<a-plane rotation="-90 0 0"></a-plane> |
我们希望地面很大,这可以增加width
和height
。每个方向长30米:
<a-plane rotation="-90 0 0"></a-plane> |
然后我们可以给地面添加一个图像纹理:
<a-assets> <!-- ... --> <img src="https://cdn.aframe.io/a-painter/images/floor.jpg"> <!-- ... --></a-assets><!-- ... --><a-plane src="#groundTexture" rotation="-90 0 0" width="30" height="30"></a-plane><!-- ... --> |
要实现格子化效果,我们可以设置repeat
属性。 repeat
接受2个数字,分别是x和y方向的重复次数(通常称为纹理的U和V)。
<a-plane src="#groundTexture" rotation="-90 0 0" width="30" height="30" repeat="10 10"></a-plane> |
调试光照
我们可以通过<a-light>s
来改变场景光照。默认情况下,如果我们不指定任何灯光,A-Frame会帮我们添加一个环境光和一个方向光。否则,场景会是黑色的。一旦我们添加了自己的光源,默认的光源设置将被删除,取而代之的是我们的设置。
我们将添加一个环境光,有淡淡的蓝绿色色调,以配合天空。环境灯光适用于场景中的所有实体(至少得有默认材质)。
我们还将添加一个点光源。点光源就像灯泡,我们可以把它们定位在场景周围,点光源对实体的影响取决于它与实体的距离:
<a-scene> <a-assets> <img src="https://i.imgur.com/mYmmbrp.jpg"> <img src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg"> <img src="https://cdn.aframe.io/a-painter/images/floor.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky src="#skyTexture"></a-sky> <a-light type="ambient" color="#445451"></a-light> <a-light type="point" intensity="2" position="2 4 4"></a-light></a-scene> |
添加动画
我们可以使用A-Frame内置动画系统来添加动画。动画随时间推移而改变一个数值或者进行插值。我们可以给实体添加 <a-animation>
元素,让我们给盒子添加上下漂浮的动画:
<a-scene> <a-assets> <img src="https://i.imgur.com/mYmmbrp.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> </a-box></a-scene> |
我们告诉 <a-animation>
:
- 给位置(position)属性设置动画。
0 2.2. -5
,表示向上20厘米高度。- 每次循环后更改动画方向(direction)。
- 设置动画每次循环的持续时间(duration)为2秒。
- 无限循环上面的动画。 </ul为了获得最佳性能,每帧一次应该在A的水平,尽量不去创造你自己的
- Text Geometry- 三维文本,渲染代价要高些。
- HTML Shader- 把HTML渲染为一个纹理。好处是容易设置样式,缺点是性能糟糕。
高级细节
<a-animation>
挂靠在A-Frame渲染循环中,每帧只调用一次。如果你需要更多的控制,需要手动插入或修改值,你可以使用tick
handler和Tween.js这样的库(AFRAME.TWEEN
)来编写一个A-Frame组件。为了获得最佳性能,每帧一次的操作应该放在A-Frame层级来执行,不要创造你自己的requestAnimationFrame
,A-Frame已经有这个了。
添加交互
让我们添加与盒子的交互:当我们看盒子时,我们会放大盒子,当我们在盒子上点击的时候,我们会让它旋转。
考虑到目前许多开发人员没有合适的带控制器的VR硬件,我们将使用内置的cursor组件来集中讨论基本移动和桌面输入。默认情况下,光标组件提供了默认的“点击”功能:在移动设备上注视,或在桌面上看着实体,并单击鼠标。但是要知道光标组件只是添加交互的一种方式,如果我们能够访问实际的控制器,那么就有可能开发出更多方式。
要得到一个固定跟随camera的光标,我们可以把光标(cursor)放在camera内部作为其子元素,如上面事件侦听器组件
手动处理光标事件的方法之一是 使用JS添加一个事件侦听器就像我们使用普通的DOM元素一样。如果您对JavaScript不熟悉,可以跳到下面的事件动画
当要呈现动画的实体发出一个事件时,<a-animation>
可以用来启动动画。这可以通过begin
属性来完成,该属性接受一个事件名称。
我们可以为光标组件添加2个动画:mouseenter
和mouseleave
事件来缩放大小,以及click
事件为沿Y轴旋转:
<a-box color="#FFF" width="4" height="10" depth="2" position="-10 2 -5" rotation="0 0 45" scale="2 0.5 3" src="#texture"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> <!-- These animations will start when the box is looked at. --> <a-animation attribute="scale" begin="mouseenter" dur="300" to="2.3 2.3 2.3"></a-animation> <a-animation attribute="scale" begin="mouseleave" dur="300" to="2 2 2"></a-animation> <a-animation attribute="rotation" begin="click" dur="2000" to="360 405 45"></a-animation></a-box> |
添加音频
音频对于在虚拟现实中提供沉浸感是很重要的。即使在背景中添加简单的白噪声也会有很长的路要走。我们建议每个场景都有一些音频。方法是添加一个<audio>
元素到我们的HTML(最好是<a-assets>
)中来播放一个音频文件:
<a-scene> <a-assets> <audio src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay preload></audio> </a-assets> <!-- ... --></a-scene> |
我们还可以通过<a-sound>
添加具有位置感的音频,该音频会随着空间距离而变大变小。我们可以使用position
把声音放置在我们的场景中。
<a-scene> <!-- ... --> <a-sound src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay="true" position="-3 1 -4"></a-sound> <!-- ... --></a-scene> |
添加文本
A-Frame提供了text组件来添加文字。WebGL显示文本不是那么容易,有许多方法来处理文字,各有优缺点,A-Frame采用SDF文本实现方案,使用了[three-bmfont-text
],简单且性能好。
让我们使用文本组件的原语(primitive)形式<a-text>
:
<a-text value="Hello, A-Frame!" color="#BBB" position="-0.9 0.2 -3" scale="1.5 1.5 1.5"></a-text> |
还有一些其他的处理文本的方法:
打开A-Frame检查器
传统上,我们会使用浏览器的开发工具和DOM检查器来调试网页。A-Frame自行设计了面向VR和3D的开发工具。通过按下组合键 <ctrl> + <alt> + i
来打开它。本章开头的场景将在一个可视化工具中被打开,并可以探测对象和实时修改数值。
或者,按下面的按钮:
试着打开检查器,并修改<a-text>
实体的内容看看效果。 阅读更多如何使用A-Frame检查器。