Shader "Unlit/TerrainShader" { Properties { _MainTex ("Texture Atlas", 2DArray) = "white" {} // 主贴图图集 _TexNo ("TexNo", 2D) = "white" {} // 贴图编号(各通道分别代表不同层) _MaskNo ("MaskNo", 2D) = "black" {} // 遮罩数据 _MaskTex ("Mask Atlas", 2DArray) = "black" {} // 遮罩图集 _TerrainSize ("Terrain Size", Vector) = (256,0,256,0) // x:宽度,z:高度 _BorderColor ("Border Color", Color) = (1,1,1,1) _BorderWidth ("Border Width", Range(0,0.1)) = 0.05 _Repeat ("Repeat", float) = 0 _DebugMask ("DebugMask", Range(0,4)) = 0 _DebugShowMaskNoOrNo ("DebugShowMaskNoOrNo", Range(0,2)) = 0 _DebugUV ("DebugUV", Range(0,2)) = 2 _UVOffsetX ("UV Offset", float) = 0 _UVOffsetY ("UV Offset", float) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // 输入:由于 Terrain 无法自定义 UV,我们只传入顶点位置 struct appdata { float2 uv : TEXCOORD1; float4 vertex : POSITION; }; // 输出:计算出的全局 uv0(用于 _TexNo 采样)会传递到片元,同时用于采样烘培 UV2 struct v2f { float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; float4 vertex : SV_POSITION; }; // 声明贴图变量 UNITY_DECLARE_TEX2DARRAY(_MainTex); sampler2D _TexNo; // 贴图编号贴图 sampler2D _MaskNo; // 遮罩贴图 UNITY_DECLARE_TEX2DARRAY(_MaskTex); int _DebugShowMaskNoOrNo; float _Repeat; float4 _TexNo_TexelSize; float4 _MaskNo_TexelSize; float4 _BorderColor; float _BorderWidth; float4 _TerrainSize; // x:宽度,z:高度(y/w分量可忽略) int _DebugMask; int _DebugUV; float _UVOffsetX; float _UVOffsetY; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // 根据 Terrain 的世界坐标计算全局 uv0 o.uv2 = v.uv; o.uv = float2(v.vertex.x / (_TerrainSize.x + 1), v.vertex.z / (_TerrainSize.z + 1)); return o; } // 依然采用原来的混合函数(利用遮罩混合不同 2DArray 层的贴图) float4 Blend(float4 color, float2 maskTexuv, float2 mainTexuv, float mask, int layer) { float4 col2 = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(mainTexuv, layer)); int idx = round(mask * 16); float4 maskTexCol = UNITY_SAMPLE_TEX2DARRAY(_MaskTex, float3(maskTexuv, idx)); return lerp(color, col2, maskTexCol); } // 将从 _TexNo 采样到的数值转换为图集层号(0~64 内的整数) int GetTexNo(float f) { float idx = f * 64.0; return round(idx); } float4 BlendTest(float4 color, float2 uv, float mask, int layer) { int idx = round(mask * 16); uv += float2(_UVOffsetX, _UVOffsetY); float4 maskTexCol = UNITY_SAMPLE_TEX2DARRAY(_MaskTex, float3(uv, idx)); return maskTexCol; } fixed4 frag(v2f i) : SV_Target { // 整个大uv往下偏移 float2 uv = i.uv + float2(0, _TexNo_TexelSize.y); // 贴图编号 float4 texNo = tex2D(_TexNo, uv); int layer1 = GetTexNo(texNo.r); int layer2 = GetTexNo(texNo.g); int layer3 = GetTexNo(texNo.b); int layer4 = GetTexNo(texNo.a); // 取出遮罩值 float4 maskNo = tex2D(_MaskNo, uv); float2 uv2 = frac(i.uv2 * _Repeat); float2 mainTextureUV2 = frac(i.uv2 * (_Repeat / 4)); float4 col = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(mainTextureUV2 , layer1)); if (layer2 > 0) { col = Blend(col, uv2, mainTextureUV2, maskNo.g, layer2); } if (layer3 > 0) { col = Blend(col, uv2, mainTextureUV2, maskNo.b, layer3); } if (layer4 > 0) { col = Blend(col, uv2, mainTextureUV2, maskNo.a, layer4); } // // uv0:全局坐标(用于 _TexNo、_MaskNo 采样) // float2 uv = i.uv; // // 采样烘培贴图,恢复原本为每4个顶点设置的局部 uv2(例如每个 tile 内从 (0,0) 到 (1,1)) // float2 uv2 = frac(i.uv2 * _Repeat); // float2 uv3 = frac(i.uv2 * _Repeat + 1); // // 为了保证采样 texNo 与 maskNo 的准确性,可对 uv0 进行微调(这里延用原来加偏移的思路) // uv = uv + float2(0, _TexNo_TexelSize.y); // // 从 _TexNo 中读取各层编号(不同通道对应不同层) // float4 texNo = tex2D(_TexNo, i.uv); // int layer1 = GetTexNo(texNo.r); // int layer2 = GetTexNo(texNo.g); // int layer3 = GetTexNo(texNo.b); // int layer4 = GetTexNo(texNo.a); // // // // 先从主贴图图集中采样第一层(使用 uv2 作为局部采样坐标) // float4 maskNo = tex2D(_MaskNo, i.uv2); // float4 col = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(uv3, layer1)); // if (layer2 > 0) // col = Blend(col, uv3, maskNo.g, layer2); // if (layer3 > 0) // col = Blend(col, uv3, maskNo.b, layer3); // if (layer4 > 0) // col = Blend(col, uv3, maskNo.a, layer4); if (_DebugShowMaskNoOrNo == 1) { return texNo; } if (_DebugShowMaskNoOrNo == 2) { return maskNo; } if (_DebugMask == 1) { return BlendTest(col, uv2, maskNo.r, layer1); } if (_DebugMask == 2) { return BlendTest(col, uv2, maskNo.g, layer2); } if (_DebugMask == 3) { return BlendTest(col, uv2, maskNo.b, layer3); } if (_DebugMask == 4) { return BlendTest(col, uv2, maskNo.a, layer4); } if (_DebugUV == 1) { return float4(i.uv, 0, 1); } if (_DebugUV == 2) { return float4(uv2, 0, 1); } return col; } ENDCG } } }