Unity草地/草坪案例分享(完整代码)

20 篇文章 5 订阅

老规矩先上图
在这里插入图片描述
最近又开始继续操刀我的新独立游戏啦,网上看了很多草地的案例受益匪浅。但是嘛最近还是选择用自己的方式去实现。
主要是因为这种方式可以更好的贴合我前后的需求。还有很多有意思的技术点,有时间一点点拿来和大家一起分享吧。
这次先来说说草地的实现方式:
由于草地需要贴合模型表面,所以这里是从附着的模型表面开始的:
一、循环Mesh三角型数值,以三角型为单位记录一组数据,内容为三个顶点位置及其法线信息。
在这里插入图片描述
代码如下:

private List<GlassPoint> myData;
        private RaycastHit Hit;
        private List<GameObject> objGlass;
        private void __CreateGlass(MeshFilter myTarget)
        {
            if (myTarget != null && myTarget.mesh != null)
            {
                int myLength = myTarget.mesh.triangles.Length;
                Vector3[] vector = myTarget.mesh.vertices;
                Vector3[] normal = myTarget.mesh.normals;
                int[] triangles = myTarget.mesh.triangles;
                myData = new List<GlassPoint>();
                for (int i = 0; i < myLength; i += 3)
                {
                    //取得索引
                    int index = triangles[i];
                    int index2 = triangles[i + 1];
                    int index3 = triangles[i + 2];
                    //修改为以第一个点为中心的相对值
                    Vector3 offset1 = vector[index];
                    Vector3 offset2 = vector[index2] - offset1;
                    Vector3 offset3 = vector[index3] - offset1;

                    //顶点沿法线偏移出去,再随机一定位置后再反射回来找位置
                    Vector3 startPos = offset1 + (normal[index].normalized*50) + (Vector3.one * UnityEngine.Random.Range(-10.0f, 10.0f));
                    if (Physics.Raycast(startPos, -normal[index], out Hit, 100))
                    {
                        GlassPoint point = new GlassPoint();
                        //通过射线取第一个点,并算出另外两个点
                        point.pos = Hit.point;
                        point.pos2 = point.pos + offset2;
                        point.pos3 = point.pos + offset3;
                        //记录法线
                        point.norm = normal[index];
                        point.norm2 = normal[index2];
                        point.norm3 = normal[index3];
                        myData.Add(point);
                    }
                }
            }
        }

以上即是每组数据记录了三个点顶及其法线的信息。
值得一提的由于所在位置和法线这不相同,这里用的是顶点沿法线偏移出去,再随机一定位置后再反射回来找位置的方式找到基点。
二、位置和信息找完后,然后就是创建Mesh将每个草的信息写进去,由于数量可能非常多,所以可能需要分开几个Mesh,代码如下:

private void CreateGlassMesh(List<GlassPoint> data)
        {
            for (int i = 0; i < 100; i++)
            {
                CreateGlass(i, data);
            }
        }
private void CreateGlass(int index, List<GlassPoint> myGlassData)
        {
            int step = 9000;
            int startIndex = index * step;
            if (startIndex > myGlassData.Count) return;
            int endIndex = Mathf.Min(startIndex + step, myGlassData.Count);

            Mesh mesh = new Mesh();
            List<Vector3> vector = new List<Vector3>();
            List<int> triangle = new List<int>();
            List<Vector2> uv = new List<Vector2>();

            int length = endIndex - startIndex;
            for (int i = 0; i < length; i++)
            {
                SingleGlass(i, ref vector, ref triangle, ref uv, myGlassData[startIndex + i], UnityEngine.Random.Range(0.8f, 1.2f), UnityEngine.Random.Range(0.4f, 1f));
            }

            mesh.SetVertices(vector);
            mesh.SetIndices(triangle.ToArray(), MeshTopology.Triangles, 0);
            mesh.uv = uv.ToArray();

            GameObject ObjGlass = new GameObject();
            ObjGlass.name = "MyGlass" + index;
            MeshFilter meshFilter = ObjGlass.AddComponent<MeshFilter>();
            MeshRenderer ren = ObjGlass.AddComponent<MeshRenderer>();
            meshFilter.mesh = mesh;
            ren.material = Com.m_matGlass;
        }

这里重点讲每个单草是怎么生成的,如图所示一共四个点顶点型的三个倒三角面,重点关注下UV,上面的X分别为0-0.5和0.5-1的范围:
在这里插入图片描述
代码如下:

