|
|
|
|
|
|
float4 tangent : TANGENT; |
|
|
|
float3 normal : NORMAL; |
|
|
|
float2 texcoord : TEXCOORD0; |
|
|
|
float2 texcoord1 : TEXCOORD1; |
|
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
|
|
float4 uvSplat01 : TEXCOORD0; // xy: splat0, zw: splat1 |
|
|
|
float4 uvSplat23 : TEXCOORD1; // xy: splat2, zw: splat3 |
|
|
|
float4 uvControlAndLM : TEXCOORD2; // xy: control, zw: lightmap |
|
|
|
float4 uvMainAndLM : TEXCOORD0; // xy: control, zw: lightmap |
|
|
|
#ifndef TERRAIN_SPLAT_BASEPASS |
|
|
|
float4 uvSplat01 : TEXCOORD1; // xy: splat0, zw: splat1 |
|
|
|
float4 uvSplat23 : TEXCOORD2; // xy: splat2, zw: splat3 |
|
|
|
#endif |
|
|
|
#if _TERRAIN_NORMAL_MAP |
|
|
|
#ifdef _NORMALMAP |
|
|
|
half3 tangent : TEXCOORD4; |
|
|
|
half3 binormal : TEXCOORD5; |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
input.positionWS = IN.positionWS; |
|
|
|
|
|
|
|
#ifdef _TERRAIN_NORMAL_MAP |
|
|
|
#ifdef _NORMALMAP |
|
|
|
input.normalWS = TangentToWorldNormal(normalTS, IN.tangent, IN.binormal, IN.normal); |
|
|
|
#else |
|
|
|
input.normalWS = normalize(IN.normal); |
|
|
|
|
|
|
input.vertexLighting = IN.fogFactorAndVertexLight.yzw; |
|
|
|
|
|
|
|
#ifdef LIGHTMAP_ON |
|
|
|
input.bakedGI = SampleLightmap(IN.uvControlAndLM.zw, input.normalWS); |
|
|
|
input.bakedGI = SampleLightmap(IN.uvMainAndLM.zw, input.normalWS); |
|
|
|
|
|
|
|
#ifndef TERRAIN_SPLAT_BASEPASS |
|
|
|
splat_control = SAMPLE_TEXTURE2D(_Control, sampler_Control, IN.uvControlAndLM.xy); |
|
|
|
splat_control = SAMPLE_TEXTURE2D(_Control, sampler_Control, IN.uvMainAndLM.xy); |
|
|
|
weight = dot(splat_control, 1); |
|
|
|
|
|
|
|
#if !defined(SHADER_API_MOBILE) && defined(TERRAIN_SPLAT_ADDPASS) |
|
|
|
|
|
|
mixedDiffuse += splat_control.b * SAMPLE_TEXTURE2D(_Splat2, sampler_Splat0, IN.uvSplat23.xy) * half4(1.0, 1.0, 1.0, defaultAlpha.b); |
|
|
|
mixedDiffuse += splat_control.a * SAMPLE_TEXTURE2D(_Splat3, sampler_Splat0, IN.uvSplat23.zw) * half4(1.0, 1.0, 1.0, defaultAlpha.a); |
|
|
|
|
|
|
|
#ifdef _TERRAIN_NORMAL_MAP |
|
|
|
#ifdef _NORMALMAP |
|
|
|
half4 nrm = 0.0f; |
|
|
|
nrm += splat_control.r * SAMPLE_TEXTURE2D(_Normal0, sampler_Normal0, IN.uvSplat01.xy); |
|
|
|
nrm += splat_control.g * SAMPLE_TEXTURE2D(_Normal1, sampler_Normal0, IN.uvSplat01.zw); |
|
|
|
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
void SplatmapFinalColor(inout half4 color, half fogCoord) |
|
|
|
{ |
|
|
|
color.rgb *= color.a; |
|
|
|
|
|
|
ApplyFog(color.rgb, fogCoord); |
|
|
|
#endif |
|
|
|
} |
|
|
|
#ifdef UNITY_INSTANCING_ENABLED |
|
|
|
TEXTURE2D(_TerrainHeightmapTexture); |
|
|
|
TEXTURE2D(_TerrainNormalmapTexture); |
|
|
|
float4 _TerrainHeightmapRecipSize; // float4(1.0f/width, 1.0f/height, 1.0f/(width-1), 1.0f/(height-1)) |
|
|
|
float4 _TerrainHeightmapScale; // float4(hmScale.x, hmScale.y / (float)(kMaxHeight), hmScale.z, 0.0f) |
|
|
|
#endif |
|
|
|
|
|
|
|
#define API_HAS_GURANTEED_R16_SUPPORT 0 //!(SHADER_API_VULKAN) |
|
|
|
float UnpackHeightmap(float4 height) |
|
|
|
{ |
|
|
|
#if (API_HAS_GURANTEED_R16_SUPPORT) |
|
|
|
return height.r; |
|
|
|
#else |
|
|
|
return (height.r + height.g * 256.0f) / 257.0f; // (255.0f * height.r + 255.0f * 256.0f * height.g) / 65535.0f |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
UNITY_INSTANCING_BUFFER_START(Terrain) |
|
|
|
UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) |
|
|
|
UNITY_INSTANCING_BUFFER_END(Terrain) |
|
|
|
|
|
|
|
void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) |
|
|
|
{ |
|
|
|
#ifdef UNITY_INSTANCING_ENABLED |
|
|
|
float2 patchVertex = vertex.xy; |
|
|
|
float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); |
|
|
|
|
|
|
|
float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale |
|
|
|
float height = UnpackHeightmap(_TerrainHeightmapTexture.Load(int3(sampleCoords, 0))); |
|
|
|
|
|
|
|
vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; |
|
|
|
vertex.y = height * _TerrainHeightmapScale.y; |
|
|
|
|
|
|
|
normal = _TerrainNormalmapTexture.Load(int3(sampleCoords, 0)).rgb * 2 - 1; |
|
|
|
uv = sampleCoords * _TerrainHeightmapRecipSize.zw; |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void TerrainInstancing(inout float4 vertex, inout float3 normal) |
|
|
|
{ |
|
|
|
float2 uv = { 0, 0 }; |
|
|
|
TerrainInstancing(vertex, normal, uv); |
|
|
|
} |
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
// Vertex and Fragment functions // |
|
|
|
|
|
|
{ |
|
|
|
VertexOutput o = (VertexOutput)0; |
|
|
|
|
|
|
|
UNITY_SETUP_INSTANCE_ID(v); |
|
|
|
TerrainInstancing(v.vertex, v.normal, v.texcoord); |
|
|
|
|
|
|
|
o.uvMainAndLM.xy = v.texcoord; |
|
|
|
o.uvMainAndLM.zw = v.texcoord * unity_LightmapST.xy + unity_LightmapST.zw; |
|
|
|
#ifndef TERRAIN_SPLAT_BASEPASS |
|
|
|
o.uvControlAndLM.xy = TRANSFORM_TEX(v.texcoord, _Control); |
|
|
|
o.uvControlAndLM.zw = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw; |
|
|
|
|
|
|
|
#ifdef _TERRAIN_NORMAL_MAP |
|
|
|
#endif |
|
|
|
#ifdef _NORMALMAP |
|
|
|
float4 vertexTangent = float4(cross(v.normal, float3(0, 0, 1)), -1.0); |
|
|
|
OutputTangentToWorld(vertexTangent, v.normal, o.tangent, o.binormal, o.normal); |
|
|
|
#else |
|
|
|
|
|
|
return o; |
|
|
|
} |
|
|
|
|
|
|
|
TEXTURE2D(_MetallicTex); SAMPLER(sampler_MetallicTex); |
|
|
|
|
|
|
|
half4 SpatmapFragment(VertexOutput IN) : SV_TARGET |
|
|
|
half4 SplatmapFragment(VertexOutput IN) : SV_TARGET |
|
|
|
#ifdef TERRAIN_SPLAT_BASEPASS |
|
|
|
half3 normalTS = float3(0, 1, 0); |
|
|
|
half3 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uvMainAndLM.xy).rgb; |
|
|
|
half smoothness = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uvMainAndLM.xy).a; |
|
|
|
half metallic = SAMPLE_TEXTURE2D(_MetallicTex, sampler_MetallicTex, IN.uvMainAndLM.xy).r; |
|
|
|
half alpha = 1; |
|
|
|
#else |
|
|
|
half4 splat_control; |
|
|
|
half weight; |
|
|
|
half4 mixedDiffuse; |
|
|
|
|
|
|
half3 albedo = mixedDiffuse.rgb; |
|
|
|
half smoothness = mixedDiffuse.a; |
|
|
|
half metallic = dot(splat_control, half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3)); |
|
|
|
half3 specular = half3(0, 0, 0); |
|
|
|
#endif |
|
|
|
half4 color = LightweightFragmentPBR(inputData, albedo, metallic, specular, smoothness, /* occlusion */ 1.0, /* emission */ half3(0, 0, 0), alpha); |
|
|
|
half4 color = LightweightFragmentPBR(inputData, albedo, metallic, half3(0, 0, 0), smoothness, /* occlusion */ 1.0, /* emission */ half3(0, 0, 0), alpha); |
|
|
|
} |
|
|
|
|
|
|
|
// Shadow pass |
|
|
|
|
|
|
|
// x: global clip space bias, y: normal world space bias |
|
|
|
float4 _ShadowBias; |
|
|
|
float3 _LightDirection; |
|
|
|
|
|
|
|
struct VertexInputLean |
|
|
|
{ |
|
|
|
float4 position : POSITION; |
|
|
|
float3 normal : NORMAL; |
|
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
|
|
}; |
|
|
|
|
|
|
|
float4 ShadowPassVertex(VertexInputLean v) : SV_POSITION |
|
|
|
{ |
|
|
|
VertexOutput o; |
|
|
|
UNITY_SETUP_INSTANCE_ID(v); |
|
|
|
TerrainInstancing(v.position, v.normal); |
|
|
|
|
|
|
|
float3 positionWS = TransformObjectToWorld(v.position.xyz); |
|
|
|
float3 normalWS = TransformObjectToWorldDir(v.normal); |
|
|
|
|
|
|
|
float invNdotL = 1.0 - saturate(dot(_LightDirection, normalWS)); |
|
|
|
float scale = invNdotL * _ShadowBias.y; |
|
|
|
|
|
|
|
// normal bias is negative since we want to apply an inset normal offset |
|
|
|
positionWS = normalWS * scale.xxx + positionWS; |
|
|
|
float4 clipPos = TransformWorldToHClip(positionWS); |
|
|
|
|
|
|
|
// _ShadowBias.x sign depens on if platform has reversed z buffer |
|
|
|
clipPos.z += _ShadowBias.x; |
|
|
|
|
|
|
|
#if UNITY_REVERSED_Z |
|
|
|
clipPos.z = min(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE); |
|
|
|
#else |
|
|
|
clipPos.z = max(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE); |
|
|
|
#endif |
|
|
|
|
|
|
|
return clipPos; |
|
|
|
} |
|
|
|
|
|
|
|
half4 ShadowPassFragment() : SV_TARGET |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
// Depth pass |
|
|
|
|
|
|
|
float4 DepthOnlyVertex(VertexInputLean v) : SV_POSITION |
|
|
|
{ |
|
|
|
VertexOutput o = (VertexOutput)0; |
|
|
|
UNITY_SETUP_INSTANCE_ID(v); |
|
|
|
TerrainInstancing(v.position, v.normal); |
|
|
|
return TransformObjectToHClip(v.position.xyz); |
|
|
|
} |
|
|
|
|
|
|
|
half4 DepthOnlyFragment() : SV_TARGET |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
#endif // LIGHTWEIGHT_PASS_LIT_TERRAIN_INCLUDED |