通过Mesh投影来实现贴花系统

在做FPS之类的游戏中,如果枪打到了墙角,并不能简单放置一来弹孔面片了事。而是要像一张贴纸一样,完全与墙角贴合。这时就需要去实现一个贴花系统来达到这种效果。

贴花系统有几种不同的实现方式,但这里仅考虑通过Mesh投影来实现贴花系统的实现原理。

这种方式的本质是,找到视野中贴花资源会影响的Mesh, 并创建一个同样大小以贴花资源为纹理的Mesh覆盖上去,从而达到贴花的目的。主要分下面两步来实现。

1. 先找到会受影响的物体,比如将弹孔贴在两面墙的夹角,那么受影响的物体就是两面墙。

怎么找到这两面墙不同的需求可能实现方式也不一样, 在场景编辑器中通过贴花来实现静态点缀效果,可以通过创建贴花资源的AABB盒来实现。如果是运行时动态创建弹孔也可以通过四次射线检测来达到,总之方式有很多。

2. 先创建一个半径为0.5单位的裁切立方体,在裁切坐标系中,贴花资源就被放在y=0平面中,贴花资源的中心就是裁切坐标系的(0, 0, 0)点。

需要说明的时这一步实际上并没有代码操作,只是一个数学抽象。我们的目的是要将所有受影响的三角形投影到y=0平面上,以便可以正确的采样贴花纹理。

3. 将受影响物体Mesh的所有三角形均转换到裁切立方体的坐标系之下对立方体的8个平面进行裁切。

在进行裁切之前,有一种情况需要处理,因为三角形是有朝向的,这个朝向是通过面法线来确定的(Unity中三角形的法线为Cross(v2-v1, v3-v1)),在正常的渲染流程中法线不能射入眼睛时,是不会被渲染的。在Unity中视锥体坐标系中,Vector3(0, 0, -1)是前向,因此眼睛的位置在Vector3(0, 0, 1)处。

在这个裁切立方体同样如此,不可能将纹理投影到一个三角形平面的背面,所以需要先先判断三角形的法线与Vector3(0, 0, 1)的夹角是否小于90度,只有小于90度才可能会被投影,才需要被裁切。

裁切时会出现,三角形完全在立方体外, 三角形完全在立方体内,三角形一部分在立方体外一部分在立方体内。前两种情况很好处理,但是第三种情况有可能会将一个三解形切成2个,因此需要格外注意。具体的裁切算法视锥体裁切算法一致,这里就不赘述。

4. 纹理采样,在创建三角形时,我们需要为每个一顶点指定一个uv坐标。前面已经说过了,我们的实现方式是将裁切后合法的三角形投影到裁切坐标系的y=0平面上, 投影之后的坐标为(x, 0, z). 因此uv可直接执行u = Lerp(0.0f, 1.0f, x + 0.5f), v = Lerp(0.0f, 1.0f, z + 0.5f).这里之所以加0.5f修正是因为立方体中心坐标为(0, 0, 0),这也意味着x,y,z的最小值均为-0.5f.

说了这么多附上源码一篇。需要说明的是,这个源码并不是我实现的,是我从网上找来之后修改的,毕竟我对Unity3d没有那么熟悉。

ps.单位相同的裁切立方体如何适应不同尺寸的贴花资源?在Unity中可以通过设置Scale拉伸坐标轴来实现,所以说3D数学真奇妙。

pps.在实现过程中发现,新创建的Mesh不能紧贴被覆盖的Mesh, 因为在相同的深度情况下,新创建的Mesh并不能保证一定在被覆盖的Mesh之后渲染,这会概率性出现新创建的Mesh与被覆盖的Mesh相互覆盖的情况。因为在创建完Mesh之后,需要根据平面法线上浮一点,以保证Z-Buffer正常工作。

发表评论

× three = twelve