摘要:本文主要介绍运用webgl的第三方框架three.js实现的一款简单的3D类网页游戏。主要内容包括介绍three.js,如何运用three.js摆放相机,设置相机角度,相机视觉角度转移,场景中物体的位置摆放和贴皮,在3d世界中的直线运动,碰撞检测等。
关键词:3d数学, webgl, three.js,
技术领域:前端开发,游戏开发,3d,webgl
一. 背景
作为 HTML5 大家庭中的一员,相比 CSS3 和 WebSocket 等大家早已熟知的新技术,WebGL 可以说是其中最神秘的一员。长久以来很多人——包括这个圈子里的技术人,对 WebGL 都不是很了解,甚至有很多的误解,但实际上去年一年到现在 WebGL 正在以爆炸般的速度发展着,很多情况都发生了变化。WebGL 就像一只丑小鸭,正在慢慢从土肥圆成长为白富美的公主。实际上,相对于国外,国内这方面的探索还是比较少,因为万恶的喝了三鹿奶粉长大的ie系列占据浏览器半壁江山,决定不可能在短期内大范围运用该技术,即使你做了一个很炫很酷的3D网站,做到逛网站好像第一人称逛商店那样具有3d真是的立体感,上线后pm肯定跟你拼了,因为过半的国内用户无法访问。弹出类似以下的对话框
这样,再好再炫的技术也是没用,因为用户关心的不是背后的技术,而是直接的用户体验。就好像实现同样的页面效果,有人用纯CSS3实现(包括所有图片),也有人直接切图。这显得用CSS3的人很geek,但是上线后根本看不到原有效果,圆形变方形,方形还是畸形,畸形的什么都看不到。说这些无非是想说明webgl其实并不是一个新鲜的概念,而是一门技术而已。没有webgl的,unity3D,flash的stage3D一样可以做到,甚至有过之而无不及,兼容所有浏览器,直接可以商用,给用户就一个字,酷。
那不就是说webgl没用,拼不过flash吗?那还花那么多时间精力去写博客,做demo干嘛。实际上,WebGL是一种3D绘图标准,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等。这样用户就可以不用安装各种各样的插件就可以访问3D型网页了。
PS,上面那段话是摘自百度百科的webgl词条,不相信可以去查一下,一模一样的。不过话说回来,个人鄙见,webgl在pc端是赢不了flash的,起码在未来十年。因为现在98.919%的浏览器都支持flash,用户情愿去装一个flash player,也不愿意改用浏览器(尤其是我们很有爱的ie6 ^_^)。所以pc端的话webgl有点尴尬。不过在移动端方面还是值得期待的,尽管刚刚发布的ios6没有说支持webgl,但是随着HTML5的web app在移动端的普及和硬件的升级,相信webgl还是可以打下移动端的半壁江山的。现在玩webgl相当于玩玩具,实属是各位技术发烧友和Geek打发周末无聊时间的必备良药。
吐槽了大半天,无非是介绍一下背景而已,下面正式开始我们的webgl之旅。
二.Three.js简介
国外有一位叫mrdoob的哥们,写了一个three.js的框架,对底层的webgl进行封装,极大方便了我们写webgl程序。关于这个框架,详情(https://github.com/mrdoob/three.js/)。
更多精彩的Demo,详见(http://mrdoob.github.com/three.js/) ,更多的基于three.js的第三方游戏框架可参见chandlerprall大牛写的一个插件,详情可见
(http://chandler.prallfamily.com/2012/04/physijs-a-physics-plugin-for-three-js/)。
说了这么多,还是有不少人不清楚three.js有什么便利,举个例子,要在场景中画一个立方体,以下代码足够了
如果是用原生的GLSL语言(可以理解为一种对底层图形硬件编程的语言),需要手工定义定点着色器,片元着色器,然后定义所有顶点数组,需要比较恐怖的代码量。
最核心的代码也需要以下这些。
以上的代码,其实我也看不明白,大意就是定义一个顶点数组,然后通过顶点着色器把顶点信息传给底层GLSL处理,光是大路货代码就要敲老半天,试问用这种方式还有心情去写具体的业务逻辑吗?
其实物体可以用其各个顶点坐标表示,顶点通过模型视图矩阵转换为视觉坐标,然后通过投影矩阵转换为裁剪坐标,接着通过透视除法规范化设备坐标,最后通过视口变换转变为窗口坐标。这就是一般的顶点变换过程。
所以一般要写webgl程序,都是用现成的封装好的框架,站在巨人的肩膀上。Three.js算是当前一个最流行最好的框架。
三. 游戏的具体实现原理
1. 游戏简介
说了这么多,还没有见到真正的游戏demo,很多童鞋估计要吐槽了。不用急,其实demo是次要,本文最重要的是通过demo去介绍如何使用一些基本的webgl的api和一些思想算法。
Demo的地址页 http://www.bimcad.org/bomber3d/ 源码也在里面,不另外附带。
游戏截图如下
相信不少80后都会记得那个红白机的炸弹人,没错,其实这个demo就是那个游戏的3D版。
2. 摆放相机
为了简化,游戏主要是用第一人称视觉,而不是生化危机那种介于第一和第三人称之间的视觉角度。下面是用Three.js设置相机的代码
其实相机就是相当于人的眼睛,人的眼睛看到的世界相当于一个视景体,如下图。
在计算机中,人能看到的东西其实就只有上图的近平面和远平面之间的东西。
PerspectiveCamera的第一个参数就是上图的视觉,第二第三个参数就是上图的W和H,第四第五个参数分别是近平面距离和远平面距离。至于camera.position.x,y,z这个就是相机在3D世界中的摆放位置。游戏中要移动人物是,只需要改变这三个值就可以了。
3.设置观看点
光是有当前的相机点还是不能确定要观看的东西的,还要设置观看点,例如你在(0,0,0)看(1,1,1)和(-1,-1,-1)是截然不同的两个世界。可以通过下面的代码实现观看点的设置。
大家如果在大一时有过被高数折磨的历史的话,那么恭喜你,下面的内容你应该很快能接受。
有人可能会问,为什么上面的公式中每一个后面都“+camera.position”?很明显,这个是因为相对于原点的平移。把当前点作为平移后球面的球心。
4. 如何运动
先看下面的公式
怎样,是不是很熟悉,再次勾起各位的青葱岁月的回忆?其实大学里学的理论知识是有用的。只不过很多人不知道怎样用而已(当然包括我自己,还不能充分运用各种理论知识)。FE们如果不想一辈子做页面,不想成为二八原则中的那80%,不想整天写点高中生就能懂的业务逻辑的话,就马上拾起高数,线代,概率课本复习吧。啊,说错了,上面这个是高中知识点。
如上图,以二维为例,三维一样的道理。假设有当前点是P1(x1,y1),观看点是P2(x2,y2),那么,λ表示的是起点P1到P与P到末点P2的比值。这样通过确定λ,我们就可以得到单位时间移动后的到达的点。程序的核心代码是如下
因为本demo是只在x,z平面,所以不涉及y的运算。当然y的运算一样的道理。
5. 如何确定当前相机所在的区块
见上图,相信有过acm经历的童鞋肯定会对线段树的大路货代码情有独钟。没错线段树的确可以把O(n)效率化成O(logn)效率。由于本demo的特殊性,把整个地图划分为一个一个的正方形区域,有些区域放置木箱,有些区域是道路。所以可以通过二维线段树快速寻找出移动后相机点是否处于木箱区域,从而能够检测碰撞。具体做法如上图把一个正方形划分为四个区域块,如果当前点落在左上方,就递归到左上方继续寻找,需要注意的是“线段”边界的确定,如果中点处不是单位长度的两个端点,那么需要取到线段的两端,然后再继续划分。最后截止划分的结束条件是,划分的区域是“单位长度”。核心代码如下。
关于材质纹理贴图
一个游戏,给玩家的第一感受是美术设计,而不是程序的geek程度。所以即使程序很牛,很复杂,美术UE不过关,也是一个失败品。从这个意义上来说,本demo绝对可以称得上是失败品中的战斗机。
其实运用three.js贴图就好像打字一样简单,如下代码就可以给地面贴图了。
同样,给木箱贴图
本人在3D方面的功力有限,因为没有系统地学习过3D计算机图形学,所以上面的贴图方式在商业中是绝对不能直接采用的,否则效果是巨卡,这里只不过是给大家一个认识而已。
7. 关于键盘按键
相信做过canvas游戏的童鞋都知道,按键的处理方式了吧。这里就顺便啰嗦一下。按键的处理方式有两种。一种是立即做出反应。比如发射激光枪!按键后,按某种固定速率自动重复,比如每秒发射两次。另一种是根据按键时间长短期发生作用。例如,按方向键向前走,直到松手之后,才会停下。本demo按照第二种方式处理按键。设置一个按键数组,按下某个按键后改变数组中相对应的布尔值为1,表示正在按下该键,这样就可以同时允许按下多个按键,当放开按键,就把对应的数组的布尔值改为0。这样只需要在requestAnimationFrame()中轮询检测键盘数组的各个布尔值做出相应的动作。
四.结束语
本文简单地介绍了运用Three.js基本的api做出自己的3D游戏。当然Three.js博大精深,很多其他的api都没有用到,例如光照等。只用到了最基本的几个。其实现在有很多很好的three.js物理引擎,例如ammo.js,相机的移动更是应该有现成的框架和方法,本demo都没有用上,而是自己写原生程序,所以疏忽之处在所难免。欢迎各位大神吐槽指教。另外,如果做得深入点的话还可以用nodejs + websocket+ socket.io搭建起在线对抗,类似于泡泡堂。还有添加AI功能等。