扩展T4M支持8张细节贴图

"开发笔记"

Posted by A-SHIN on July 18, 2016

“Yeah It’s on. ”

前言

当前项目某些场景较大,细节丰富。U3D地形系统生成的地形经T4M插件转换后最多只支持6张细节贴图,为了满足美术需求,编写了支持更多细节贴图shader。
左边为U3D地形系统,右边为T4M转换后的模型:

正文

由于控制贴图RGBA四个通道最多能控制4张细节贴图,所以我们需要两张控制贴图来控制8张细节贴图。这样总共就是10张贴图。经测试一个pass最多支持8张自定义纹理,因此分为两个pass来渲染。
使用顶点片段着色器,那就是两个ForwardBase加上两个ForwardAdd。使用Blend One One进行混合。
具体实现如下:

Shader "T4MShaders/ShaderModel3/Diffuse/T4M 8 Textures" {
Properties {
	_Splat0 ("Layer 1 (R)", 2D) = "white" {}
	_Splat1 ("Layer 2 (G)", 2D) = "white" {}
	_Splat2 ("Layer 3 (B)", 2D) = "white" {}
	_Splat3 ("Layer 4 (A)", 2D) = "white" {}
	_Splat4 ("Layer 5", 2D) = "white" {}
	_Splat5 ("Layer 6", 2D) = "white" {}
	_Splat6 ("Layer 7", 2D) = "white" {}
	_Splat7 ("Layer 8", 2D) = "white" {}
	_Control ("Control (RGBA)", 2D) = "white" {}
	_Control2 ("Control2 (RGBA)", 2D) = "white" {}
	_MainTex ("Never Used", 2D) = "white" {}
} 

SubShader {
	Tags {
		"SplatCount" = "8"
		"Queue" = "Geometry-100"
		"RenderType" = "Opaque"
	}

	// --------------------------------------------------------------------------------------------------
	// ForwardBase

	Pass
		{
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0
			//#pragma exclude_renderers gles xbox360 ps3	// 不使用gles编译器编译,导致手机上shader不起作用
			#pragma exclude_renderers xbox360 ps3
			#pragma fragmentoption ARB_precision_hint_fastest
			#pragma multi_compile_fwdbase
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			#include "HLSLSupport.cginc"
			#include "UnityShaderVariables.cginc"

			sampler2D _Control;
			sampler2D _Splat0, _Splat1, _Splat2, _Splat3;
			float4 _Control_ST;
			float4 _Splat0_ST;
			float4 _Splat1_ST;
			float4 _Splat2_ST;
			float4 _Splat3_ST;

#ifdef LIGHTMAP_OFF
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv_Control : TEXCOORD0;
				float4 pack1 : TEXCOORD1;
				float4 pack2 : TEXCOORD2;

				fixed3 normal : TEXCOORD3;
				fixed3 vlight : TEXCOORD4;
				LIGHTING_COORDS(5, 6)
			};
#endif
#ifndef LIGHTMAP_OFF
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv_Control : TEXCOORD0;
				float4 pack1 : TEXCOORD1;
				float4 pack2 : TEXCOORD2;

				float2 lmap : TEXCOORD3;
				LIGHTING_COORDS(4, 5)
			};
#endif

#ifndef LIGHTMAP_OFF
			float4 unity_LightmapST;
#endif
			v2f vert(appdata_full v) 
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv_Control = TRANSFORM_TEX(v.texcoord, _Control);
				
				o.pack1.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
				o.pack1.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
				o.pack2.xy = TRANSFORM_TEX(v.texcoord, _Splat2);
				o.pack2.zw = TRANSFORM_TEX(v.texcoord, _Splat3);


#ifndef LIGHTMAP_OFF
				o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
				float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
#ifdef LIGHTMAP_OFF
					o.normal = worldN;
#endif

				// SH/ambient and vertex lights
#ifdef LIGHTMAP_OFF
				float3 shlight = ShadeSH9(float4(worldN, 1.0));
					o.vlight = shlight;
#ifdef VERTEXLIGHT_ON
				float3 worldPos = mul(_Object2World, v.vertex).xyz;
					o.vlight += Shade4PointLights(
					unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
					unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
					unity_4LightAtten0, worldPos, worldN);
#endif // VERTEXLIGHT_ON
#endif // LIGHTMAP_OFF

				// pass lighting information to pixel shader
				TRANSFER_VERTEX_TO_FRAGMENT(o);

				return o;
			}

