正式开始做了(什么都不说才是坠吼的)
建立最小模板项目
因为不想在sdk-hellovr上面直接做改动,所以照着他的代码把build.gradle,AndroidManifest.xml扒了下来,随后在核心类中完成了与GvrView之间的互相绑定,并将几个要实现的接口都列了出来。这样就可以开始开发了。
正方形图元绘制
新建一个MeshedRectangle类。直接传入逆时针顺序的四个角坐标。
先不考虑纹理,着色器可以简单一点。发现在java/res同级路径下建立asset文件夹,可以把.glsl格式的着色器丢到里面去,在运行时读取而不用在Java程序中硬编码,写起来也费劲。令人惊喜的是,Android Studio居然有插件实现了对GLSL的支持!写个着色器都有高亮岂不美哉?
接下来,编译并链接着色器。为此我们也建立一个类似的工具类,只不过这次要从资源文件中读入着色器代码。
三角形图元绘制
那就先在眼前绘制一个纯色三角形图元吧。没想到这个这就踩了小半天的坑。
- 用到的
float[]没有用new分配空间直接传进去作为参数运行时居然都不报错… - 传进去的
FloatBuffer,ShortBuffer要通过ByteBuffer才能从数组转换过去,详情参考Android三角形绘制。
正方形图元绘制与纹理
也创建一个纹理类,在绘制同一种正方形图元之前绑定一下吧,这部分代码直接参考sdk-hellovr。
复制代码都能复制错…
编译链接着色器的代码最好全盘复制sdk-hellovr,最后一步错误处理很有用,我抄着色器的时候把texture2D抄成texture2d半天没发现…
找了一张简单的图片,看上去有模有样的。
基础场景搭建
按照前方为$z$轴正方向,右侧为$x$轴正方向,上面为$y$轴正方向建立坐标系。
值得注意的是LookAt传入的第二组坐标并非视线方向而是视线上一点,我们要传入摄像机位置与期望的视线方向之和。
实现基础移动
虽然对于Android的事件分发机制还并不是很了解,但通过重载MainActivity的onTouchEvent方法,实现了对于trigger release事件的捕获。因此,现在已经可以做到按下trigger持续移动,松开trigger停止了。
只不过,现在做到的是向某个固定方向移动,还需要能够做到向视线方向移动。摄像机的$y$坐标,即高度,一直保持不变,因此,实际移动方向是视线方向在$x-z$平面上的投影。
视线方向可以直接通过headTransform.getForwardVector获取,做完之后发现正好是倒着走的,于是就都取了个负号就能正常跑了。但实际上我对于其原理还不是特别理解。
在onNewFrame中实现移动,根据这一帧的时间(上一帧绘制所用时间)乘以速度,再乘以速度单位向量分量更新摄像头位置。注意$y$是固定不动的。
现在trigger被设定为只能用来进行移动,事实上这是不太够的,后面最好还要支持识别长按、短按。
实现基础碰撞
为了不移动到场景外面,以及实现躲避障碍物、陷阱,要实现一个简单的碰撞系统,当然,是2D的。
设置可通行、不可通行两种触发器,分别用来实现墙、障碍物,以及陷阱、机关等等。
为此我们要实现判断两条2D线段是否相交、以及求交点的功能。去网上找了一下没有找到特别好用的库,照着原先的计算几何代码看一下吧。
具体实现是在每堵墙外侧设一个空气墙,如果某一帧要往墙里面走则会停在墙上。具体实现就是两条线段求交点,如果存在的话则会被卡到交点上。但如果你的当前位置在墙上,则不管你怎么走都会相交,就等于被卡死到这堵墙上了。因此,我们还需要给空气墙设置一个外侧法向,使得允许往外走但不能往里走。
于是,现在我们终于实现了在一个封闭区域进行漫游了!到这里可以认为这篇博客要实现的功能已经做完了。