#ifndef UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED #define UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #ifdef UNITY_6000_1_OR_NEWER #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GBufferOutput.hlsl" #else #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityGBuffer.hlsl" #endif #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" #if defined(PARALLAX) || defined(_TERRAIN_TRIPLANAR) || defined(_TERRAIN_TRIPLANAR_ONE) || defined(_PUDDLES) #define TANGENT #endif struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { 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 (defined(_NORMALMAPS) || defined(TANGENT)) && !defined(TERRAIN_SPLAT_BASEPASS) half4 normal : TEXCOORD3; // xyz: normal, w: viewDir.x half4 tangent : TEXCOORD4; // xyz: tangent, w: viewDir.y half4 bitangent : TEXCOORD5; // xyz: bitangent, w: viewDir.z #if defined(ENABLE_TERRAIN_PERPIXEL_NORMAL) half3 vertexSH : TEXCOORD12; // SH #endif #else half3 normal : TEXCOORD3; half3 vertexSH : TEXCOORD4; // SH #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX half4 fogFactorAndVertexLight : TEXCOORD6; // x: fogFactor, yzw: vertex light #else half fogFactor : TEXCOORD6; #endif float3 positionWS : TEXCOORD7; #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) float4 shadowCoord : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif float4 uvSplat45 : TEXCOORD10; // xy: splat4, zw: splat5 float4 uvSplat67 : TEXCOORD11; // xy: splat6, zw: splat7 float4 clipPos : SV_POSITION; UNITY_VERTEX_OUTPUT_STEREO }; void InitializeInputData(Varyings IN, half3 normalTS, out InputData inputData) { inputData = (InputData)0; inputData.positionWS = IN.positionWS; inputData.positionCS = IN.clipPos; #if (defined(_NORMALMAPS) || defined(TANGENT)) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL) && !defined(TERRAIN_SPLAT_BASEPASS) half3 viewDirWS = half3(IN.normal.w, IN.tangent.w, IN.bitangent.w); inputData.tangentToWorld = half3x3(-IN.tangent.xyz, IN.bitangent.xyz, IN.normal.xyz); inputData.normalWS = TransformTangentToWorld(normalTS, inputData.tangentToWorld); half3 SH = 0; #elif defined(ENABLE_TERRAIN_PERPIXEL_NORMAL) half3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS); float2 sampleCoords = (IN.uvMainAndLM.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; half3 normalWS = TransformObjectToWorldNormal(normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, sampler_TerrainNormalmapTexture, sampleCoords).rgb * 2 - 1)); half3 tangentWS = cross(GetObjectToWorldMatrix()._13_23_33, normalWS); inputData.normalWS = TransformTangentToWorld(normalTS, half3x3(-tangentWS, cross(normalWS, tangentWS), normalWS)); half3 SH = IN.vertexSH; #else half3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS); inputData.normalWS = IN.normal; half3 SH = IN.vertexSH; #endif inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS); inputData.viewDirectionWS = viewDirWS; #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) inputData.shadowCoord = IN.shadowCoord; #elif defined(MAIN_LIGHT_CALCULATE_SHADOWS) inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS); #else inputData.shadowCoord = float4(0, 0, 0, 0); #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX inputData.fogCoord = InitializeInputDataFog(float4(IN.positionWS, 1.0), IN.fogFactorAndVertexLight.x); inputData.vertexLighting = IN.fogFactorAndVertexLight.yzw; #else inputData.fogCoord = InitializeInputDataFog(float4(IN.positionWS, 1.0), IN.fogFactor); #endif #if defined(DYNAMICLIGHTMAP_ON) inputData.bakedGI = SAMPLE_GI(IN.uvMainAndLM.zw, IN.dynamicLightmapUV, SH, inputData.normalWS); #else inputData.bakedGI = SAMPLE_GI(IN.uvMainAndLM.zw, SH, inputData.normalWS); #endif inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(IN.clipPos); inputData.shadowMask = SAMPLE_SHADOWMASK(IN.uvMainAndLM.zw) #if defined(DEBUG_DISPLAY) #if defined(DYNAMICLIGHTMAP_ON) inputData.dynamicLightmapUV = IN.dynamicLightmapUV; #endif #if defined(LIGHTMAP_ON) inputData.staticLightmapUV = IN.uvMainAndLM.zw; #else inputData.vertexSH = SH; #endif #endif } void TriplanarBase(in out half4 baseMap, half4 front, half4 side, float3 weights, float2 splat, half firstToAllSteep) { baseMap = firstToAllSteep == 1 ? (baseMap * weights.y + front * weights.z + side * weights.x) : (baseMap * saturate(weights.y + (1 - splat.g))) + (((front * weights.z) + (side * weights.x)) * (splat.r)); } float2 TerrainFrontUV(float3 wPos, half4 splatUV, float2 tc, float3 flip) { return float2(tc.x * -flip.z, (wPos.y - _TerrainSizeXZPosY.z) * (splatUV.y / _TerrainSizeXZPosY.y) + splatUV.w); } float2 TerrainSideUV(float3 wPos, half4 splatUV, float2 tc, float3 flip) { return float2(tc.y * flip.x, (wPos.y - _TerrainSizeXZPosY.z) * (splatUV.x / _TerrainSizeXZPosY.x) + splatUV.z); } #ifndef TERRAIN_SPLAT_BASEPASS #include "InTerra_SplatmapMix.hlsl" #endif void SplatmapFinalColor(inout half4 color, half fogCoord) { color.rgb *= color.a; #ifndef TERRAIN_GBUFFER // Technically we don't need fogCoord, but it is still passed from the vertex shader. #ifdef TERRAIN_SPLAT_ADDPASS color.rgb = MixFogColor(color.rgb, half3(0,0,0), fogCoord); #else color.rgb = MixFog(color.rgb, fogCoord); #endif #endif } /////////////////////////////////////////////////////////////////////////////// // Vertex and Fragment functions // /////////////////////////////////////////////////////////////////////////////// // Used in Standard Terrain shader Varyings SplatmapVert(Attributes v) { Varyings o = (Varyings)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); TerrainInstancing(v.positionOS, v.normalOS, v.texcoord); VertexPositionInputs Attributes = GetVertexPositionInputs(v.positionOS.xyz); o.uvMainAndLM.xy = v.texcoord; o.uvMainAndLM.zw = v.texcoord * unity_LightmapST.xy + unity_LightmapST.zw; float2 tc = _WorldMapping ? (Attributes.positionWS.xz / _TerrainSizeXZPosY.xy) : v.texcoord; #ifndef TERRAIN_SPLAT_BASEPASS o.uvSplat01.xy = TRANSFORM_TEX(tc, _Splat0); o.uvSplat01.zw = TRANSFORM_TEX(tc, _Splat1); #ifndef _LAYERS_TWO o.uvSplat23.xy = TRANSFORM_TEX(tc, _Splat2); o.uvSplat23.zw = TRANSFORM_TEX(tc, _Splat3); #ifdef _LAYERS_EIGHT o.uvSplat45.xy = TRANSFORM_TEX(tc, _Splat4); o.uvSplat45.zw = TRANSFORM_TEX(tc, _Splat5); o.uvSplat67.xy = TRANSFORM_TEX(tc, _Splat6); o.uvSplat67.zw = TRANSFORM_TEX(tc, _Splat7); #endif #endif #endif #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV = v.texcoord * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #if (defined(_NORMALMAPS) || defined(TANGENT)) && !defined(TERRAIN_SPLAT_BASEPASS) half3 viewDirWS = GetWorldSpaceNormalizeViewDir(Attributes.positionWS); float4 vertexTangent = float4(cross(float3(0, 0, 1), v.normalOS), 1.0); VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, vertexTangent); o.normal = half4(normalInput.normalWS, viewDirWS.x); o.tangent = half4(normalInput.tangentWS, viewDirWS.y); o.bitangent = half4(normalInput.bitangentWS, viewDirWS.z); #else o.normal = half4(TransformObjectToWorldNormal(v.normalOS), 0); o.vertexSH = SampleSH(o.normal); #endif half fogFactor = 0; #if !defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(Attributes.positionCS.z); #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX o.fogFactorAndVertexLight.x = fogFactor; o.fogFactorAndVertexLight.yzw = VertexLighting(Attributes.positionWS, o.normal.xyz); #else o.fogFactor = fogFactor; #endif o.positionWS = Attributes.positionWS; o.clipPos = Attributes.positionCS; #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(Attributes); #endif return o; } // Used in Standard Terrain shader #ifdef TERRAIN_GBUFFER #ifdef UNITY_6000_1_OR_NEWER GBufferFragOutput SplatmapFragment(Varyings IN) #else FragmentOutput SplatmapFragment(Varyings IN) #endif #else #ifdef UNITY_6000_1_OR_NEWER void SplatmapFragment( Varyings IN , out half4 outColor : SV_Target0 #ifdef _WRITE_RENDERING_LAYERS , out float4 outRenderingLayers : SV_Target1 #endif ) #else half4 SplatmapFragment(Varyings IN) : SV_TARGET #endif #endif { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); #ifdef _ALPHATEST_ON ClipHoles(IN.uvMainAndLM.xy); #endif half3 normalTS = half3(0.0h, 0.0h, 1.0h); #ifdef TERRAIN_SPLAT_BASEPASS half4 mainTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uvMainAndLM.xy); half4 metallic_AO_splat01 = SAMPLE_TEXTURE2D(_MetallicTex, sampler_MetallicTex, IN.uvMainAndLM.xy); #ifdef _TERRAIN_TRIPLANAR_ONE float3 weights = abs(IN.normal); weights = pow(weights, _TriplanarSharpness); weights = weights / (weights.x + weights.y + weights.z); float3 flipUV = float3(1, 1, -1); //IN.normal.rgb < 0 ? -1 : 1; float2 frontUV = TerrainFrontUV(IN.positionWS, _MainTex_ST, IN.uvMainAndLM.xy, flipUV); float2 sideUV = TerrainSideUV(IN.positionWS, _MainTex_ST, IN.uvMainAndLM.xy, flipUV); half4 cFront = SAMPLE_TEXTURE2D(_TriplanarTex, sampler_TriplanarTex, frontUV); half4 cSide = SAMPLE_TEXTURE2D(_TriplanarTex, sampler_TriplanarTex, sideUV); TriplanarBase(mainTex, cFront, cSide, weights, metallic_AO_splat01.b, _TriplanarOneToAllSteep); float3 tint = SAMPLE_TEXTURE2D(_TerrainColorTintTexture, SamplerState_Linear_Repeat, IN.uvMainAndLM.xy *_TerrainColorTintTexture_ST.xy + _TerrainColorTintTexture_ST.zw).rgb; mainTex.rgb = lerp(mainTex.rgb, ((mainTex.rgb) * (tint)), _TerrainColorTintStrenght).rgb; half4 mAoFront = SAMPLE_TEXTURE2D(_Triplanar_MetallicAO, sampler_Triplanar_MetallicAO, frontUV); half4 mAoSide = SAMPLE_TEXTURE2D(_Triplanar_MetallicAO, sampler_Triplanar_MetallicAO, sideUV); TriplanarBase(metallic_AO_splat01, mAoFront, mAoSide, weights, metallic_AO_splat01.b, _TriplanarOneToAllSteep); #endif half3 albedo = mainTex.rgb; half smoothness = lerp(mainTex.a, 1.0f, _InTerra_GlobalWetness); half metallic = metallic_AO_splat01.r; half occlusion = metallic_AO_splat01.g; half alpha = 1; #else half alpha; half4 mixedDiffuse = half4(0.0f, 0.0f, 0.0f, 0.0f);; half4 defaultSmoothness; half metallic = 0; half occlusion = 1; half smoothness = 0; float3 tangentViewDir = float3(0,0,0); float2 uv[_LAYER_COUNT]; uv[0] = IN.uvSplat01.xy; uv[1] = IN.uvSplat01.zw; #ifndef _LAYERS_TWO uv[2] = IN.uvSplat23.xy; uv[3] = IN.uvSplat23.zw; #ifdef _LAYERS_EIGHT float2 tc = _WorldMapping ? (IN.positionWS.xz / _TerrainSizeXZPosY.xy) : IN.uvMainAndLM.xy; uv[4] = TRANSFORM_TEX(tc, _Splat4); uv[5] = TRANSFORM_TEX(tc, _Splat5); uv[6] = TRANSFORM_TEX(tc, _Splat6); uv[7] = TRANSFORM_TEX(tc, _Splat7); #endif #endif #if defined(_LAYERS_EIGHT) && defined(TERRAIN_SPLAT_ADDPASS) alpha = 0; #else #ifdef PARALLAX float3x3 objectToTangent = float3x3((IN.tangent.xyz), (cross(IN.normal.xyz, IN.tangent.xyz)) * -1, IN.normal.xyz); tangentViewDir = -normalize(mul(objectToTangent, GetWorldSpaceViewDir(IN.positionWS))); #endif #if defined(TRIPLANAR) || defined(PARALLAX) || defined(_PUDDLES) SplatmapMix(IN.uvMainAndLM, uv, IN.normal.xyz, tangentViewDir, IN.positionWS, alpha, mixedDiffuse, smoothness, metallic, occlusion, normalTS); #else SplatmapMix(IN.uvMainAndLM, uv, float3(0,0,0), float3(0, 0, 0), IN.positionWS, alpha, mixedDiffuse, smoothness, metallic, occlusion, normalTS); #endif #endif half3 albedo = mixedDiffuse.rgb; #endif InputData inputData; InitializeInputData(IN, normalTS, inputData); // SETUP_DEBUG_TEXTURE_DATA(inputData, IN.uvMainAndLM.xy, _BaseMap); #if defined(_DBUFFER) half3 specular = half3(0.0h, 0.0h, 0.0h); ApplyDecal(IN.clipPos, albedo, specular, inputData.normalWS, metallic, occlusion, smoothness); #endif #ifdef TERRAIN_GBUFFER BRDFData brdfData; InitializeBRDFData(albedo, metallic, /* specular */ half3(0.0h, 0.0h, 0.0h), smoothness, alpha, brdfData); // Baked lighting. half4 color; Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask); MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask); color.rgb = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.positionWS, inputData.normalWS, inputData.viewDirectionWS); color.a = alpha; SplatmapFinalColor(color, inputData.fogCoord); // Dynamic lighting: emulate SplatmapFinalColor() by scaling gbuffer material properties. This will not give the same results // as forward renderer because we apply blending pre-lighting instead of post-lighting. // Blending of smoothness and normals is also not correct but close enough? brdfData.albedo.rgb *= alpha; brdfData.diffuse.rgb *= alpha; brdfData.specular.rgb *= alpha; brdfData.reflectivity *= alpha; inputData.normalWS = inputData.normalWS * alpha; smoothness *= alpha; #ifdef UNITY_6000_1_OR_NEWER return PackGBuffersBRDFData(brdfData, inputData, smoothness, color.rgb, occlusion); #else return BRDFDataToGbuffer(brdfData, inputData, smoothness, color.rgb, occlusion); #endif #else half4 color = UniversalFragmentPBR(inputData, albedo, metallic, /* specular */ half3(0.0h, 0.0h, 0.0h), smoothness, occlusion, /* emission */ half3(0, 0, 0), alpha); SplatmapFinalColor(color, inputData.fogCoord); #ifdef UNITY_6000_1_OR_NEWER outColor = half4(color.rgb, 1.0h); #ifdef _WRITE_RENDERING_LAYERS uint renderingLayers = GetMeshRenderingLayer(); outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); #endif #else return half4(color.rgb, 1.0h); #endif #endif } // Shadow pass // Shadow Casting Light geometric parameters. These variables are used when applying the shadow Normal Bias and are set by UnityEngine.Rendering.Universal.ShadowUtils.SetupShadowCasterConstantBuffer in com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs // For Directional lights, _LightDirection is used when applying shadow Normal Bias. // For Spot lights and Point lights, _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex. float3 _LightDirection; float3 _LightPosition; struct AttributesLean { float4 position : POSITION; float3 normalOS : NORMAL; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VaryingsLean { float4 clipPos : SV_POSITION; float2 texcoord : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; VaryingsLean ShadowPassVertex(AttributesLean v) { VaryingsLean o = (VaryingsLean)0; UNITY_SETUP_INSTANCE_ID(v); TerrainInstancing(v.position, v.normalOS, v.texcoord); float3 positionWS = TransformObjectToWorld(v.position.xyz); float3 normalWS = TransformObjectToWorldNormal(v.normalOS); #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - positionWS); #else float3 lightDirectionWS = _LightDirection; #endif float4 clipPos = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS)); #if UNITY_REVERSED_Z clipPos.z = min(clipPos.z, UNITY_NEAR_CLIP_VALUE); #else clipPos.z = max(clipPos.z, UNITY_NEAR_CLIP_VALUE); #endif o.clipPos = clipPos; o.texcoord = v.texcoord; return o; } half4 ShadowPassFragment(VaryingsLean IN) : SV_TARGET { #ifdef _ALPHATEST_ON ClipHoles(IN.texcoord); #endif return 0; } // Depth pass VaryingsLean DepthOnlyVertex(AttributesLean v) { VaryingsLean o = (VaryingsLean)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); TerrainInstancing(v.position, v.normalOS); o.clipPos = TransformObjectToHClip(v.position.xyz); o.texcoord = v.texcoord; return o; } half4 DepthOnlyFragment(VaryingsLean IN) : SV_TARGET { #ifdef _ALPHATEST_ON ClipHoles(IN.texcoord); #endif #ifdef SCENESELECTIONPASS // We use depth prepass for scene selection in the editor, this code allow to output the outline correctly return half4(_ObjectId, _PassValue, 1.0, 1.0); #endif return 0; } #endif