#ifndef LIGHTMAP_OFF
			sampler2D unity_Lightmap;
#ifndef DIRLIGHTMAP_OFF
			sampler2D unity_LightmapInd;
#endif
#endif

			fixed4 frag(v2f i) : COLOR
			{
				fixed4 splat_control = tex2D(_Control, i.uv_Control);
				fixed3 col;
				col = splat_control.r * tex2D(_Splat0, i.pack1.xy).rgb;
				col += splat_control.g * tex2D(_Splat1, i.pack1.zw).rgb;
				col += splat_control.b * tex2D(_Splat2, i.pack2.xy).rgb;
				col += splat_control.a * tex2D(_Splat3, i.pack2.zw).rgb;
				//return fixed4(col,0);


#ifdef UNITY_COMPILER_HLSL
				SurfaceOutput o = (SurfaceOutput)0;
#else
				SurfaceOutput o;
#endif
				o.Albedo = 0.0;
				o.Emission = 0.0;
				o.Specular = 0.0;
				o.Alpha = 0.0;
				o.Gloss = 0.0;
#ifdef LIGHTMAP_OFF
				o.Normal = i.normal;
#endif
				o.Albedo = col.rgb;
				o.Alpha = 0;

				// compute lighting & shadowing factor
				fixed atten = LIGHT_ATTENUATION(i);
				fixed4 c = 0;

				// realtime lighting: call lighting function
#ifdef LIGHTMAP_OFF
				c = LightingLambert(o, _WorldSpaceLightPos0.xyz, atten);
#endif // LIGHTMAP_OFF || DIRLIGHTMAP_OFF
#ifdef LIGHTMAP_OFF
				c.rgb += o.Albedo * i.vlight;
#endif // LIGHTMAP_OFF

				// lightmaps:
#ifndef LIGHTMAP_OFF
#ifndef DIRLIGHTMAP_OFF
				// directional lightmaps
				fixed4 lmtex = tex2D(unity_Lightmap, i.lmap.xy);
				fixed4 lmIndTex = tex2D(unity_LightmapInd, i.lmap.xy);
				half3 lm = LightingLambert_DirLightmap(o, lmtex, lmIndTex, 0).rgb;
#else // !DIRLIGHTMAP_OFF
				// single lightmap
				fixed4 lmtex = tex2D(unity_Lightmap, i.lmap.xy);
				fixed3 lm = DecodeLightmap(lmtex);
#endif // !DIRLIGHTMAP_OFF

				// combine lightmaps with realtime shadows
#ifdef SHADOWS_SCREEN
#if defined(UNITY_NO_RGBM)
				c.rgb += o.Albedo * min(lm, atten * 2);
#else
				c.rgb += o.Albedo * max(min(lm, (atten * 2)*lmtex.rgb), lm*atten);
#endif
#else // SHADOWS_SCREEN
				c.rgb += o.Albedo * lm;
#endif // SHADOWS_SCREEN
				c.a = o.Alpha;
#endif // LIGHTMAP_OFF

				return c;
			}
			ENDCG
		}

		Pass
		{
			Tags{ "LightMode" = "ForwardBase" }
			Blend One One ZWrite Off Fog{ Color(0, 0, 0, 0) }
			CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
//#pragma exclude_renderers gles xbox360 ps3
#pragma exclude_renderers xbox360 ps3
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"

			sampler2D _Control2;
			sampler2D _Splat4, _Splat5, _Splat6, _Splat7;
			float4 _Control2_ST;
			float4 _Splat4_ST;
			float4 _Splat5_ST;
			float4 _Splat6_ST;
			float4 _Splat7_ST;

#ifdef LIGHTMAP_OFF
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv_Control2 : TEXCOORD0;
				float4 pack3 : TEXCOORD1;
				float4 pack4 : TEXCOORD2;

				fixed3 normal : TEXCOORD3;
				fixed3 vlight : TEXCOORD4;
				LIGHTING_COORDS(5, 6)
			};
