Files
kronic e362aaeb43 Import art/VFX asset packs + game-feel systems; normalize texture extensions to lowercase for LFS
Add BefourStudios SciFi environment packs, Gabriel Aguiar VFX, and the
ShaderCrew Toon Shader embedded packages, plus combat/enemy/wave/death
gameplay systems and supporting vault docs/screenshots.

Rename 11 vendor textures from uppercase .PNG/.HDR to lowercase so the
case-sensitive Git LFS filters (*.png/*.hdr) match on case-sensitive
filesystems (Linux CI, case-sensitive macOS), not just locally where
core.ignorecase=true masks the gap. Each .meta moved with its asset so
GUID references are preserved. All ~1000 binaries tracked via LFS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:50:43 -07:00

599 lines
22 KiB
HLSL

#ifndef UNIVERSAL_LIGHTING_TOON_INCLUDED
#define UNIVERSAL_LIGHTING_TOON_INCLUDED
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/BRDF.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Debug/Debugging3D.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GlobalIllumination.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/AmbientOcclusion.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
#if defined(LIGHTMAP_ON)
#define DECLARE_LIGHTMAP_OR_SH(lmName, shName, index) float2 lmName : TEXCOORD##index
#define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT) OUT.xy = lightmapUV.xy * lightmapScaleOffset.xy + lightmapScaleOffset.zw;
#define OUTPUT_SH(normalWS, OUT)
#else
#define DECLARE_LIGHTMAP_OR_SH(lmName, shName, index) half3 shName : TEXCOORD##index
#define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT)
#define OUTPUT_SH(normalWS, OUT) OUT.xyz = SampleSHVertex(normalWS)
#endif
///////////////////////////////////////////////////////////////////////////////
// Lighting Functions //
///////////////////////////////////////////////////////////////////////////////
half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal)
{
half NdotL = saturate(dot(normal, lightDir));
return lightColor * NdotL;
}
half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal, half modifiedLightIntensity)
{
return lightColor * modifiedLightIntensity;
}
//half3 LightingSpecular(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specular, half smoothness)
//{
// float3 halfVec = SafeNormalize(float3(lightDir) + float3(viewDir));
// half NdotH = half(saturate(dot(normal, halfVec)));
// half modifier = pow(NdotH, smoothness);
// half3 specularReflection = specular.rgb * modifier;
// return lightColor * specularReflection;
//}
half3 LightingSpecular(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specular, half smoothness, ToonShadingData toonShadingData)
{
float3 halfVec = SafeNormalize(float3(lightDir) + float3(viewDir));
if (toonShadingData.enableToonShading == 1)
{
if (toonShadingData.specularAffectedByNormalMap == 0)
{
normal = toonShadingData.normalWSNoMap;
}
else
{
normal = toonShadingData.normalWS;
}
}
half NdotH = saturate(dot(normal, halfVec));
half modifier = pow(NdotH, smoothness);
half3 specularReflection;
if (toonShadingData.enableToonShading == 1)
{
//if (toonShadingData.specularEdgeSmoothness < 1)
//{
// specularReflection = specular.rgb * modifier;
// specularReflection = smoothstep(0.01, 0.01 + toonShadingData.cellTransitionSmoothness, specularReflection);
//}
//else
//{
// specularReflection = specular.rgb * modifier;
//}
specularReflection = PosterizeShifted(saturate(specular.rgb) * modifier, toonShadingData);
}
else
{
specularReflection = saturate(specular.rgb) * modifier;
}
return lightColor * specularReflection;
}
half3 LightingPhysicallyBased(BRDFData brdfData, BRDFData brdfDataClearCoat,
half3 lightColor, half3 lightDirectionWS, half lightAttenuation,
half3 normalWS, half3 viewDirectionWS,
half clearCoatMask, bool specularHighlightsOff,
ToonShadingData toonShadingData)
{
toonShadingData.normalWS = normalWS;
//half NdotL = saturate(dot(normalWS, lightDirectionWS));
half NdotL = CalculateCellShadingPartitioning(lightDirectionWS, toonShadingData);
half3 radiance = lightColor * (lightAttenuation * NdotL);
half3 brdf = brdfData.diffuse;
#ifndef _SPECULARHIGHLIGHTS_OFF
[branch]
if (!specularHighlightsOff)
{
half3 specularNormal;
if (toonShadingData.specularAffectedByNormalMap == 1)
{
specularNormal = normalWS;
}
else
{
specularNormal = toonShadingData.normalWSNoMap;
}
//brdf += brdfData.specular * DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS); //original
half3 specular = brdfData.specular * DirectBRDFSpecular(brdfData, specularNormal, lightDirectionWS, viewDirectionWS);
if (toonShadingData.enableToonShading == 1)
{
//specular = specular / brdfData.normalizationTerm;
specular = PosterizeShifted(specular, toonShadingData);
// specular = specular * brdfData.normalizationTerm;
}
brdf += specular;
#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
// Clear coat evaluates the specular a second timw and has some common terms with the base specular.
// We rely on the compiler to merge these and compute them only once.
half brdfCoat = kDielectricSpec.r * DirectBRDFSpecular(brdfDataClearCoat, normalWS, lightDirectionWS, viewDirectionWS);
// Mix clear coat and base layer using khronos glTF recommended formula
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md
// Use NoV for direct too instead of LoH as an optimization (NoV is light invariant).
half NoV = saturate(dot(normalWS, viewDirectionWS));
// Use slightly simpler fresnelTerm (Pow4 vs Pow5) as a small optimization.
// It is matching fresnel used in the GI/Env, so should produce a consistent clear coat blend (env vs. direct)
half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * Pow4(1.0 - NoV);
brdf = brdf * (1.0 - clearCoatMask * coatFresnel) + brdfCoat * clearCoatMask;
#endif // _CLEARCOAT
}
#endif // _SPECULARHIGHLIGHTS_OFF
return brdf * radiance;
}
half3 LightingPhysicallyBased(BRDFData brdfData, BRDFData brdfDataClearCoat, Light light, half3 normalWS, half3 viewDirectionWS, half clearCoatMask, bool specularHighlightsOff,
ToonShadingData toonShadingData)
{
return LightingPhysicallyBased(brdfData, brdfDataClearCoat, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, clearCoatMask, specularHighlightsOff, toonShadingData);
}
// Backwards compatibility
half3 LightingPhysicallyBased(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS,
ToonShadingData toonShadingData)
{
#ifdef _SPECULARHIGHLIGHTS_OFF
bool specularHighlightsOff = true;
#else
bool specularHighlightsOff = false;
#endif
const BRDFData noClearCoat = (BRDFData) 0;
return LightingPhysicallyBased(brdfData, noClearCoat, light, normalWS, viewDirectionWS, 0.0, specularHighlightsOff, toonShadingData);
}
half3 LightingPhysicallyBased(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS,
ToonShadingData toonShadingData)
{
Light light;
light.color = lightColor;
light.direction = lightDirectionWS;
light.distanceAttenuation = lightAttenuation;
light.shadowAttenuation = 1;
return LightingPhysicallyBased(brdfData, light, normalWS, viewDirectionWS, toonShadingData);
}
half3 LightingPhysicallyBased(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS, bool specularHighlightsOff,
ToonShadingData toonShadingData)
{
const BRDFData noClearCoat = (BRDFData) 0;
return LightingPhysicallyBased(brdfData, noClearCoat, light, normalWS, viewDirectionWS, 0.0, specularHighlightsOff, toonShadingData);
}
half3 LightingPhysicallyBased(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS, bool specularHighlightsOff,
ToonShadingData toonShadingData)
{
Light light;
light.color = lightColor;
light.direction = lightDirectionWS;
light.distanceAttenuation = lightAttenuation;
light.shadowAttenuation = 1;
return LightingPhysicallyBased(brdfData, light, viewDirectionWS, specularHighlightsOff, specularHighlightsOff, toonShadingData);
}
half3 VertexLighting(float3 positionWS, half3 normalWS)
{
half3 vertexLightColor = half3(0.0, 0.0, 0.0);
#ifdef _ADDITIONAL_LIGHTS_VERTEX
uint lightsCount = GetAdditionalLightsCount();
uint meshRenderingLayers = GetMeshRenderingLayer();
LIGHT_LOOP_BEGIN(lightsCount)
Light light = GetAdditionalLight(lightIndex, positionWS);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
half3 lightColor = light.color * light.distanceAttenuation;
vertexLightColor += LightingLambert(lightColor, light.direction, normalWS);
}
LIGHT_LOOP_END
#endif
return vertexLightColor;
}
struct LightingData
{
half3 giColor;
half3 mainLightColor;
half3 additionalLightsColor;
half3 vertexLightingColor;
half3 emissionColor;
};
half3 CalculateLightingColor(LightingData lightingData, half3 albedo)
{
half3 lightingColor = 0;
if (IsOnlyAOLightingFeatureEnabled())
{
return lightingData.giColor; // Contains white + AO
}
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_GLOBAL_ILLUMINATION))
{
lightingColor += lightingData.giColor;
}
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_MAIN_LIGHT))
{
lightingColor += lightingData.mainLightColor;
}
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_ADDITIONAL_LIGHTS))
{
lightingColor += lightingData.additionalLightsColor;
}
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_VERTEX_LIGHTING))
{
lightingColor += lightingData.vertexLightingColor;
}
lightingColor *= albedo;
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_EMISSION))
{
lightingColor += lightingData.emissionColor;
}
return lightingColor;
}
half4 CalculateFinalColor(LightingData lightingData, half alpha)
{
half3 finalColor = CalculateLightingColor(lightingData, 1);
return half4(finalColor, alpha);
}
half4 CalculateFinalColor(LightingData lightingData, half3 albedo, half alpha, float fogCoord)
{
#if defined(_FOG_FRAGMENT)
#if (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
float viewZ = -fogCoord;
float nearToFarZ = max(viewZ - _ProjectionParams.y, 0);
half fogFactor = ComputeFogFactorZ0ToFar(nearToFarZ);
#else
half fogFactor = 0;
#endif
#else
half fogFactor = fogCoord;
#endif
half3 lightingColor = CalculateLightingColor(lightingData, albedo);
half3 finalColor = MixFog(lightingColor, fogFactor);
return half4(finalColor, alpha);
}
LightingData CreateLightingData(InputData inputData, SurfaceData surfaceData)
{
LightingData lightingData;
lightingData.giColor = inputData.bakedGI;
lightingData.emissionColor = surfaceData.emission;
lightingData.vertexLightingColor = 0;
lightingData.mainLightColor = 0;
lightingData.additionalLightsColor = 0;
return lightingData;
}
half3 CalculateBlinnPhong(Light light, InputData inputData, SurfaceData surfaceData, ToonShadingData toonShadingData)
{
half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
float modifiedLightIntensity = CalculateCellShadingPartitioning(light.direction, toonShadingData);
//half3 lightDiffuseColor = LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS, modifiedLightIntensity);
half3 lightDiffuseColor = attenuatedLightColor * modifiedLightIntensity;
half3 lightSpecularColor = half3(0, 0, 0);
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
half smoothness = exp2(10 * surfaceData.smoothness + 1);
lightSpecularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, half4(surfaceData.specular, 1), smoothness, toonShadingData);
#endif
#if _ALPHAPREMULTIPLY_ON
return lightDiffuseColor * surfaceData.albedo * surfaceData.alpha + lightSpecularColor;
#else
return lightDiffuseColor * surfaceData.albedo + lightSpecularColor;
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Fragment Functions //
// Used by ShaderGraph and others builtin renderers //
///////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// PBR lighting...
////////////////////////////////////////////////////////////////////////////////
half4 UniversalFragmentPBR(InputData inputData, SurfaceData surfaceData, ToonShadingData toonShadingData)
{
#if defined(_SPECULARHIGHLIGHTS_OFF)
bool specularHighlightsOff = true;
#else
bool specularHighlightsOff = false;
#endif
BRDFData brdfData;
// NOTE: can modify "surfaceData"...
InitializeBRDFData(surfaceData, brdfData);
#if defined(DEBUG_DISPLAY)
half4 debugColor;
if (CanDebugOverrideOutputColor(inputData, surfaceData, brdfData, debugColor))
{
return debugColor;
}
#endif
// Clear-coat calculation...
BRDFData brdfDataClearCoat = CreateClearCoatBRDFData(surfaceData, brdfData);
half4 shadowMask = CalculateShadowMask(inputData);
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
//if (toonShadingData.shadingAffectByNormalMap == 0)
//{
// aoFactor.indirectAmbientOcclusion = 1;
// aoFactor.directAmbientOcclusion = 1;
//}
uint meshRenderingLayers = GetMeshRenderingLayer();
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
// NOTE: We don't apply AO to the GI here because it's done in the lighting calculation below...
half3 normal = inputData.normalWS;
if (toonShadingData.enableToonShading == 1 && toonShadingData.shadingAffectByNormalMap == 0)
{
normal = toonShadingData.normalWSNoMap;
}
MixRealtimeAndBakedGI(mainLight, normal, inputData.bakedGI);
LightingData lightingData = CreateLightingData(inputData, surfaceData);
lightingData.giColor = GlobalIllumination(brdfData, brdfDataClearCoat, surfaceData.clearCoatMask,
inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS,
normal, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
{
lightingData.mainLightColor = LightingPhysicallyBased(brdfData, brdfDataClearCoat,
mainLight,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff, toonShadingData);
}
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
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, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff, toonShadingData);
}
}
#endif
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff, toonShadingData);
}
LIGHT_LOOP_END
#endif
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
lightingData.vertexLightingColor += inputData.vertexLighting * brdfData.diffuse;
#endif
#if REAL_IS_HALF
// Clamp any half.inf+ to HALF_MAX
return min(CalculateFinalColor(lightingData, surfaceData.alpha), HALF_MAX);
#else
return CalculateFinalColor(lightingData, surfaceData.alpha);
#endif
}
// Deprecated: Use the version which takes "SurfaceData" instead of passing all of these arguments...
half4 UniversalFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
half smoothness, half occlusion, half3 emission, half alpha, ToonShadingData toonShadingData)
{
SurfaceData surfaceData;
surfaceData.albedo = albedo;
surfaceData.specular = specular;
surfaceData.metallic = metallic;
surfaceData.smoothness = smoothness;
surfaceData.normalTS = half3(0, 0, 1);
surfaceData.emission = emission;
surfaceData.occlusion = occlusion;
surfaceData.alpha = alpha;
surfaceData.clearCoatMask = 0;
surfaceData.clearCoatSmoothness = 1;
return UniversalFragmentPBR(inputData, surfaceData, toonShadingData);
}
////////////////////////////////////////////////////////////////////////////////
/// Phong lighting...
////////////////////////////////////////////////////////////////////////////////
half4 UniversalFragmentBlinnPhong(InputData inputData, SurfaceData surfaceData, ToonShadingData toonShadingData)
{
#if defined(DEBUG_DISPLAY)
half4 debugColor;
if (CanDebugOverrideOutputColor(inputData, surfaceData, debugColor))
{
return debugColor;
}
#endif
uint meshRenderingLayers = GetMeshRenderingLayer();
half4 shadowMask = CalculateShadowMask(inputData);
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, aoFactor);
inputData.bakedGI *= surfaceData.albedo;
LightingData lightingData = CreateLightingData(inputData, surfaceData);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
{
lightingData.mainLightColor += CalculateBlinnPhong(mainLight, inputData, surfaceData, toonShadingData);
}
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
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, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += CalculateBlinnPhong(light, inputData, surfaceData, toonShadingData);
}
}
#endif
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += CalculateBlinnPhong(light, inputData, surfaceData, toonShadingData);
}
LIGHT_LOOP_END
#endif
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
lightingData.vertexLightingColor += inputData.vertexLighting * surfaceData.albedo;
#endif
return CalculateFinalColor(lightingData, surfaceData.alpha);
}
// Deprecated: Use the version which takes "SurfaceData" instead of passing all of these arguments...
half4 UniversalFragmentBlinnPhong(InputData inputData, half3 diffuse, half4 specularGloss, half smoothness, half3 emission, half alpha, half3 normalTS, ToonShadingData toonShadingData)
{
SurfaceData surfaceData;
surfaceData.albedo = diffuse;
surfaceData.alpha = alpha;
surfaceData.emission = emission;
surfaceData.metallic = 0;
surfaceData.occlusion = 1;
surfaceData.smoothness = smoothness;
surfaceData.specular = specularGloss.rgb;
surfaceData.clearCoatMask = 0;
surfaceData.clearCoatSmoothness = 1;
surfaceData.normalTS = normalTS;
return UniversalFragmentBlinnPhong(inputData, surfaceData, toonShadingData);
}
////////////////////////////////////////////////////////////////////////////////
/// Unlit
////////////////////////////////////////////////////////////////////////////////
half4 UniversalFragmentBakedLit(InputData inputData, SurfaceData surfaceData)
{
#if defined(DEBUG_DISPLAY)
half4 debugColor;
if (CanDebugOverrideOutputColor(inputData, surfaceData, debugColor))
{
return debugColor;
}
#endif
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
LightingData lightingData = CreateLightingData(inputData, surfaceData);
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_AMBIENT_OCCLUSION))
{
lightingData.giColor *= aoFactor.indirectAmbientOcclusion;
}
return CalculateFinalColor(lightingData, surfaceData.albedo, surfaceData.alpha, inputData.fogCoord);
}
// Deprecated: Use the version which takes "SurfaceData" instead of passing all of these arguments...
half4 UniversalFragmentBakedLit(InputData inputData, half3 color, half alpha, half3 normalTS)
{
SurfaceData surfaceData;
surfaceData.albedo = color;
surfaceData.alpha = alpha;
surfaceData.emission = half3(0, 0, 0);
surfaceData.metallic = 0;
surfaceData.occlusion = 1;
surfaceData.smoothness = 1;
surfaceData.specular = half3(0, 0, 0);
surfaceData.clearCoatMask = 0;
surfaceData.clearCoatSmoothness = 1;
surfaceData.normalTS = normalTS;
return UniversalFragmentBakedLit(inputData, surfaceData);
}
#endif