分割为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原始地形的效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值