Project_WL/Assets/ThirdParty/Toon Shaders Pro/Shaders/Toon Terrain/TerrainLitPasses.hlsl

624 lines
24 KiB
HLSL

#ifndef UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED
#define UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityGBuffer.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
#ifdef _USE_STOCHASTIC_TEXTURING
#define TERRAIN_SAMPLE stochasticTexturing
#else
#define TERRAIN_SAMPLE SAMPLE_TEXTURE2D
#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(_NORMALMAP) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
half4 normalWS : TEXCOORD3; // xyz: normal, w: viewDir.x
half4 tangentWS : TEXCOORD4; // xyz: tangent, w: viewDir.y
half4 bitangentWS : TEXCOORD5; // xyz: bitangent, w: viewDir.z
#else
half3 normalWS : 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
float3 viewWS : TEXCOORD10;
float4 positionCS : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO
};
#include "ToonTerrainFunctions.hlsl"
// Take a 2D seed value and output a random 2D vector.
inline float2 hash2D2D(float2 s)
{
return frac(sin(fmod(float2(dot(s, float2(127.1, 311.7)), dot(s, float2(269.5, 183.3))), 3.14159)) * 43758.5453);
}
// Based on https://www.reddit.com/r/Unity3D/comments/dhr5g2/i_made_a_stochastic_texture_sampling_shader/
float4 stochasticTexturing(texture2D tex, sampler samplerState, float2 uv)
{
float4x3 BW_vx;
float2 skewUV = mul(float2x2(1.0f, 0.0f, -0.57735027f, 1.15470054f), uv * 3.464f);
float2 vxID = float2(floor(skewUV));
float3 barycentric = float3(frac(skewUV), 0);
barycentric.z = 1.0 - barycentric.x - barycentric.y;
BW_vx = ((barycentric.z > 0) ?
float4x3(float3(vxID, 0), float3(vxID + float2(0, 1), 0), float3(vxID + float2(1, 0), 0), barycentric.zyx) :
float4x3(float3(vxID + float2(1, 1), 0), float3(vxID + float2(1, 0), 0), float3(vxID + float2(0, 1), 0), float3(-barycentric.z, 1.0 - barycentric.y, 1.0 - barycentric.x)));
// Calculate derivatives to avoid triangular grid artifacts.
float2 dx = ddx(uv);
float2 dy = ddy(uv);
// Blend samples with calculated weights.
return mul(SAMPLE_TEXTURE2D_GRAD(tex, samplerState, uv + hash2D2D(BW_vx[0].xy), dx, dy), BW_vx[3].x) +
mul(SAMPLE_TEXTURE2D_GRAD(tex, samplerState, uv + hash2D2D(BW_vx[1].xy), dx, dy), BW_vx[3].y) +
mul(SAMPLE_TEXTURE2D_GRAD(tex, samplerState, uv + hash2D2D(BW_vx[2].xy), dx, dy), BW_vx[3].z);
}
void InitializeInputData(Varyings IN, half3 normalTS, out InputData inputData)
{
inputData = (InputData)0;
inputData.positionWS = IN.positionWS;
inputData.positionCS = IN.positionCS;
#if defined(_NORMALMAP) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
half3 viewDirWS = half3(IN.normalWS.w, IN.tangentWS.w, IN.bitangentWS.w);
inputData.tangentToWorld = half3x3(-IN.tangentWS.xyz, IN.bitangentWS.xyz, IN.normalWS.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 = 0;
#else
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS);
inputData.normalWS = IN.normalWS;
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.positionCS);
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
}
#ifndef TERRAIN_SPLAT_BASEPASS
void NormalMapMix(float4 uvSplat01, float4 uvSplat23, inout half4 splatControl, inout half3 mixedNormal)
{
#if defined(_NORMALMAP)
half3 nrm = half(0.0);
nrm += splatControl.r * UnpackNormalScale(TERRAIN_SAMPLE(_Normal0, sampler_Normal0, uvSplat01.xy), _NormalScale0);
nrm += splatControl.g * UnpackNormalScale(TERRAIN_SAMPLE(_Normal1, sampler_Normal0, uvSplat01.zw), _NormalScale1);
nrm += splatControl.b * UnpackNormalScale(TERRAIN_SAMPLE(_Normal2, sampler_Normal0, uvSplat23.xy), _NormalScale2);
nrm += splatControl.a * UnpackNormalScale(TERRAIN_SAMPLE(_Normal3, sampler_Normal0, uvSplat23.zw), _NormalScale3);
// avoid risk of NaN when normalizing.
#if HAS_HALF
nrm.z += half(0.01);
#else
nrm.z += 1e-5f;
#endif
mixedNormal = normalize(nrm.xyz);
#endif
}
void SplatmapMix(float4 uvMainAndLM, float4 uvSplat01, float4 uvSplat23, inout half4 splatControl, out half weight, out half4 mixedDiffuse, out half4 defaultSmoothness, inout half3 mixedNormal)
{
half4 diffAlbedo[4];
diffAlbedo[0] = TERRAIN_SAMPLE(_Splat0, sampler_Splat0, uvSplat01.xy);
diffAlbedo[1] = TERRAIN_SAMPLE(_Splat1, sampler_Splat0, uvSplat01.zw);
diffAlbedo[2] = TERRAIN_SAMPLE(_Splat2, sampler_Splat0, uvSplat23.xy);
diffAlbedo[3] = TERRAIN_SAMPLE(_Splat3, sampler_Splat0, uvSplat23.zw);
// This might be a bit of a gamble -- the assumption here is that if the diffuseMap has no
// alpha channel, then diffAlbedo[n].a = 1.0 (and _DiffuseHasAlphaN = 0.0)
// Prior to coming in, _SmoothnessN is actually set to max(_DiffuseHasAlphaN, _SmoothnessN)
// This means that if we have an alpha channel, _SmoothnessN is locked to 1.0 and
// otherwise, the true slider value is passed down and diffAlbedo[n].a == 1.0.
defaultSmoothness = half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a);
defaultSmoothness *= half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
#ifndef _TERRAIN_BLEND_HEIGHT // density blending
if(_NumLayersCount <= 4)
{
// 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
half4 opacityAsDensity = saturate((half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a) - (1 - splatControl)) * 20.0);
opacityAsDensity += 0.001h * splatControl; // if all weights are zero, default to what the blend mask says
half4 useOpacityAsDensityParam = { _DiffuseRemapScale0.w, _DiffuseRemapScale1.w, _DiffuseRemapScale2.w, _DiffuseRemapScale3.w }; // 1 is off
splatControl = lerp(opacityAsDensity, splatControl, useOpacityAsDensityParam);
}
#endif
// Now that splatControl has changed, we can compute the final weight and normalize
weight = dot(splatControl, 1.0h);
#ifdef TERRAIN_SPLAT_ADDPASS
clip(weight <= 0.005h ? -1.0h : 1.0h);
#endif
#ifndef _TERRAIN_BASEMAP_GEN
// Normalize weights before lighting and restore weights in final modifier functions so that the overal
// lighting result can be correctly weighted.
splatControl /= (weight + HALF_MIN);
#endif
mixedDiffuse = 0.0h;
mixedDiffuse += diffAlbedo[0] * half4(_DiffuseRemapScale0.rgb * splatControl.rrr, 1.0h);
mixedDiffuse += diffAlbedo[1] * half4(_DiffuseRemapScale1.rgb * splatControl.ggg, 1.0h);
mixedDiffuse += diffAlbedo[2] * half4(_DiffuseRemapScale2.rgb * splatControl.bbb, 1.0h);
mixedDiffuse += diffAlbedo[3] * half4(_DiffuseRemapScale3.rgb * splatControl.aaa, 1.0h);
NormalMapMix(uvSplat01, uvSplat23, splatControl, mixedNormal);
}
#endif
#ifdef _TERRAIN_BLEND_HEIGHT
void HeightBasedSplatModify(inout half4 splatControl, in half4 masks[4])
{
// heights are in mask blue channel, we multiply by the splat Control weights to get combined height
half4 splatHeight = half4(masks[0].b, masks[1].b, masks[2].b, masks[3].b) * splatControl.rgba;
half maxHeight = max(splatHeight.r, max(splatHeight.g, max(splatHeight.b, splatHeight.a)));
// Ensure that the transition height is not zero.
half transition = max(_HeightTransition, 1e-5);
// This sets the highest splat to "transition", and everything else to a lower value relative to that, clamping to zero
// Then we clamp this to zero and normalize everything
half4 weightedHeights = splatHeight + transition - maxHeight.xxxx;
weightedHeights = max(0, weightedHeights);
// We need to add an epsilon here for active layers (hence the blendMask again)
// so that at least a layer shows up if everything's too low.
weightedHeights = (weightedHeights + 1e-6) * splatControl;
// Normalize (and clamp to epsilon to keep from dividing by zero)
half sumHeight = max(dot(weightedHeights, half4(1, 1, 1, 1)), 1e-6);
splatControl = weightedHeights / sumHeight.xxxx;
}
#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
}
void SetupTerrainDebugTextureData(inout InputData inputData, float2 uv)
{
#if defined(DEBUG_DISPLAY)
#if defined(TERRAIN_SPLAT_ADDPASS)
if (_DebugMipInfoMode != DEBUGMIPINFOMODE_NONE)
{
discard; // Layer 4 & beyond are done additively, doesn't make sense for the mipmap streaming debug views -> stop.
}
#endif
switch (_DebugMipMapTerrainTextureMode)
{
case DEBUGMIPMAPMODETERRAINTEXTURE_CONTROL:
SETUP_DEBUG_TEXTURE_DATA_FOR_TEX(inputData, TRANSFORM_TEX(uv, _Control), _Control);
break;
case DEBUGMIPMAPMODETERRAINTEXTURE_LAYER0:
SETUP_DEBUG_TEXTURE_DATA_FOR_TEX(inputData, TRANSFORM_TEX(uv, _Splat0), _Splat0);
break;
case DEBUGMIPMAPMODETERRAINTEXTURE_LAYER1:
SETUP_DEBUG_TEXTURE_DATA_FOR_TEX(inputData, TRANSFORM_TEX(uv, _Splat1), _Splat1);
break;
case DEBUGMIPMAPMODETERRAINTEXTURE_LAYER2:
SETUP_DEBUG_TEXTURE_DATA_FOR_TEX(inputData, TRANSFORM_TEX(uv, _Splat2), _Splat2);
break;
case DEBUGMIPMAPMODETERRAINTEXTURE_LAYER3:
SETUP_DEBUG_TEXTURE_DATA_FOR_TEX(inputData, TRANSFORM_TEX(uv, _Splat3), _Splat3);
break;
default:
break;
}
// TERRAIN_STREAM_INFO: no streamInfo will have been set (no MeshRenderer); set status to "6" to reflect in the debug status that this is a terrain
// also, set the per-material status to "4" to indicate warnings
inputData.streamInfo = TERRAIN_STREAM_INFO;
#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;
#ifndef TERRAIN_SPLAT_BASEPASS
o.uvSplat01.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
o.uvSplat01.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
o.uvSplat23.xy = TRANSFORM_TEX(v.texcoord, _Splat2);
o.uvSplat23.zw = TRANSFORM_TEX(v.texcoord, _Splat3);
#endif
#if defined(DYNAMICLIGHTMAP_ON)
o.dynamicLightmapUV = v.texcoord * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
#if defined(_NORMALMAP) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(Attributes.positionWS);
float4 vertexTangent = float4(cross(float3(0, 0, 1), v.normalOS), 1.0);
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, vertexTangent);
o.normalWS = half4(normalInput.normalWS, viewDirWS.x);
o.tangentWS = half4(normalInput.tangentWS, viewDirWS.y);
o.bitangentWS = half4(normalInput.bitangentWS, viewDirWS.z);
#else
o.normalWS = TransformObjectToWorldNormal(v.normalOS);
o.vertexSH = SampleSH(o.normalWS);
#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.normalWS.xyz);
#else
o.fogFactor = fogFactor;
#endif
o.positionWS = Attributes.positionWS;
o.positionCS = Attributes.positionCS;
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
o.shadowCoord = GetShadowCoord(Attributes);
#endif
o.viewWS = GetWorldSpaceNormalizeViewDir(o.positionWS);
return o;
}
void ComputeMasks(out half4 masks[4], half4 hasMask, Varyings IN)
{
masks[0] = 0.5h;
masks[1] = 0.5h;
masks[2] = 0.5h;
masks[3] = 0.5h;
#ifdef _MASKMAP
masks[0] = lerp(masks[0], SAMPLE_TEXTURE2D(_Mask0, sampler_Mask0, IN.uvSplat01.xy), hasMask.x);
masks[1] = lerp(masks[1], SAMPLE_TEXTURE2D(_Mask1, sampler_Mask0, IN.uvSplat01.zw), hasMask.y);
masks[2] = lerp(masks[2], SAMPLE_TEXTURE2D(_Mask2, sampler_Mask0, IN.uvSplat23.xy), hasMask.z);
masks[3] = lerp(masks[3], SAMPLE_TEXTURE2D(_Mask3, sampler_Mask0, IN.uvSplat23.zw), hasMask.w);
#endif
masks[0] *= _MaskMapRemapScale0.rgba;
masks[0] += _MaskMapRemapOffset0.rgba;
masks[1] *= _MaskMapRemapScale1.rgba;
masks[1] += _MaskMapRemapOffset1.rgba;
masks[2] *= _MaskMapRemapScale2.rgba;
masks[2] += _MaskMapRemapOffset2.rgba;
masks[3] *= _MaskMapRemapScale3.rgba;
masks[3] += _MaskMapRemapOffset3.rgba;
}
// Terrain fragment shader.
void SplatmapFragment(Varyings i , out half4 outColor : SV_Target0
#ifdef _WRITE_RENDERING_LAYERS
, out float4 outRenderingLayers : SV_Target1
#endif
)
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN);
#ifdef _ALPHATEST_ON
ClipHoles(i.uvMainAndLM.xy);
#endif
half3 normalTS = half3(0.0h, 0.0h, 1.0h);
#ifdef TERRAIN_SPLAT_BASEPASS
half3 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uvMainAndLM.xy).rgb;
half smoothness = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uvMainAndLM.xy).a;
half metallic = SAMPLE_TEXTURE2D(_MetallicTex, sampler_MetallicTex, i.uvMainAndLM.xy).r;
half alpha = 1;
half occlusion = 1;
half3 specularStrength = 1;
#else
half4 hasMask = half4(_LayerHasMask0, _LayerHasMask1, _LayerHasMask2, _LayerHasMask3);
half4 masks[4];
ComputeMasks(masks, hasMask, i);
float2 splatUV = (i.uvMainAndLM.xy * (_Control_TexelSize.zw - 1.0f) + 0.5f) * _Control_TexelSize.xy;
half4 splatControl = SAMPLE_TEXTURE2D(_Control, sampler_Control, splatUV);
half alpha = dot(splatControl, 1.0h);
#ifdef _TERRAIN_BLEND_HEIGHT
// disable Height Based blend when there are more than 4 layers (multi-pass breaks the normalization)
if (_NumLayersCount <= 4)
HeightBasedSplatModify(splatControl, masks);
#endif
half weight;
half4 mixedDiffuse;
half4 defaultSmoothness;
SplatmapMix(i.uvMainAndLM, i.uvSplat01, i.uvSplat23, splatControl, weight, mixedDiffuse, defaultSmoothness, normalTS);
half3 albedo = mixedDiffuse.rgb;
half4 defaultMetallic = half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3);
half4 defaultOcclusion = half4(_MaskMapRemapScale0.g, _MaskMapRemapScale1.g, _MaskMapRemapScale2.g, _MaskMapRemapScale3.g) +
half4(_MaskMapRemapOffset0.g, _MaskMapRemapOffset1.g, _MaskMapRemapOffset2.g, _MaskMapRemapOffset3.g);
half4 maskSmoothness = half4(masks[0].a, masks[1].a, masks[2].a, masks[3].a);
defaultSmoothness = lerp(defaultSmoothness, maskSmoothness, hasMask);
half smoothness = dot(splatControl, defaultSmoothness);
half4 maskMetallic = half4(masks[0].r, masks[1].r, masks[2].r, masks[3].r);
defaultMetallic = lerp(defaultMetallic, maskMetallic, hasMask);
half metallic = dot(splatControl, defaultMetallic);
half3 specularStrength = splatControl.x * _Specular0 + splatControl.y * _Specular1 + splatControl.z * _Specular2 + splatControl.w * _Specular3;
half4 maskOcclusion = half4(masks[0].g, masks[1].g, masks[2].g, masks[3].g);
defaultOcclusion = lerp(defaultOcclusion, maskOcclusion, hasMask);
half occlusion = dot(splatControl, defaultOcclusion);
#endif
InputData inputData;
InitializeInputData(i, normalTS, inputData);
SetupTerrainDebugTextureData(inputData, i.uvMainAndLM.xy);
#if defined(_DBUFFER)
half3 specular = half3(0.0h, 0.0h, 0.0h);
ApplyDecal(i.positionCS,
albedo,
specular,
inputData.normalWS,
metallic,
occlusion,
smoothness);
#endif
// Calculate the specular light offset values.
float2 offsetUVs = TRANSFORM_TEX(i.positionWS.xz, _SpecularOffsetNoiseMap);
float specularOffset = SAMPLE_TEXTURE2D(_SpecularOffsetNoiseMap, sampler_SpecularOffsetNoiseMap, offsetUVs).r;
specularOffset = (specularOffset - 0.5f) * _SpecularOffsetNoiseStrength;
// Get the main light.
Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask);
// Set up BRDF data for lighting calculations.
BRDFData brdfData;
ToonBRDFData(albedo, metallic, 0.0f, smoothness, alpha, brdfData);
// Calculate global illumination.
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask);
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData.normalizedScreenSpaceUV, 1.0h);
half3 giColor = ToonGlobalIllumination(brdfData, inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.normalWS, inputData.viewDirectionWS);
giColor = lerp(0.0, giColor, _GIStrength);
half3 mainLightColor = CalculateToonLighting(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS, specularOffset);
half3 additionalLightColor = 0.0h;
#ifdef _ADDITIONAL_LIGHTS
// Apply secondary lights.
uint lightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
// Apply secondary lights (Forward rendering).
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); ++lightIndex)
{
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
Light light = GetAdditionalLight(lightIndex, inputData.positionWS, inputData.shadowMask);
additionalLightColor += CalculateToonLighting(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, specularOffset);
}
#endif
// Apply secondary lights (Forward+ rendering).
LIGHT_LOOP_BEGIN(lightCount)
Light light = GetAdditionalLight(lightIndex, inputData.positionWS, inputData.shadowMask);
additionalLightColor += CalculateToonLighting(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, specularOffset);
LIGHT_LOOP_END
#endif
float4 baseColor = float4(albedo * (giColor + mainLightColor + additionalLightColor), alpha);
SplatmapFinalColor(baseColor, inputData.fogCoord);
outColor = half4(baseColor.rgb, 1.0h);
#ifdef _WRITE_RENDERING_LAYERS
outRenderingLayers = float4(EncodeMeshRenderingLayer(), 0, 0, 0);
#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 positionCS : 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 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
o.positionCS = positionCS;
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.positionCS = 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 IN.positionCS.z;
}
#endif