Unity grass/lawn case sharing (full code)

The old rule is to first upload the picture.
insert image description here
Recently, I have continued to work on my new independent game. I have benefited a lot from reading a lot of grassland cases on the Internet. But recently I still choose to implement it in my own way.
Mainly because this method can better fit my needs before and after. There are still many interesting technical points, if you have time, I will share them with you.
This time, let’s first talk about the implementation of the grass:
since the grass needs to fit the surface of the model, it starts from the surface of the attached model:
1. Cycle Mesh triangle values, record a set of data in units of triangles, the content is Three vertex positions and their normal information.
insert image description here
code show as below:

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);
                    }
                }
            }
        }

The above is that each set of data records the information of the three vertices and their normals.
It is worth mentioning that since the location is different from the normal, here the base point is found by offsetting the vertex along the normal, and then reflecting back to find the location at a random location.
2. After finding the location and information, create a Mesh to write the information of each grass. Since the number may be very large, it may be necessary to separate several Mesh, the code is as follows:

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;
        }

Here we focus on how each single grass is generated. As shown in the figure, there are three inverted triangular surfaces with four points and vertices. Focus on the lower UV. The X above is the range of 0-0.5 and 0.5-1 respectively:
insert image description here
code as follows:

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);
        }

The following is the material part. Use the transparency test to cut out the shape of the grass, and then use the swinging upper UV to make the effect of wind blowing. The code:

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
}

The texture of the grass is roughly like this:
insert image description here
there is a gap in the middle, because the face is going to be changed... Hee hee.

This may not be a clever way, but it is a way that meets the needs of my project, and I hope it will be helpful to everyone, thank you.

Guess you like

Origin blog.csdn.net/ww1351646544/article/details/119875969