上次发了篇介绍车子压倒草的博文
http://www.cnblogs.com/JinT-Hwang/p/8169146.html
中的2.部分。
不过存在一个问题,就是如果草很多,会有很多个碰撞体,虽然物理效果比较真实但是可能会导致性能问题。
这次介绍一下在草的材质中编码,实现车子压倒草的功能。
material中会用到一些数学的知识,还需要各位有一定的概念才能很好的理解。
关键词:unreal4、虚幻4、汽车、草、压倒、伏地、Material、材质编辑器、Shader
实现思路:通过求出草离车子的水平和垂直距离(如果只判断车子离草的距离,结果周围的草会成一个圆形倒地而不是一个长方形),再根据距离让草旋转不同的角度。
思路详解:
1.要实现的功能如下图。
2.O点是车子的中心坐标,A是草的中心坐标,只需求出AB和AC的长度,然后再跟自己设定的触发距离比较,符合就草根据距离做逐渐倒地的动作。(需要两个实现,一距离判断,二倒地动作)。
3.只有O、A两点的坐标还不足以求出AB和AC的距离,注意上图表示的是车子本地XY轴坐标系,车子不是在世界原点。
4.要求出BO(AC)的长度,至少要知道Cos∠AOB,此处可利用点乘(即数量积、点积),向量OA·向量OB=|向量OA|·|向量OB|·Cos∠AOB,求出Cos∠AOB后,即可求出OB的向量和OB长度。
5.求出OB长度后,用勾股定理即可求出AB的长度,第一个实现就完成了,第二个实现以及细节的请往下看。
一、虚幻4如何通过Material编程实现车子压倒草的功能
A.先创建一个MaterialParameterCollection(材质参数集合),因为material下的局限,只能通过调用MaterialParameterCollection来获取外部标量和矢量。具体介绍可参考下方官方文档。
创建后双击打开,Scalar Parameters下创建的是标量(0),Vector Parameters下创建的是矢量(0,0,0,0),此处先创建一个矢量的,命名为CarPosition,用来储存车子坐标,来传入Material使用的。
打开车子的蓝图,在EventTick事件引出一条线用于实时获取车子坐标。
车子的坐标有了,车子指向草的矢量(向量)就可得到了,还需要一个车子指向前方的矢量,此处车子的蓝图的component面板中添加一个空的组件,调整到车后底部并且对准车子的中心线。
在前面的MaterialParameterCollection中再添加一个表示车后坐标的矢量参数。
回到车子的蓝图中,从Event中再引一条线出来用来获取车子后面点的坐标。
B.将前面的MaterialParameterCollection拖动到草的Material里,点击该Material Parameter Collection,并在Details面板中的Parameter Name下拉框选到表示车子坐标矢量的参数,再复制一个,选到表示车子背后坐标矢量的参数。
两个坐标需要先用Mask去掉第四个坐标,将Float4转换为float3。在material里的参数、操作是以颜色RGBA码来表示的。
车尾坐标指向车中心坐标的向量,用车中心的坐标减去车尾的坐标即可得到(后面简称向量DO)。此处相减前都乘了(1,1,0),这是去掉Z轴坐标的干扰,计算的时候只要算XY轴平面的距离即可,只要2D向量即可满足。当然,也可以前面mask的时候只保留RG值组成为Float2坐标。
同理,车中心坐标指向草中心坐标的向量(后面简称向量OA)。下图的Object Position是指当前这个material的中心在世界坐标中的位置。
根据点乘的公式可得, Cos∠AOB=(向量OA·向量OB)÷(|向量OA|·|向量OB|),此处向量OB不知道,用向量DO代替Cos∠AOB=(向量OA·向量DO)÷(|向量OA|·|向量DO|),因为向量DO的指向和向量OB的指向相同,所以求出来的Cos∠AOB值是一样的。
向量的点积,用Dot接上两个向量即可求出,向量的绝对值可用Vector Length即可求出(前面是Float2的用V2,前面是Float3的用V3)。此处有两种做法,一种是用Normal将向量归一化然后再求Cos,或者直接用本身向量长度求也可以。
可以先求向量OB,再求向量OB的长度,或者直接向量OA的绝对值乘上Cos∠AOB直接得到OB的长度。
但是此处有一点要注意,向量OA和向量DO夹角大于90°时,Cos∠AOB的值时负值的,会导致车子的前面部分草有压倒的反应,但是后面部分草没有反应。
要求的是Cos∠AOB,但是当角度大于90°时,cos的值实际上是cos∠AOB’。当∠AOB’增大时,∠AOB在减小(由90°->0°),由上面的cos值变化图,可以看出当角度大于90°时,要求的cos∠AOB跟当前的cos值时相反的关系。
当cos∠AOB大于零时,仍使用当前的cos值,cos∠AOB小于零时,即乘以-1即可得到向量OB。
然后即可用勾股定理求出向量OB的长度,或者用向量OA=向量OB+向量BA,求出向量OB后再求向量OB的长度。到这里草在水平方向和垂直方向离车子的距离就都求出来了,分别是|向量OA|和|向量OB|。
C.接下来需要实现草旋转的部分。如下。RotationAngle的值需为0~1.
旋转是根据UV坐标系来旋转的,x轴向右,y轴向上,z轴垂直xy轴从屏幕出来。
(1,0,0)表示沿着本地X轴旋转,但我们需要在世界中,面向相机的方向倒下去,所以加上Transform Vector将本地的坐标空间转换成世界坐标,此处除了使用View Space也可以使用Camera Space,效果等同。
还需要自定义三个参数来做判断依据,CarLong和CarWidth两个参数用来判断是否在车子底下,OutSideWidth用来结合另外两个,来判断外圈草倒地动作的范围。
上面的自定义参数,carlong储存车子长,carwidth储存车子宽,outsidewidth储存草开始触发操作,离车子矩形边缘的距离。
判断草是否在触发范围内,如下图。第一个if,出口接material上的World Position Offset,当A>B时,如果有之前有别的代码连World Position Offset上,可连在此处,否则连一个0常量即可,第二个if当A>B时,相同,当A<B时,接旋转草代码。
草旋转倒地也要做判断,当草在车子底下时为倒地90°,在车子外但在+outsidewidth内时,根据距离由远及近0°->90°改变倒地的角度。
此处将90÷OutSideWidth得到每1单位OutSideWidth对应多少°,|向量OB|(即草竖直方向离车子中心的距离)减去CarLong得到草离车子边的距离L,然后L去乘上面得到的等分,得到L对应的角度,因为初始的角度时90°,即有这样的关系,草离车子边的距离L:OutSideWidth->0时,草的角度∠:0->90,90再减去乘出来度数可得到草应旋转的度数,然后除以360可以得到需要的浮点数。
此处还可以根据车子的前进后退,让草分别做出往前旋转还是往后旋转,在前面的MaterialParameterCollection中新增一个CarForwardSpeed,并在EventTick下实时获取车子的速度方向,然后根据CarForwardSpeed的值传+360或-360到上上图中。
判断草水平方向离车子边的如下图,同理。
完成后,大体结构如下。
如果需要HLSL代码,可以点击window->HLSL Code。
到此就完成了。