分割爲mesh之後的地形shader編寫——2

https://blog.csdn.net/wodownload2/article/details/106071916
繼續我們的地形shader之旅。

我們的阿泰被染色成了下面的丫子:
在這裏插入圖片描述
是的,我們多了一個層,這個層的貼圖這個樣子:
在這裏插入圖片描述

有兩個圖,就需要進行混合。
這裏即涉及到三個圖的處理:
1、貼圖1(金泰熙)
2、貼圖2(上面的那個泥土地)
3、混合圖(權重圖)

貼圖1和貼圖2好處理,就是美術給的素材。
混合圖是啥?
在這裏插入圖片描述
當我們用筆刷,刷好地形之後,再地形數據的下面,就會有一個或者多個混合圖出現。上面出現了一張,爲啥時一張?
因爲我們有兩個圖進行混合,一個圖可以支持四個圖(因爲有rgba四個通道)。

加入有5個圖,那麼則會有2個混合圖出現。這裏不展開講述。

ok,有了混合圖之後,這個混合圖是一個整體,其分辨率是多少呢?
在這裏插入圖片描述
這個控制圖,就是混合圖的分辨率。

ok,我們要將地形分成的是5x5塊,上一篇文章已經講過了,請參考上一篇。
那麼混合圖也需要分成5x5這麼多塊,ok,分割代碼如下:

    private static void CreateSplitAlphamap(Texture2D alphamap, int blockX, int blockZ)
    {
        Texture2D tex = new Texture2D(m_blockAlphamapWidth, m_blockAlphamapHeight, TextureFormat.ARGB32, false);
        for (int i = 0; i < m_blockAlphamapHeight; ++i)
        {
            for (int j = 0; j < m_blockAlphamapWidth; ++j)
            {
                int x = j + blockX * m_blockAlphamapWidth;
                int y = i + blockZ * m_blockAlphamapHeight;
                Color color = alphamap.GetPixel(x, y);
                tex.SetPixel(j, i, color);
            }
        }
        //https://blog.csdn.net/hakukou/article/details/104667576/
        string texName = m_sceneData.sceneName + "_alphamap_" + blockX + "_" + blockZ;
        string assetPath = PathManager.m_splitTexesDir + "/" + texName + ".png";
        if (File.Exists(assetPath)) File.Delete(assetPath);
        byte[] data = tex.EncodeToPNG();
        File.WriteAllBytes(assetPath, data);
        AssetDatabase.Refresh();
    }

ok,分割之後的丫子:
在這裏插入圖片描述

最後一步,是如何使用這個混合圖,我們有25塊分割之後的mesh,那麼就需要有25個材質,每個材質有三個屬性,如下:
在這裏插入圖片描述

將其每個材質,拖拽到左邊的mesh上去,對應起來,不要亂,同時混合圖要一個一個拖到每個材質球上去:
在這裏插入圖片描述

混合圖的uv怎麼弄?
我這裏只給出思想,具體實現,就要看下面的代碼了:
1、我們將地形分成了25塊
2、每塊加入是分成64x64個小格子
3、每塊有65x65個頂點,那麼每個頂點的uv,應該等於所在的座標(x,z)/64,x和z從0~64
4、用這個uv去採樣每塊的混合圖就可以了