#endif
#ifndef LIGHTMAP_OFF
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv_Control2 : TEXCOORD0;
				float4 pack3 : TEXCOORD1;
				float4 pack4 : TEXCOORD2;

				float2 lmap : TEXCOORD3;
				LIGHTING_COORDS(4, 5)
			};
#endif

#ifndef LIGHTMAP_OFF
			float4 unity_LightmapST;
#endif
			v2f vert(appdata_full v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv_Control2 = TRANSFORM_TEX(v.texcoord, _Control2);

				o.pack3.xy = TRANSFORM_TEX(v.texcoord, _Splat4);
				o.pack3.zw = TRANSFORM_TEX(v.texcoord, _Splat5);
				o.pack4.xy = TRANSFORM_TEX(v.texcoord, _Splat6);
				o.pack4.zw = TRANSFORM_TEX(v.texcoord, _Splat7);


#ifndef LIGHTMAP_OFF
				o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
				float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
#ifdef LIGHTMAP_OFF
					o.normal = worldN;
#endif

				// SH/ambient and vertex lights
#ifdef LIGHTMAP_OFF
				float3 shlight = ShadeSH9(float4(worldN, 1.0));
					o.vlight = shlight;
#ifdef VERTEXLIGHT_ON
				float3 worldPos = mul(_Object2World, v.vertex).xyz;
					o.vlight += Shade4PointLights(
					unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
					unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
					unity_4LightAtten0, worldPos, worldN);
#endif // VERTEXLIGHT_ON
#endif // LIGHTMAP_OFF

				// pass lighting information to pixel shader
				TRANSFER_VERTEX_TO_FRAGMENT(o);

				return o;
			}

#ifndef LIGHTMAP_OFF
			sampler2D unity_Lightmap;
#ifndef DIRLIGHTMAP_OFF
			sampler2D unity_LightmapInd;