private void SingleGlass(int index, ref List<Vector3> vert, ref List<int> triangle, ref List<Vector2> uv, GlassPoint data, float w, float h)
        {
            Vector3 normal = ((data.norm + data.norm2 + data.norm3) / 3).normalized;

            Vector3 offset = normal * h;
            Vector3 pos4 = (data.pos + data.pos2 + data.pos3) / 3 - normal * 0.3f;//防止因误差出现飞天草
            Vector3 pos1 = data.pos + offset;
            Vector3 pos2 = data.pos2 + offset;
            Vector3 pos3 = data.pos3 + offset;

            vert.Add(pos1);
            vert.Add(pos2);
            vert.Add(pos3);
            vert.Add(pos4);

            uv.Add(new Vector2(0, 1));
            uv.Add(new Vector2(0.5f, 1));
            uv.Add(new Vector2(1, 1));
            uv.Add(new Vector2(0.5f, 0));

            index *= 4;
            triangle.Add(index);
            triangle.Add(index + 1);
            triangle.Add(index + 3);

            triangle.Add(index + 1);
            triangle.Add(index + 2);
            triangle.Add(index + 3);

            triangle.Add(index + 2);
            triangle.Add(index);
            triangle.Add(index + 3);
        }

下面是材质部分,使用透明度测试的方式裁剪处草的形状,再用摆动上端UV做出风吹的效果,上代码:

Shader "Custom/Glass" {
	Properties{
		[Header(Ground_Base)]
		_MainTex("RGB:基础色 A:透明通道",2D) = "white"{}
		_MainCol("基本色",Color) = (0.5,0.5,0.5,1.0)
		_Speed("速度 ",Range(0,10)) = 5
		_Scope("幅度 ",Range(0,1)) = 0.3
	}
		SubShader{
			Tags{"RenderType" = "Opaque"}

			Pass{
				Tags{"LightMode" = "ForwardBase"}
				//ZWrite off
			Cull off
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Lighting.cginc"
				#include "AutoLight.cginc"
				#include "UnityCG.cginc"
				#pragma target 3.0
			//地面参数
			uniform sampler2D _MainTex; uniform half4 _MainTex_ST;
			uniform half3 _MainCol;
			uniform half _Speed;
			uniform fixed _Scope;

			//输入结构
			struct a2v {
				float4 vertex:       POSITION;				//顶点信息
				float2 uv0:          TEXCOORD0;				//UV信息
			};
			//输出结构
			struct v2f {
				float4 pos:SV_POSITION;						//屏幕定点位置
				float2 uv0:TEXCOORD0;						//UV
			};
			v2f vert(a2v v) {
				v2f o;																			//新输出结构
				o.pos = UnityObjectToClipPos(v.vertex);											//顶点位置    OS>CS
				o.uv0 = v.uv0 *_MainTex_ST.xy + _MainTex_ST.zw;									//传弟UV
				o.uv0.x = o.uv0.x + (sin(_Time.x*_Speed)*_Scope * o.uv0.y);						//摆动
				return o;
			}
			float4 frag(v2f i) :SV_TARGET{
				//纹理采样
				half4 var_MainTex = tex2D(_MainTex, i.uv0);
				//裁剪
				clip(var_MainTex.a - 0.1);
				//最终混合
				half3 finalRGB = var_MainTex * _MainCol;
				return half4(finalRGB ,1);
		}
	ENDCG
}

草地的贴图大概是这样:
在这里插入图片描述
中间有个间隔,因为要换面了…嘻嘻.

这或许不是个高明的方式,但是一个符合我项目需求的方式,分享给大家希望对大家有帮助,谢谢。

引用提到了一种使用草地着色器的方法,该方法通过传递透明度贴图和顶点色来控制草的形状和颜色。这种方法可以有效减少DrawCall的数量,并且通过传递顶点的法线可以产生不同的受光效果。这样的草地着色器可以实现草地的交互效果。 另外,引用提到了一些草地着色器的拓展性功能,例如延迟恢复、画笔工具、风压控制和引燃效果等。这些功能可以进一步增强草地的交互效果。 而引用提到了草地着色器的优化方法。草地在风会有规律性的摆动,因此需要生成具有规律的系数来控制草的宽度和高度。通过这种方法,可以实现个体上的随机性和整体上的一致性动画效果。 综上所述,Unity草地着色器可以通过传递透明度贴图和顶点色来控制草的形状和颜色,并可以通过拓展性功能实现更多的交互效果。另外,通过优化草地着色器,可以实现草地在风的规律性摆动效果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [UnityShader[4]几何着色器与可交互草地](https://blog.csdn.net/Thanatos_Left/article/details/126141627)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小盖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值