乐府互娱技术分享!Cocos Creator用2D 相机实现

发布时间:2022-09-25 04:56:44 来源:世界杯决赛怎么押注 作者:世界杯押注App下载

  用个 3D 相机,设置 3D 节点去旋转 rotationY,然后再移动这个节点

  我的第一反应也是如此。但是这样一来就得新增一个 3D 相机,并且需要增加一个分组,使这张图只被 3D 相机渲染不被 2D 相机渲染,同时还要额外管理这个 3D 相机。

  既然要模拟 3D 旋转运动,我们首先要知道它到底是怎么运动的,它的运动在 3D 相机与 2D 相机下又有什么区别?

  3D 与 2D 相机的区别也就是投影矩阵的区别,即透视投影与正交投影的区别,也就是有没有近大远小的效果。我们可以将正交投影理解为相机无限远的透视投影,远到可以忽视屏幕与物体间的距离。反之,想要模拟透视投影就需要假设一个与屏幕有一定距离的相机。

  首先我们假设在图形的本地空间坐标系内:原点在图片的中心点,假设某顶点的坐标为(x , y , z),Z 轴的正方向朝屏幕内侧。

  图形围绕 X 轴旋转了角度 A,此时该顶点坐标变为了(xcosA,y,xsinA)。

  上图中,黑点为旋转前的点,红点是旋转了角度 A 后得到的点,绿点是旋转后的点与相机连线与屏幕的交点。

  其中(x,y)为变换前的坐标,A 为变换角度,n 为假设的投影点到屏幕的距离。

  我们已经知道了旋转角度与变换前后坐标的公式,那么该如何修改图片的顶点坐标呢?

  创建自定义材质,给材质增加参数。这个参数会作为 uniform 变量传入 shader。

  Assembler 是指处理渲染组件顶点数据的一系列方法。不同渲染组件可能有不同的顶点数据、顶点数量、填充规则,也会使用不同的 Assembler。我们目前使用的 Cocos Creator 的 2D 渲染中,Assember2D 类是一个重要的基础类。

  最常用的 cc.Sprite 的各种模式(simple,sliced,tiled等)在内部都对应了不同的 Assembler 派生类。同样是一个四边形的节点,不同的 Assembler 可以将其转化成不同数量的顶点实现不同的渲染效果。

  接着对底层 Assembler 进行分析。我们图片使用的是 simple 模式,所以我们查看 simple 脚本,发现 simple 脚本就三个函数:

  而 updateVerts 函数中只是计算了一些图片本地数据(计算出纹理中,上下左右四个边距离锚点的距离),然后调用了父类 Assembler2D 的 updateWorldVerts 函数将把节点的本地坐标转换为世界坐标保存到 verts(顶点数据)中。

  获取到图片节点 cc.Sprite 的 assembler;将重新计算后的坐标节点(内部)替换原来的顶点坐标;重写 assembler-2d 中 updateWorldVerts 方法以使用计算后的坐标;最后将节点的渲染标记标脏,以重新刷新节点的渲染。

  看上去是没问题的,但是慢放时图片似乎有一些变形。我们用九宫格图做对照,果然,移动过程中是变形的,图片沿着对角线发生扭曲了:

  为什么会扭曲呢?这是因为我们将 2D 的点的位置修改成 3D 顶点的位置数据,让图形扭曲成我们要的样式,但是顶点的 UV 坐标仍是 2D 的,是没有深度的。这里的问题在于我们进行了错误的 UV 坐标插值。3D 的 UV 插值不一定是线性的,除非顶点的 Z 坐标都是一样的才行。

  因此这个方法并不可取。我们重新回过头来思考实现方案。为什么 2D 相机与 3D 相机的显示不同呢?在之前的尝试中我们了解到是因为投影不同,那么是不是修改相机投影就可以了呢?

  想要修改相机投影,就要知道投影是怎么影响节点的渲染的,所以我们对图片默认纹理进行了分析。

  通过查看官方的[内置 shader 变量]文档,我们发现,cc_matWorld 是将顶点的坐标转换为世界坐标的变换矩阵,而 cc_matViewProj 则是模型坐标到透视的变换矩阵。那么想要修改相机投影,最后肯定是修改 cc_matViewProj 的值了。

  我们不断从一个参数到另一个参数、从一个函数到另一个函数,终于发现相机根据是否是透视投影有不同的矩阵计算,最终得到了我们要的_matViewProj也就是视图投影矩阵。现在就来了解一下相机的透视矩阵是如何计算的。

  所以只要将目标图片的 cc_matViewProj 改为透视投影的 vp 值(透视矩阵*视图矩阵并将其转化为数组)就可以用 2D 相机实现 3D 投影的效果了。

  知道了投影矩阵怎么修改、以及修改的矩阵应该写入哪里,那么现在我们应该怎么将自定义的参数传入 shader 中呢?答案就是:自定义材质!

  我们只需要在新建的 Effect 文件的properties中声明一个类型为16位数组的变量(比如 mat_vp),然后获取图片的材质并将我们计算好的视图投影矩阵转为数组值传入自定义的材质中 material.setProperty(mat_vp, arr) 就可以了。

  1. 新建一个 effect 脚本和对应的 material 材质,并将材质挂载在图片上。

  2. 在 effect 中自定义一个参数存放我们的计算得出的矩阵数组,并在顶点着色器中使用。

  5. 然后将节点设为 3D 节点,根据需要的角度去旋转节点的 RotationY 就可以了。