“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烘焙。