#endif
#endif

			fixed4 frag(v2f i) : COLOR
			{
				fixed4 splat_control = tex2D(_Control2, i.uv_Control2);
				fixed3 col;
				col = splat_control.r * tex2D(_Splat4, i.pack3.xy).rgb;
				col += splat_control.g * tex2D(_Splat5, i.pack3.zw).rgb;
				col += splat_control.b * tex2D(_Splat6, i.pack4.xy).rgb;
				col += splat_control.a * tex2D(_Splat7, i.pack4.zw).rgb;
				//return fixed4(col,0);


#ifdef UNITY_COMPILER_HLSL
				SurfaceOutput o = (SurfaceOutput)0;
#else
				SurfaceOutput o;
#endif
				o.Albedo = 0.0;
				o.Emission = 0.0;
				o.Specular = 0.0;
				o.Alpha = 0.0;
				o.Gloss = 0.0;
#ifdef LIGHTMAP_OFF
				o.Normal = i.normal;
#endif
				o.Albedo = col.rgb;
				o.Alpha = 0;

				// compute lighting & shadowing factor
				fixed atten = LIGHT_ATTENUATION(i);
				fixed4 c = 0;

				// realtime lighting: call lighting function
#ifdef LIGHTMAP_OFF
				c = LightingLambert(o, _WorldSpaceLightPos0.xyz, atten);
#endif // LIGHTMAP_OFF || DIRLIGHTMAP_OFF
#ifdef LIGHTMAP_OFF
				c.rgb += o.Albedo * i.vlight;
#endif // LIGHTMAP_OFF

				// lightmaps:
#ifndef LIGHTMAP_OFF
#ifndef DIRLIGHTMAP_OFF
				// directional lightmaps
				fixed4 lmtex = tex2D(unity_Lightmap, i.lmap.xy);
				fixed4 lmIndTex = tex2D(unity_LightmapInd, i.lmap.xy);
				half3 lm = LightingLambert_DirLightmap(o, lmtex, lmIndTex, 0).rgb;
#else // !DIRLIGHTMAP_OFF
				// single lightmap
				fixed4 lmtex = tex2D(unity_Lightmap, i.lmap.xy);
				fixed3 lm = DecodeLightmap(lmtex);
#endif // !DIRLIGHTMAP_OFF

				// combine lightmaps with realtime shadows
#ifdef SHADOWS_SCREEN
#if defined(UNITY_NO_RGBM)
				c.rgb += o.Albedo * min(lm, atten * 2);
#else
				c.rgb += o.Albedo * max(min(lm, (atten * 2)*lmtex.rgb), lm*atten);
#endif
#else // SHADOWS_SCREEN
				c.rgb += o.Albedo * lm;
#endif // SHADOWS_SCREEN
				c.a = o.Alpha;
#endif // LIGHTMAP_OFF

				return c;
			}
				ENDCG
		}


		// --------------------------------------------------------------------------------------------------
		// ForwardAdd

		Pass
			{
				Tags{ "LightMode" = "ForwardAdd" }
				Blend One One ZWrite Off Fog{ Color(0, 0, 0, 0) }

				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma target 3.0
				//#pragma exclude_renderers gles xbox360 ps3
				#pragma exclude_renderers xbox360 ps3
				#pragma fragmentoption ARB_precision_hint_fastest
				#pragma multi_compile_fwdadd
				#include "UnityCG.cginc"
				#include "Lighting.cginc"
				#include "AutoLight.cginc"
				#include "HLSLSupport.cginc"
				#include "UnityShaderVariables.cginc"

				sampler2D _Control;
				sampler2D _Splat0, _Splat1, _Splat2, _Splat3;
				float4 _Control_ST;
				float4 _Splat0_ST;
				float4 _Splat1_ST;
				float4 _Splat2_ST;
				float4 _Splat3_ST;

				struct v2f {
					float4 pos : SV_POSITION;
					float2 uv_Control : TEXCOORD0;
					float4 pack1 : TEXCOORD1;
					float4 pack2 : TEXCOORD2;

					fixed3 normal : TEXCOORD3;
					half3 lightDir : TEXCOORD4;
					LIGHTING_COORDS(5, 6)
				};

				v2f vert(appdata_full v)
				{
					v2f o;
					o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
					o.uv_Control = TRANSFORM_TEX(v.texcoord, _Control);

					o.pack1.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
					o.pack1.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
					o.pack2.xy = TRANSFORM_TEX(v.texcoord, _Splat2);
					o.pack2.zw = TRANSFORM_TEX(v.texcoord, _Splat3);

					o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);
					float3 lightDir = WorldSpaceLightDir(v.vertex);
						o.lightDir = lightDir;

					// pass lighting information to pixel shader
					TRANSFER_VERTEX_TO_FRAGMENT(o);

					return o;
				}

				fixed4 frag(v2f i) : COLOR
				{
					fixed4 splat_control = tex2D(_Control, i.uv_Control);
					fixed3 col;
					col = splat_control.r * tex2D(_Splat0, i.pack1.xy).rgb;
					col += splat_control.g * tex2D(_Splat1, i.pack1.zw).rgb;
					col += splat_control.b * tex2D(_Splat2, i.pack2.xy).rgb;
					col += splat_control.a * tex2D(_Splat3, i.pack2.zw).rgb;
					//return fixed4(col, 0);


#ifdef UNITY_COMPILER_HLSL
					SurfaceOutput o = (SurfaceOutput)0;
#else
					SurfaceOutput o;
#endif
					o.Albedo = 0.0;
					o.Emission = 0.0;
					o.Specular = 0.0;
					o.Alpha = 0.0;
					o.Gloss = 0.0;
					o.Normal = i.normal;

					o.Albedo = col.rgb;
					o.Alpha = 0;

#ifndef USING_DIRECTIONAL_LIGHT
					fixed3 lightDir = normalize(i.lightDir);
#else
					fixed3 lightDir = i.lightDir;
#endif
					fixed4 c = LightingLambert(o, lightDir, LIGHT_ATTENUATION(i));
					//fixed4 c = fixed4(o.Albedo, o.Alpha);
					
					c.a = 0.0;
					return c;
				}
					ENDCG
			}

		Pass
			{
				Tags{ "LightMode" = "ForwardAdd" }
				Blend One One ZWrite Off Fog{ Color(0, 0, 0, 0) }

				CGPROGRAM
				#pragma vertex vert2
				#pragma fragment frag2
				#pragma target 3.0
				//#pragma exclude_renderers gles xbox360 ps3
				#pragma exclude_renderers xbox360 ps3
				#pragma fragmentoption ARB_precision_hint_fastest
				#pragma multi_compile_fwdadd
				#include "UnityCG.cginc"
				#include "Lighting.cginc"
				#include "AutoLight.cginc"
				#include "HLSLSupport.cginc"
				#include "UnityShaderVariables.cginc"

				sampler2D _Control2;
				sampler2D _Splat4, _Splat5, _Splat6, _Splat7;
				float4 _Control2_ST;
				float4 _Splat4_ST;
				float4 _Splat5_ST;
				float4 _Splat6_ST;
				float4 _Splat7_ST;

				struct v2f2 {
					float4 pos : SV_POSITION;
					float2 uv_Control2 : TEXCOORD0;
					float4 pack3 : TEXCOORD1;
					float4 pack4 : TEXCOORD2;

					fixed3 normal : TEXCOORD3;
					half3 lightDir : TEXCOORD4;
					LIGHTING_COORDS(5, 6)
				};

				v2f2 vert2(appdata_full v)
				{
					v2f2 o;
					o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
					o.uv_Control2 = TRANSFORM_TEX(v.texcoord, _Control2);

					o.pack3.xy = TRANSFORM_TEX(v.texcoord, _Splat4);
					o.pack3.zw = TRANSFORM_TEX(v.texcoord, _Splat5);
					o.pack4.xy = TRANSFORM_TEX(v.texcoord, _Splat6);
					o.pack4.zw = TRANSFORM_TEX(v.texcoord, _Splat7);

					o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);
					float3 lightDir = WorldSpaceLightDir(v.vertex);
						o.lightDir = lightDir;

					// pass lighting information to pixel shader
					TRANSFER_VERTEX_TO_FRAGMENT(o);

					return o;
				}

				fixed4 frag2(v2f2 i) : COLOR
				{
					fixed4 splat_control2 = tex2D(_Control2, i.uv_Control2);
					fixed3 col2;
					col2 = splat_control2.r * tex2D(_Splat4, i.pack3.xy).rgb;
					col2 += splat_control2.g * tex2D(_Splat5, i.pack3.zw).rgb;
					col2 += splat_control2.b * tex2D(_Splat6, i.pack4.xy).rgb;
					col2 += splat_control2.a * tex2D(_Splat7, i.pack4.zw).rgb;
					//return fixed4(col2, 0);


#ifdef UNITY_COMPILER_HLSL
					SurfaceOutput o = (SurfaceOutput)0;
#else
					SurfaceOutput o;
#endif
					o.Albedo = 0.0;
					o.Emission = 0.0;
					o.Specular = 0.0;
					o.Alpha = 0.0;
					o.Gloss = 0.0;
					o.Normal = i.normal;

					o.Albedo = col2.rgb;
					o.Alpha = 0;

#ifndef USING_DIRECTIONAL_LIGHT
					fixed3 lightDir = normalize(i.lightDir);
#else
					fixed3 lightDir = i.lightDir;
#endif
					fixed4 c = LightingLambert(o, lightDir, LIGHT_ATTENUATION(i));
					//fixed4 c = fixed4(o.Albedo, o.Alpha);
					
					c.a = 0.0;
					return c;
				}
					ENDCG
			}

}
FallBack "Diffuse"
}

后记

建议尽量少用(相比而言会多一倍的渲染开销)。如果地图细节较多,可分块处理,保证每块地形的所有贴图在一个pass中处理,并使用lightmap烘焙。

完结