#ifndef BADDOG_AREA_SPINE_LIT_PASSES_INCLUDED #define BADDOG_AREA_SPINE_LIT_PASSES_INCLUDED #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #undef LIGHTMAP_ON CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; half _Cutoff; half4 _Color; half4 _Black; CBUFFER_END sampler2D _MainTex; inline half3 BGSpine_GammaToTargetSpace(half3 gammaColor) { #if UNITY_COLORSPACE_GAMMA return gammaColor; #else return SRGBToLinear(gammaColor); #endif } inline half4 BGSpine_PMAGammaToTargetSpace(half4 gammaPMAColor) { #if UNITY_COLORSPACE_GAMMA return gammaPMAColor; #else return gammaPMAColor.a == 0 ? half4(SRGBToLinear(gammaPMAColor.rgb), gammaPMAColor.a) : half4(SRGBToLinear(gammaPMAColor.rgb / gammaPMAColor.a) * gammaPMAColor.a, gammaPMAColor.a); #endif } float4 BGSpine_fragTintedColor(float4 texColor, float3 darkTintColor, float4 lightTintColorPMA, float lightColorAlpha, float darkColorAlpha) { float a = texColor.a * lightTintColorPMA.a; #if !defined(_STRAIGHT_ALPHA_INPUT) float3 texDarkColor = texColor.a - texColor.rgb; #else float3 texDarkColor = (1 - texColor.rgb); #endif float3 darkColor = texDarkColor * darkTintColor.rgb * lightColorAlpha; float3 lightColor = texColor.rgb * lightTintColorPMA.rgb; float4 fragColor = float4(darkColor + lightColor, a); #if defined(_STRAIGHT_ALPHA_INPUT) fragColor.rgb *= texColor.a; #endif #if defined(_DARK_COLOR_ALPHA_ADDITIVE) fragColor.a = a * (1 - darkColorAlpha); #endif return fragColor; } #if defined(_WRITE_RENDERING_LAYERS) || defined(_LIGHT_LAYERS) uint BGSpine_GetMeshRenderingLayerBackwardsCompatible() { // URP 14+ exposes GetMeshRenderingLayer(). Older: GetMeshRenderingLightLayer(). // We prefer the 14+ API when present. #if defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) // No reliable version macro on all targets; best-effort fallback. return 0; #else #if defined(UNIVERSAL_REALTIME_LIGHTS_INCLUDED) // In URP 17 this exists. return GetMeshRenderingLayer(); #else return 0; #endif #endif } #else uint BGSpine_GetMeshRenderingLayerBackwardsCompatible() { return 0; } #endif // ----------------------------------------------------------------------------- // ShadowCaster pass (names kept to match existing shader pragmas) // ----------------------------------------------------------------------------- #if defined(BGAREA_SPINE_PASS_SHADOWCASTER) #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" float3 _LightDirection; struct AttributesSpine { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 vertexColor : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VaryingsSpine { float4 positionCS : SV_POSITION; float4 texcoordAndAlpha : TEXCOORD0; }; float4 GetShadowPositionHClip(float3 positionOS, half3 normalWS) { float3 positionWS = TransformObjectToWorld(positionOS); float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection)); #if UNITY_REVERSED_Z positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); #else positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); #endif return positionCS; } VaryingsSpine ShadowPassVertexSkeletonLit(AttributesSpine input) { VaryingsSpine output; UNITY_SETUP_INSTANCE_ID(input); output.texcoordAndAlpha.xyz = float3(TRANSFORM_TEX(input.texcoord, _MainTex).xy, 0); half3 fixedNormalOS = half3(0, 0, -1); half3 normalWS = normalize(TransformObjectToWorldNormal(fixedNormalOS)); #ifdef _DOUBLE_SIDED_LIGHTING half3 viewDirWS = UNITY_MATRIX_V[2].xyz; half faceSign = sign(dot(viewDirWS, normalWS)); normalWS *= faceSign; #endif output.positionCS = GetShadowPositionHClip(input.positionOS.xyz, normalWS); output.texcoordAndAlpha.a = input.vertexColor.a; return output; } half4 ShadowPassFragmentSkeletonLit(VaryingsSpine input) : SV_TARGET { fixed4 texureColor = tex2D(_MainTex, input.texcoordAndAlpha.xy); clip(texureColor.a * input.texcoordAndAlpha.a - _Cutoff); return 0; } #endif // BGAREA_SPINE_PASS_SHADOWCASTER // ----------------------------------------------------------------------------- // DepthOnly pass // ----------------------------------------------------------------------------- #if defined(BGAREA_SPINE_PASS_DEPTHONLY) #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct AttributesSpine { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 vertexColor : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VaryingsSpine { float4 positionCS : SV_POSITION; float4 texcoordAndAlpha : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; VaryingsSpine DepthOnlyVertex(AttributesSpine input) { VaryingsSpine output = (VaryingsSpine)0; UNITY_SETUP_INSTANCE_ID(input); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); output.texcoordAndAlpha.xyz = float3(TRANSFORM_TEX(input.texcoord, _MainTex).xy, 0); output.texcoordAndAlpha.a = input.vertexColor.a; output.positionCS = TransformObjectToHClip(input.positionOS.xyz); return output; } half4 DepthOnlyFragment(VaryingsSpine input) : SV_TARGET { fixed4 texureColor = tex2D(_MainTex, input.texcoordAndAlpha.xy); clip(texureColor.a * input.texcoordAndAlpha.a - _Cutoff); return input.positionCS.z; } #endif // BGAREA_SPINE_PASS_DEPTHONLY // ----------------------------------------------------------------------------- // DepthNormals pass // ----------------------------------------------------------------------------- #if defined(BGAREA_SPINE_PASS_DEPTHNORMALS) #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct AttributesSpine { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 vertexColor : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VaryingsSpine { float3 normalWS : NORMAL; float4 positionCS : SV_POSITION; float4 texcoordAndAlpha : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; VaryingsSpine DepthNormalsVertex(AttributesSpine input) { VaryingsSpine output = (VaryingsSpine)0; UNITY_SETUP_INSTANCE_ID(input); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); half3 fixedNormal = half3(0, 0, -1); half3 normalWS = normalize(mul((float3x3)unity_ObjectToWorld, fixedNormal)); #ifdef _DOUBLE_SIDED_LIGHTING half3 viewDirWS = UNITY_MATRIX_V[2].xyz; half faceSign = sign(dot(viewDirWS, normalWS)); normalWS *= faceSign; #endif output.normalWS = normalWS; output.texcoordAndAlpha.xyz = float3(TRANSFORM_TEX(input.texcoord, _MainTex).xy, 0); output.texcoordAndAlpha.a = input.vertexColor.a; output.positionCS = TransformObjectToHClip(input.positionOS.xyz); return output; } void DepthNormalsFragment( VaryingsSpine input, out half4 outNormalWS : SV_Target0 #ifdef _WRITE_RENDERING_LAYERS , out float4 outRenderingLayers : SV_Target1 #endif ) { fixed4 texureColor = tex2D(_MainTex, input.texcoordAndAlpha.xy); clip(texureColor.a * input.texcoordAndAlpha.a - _Cutoff); float3 normalWS = input.normalWS; #if defined(_GBUFFER_NORMALS_OCT) float2 octNormalWS = PackNormalOctQuadEncode(normalWS); float2 remappedOctNormalWS = saturate(octNormalWS * 0.5 + 0.5); half3 packedNormalWS = PackFloat2To888(remappedOctNormalWS); outNormalWS = half4(packedNormalWS, 0.0); #else outNormalWS = half4(normalWS, 0.0); #endif #ifdef _WRITE_RENDERING_LAYERS uint renderingLayers = BGSpine_GetMeshRenderingLayerBackwardsCompatible(); outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); #endif } #endif // BGAREA_SPINE_PASS_DEPTHNORMALS // ----------------------------------------------------------------------------- // ForwardLit pass // ----------------------------------------------------------------------------- #if defined(BGAREA_SPINE_PASS_FORWARDLIT) #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" // Unity 6 / URP 17: _FORWARD_PLUS keywords/macros were deprecated in favor of _CLUSTER_LIGHT_LOOP. // Keep the existing Spine-derived code path compiling by mapping the deprecated macro to the new one. #if USE_CLUSTER_LIGHT_LOOP && !defined(FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK) #define FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK CLUSTER_LIGHT_LOOP_SUBTRACTIVE_LIGHT_CHECK #endif // Area light include (arealight package) #include "Packages/com.baddog.rendering.arealight/Shaders/Include/BGAreaLighting.hlsl" // Receive shadows for main light when requested #if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF) #define BGAREA_SPINE_RECEIVE_SHADOWS #endif // Fog keyword guard (mirrors your modified file) #if !(defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)) #undef _FOG #endif // Adaptive Probe Volumes (keep behavior close to Spine) #if !defined(DYNAMICLIGHTMAP_ON) && !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) && defined(__PROBEVOLUME_HLSL__) #define BGAREA_SPINE_USE_ADAPTIVE_PROBE_VOLUMES #endif struct appdata { float3 pos : POSITION; float3 normal : NORMAL; half4 color : COLOR; float2 uv0 : TEXCOORD0; #if defined(_TINT_BLACK_ON) float2 tintBlackRG : TEXCOORD1; float2 tintBlackB : TEXCOORD2; #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VertexOutput { half4 color : COLOR0; #if defined(_FOG) float3 uv0AndFog : TEXCOORD0; #else float2 uv0 : TEXCOORD0; #endif float4 pos : SV_POSITION; #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) float4 shadowCoord : TEXCOORD1; half3 shadowedColor : TEXCOORD2; #endif // Always provide position/normal for per-pixel area light and (optional) Forward+/APV. float3 positionWS : TEXCOORD3; half3 normalWS : TEXCOORD4; #if defined(_TINT_BLACK_ON) float3 darkColor : TEXCOORD5; #endif #if defined(BGAREA_SPINE_USE_ADAPTIVE_PROBE_VOLUMES) && defined(_ADAPTIVE_PROBE_VOLUMES_PER_PIXEL) float3 positionCS : TEXCOORD6; #endif UNITY_VERTEX_OUTPUT_STEREO }; #if defined(_FOG) #define BGSpine_PackedUV0(i) (i.uv0AndFog.xy) #define BGSpine_PackedFog(i) (i.uv0AndFog.z) #else #define BGSpine_PackedUV0(i) (i.uv0.xy) #endif half3 BGSpine_ProcessLight(float3 positionWS, half3 normalWS, uint meshRenderingLayers, int lightIndex) { Light light = GetAdditionalLight(lightIndex, positionWS); #ifdef USE_LIGHT_LAYERS if (!IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) return half3(0, 0, 0); #endif half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation); return LightingLambert(attenuatedLightColor, light.direction, normalWS); } half3 BGSpine_LightweightLightVertexSimplified(float3 positionWS, half3 normalWS, out half3 shadowedColor) { Light mainLight = GetMainLight(); half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation); half3 mainLightColor = LightingLambert(attenuatedLightColor, mainLight.direction, normalWS); half3 additionalLightColor = half3(0, 0, 0); // Note: we don't add any lighting in the fragment shader in non-Forward+ case. #if defined(_ADDITIONAL_LIGHTS) || defined(_ADDITIONAL_LIGHTS_VERTEX) uint meshRenderingLayers = BGSpine_GetMeshRenderingLayerBackwardsCompatible(); #if USE_FORWARD_PLUS || USE_CLUSTER_LIGHT_LOOP for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) { FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK additionalLightColor += BGSpine_ProcessLight(positionWS, normalWS, meshRenderingLayers, (int)lightIndex); } #else uint pixelLightCount = GetAdditionalLightsCount(); for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex) { additionalLightColor += BGSpine_ProcessLight(positionWS, normalWS, meshRenderingLayers, (int)lightIndex); } #endif #endif shadowedColor = additionalLightColor; return mainLightColor + additionalLightColor; } #if defined(_ADDITIONAL_LIGHTS) && (USE_FORWARD_PLUS || USE_CLUSTER_LIGHT_LOOP) half3 BGSpine_LightweightLightFragmentSimplified(float3 positionWS, float2 positionCS, half3 normalWS, out half3 shadowedColor) { half3 additionalLightColor = half3(0, 0, 0); shadowedColor = half3(0, 0, 0); // LIGHT_LOOP_BEGIN needs these fields. InputData inputData; inputData.positionWS = positionWS; inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(positionCS); uint meshRenderingLayers = BGSpine_GetMeshRenderingLayerBackwardsCompatible(); uint pixelLightCount = GetAdditionalLightsCount(); LIGHT_LOOP_BEGIN(pixelLightCount) additionalLightColor += BGSpine_ProcessLight(positionWS, normalWS, meshRenderingLayers, (int)lightIndex); LIGHT_LOOP_END return additionalLightColor; } #endif VertexOutput vert(appdata v) { VertexOutput o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); half4 color = BGSpine_PMAGammaToTargetSpace(v.color); float3 positionWS = TransformObjectToWorld(v.pos); half3 fixedNormal = half3(0, 0, -1); half3 normalWS = normalize(mul((float3x3)unity_ObjectToWorld, fixedNormal)); o.pos = TransformWorldToHClip(positionWS); #if defined(_FOG) half fogFactor = ComputeFogFactor(o.pos.z); BGSpine_PackedFog(o) = fogFactor; #endif BGSpine_PackedUV0(o) = v.uv0; #ifdef _DOUBLE_SIDED_LIGHTING // Match Spine behavior: flip normal based on view direction. half3 viewDirWS = UNITY_MATRIX_V[2].xyz; half faceSign = sign(dot(viewDirWS, normalWS)); normalWS *= faceSign; #endif o.positionWS = positionWS; o.normalWS = normalWS; #if defined(_TINT_BLACK_ON) color *= _Color; o.darkColor = BGSpine_GammaToTargetSpace(half3(v.tintBlackRG.r, v.tintBlackRG.g, v.tintBlackB.r)) + (_Black.rgb * v.color.a); #endif half3 shadowedColor; #if !defined(_LIGHT_AFFECTS_ADDITIVE) if (color.a == 0) { o.color = color; #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) o.shadowedColor = color.rgb; o.shadowCoord = float4(0, 0, 0, 0); #endif return o; } #endif color.rgb *= BGSpine_LightweightLightVertexSimplified(positionWS, normalWS, shadowedColor); // Ambient light via SH / APV / Lightmap macro (mirrors Spine; safe even if v.lightmapUV doesn't exist). half3 vertexSH; float4 ignoredProbeOcclusion; #if defined(OUTPUT_SH4) OUTPUT_SH4(positionWS, normalWS.xyz, GetWorldSpaceNormalizeViewDir(positionWS), vertexSH, ignoredProbeOcclusion); #else OUTPUT_SH(normalWS.xyz, vertexSH); #endif #if defined(BGAREA_SPINE_USE_ADAPTIVE_PROBE_VOLUMES) #if !defined(_ADAPTIVE_PROBE_VOLUMES_PER_PIXEL) half4 shadowMask = 1.0; half3 bakedGI = SAMPLE_GI(vertexSH, GetAbsolutePositionWS(positionWS), normalWS.xyz, GetWorldSpaceNormalizeViewDir(positionWS), o.pos.xy, ignoredProbeOcclusion, shadowMask) * v.color.a; #else half3 bakedGI = half3(0.0, 0.0, 0.0); o.positionCS = o.pos; #endif #else half3 bakedGI = SAMPLE_GI(v.lightmapUV, vertexSH, normalWS) * v.color.a; #endif color.rgb += bakedGI; o.color = color; #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) shadowedColor += bakedGI; o.shadowedColor = shadowedColor; VertexPositionInputs vertexInput; vertexInput.positionWS = positionWS; vertexInput.positionCS = o.pos; o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } half4 frag(VertexOutput i #ifdef USE_WRITE_RENDERING_LAYERS , out float4 outRenderingLayers : SV_Target1 #endif ) : SV_Target0 { half4 tex = tex2D(_MainTex, BGSpine_PackedUV0(i)); #if !defined(_TINT_BLACK_ON) && defined(_STRAIGHT_ALPHA_INPUT) tex.rgb *= tex.a; #endif // Optional per-pixel APV contribution (mirrors Spine pattern). #if defined(BGAREA_SPINE_USE_ADAPTIVE_PROBE_VOLUMES) && defined(_ADAPTIVE_PROBE_VOLUMES_PER_PIXEL) half3 vertexSH; float4 ignoredProbeOcclusion; OUTPUT_SH4(i.positionWS, i.normalWS.xyz, GetWorldSpaceNormalizeViewDir(i.positionWS), vertexSH, ignoredProbeOcclusion); half4 shadowMask = 1.0; half3 bakedGI = SAMPLE_GI(vertexSH, GetAbsolutePositionWS(i.positionWS), i.normalWS.xyz, GetWorldSpaceNormalizeViewDir(i.positionWS), i.positionCS.xy, ignoredProbeOcclusion, shadowMask) * i.color.a; i.color.rgb += bakedGI; #endif if (i.color.a == 0) { #if defined(_TINT_BLACK_ON) return BGSpine_fragTintedColor(tex, i.darkColor, i.color, _Color.a, _Black.a); #else return tex * i.color; #endif } // Forward+/Cluster additional lights are processed in fragment. #if defined(_ADDITIONAL_LIGHTS) && (USE_FORWARD_PLUS || USE_CLUSTER_LIGHT_LOOP) half3 shadowedColor; i.color.rgb += BGSpine_LightweightLightFragmentSimplified(i.positionWS, i.pos.xy, i.normalWS, shadowedColor); #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) i.shadowedColor += shadowedColor; #endif #endif // Area light contribution (matches BadDog arealight design: independent loop over compact area buffer). #if defined(_ENABLE_BG_AREA_LIGHTING) half3 viewDirWS = GetWorldSpaceNormalizeViewDir(i.positionWS); half3 areaRadiance = EvaluateAreaLight( i.positionWS, i.normalWS, viewDirWS, half3(1.0, 1.0, 1.0), // brdfDiffuse: treat as radiance term, will be multiplied by tex later. half3(0.0, 0.0, 0.0), // brdfSpecular: Spine lit is diffuse-only by default. 1.0, // perceptualRoughness float3(0.0, 0.0, 0.0), // fresnel0 true // specularHighlightsOff ); i.color.rgb += areaRadiance; #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) i.shadowedColor += areaRadiance; #endif #endif #if defined(BGAREA_SPINE_RECEIVE_SHADOWS) half shadowAttenuation = MainLightRealtimeShadow(i.shadowCoord); i.color.rgb = lerp(i.shadowedColor, i.color.rgb, shadowAttenuation); #endif #ifdef USE_WRITE_RENDERING_LAYERS uint renderingLayers = BGSpine_GetMeshRenderingLayerBackwardsCompatible(); outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); #endif #if defined(_TINT_BLACK_ON) half4 pixel = BGSpine_fragTintedColor(tex, i.darkColor, i.color, _Color.a, _Black.a); #else half4 pixel = tex * i.color; #endif #if defined(_FOG) pixel.rgb = MixFogColor(pixel.rgb, unity_FogColor.rgb * pixel.a, BGSpine_PackedFog(i)); #endif return pixel; } #endif // BGAREA_SPINE_PASS_FORWARDLIT #endif // BADDOG_AREA_SPINE_LIT_PASSES_INCLUDED