苗疆三刀的随手记 2019-06-25
原文:An A-Frame tutorial for WebVR beginners
作者:Mary
译者:大田
A-frame是一款基于WebGL库three.js构建的WebVR框架,Web开发人员使用它可以很方便地创建虚拟现实场景。尽管three.js在创建WebGL的场景方面已经够用了,但A-frame通过引入实体-组件-系统模式的JavaScript库,比起three.js能更进一步简化VR开发。
A-frame最大的优势在于它的扩展性很好。如果你想构建一个简单的VR场景,既可以通过HTML代码构造,也可以通过编写自定义组件和系统来构造,所以A-frame易于使用,不需要会JavaScript,只要会写HTML就行。在本教程中,我们将学习如何构建一个冬天的场景和一个雪人,整个过程只涉及A-frame基本图形组件,不需要使用任何自定义的JavaScript代码。
在开始之前,我们需要先获取A-frame基础场景模板,该模板包含一个最基本的场景项目,我们基于它构造冬天场景。现在从git上获取该模板代码,并用Node将它启动。
$ git clone https://github.com/aframevr/aframe-boilerplate.git $ cd aframe-boilerplate && rm -rf .git && npm install $ npm start
在浏览器中访问127.0.0.1:3000,你可以看到一些形状和平面,现在我们将基于现在这个VR场景打造冬日雪人。
如果你不想使用Node(如果不使用Node,将错过很多炫酷的热加载哦)开发,可以在Html中加入以下链接,引入JavaScript文件。
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
现在index.html看起来应该是这样的:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, World! - A-Frame</title> <meta name="description" content="Hello, World! - A-Frame"> <script src="https://aframe.io/releases/0.3.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>
制作雪人不需要使用盒子,柱面和平现,现在我们将其移除。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, World! - A-Frame</title> <meta name="description" content="Hello, World! - A-Frame"> <script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script> </head> <body> <a-scene> <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> <a-sky color="#ECECEC"></a-sky> </a-scene> </body> </html>
现在只有一个球漂浮在空间的中央。接下来我们需要调整雪球的位置,大小和球面材料等属性,但首先来快速了解一下A-frame中的坐标系统。
要直接解释A-frame的坐标系统是什么有点难。在我看来,它是等同于Blender(一款3D动画制作软件,和3DMax类似)中的定位系统,在这个系统中有宽度,深度和高度三个概念,分别对应X轴、Y轴和Z轴。宽度对应X轴,深度对应Y轴,高度对应Z轴。
在A-frame中,相机的默认坐标为(0,0,0),相对于该坐标,X轴上负值就相当于画面的左边,正值就是右边;对于Z轴,负值是在画面的前面,正值是后面;对于Y轴,正值是上方,负值是下方。
例如,立方体位于(1,-1,1),即等相当于位于画面中心的右侧一个象素,水平线下方一个象素,画面往后一个象素的位置。
<a-sphere position="0 0 -15" radius="2" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere>
现在我们重新设置了球体,将它放在我们与中心点之间,距离中心点15象素的位置。(记住,在Z轴上,负值就是靠近我们,正值是远离我们。)现在的球体半径为2个象素,它将是雪人的底座,比之前的要大。
这里将color属性换成了material属性,这么做是为了给球体添加上阴影效果。shader属性定义了球体表面材质贴图的类型,它的默认值就为standard,不用设置也行,但这里为了保证代码清晰,仍旧将其写出来。
雪的颜色显然得是白色的,您可以指定color属性为white或十六进制颜色代码。metalness(金属质感)和roughness(粗糙质感)两个属性默认值为0.5,因为雪人不是金属,所以没有金属质感,这里设置metalness(金属质感)等于0;再者,雪人的表面是雪组成的,理应很粗糙,所以设置roughness(粗糙质感)等于1。
为了完成雪人的身体,我们再复制两个球体,并调整球体位置和大小。
<a-sphere position="0 0 -15" radius="2" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 2 -15" radius="1.7" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 4 -15" radius="1.3" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere>
现在您的代码看起来应该是这样的(我把页面的标题和内容改成了“雪人”):
<!DOCTYPE HTML> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Snowman</title> <meta name="description" content="Snowman"> <script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script> </script> </head> <body> <a-scene> <a-sphere position="0 0 -15" radius="2" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 2 -15" radius="1.7" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 4 -15" radius="1.3" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sky color="#ECECEC"></a-sky> </a-scene> </body> </html>
我们要增加两个球体来当眼睛,在雪人身体的三个球体代码后面添加以下代码。
<a-sphere position="-0.4 4.3 -13.9" radius="0.2" material="shader: standard; color: #000; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0.4 4.3 -13.9" radius="0.2" material="shader: standard; color: #000; metalness: 0; roughness: 1"></a-sphere>
球体是用来做眼睛的,这里将它的半径设置为0.2象素,设为黑色,放置在雪人的面部区域,这样眼晴就做好了。现在雪人还需要一个胡萝卜鼻子:
<a-cone position="0 4 -13.2" rotation="90 0 0" radius-bottom="0.2" radius-top="0" material="shader: standard; color: #ce6548; metalness: 0"></a-cone>
鼻子是圆锥体,但是基础形状里没有圆锥,这里我们用一个小妙招用圆柱体来做:设置圆柱体一端半径为0.2个象素作为圆锥体的底部,另一端半径为0象素,作为圆锥的顶端,设成橙色,放在眼睛的下方,这样鼻子就做好了。但是现在鼻子的朝向不太对,最后,我们将它的X轴旋转90度,这样胡萝卜鼻子就能正对着我们。
现在在你的浏览器访问127.0.0.1:3000查看VR场景,就能看到刚才完成的雪人了,只不过现在它看起来还是漂浮在一片空白中。
天空球就是能营造出天空场景效果的球体。
接下来我们要添加天空球,不过先别急,我们先导入一个等距离长方圆柱投影图像资源,将其添加为背景,现在在body
标签的下方添加以下代码:
<a-assets> <img id="snowy" src="snowy.jpg"> </a-assets>
以上代码所做的是提前将资源预加载到场景中。像这样把所有的静态资源都在a-asset
标签中声明好是很好的编程习惯,虽然也可以在声明实体对象时直接加载资源,但预加载使代码组织更清晰,所以最好是预加载。
在添加新的天空球之前,先移除的旧的天空球代码:
<a-sky color="#ECECEC"></a-sky>
现在我们来添加新的天空球,这一次不再使用a-sky
标签,而是使用a-entity
标签,它的效果和a-sphere
标签一样。现在将以下代码添加到雪人的身体和面部下方:
<a-entity geometry="primitive: sphere; radius: 50" material="shader: flat; src: #snowy" rotation="0 40 0" position="0 0 0" scale="1 1 -1"> </a-entity>
如你所见,我们使用的不是sphere
标签,而是a-entity
标签,所以需要设置geometry
属性。这里将a-entity
中的primitive
属性设置为sphere
,半径为50像素。
天空球的贴图材质设置为平面,这样它就不是立体图形,这样在有光照时,能避免天空中出现反射光照的效果,更自然。图像源文件设置为刚才预加载的图片资源ID(ID以#为前缀)。出于美学方面的考虑,现在设置scale
为(1,1,-1),这么做能让天空球在Y轴稍微旋转一下,这样图像的文字就能显示在天空球内部,我们就能看到了。
设置primitive
为sky
也可以添加天空球,使用这种写可以不用设置对背景图片的缩放,比使用sphere更简单。使用这种写法时,天空球的半径默认为5000像素,如果你不需要天空球大到这种程序,注意进行适当调整。
要有光!
A-frame场景中默认就设置有光源:
<!-- Default lighting injected by A-Frame. --> <a-entity light="type: ambient; color: #BBB"></a-entity> <a-entity light="type: directional; color: #FFF; intensity: 0.6" position="-0.5 1 1"></a-entity>
这里我们希望阴影呈现出蓝色色调,而光应该都从背景中太阳的方向照射过来,所以我设置环境光偏蓝色调,重新调整光源的位置,增加光照的强度。
<a-entity light="type: ambient; color: #405e94"></a-entity> <a-entity light="type: directional; color: #FFF; intensity: 0.8" position="5 5 10"></a-entity>
当用VR眼镜查看画面时,雪人看起来还是漂浮在半空中。虽然这不是最优雅的解决方案,但我用下面的配置来创建了一组平面效果作为地面,来解决这个问题:
<a-plane material="shader: flat; src: #snowy; repeat: 1 0.48" position="0 -1 0" rotation="-90 0 0" width="200" height="100"> </a-plane>
该平面使用了背景图片,但是只显示下半部分。现在雪人看来起就像是坐在地上了,虽然画面中能看出一些缝隙,不够完美,但大体上来说还是不错的。
最终代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Snowman</title> <meta name="description" content="Snowman"> <script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script> </head> <body> <a-assets> <img id="snowy" src="snowy.jpg"> </a-assets> <a-scene> <a-sphere position="0 0 -15" radius="2" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 2 -15" radius="1.7" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0 4 -15" radius="1.3" material="shader: standard; color: #fff; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="-0.4 4.3 -13.9" radius="0.2" material="shader: standard; color: #000; metalness: 0; roughness: 1"></a-sphere> <a-sphere position="0.4 4.3 -13.9" radius="0.2" material="shader: standard; color: #000; metalness: 0; roughness: 1"></a-sphere> <a-cone position="0 4 -13.2" rotation="90 0 0" radius-bottom="0.2" radius-top="0" material="shader: standard; color: #ce6548; metalness: 0"></a-cone> <a-plane material="shader: flat; src: #snowy; repeat: 1 0.48" position="0 -1 0" rotation="-90 0 0" width="200" height="100"> </a-plane> <a-entity geometry="primitive: sphere; radius: 50" material="shader: flat; src: #snowy" rotation="0 40 0" position="0 0 0" scale="1 1 -1"> </a-entity> <a-entity light="type: ambient; color: #405e94"></a-entity> <a-entity light="type: directional; color: #FFF; intensity: 0.8" position="5 5 10"></a-entity> </a-scene> </body> </html>
在我的最终作品里还使用了一个名为aframe-particle-system-component
的自定义组件,在本教程中就不做说明了,你可以访问以下链接查看效果。
相关VR教程:【WebVR教程翻译】一步一步教你如何制作A-frame动画效果
又到了插播广告的时间:)如果你喜欢我的文章,想学习更多VR知识,欢迎关注我的微信公众号:差不多一个意思(搜索微信公众号:chabuduoyigeyisi)