小草弯弯开源库评测

小草弯弯开源库评测

【博物纳新】是UWA旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。

更多精彩内容请关注:lab.uwa4d.com


导读

GrassBending是一个实现了草地交互弯曲效果的开源项目。该项目中,提供了一个用于替换低矮植被(hua hua cao cao)的Shader,和用于传递数据的相关脚本。本文将简单介绍该项目的使用、实现,并展示在不同机型上的性能数据。

项目信息

Unity版本:2018.3+
https://lab.uwa4d.com/lab/5c29315472745c25a802d1a7

效果展示

请输入图片描述

使用方法

此项目中,提供了一个SampleScene,展示了如何使用这些脚本和Shader实现草地交互效果,即上文中效果展示的场景。在示例场景中,植被使用Unity Terrian制作,并且已经包含了6种不同的Details预设,并且它们的Billboard选项都是开启的。在示例场景中,作者提供的Shader为 "Hidden/TerrainEngine/Details/BillboardWavingDoublePass" ,可以直接应用到Billboard类型的Terrian Details上的。

对于角色或其他希望与草地相互作用的物体,您需要在其上挂载一个BendGrassWhenEnabled或BendGrassWhenVisible组件,用来标记交互关系。显然,这些脚本会在其所在上GameObject状态发生改变时,启用对应的检查,并将数据传递到草地的Shader中。

以BendGrassWhenEnabled为例,其上有两个参数。BendRadius用于控制物体影响草的球体范围大小。Priority则用来控制弯曲源的优先级,以防有超过上限的角色(或物体)同时影响到草地的弯曲,数字越小优先级越高。下图为调节Bend Radius造成的效果区别:

请输入图片描述

实现原理

本项目中,使用的是在Shader中改变顶点位置从而达到弯曲效果的方法。这种方法也是一个比较常见于手游项目的方法。

在添加了BendGrassWhenEnabled或BendGrassWhenVisible组件后,这些物体会在OnEnable/OnBecameVisible时会被添加为一个bender,再将所有benders的Position、BendRadius数据作为bendData传递给Shader。

1Shader.SetGlobalVectorArray(Shader.PropertyToID("_BendData"), bendData);

Shader拿到这些数据后,计算出顶点与bender的距离,并与BenderRadius进行比较,计算出顶点受的弯曲力大小bendPower和弯曲方向bendDir,并根据顶点色的alpha值,进行顶点位置的偏移,相关代码如下:

 1float bendRadius = _BendData[i].w;
 2//角色位置
 3float3 benderWorldPos = _BendData[i].xyz;
 4//顶点位置
 5float3 vertexWorldPos = mul(unity_ObjectToWorld, v.vertex);
 6
 7//角色和顶点距离
 8float distToBender = distance(float3(vertexWorldPos.x, 0, vertexWorldPos.z), float3(benderWorldPos.x, 0, benderWorldPos.z));
 9//顶点受到的弯曲力
10float bendPower = (bendRadius - min(bendRadius, distToBender)) / (bendRadius + 0.001);
11//弯曲方向
12float3 bendDir = normalize(vertexWorldPos - benderWorldPos);
13//顶点偏移量
14float2 vertexOffset = bendDir.xz * bendPower * v.texcoord.y * v.tangent.y;
15//根据顶点色的alpha值计算该顶点是否需要进行偏移
16v.vertex.xz += lerp(float2(0, 0), vertexOffset, saturate(bendRadius * v.color.w));

通过Wireframe视图可以更明显地看出这一步实现的效果,注意看角色走过时网格发生的变化:

请输入图片描述

在草地的绘制上,Shader通过两个Pass分别绘制了不透明和半透明边缘(Alpha Blend)的部分。也因此,在渲染的性能效率上会比较低,在使用时可以根据项目的实际情况进行调整。

性能测试

从上述的介绍可知,在这个项目中,使用Shader中偏移顶点的方式,可以将主要计算工作放在了GPU端,减轻CPU端的性能压力。

对此,我们在【红米4X】、【Vivo Y85】、【小米8】这三款不同档次的机型上进行了性能测试,测试版本开启了多线程渲染。最终得到了如下数据:

请输入图片描述

以红米4X为例,具体查看一下其中逻辑代码的耗时情况。从这里看到逻辑代码的耗时主要集中在Gfx.WaitForPresent和Camera.Render两个函数上,由此再具体查看两个函数的耗时曲线。


(红米4X的逻辑代码耗时Top10函数列表,此处统计数据包含了所有线程的耗时情况)


(Gfx.WaitForPresent&Camera.Render函数耗时曲线)

在耗时曲线中,波动比较明显。耗时波动受视野内渲染的草量影响,如上图所示,渲染草量多时(红框区域),耗时也会有明显上涨的峰值波段,渲染草量少时(绿框区域),耗时明显偏低。

综合以上数据,可以看到Gfx.WaitForPresent有超过30ms的耗时。

首先我们来复习一下这个函数的意义:【扒一扒Profiler中的占坑鬼】,由此推测这里主要的性能瓶颈在GPU端,结合上文的分析,认为和Overdraw的关系比较大。对应Camera.Render的高值推测也是受GPU端压力造成的等待同步时间。

总结

使用Shader中偏移顶点的方式来实现草的交互效果也是一种常见可借鉴的思路。在移动端中更常见的是使用Mesh而非Terrian实现草地,并结合分块LOD处理分级效果。对于需要在移动端上实现上述效果的项目,建议参考此项目的实现思路,进行改进和优化后使用。

快用UWA Lab合辑Mark好项目!

请输入图片描述

今天的推荐就到这儿啦,或者它可直接使用,或者它需要您的润色,或者它启发了您的思路......

请不要吝啬您的点赞和转发,让我们知道我们在做对的事。当然如果您可以留言给出宝贵的意见,我们会越做越好。