Csharp/C#教程:Unity实现喷漆效果分享

本文实例为大家分享了Unity实现喷漆效果展示的具体代码,供大家参考,具体内容如下

喷漆功能

**应用场景:**如墙上的标语贴花,汽车上的喷漆等。

选择方案:

1、当然实现方法各式各异,最最最简单,也是最“不堪入目”的方法是直接给一个面片,然后获取喷漆位置,加上一个要喷漆表面法线方向的偏移,作为最终面片放置位置,当然,不要忘了设置面片的方向。这种方法虽然说简单,但是效果并不理想,会出经常现与其他物体穿插的情况,如果游戏中曲面太多,那么这个方案基本没法看。
2、对于个别特殊的需求来讲,比如说人物身上的纹身,完全可以用一个shader里实现,此方法仅限于一个贴花对应一个物体,如果是一对多的情况,请看后边这两种。
3、有一种简易的方法是用Projector,这种方法实现较为简单,不多说。
4、接下来说一种动态生成网格方案,也较为常用,接下来就详细说说这种方案。

实现思路:

喷漆的网格是根据场景中所喷位置的物体的网格动态生成的,喷漆的时候,获取规定范围内的物体,再用一个立方体(也可以用球体)去截取这些物体的Mesh,从而构造新的网格,将喷漆渲染在这个Mesh就OK了。

代码实现:

首先,我们需要一个获取规定范围内MeshRenderer的函数:

publicGameObject[]GetAffectedObjects(Boundsbounds,LayerMaskaffectedLayers) { MeshRenderer[]renderers=FindObjectsOfType<MeshRenderer>(); List<GameObject>objects=newList<GameObject>(); foreach(Rendererrinrenderers) { if(!r.enabled)continue; if((1<<r.gameObject.layer&affectedLayers.value)==0)continue; if(r.GetComponent<Decal>()!=null)continue; if(bounds.Intersects(r.bounds)) { objects.Add(r.gameObject); } } returnobjects.ToArray(); }

然后拿到这些GameObject去做裁剪,裁剪函数:

publicvoidBuildDecal(GameObjectaffectedObject,boolisLast) { MeshaffectedMesh=affectedObject.GetComponent<MeshFilter>().sharedMesh; if(affectedMesh==null)return; //这里预存了已获取物体的vertices和triangles,减少了不必要的GC Vector3[]vertices=GetVertexList(affectedObject); int[]triangles=GetTriangleList(affectedObject); //目标顶点转换到当前物体的模型空间 Matrix4x4matrix=this.transform.worldToLocalMatrix*affectedObject.transform.localToWorldMatrix; //将主要计算移入异步 Loom.RunAsync(()=> { for(inti=0;i<triangles.Length;i+=3) { inti1=triangles[i]; inti2=triangles[i+1]; inti3=triangles[i+2]; Vector3v1=matrix.MultiplyPoint(vertices[i1]); Vector3v2=matrix.MultiplyPoint(vertices[i2]); Vector3v3=matrix.MultiplyPoint(vertices[i3]); Vector3side1=v2-v1; Vector3side2=v3-v1; Vector3normal=Vector3.Cross(side1,side2).normalized; if(Vector3.Angle(-Vector3.forward,normal)>=maxAngle)continue; DecalPolygonpoly=newDecalPolygon(v1,v2,v3); //用立方体裁剪 poly=DecalPolygon.ClipPolygon(poly,right); if(poly==null)continue; poly=DecalPolygon.ClipPolygon(poly,left); if(poly==null)continue; poly=DecalPolygon.ClipPolygon(poly,top); if(poly==null)continue; poly=DecalPolygon.ClipPolygon(poly,bottom); if(poly==null)continue; poly=DecalPolygon.ClipPolygon(poly,front); if(poly==null)continue; poly=DecalPolygon.ClipPolygon(poly,back); if(poly==null)continue; AddPolygon(poly,normal); } if(isLast) { RenderDecal(); } }); }

DecalPolygon构建了新的三角形(这里注意顶点的空间变换),然后分别用立方体的每一个面去做裁剪,转换成数学算法,其实是判面与面的关系,具体实现:

///<summary> ///两面相交裁剪 ///</summary> publicstaticDecalPolygonClipPolygon(DecalPolygonpolygon,Planeplane) { //相交为True bool[]positive=newbool[9]; intpositiveCount=0; for(inti=0;i<polygon.vertices.Count;i++) { positive[i]=!plane.GetSide(polygon.vertices[i]);//不在裁剪面正面,说明有相交 if(positive[i])positiveCount++; } if(positiveCount==0) returnnull;//全都在裁剪面正面面,不相交 if(positiveCount==polygon.vertices.Count)returnpolygon;//全都在裁剪面反面,完全相交 DecalPolygontempPolygon=newDecalPolygon(); for(inti=0;i<polygon.vertices.Count;i++) { intnext=i+1; next%=polygon.vertices.Count; if(positive[i]) { tempPolygon.vertices.Add(polygon.vertices[i]); } if(positive[i]!=positive[next]) { Vector3v1=polygon.vertices[next]; Vector3v2=polygon.vertices[i]; Vector3v=LineCast(plane,v1,v2); tempPolygon.vertices.Add(v); } } returntempPolygon; }

OK,到这里已经为新的Mesh准备好了所有的数据,接下来将计算好的数据移步到主线程做渲染:

publicvoidRenderDecal() { //主线程渲染 Loom.QueueOnMainThread(()=> { if(sprite==null||Renderer==null||filter==null) { return; } //生成uv信息 GenerateTexCoords(0,sprite); //距离偏移 Push(pushDistance); Meshmesh=CreateMesh(); if(mesh!=null){ mesh.name="DecalMesh"; filter.mesh=mesh; Renderer.material=material; Renderer.enabled=true; } }); }

这样,一个喷漆功能就做好了,有几点需要注意是的是:

1.GC的控制

示例:Vector3[]vertices=mesh.vertices;
注意这里不是简单的内存引用,而是会申请新的内存,所以这样的临时变量会造成GC,当物体的顶点上十几K,甚至几十K的时候,这样的GC是吃不消的!为了尽量避免这样的情况,可以做一次预存处理,对没有检测过物体的顶点和三角形数据进行保存,下次用的时候直接取,从而取代mesh.vertices;

2.计算量的问题

还是出于性能的考虑,当与之裁剪的Mesh顶点数太多,在主线程for循环几十K次,不出意外PC端也会卡顿,所以异步是一个较好的选择。复杂的裁剪计算交给其他线程,计算好主线程直接拿数据做渲染;

3.效果问题

由于新生成的喷漆Mesh是由原有物体的mesh裁剪所得的,而这两个Mesh位置是重叠在一起的,两个完全重叠的面,如果其他因变量也相同的情况下,让计算机渲染,计算机也不知道该先渲染哪个,这样就出现z-fighting的问题。所以加一个Push()方法,将新Mesh的顶点沿当前顶点的法线方向挤出一点距离,这样就实现了一个喷漆功能。

您可能感兴趣的文章:Unity3D实现自动寻路Unity切割图集转换为多张图片Unity实现简单手势识别Unity使用EzySlice实现模型多边形顺序切割UnityWebRequest前后端交互实现过程解析Unity实现本地文本多语言化Unity通用泛型单例设计模式(普通型和继承自MonoBehaviour)

标签: ni

vue中全局路由守卫中替代this操作(this.$store/this.$vux)

unity使用射线实现贴花系统

上述就是C#学习教程:Unity实现喷漆效果分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/903838.html

(0)
上一篇 2021年10月21日
下一篇 2021年10月21日

精彩推荐