private static void SplitTerrainMesh(int x, int z, int step)
    {
        BlockData blockData = new BlockData();
        int id = x + z * m_sceneData.splitX;
        blockData.id = id;
        blockData.x = x;
        blockData.z = z;

        //採樣,創建mesh,保存mesh
        Mesh mesh = new Mesh();
        int verticesCount = (m_blockHeightmapWidth / step + 1) * (m_blockHeightmapHeight / step + 1);
        Vector3[] vertices = new Vector3[verticesCount];
        int[] triangleIndices = new int[m_blockHeightmapWidth * m_blockHeightmapHeight * 2 * 3];
        Vector2[] uvs = new Vector2[verticesCount];
        int vertexIndex = 0;
        int triangleIndex = 0;
        float[,] heights = m_terrain.terrainData.GetHeights(0, 0, m_heightmapWidth, m_heightmapHeight);
        for (int i = 0; i <= m_blockHeightmapHeight; i += step)
        {
            for (int j = 0; j <= m_blockHeightmapWidth; j += step)
            {
                int indexX = x * m_blockHeightmapWidth + j;
                int indexZ = z * m_blockHeightmapHeight + i;
                float height = heights[indexZ, indexX];
                float posX = indexX / m_scaleX;
                float posZ = indexZ / m_scaleZ;
                float posY = height * m_terrainY; //GetHeights存儲的是0~1的值,需要乘以高度的最大值,才能得到實際的地形高度,並且第一維存儲的是z值,第二維存儲的是x值
                //參考:https://docs.unity3d.com/ScriptReference/TerrainData.GetHeights.html
                //float h = m_terrain.terrainData.GetHeight((int)posX, (int)posZ);
                //https://docs.unity3d.com/ScriptReference/TerrainData.GetHeight.html GetHeight得到的是地形的實際高度
                Vector3 scale = m_terrain.terrainData.heightmapScale;
                vertices[vertexIndex] = new Vector3(posX, posY, posZ);
                float u = j * 1.0f / m_blockHeightmapWidth;
                float v = i * 1.0f / m_blockHeightmapHeight;
                uvs[vertexIndex] = new Vector2(u, v);
                if (j < m_blockHeightmapWidth && i < m_blockHeightmapHeight)
                {
                    triangleIndices[triangleIndex++] = vertexIndex;
                    triangleIndices[triangleIndex++] = vertexIndex + m_blockHeightmapWidth + 1;
                    triangleIndices[triangleIndex++] = vertexIndex + m_blockHeightmapWidth + 2;

                    triangleIndices[triangleIndex++] = vertexIndex;
                    triangleIndices[triangleIndex++] = vertexIndex + m_blockHeightmapWidth + 2;
                    triangleIndices[triangleIndex++] = vertexIndex + 1;
                }
                vertexIndex++;
            }
        }
        mesh.vertices = vertices;
        mesh.triangles = triangleIndices;
        mesh.uv = uvs;
        string meshName = SceneManager.GetActiveScene().name + "_mesh_" + x + "_" + z;
        string meshPath = PathManager.m_splitMeshesDir + "/" + meshName + ".asset";
        if (File.Exists(meshPath))
        {
            File.Delete(meshName);
        }
        AssetDatabase.CreateAsset(mesh, meshPath);
        AssetDatabase.Refresh();
    }

最後,我們給出混合了兩個貼圖的地形shader:

Shader "Unlit/TerrainShader"
{
	Properties
	{
		_BlendTex("_BlendTex", 2D) = "white"{}
		_SplatTex0("_SplatTex0", 2D) = "white" {}
		_SplatTex1("_SplatTex1", 2D) = "white" {}

	}
		SubShader
	{
		Tags { "RenderType" = "Opaque" }
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 worldPosition:TEXCOORD2;
				float2 uv : TEXCOORD0;
			};

			sampler2D _BlendTex;
			sampler2D _SplatTex0;
			sampler2D _SplatTex1;

			v2f vert(appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldPosition = mul(unity_ObjectToWorld, v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				float4 blendColor = tex2D(_BlendTex, i.uv);
				float2 uv0 = i.worldPosition.xz * (1.0f / 320); //這個是上節的知識你還記得嗎?
				float4 color0 = tex2D(_SplatTex0, uv0);
				float4 color1 = tex2D(_SplatTex1, uv0);
				float4 color = blendColor.r * color0 + blendColor.g * color1;
				return color;
			}
			ENDCG
		}
	}
}

最後的結果:
在這裏插入圖片描述
左圖是我們的分割之後的mesh效果,右圖是unity原始地形的效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章