From 99d134e9d4835d1e2cfdf17156e1ca8c12e3ee88 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 4 Nov 2022 17:34:42 +0100 Subject: [PATCH] [unity] Lit Spine URP shaders now support `Forward+` rendering path, additional lights are no longer ignored. Closes #2173. --- CHANGELOG.md | 1 + .../Shaders/Include/Spine-Common-URP.hlsl | 34 ++++++ .../Include/Spine-Common-URP.hlsl.meta | 7 ++ .../Spine-SkeletonLit-ForwardPass-URP.hlsl | 53 +++++++-- .../Include/Spine-Sprite-ForwardPass-URP.hlsl | 109 ++++++++++++++---- .../Shaders/Spine-SkeletonLit-URP.shader | 3 + .../Shaders/Spine-Sprite-URP.shader | 3 + .../package.json | 2 +- 8 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl create mode 100644 spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index a3725b597..918cc1585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ * PMA textures now have `sRGB (Color Texture)` disabled by default, the preset template `PMATexturePreset.preset` has been adjusted accordingly. As PMA textures are only allowed with Gamma color space, `sRGB (Color Texture)` shall be disabled to prevent border artifacts when mipmaps are enabled. In Gamma color space having this setting disabled has no drawbacks, only benefits. * `SkeletonRenderTexture` and `SkeletonGraphicRenderTexture` components now support automatic down-scaling when required size on screen exceeds `Max Render Texture Size`. * Added `Spine/SkeletonGraphic Fill` shader to provide functionality of `Spine/Skeleton Fill` shader for `SkeletonGraphic`. + * Lit Spine URP shaders (`Universal Render Pipeline/Spine/Sprite` and `Universal Render Pipeline/Spine/Skeleton Lit`) now support `Forward+` rendering path as introduced by Unity 2022.2 and URP version 14. * **Breaking changes** * Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead. diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl new file mode 100644 index 000000000..f338e29c1 --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl @@ -0,0 +1,34 @@ +#ifndef SPINE_COMMON_URP_INCLUDED +#define SPINE_COMMON_URP_INCLUDED + +#ifdef _LIGHT_LAYERS +uint GetMeshRenderingLayerBackwardsCompatible() +{ + return GetMeshRenderingLayer(); +} +#else +uint GetMeshRenderingLayerBackwardsCompatible() +{ + return 0; +} +#endif + +#if USE_FORWARD_PLUS +// note: LIGHT_LOOP_BEGIN accesses inputData.normalizedScreenSpaceUV and inputData.positionWS. +#define LIGHT_LOOP_BEGIN_SPINE LIGHT_LOOP_BEGIN +#define LIGHT_LOOP_END_SPINE LIGHT_LOOP_END +#elif !_USE_WEBGL1_LIGHTS +#define LIGHT_LOOP_BEGIN_SPINE(lightCount) \ + for (uint lightIndex = 0u; lightIndex < lightCount; ++lightIndex) { + +#define LIGHT_LOOP_END_SPINE } +#else +// WebGL 1 doesn't support variable for loop conditions +#define LIGHT_LOOP_BEGIN_SPINE(lightCount) \ + for (int lightIndex = 0; lightIndex < _WEBGL1_MAX_LIGHTS; ++lightIndex) { \ + if (lightIndex >= (int)lightCount) break; + +#define LIGHT_LOOP_END_SPINE } +#endif + +#endif diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl.meta b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl.meta new file mode 100644 index 000000000..37c1bb69b --- /dev/null +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c898d902be2aa254fac1da6de09f0a87 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-SkeletonLit-ForwardPass-URP.hlsl b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-SkeletonLit-ForwardPass-URP.hlsl index 34cb3528c..e8114767b 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-SkeletonLit-ForwardPass-URP.hlsl +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-SkeletonLit-ForwardPass-URP.hlsl @@ -5,6 +5,7 @@ #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "SpineCoreShaders/Spine-Common.cginc" +#include "Spine-Common-URP.hlsl" #if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF) #define SKELETONLIT_RECEIVE_SHADOWS @@ -32,7 +33,19 @@ struct VertexOutput { UNITY_VERTEX_OUTPUT_STEREO }; -half3 LightweightLightVertexSimplified(float3 positionWS, half3 normalWS, out half3 shadowedColor) { +half3 ProcessLight(float3 positionWS, half3 normalWS, uint meshRenderingLayers, int lightIndex) +{ + Light light = GetAdditionalLight(lightIndex, positionWS); +#ifdef _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 LightweightLightVertexSimplified(float3 positionWS, float3 positionCS, half3 normalWS, out half3 shadowedColor) { Light mainLight = GetMainLight(); half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation); half3 mainLightColor = LightingLambert(attenuatedLightColor, mainLight.direction, normalWS); @@ -40,12 +53,26 @@ half3 LightweightLightVertexSimplified(float3 positionWS, half3 normalWS, out ha half3 additionalLightColor = half3(0, 0, 0); // Note: we don't add any lighting in the fragment shader, thus we include both variants below #if defined(_ADDITIONAL_LIGHTS) || defined(_ADDITIONAL_LIGHTS_VERTEX) - for (int i = 0; i < GetAdditionalLightsCount(); ++i) + uint meshRenderingLayers = GetMeshRenderingLayerBackwardsCompatible(); +#if USE_FORWARD_PLUS + for (uint lightIndex = 0; lightIndex < min(_AdditionalLightsDirectionalCount, MAX_VISIBLE_LIGHTS); lightIndex++) { - Light light = GetAdditionalLight(i, positionWS); - half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation); - additionalLightColor += LightingLambert(attenuatedLightColor, light.direction, normalWS); + additionalLightColor += ProcessLight(positionWS, normalWS, meshRenderingLayers, lightIndex); } +#endif + int pixelLightCount = GetAdditionalLightsCount(); + // fill out InputData struct + InputData inputData; // LIGHT_LOOP_BEGIN macro requires InputData struct in USE_FORWARD_PLUS branch + inputData.positionWS = positionWS; +#if defined(_ADDITIONAL_LIGHTS) && USE_FORWARD_PLUS + inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(positionCS); +#else + inputData.normalizedScreenSpaceUV = 0; +#endif + + LIGHT_LOOP_BEGIN_SPINE(pixelLightCount) + additionalLightColor += ProcessLight(positionWS, normalWS, meshRenderingLayers, lightIndex); + LIGHT_LOOP_END_SPINE #endif shadowedColor = additionalLightColor; return mainLightColor + additionalLightColor; @@ -62,6 +89,7 @@ VertexOutput vert(appdata v) { half3 normalWS = normalize(mul((float3x3)unity_ObjectToWorld, fixedNormal)); o.uv0 = v.uv0; o.pos = TransformWorldToHClip(positionWS); + float3 positionCS = o.pos; #ifdef _DOUBLE_SIDED_LIGHTING // unfortunately we have to compute the sign here in the vertex shader @@ -83,7 +111,7 @@ VertexOutput vert(appdata v) { } #endif // !defined(_LIGHT_AFFECTS_ADDITIVE) - color.rgb *= LightweightLightVertexSimplified(positionWS, normalWS, shadowedColor); + color.rgb *= LightweightLightVertexSimplified(positionWS, positionCS, normalWS, shadowedColor); #if defined(SKELETONLIT_RECEIVE_SHADOWS) o.shadowedColor = shadowedColor; #endif @@ -103,7 +131,12 @@ VertexOutput vert(appdata v) { return o; } -half4 frag(VertexOutput i) : SV_Target{ +half4 frag(VertexOutput i +#ifdef _WRITE_RENDERING_LAYERS + , out float4 outRenderingLayers : SV_Target1 +#endif +) : SV_Target0 +{ half4 tex = tex2D(_MainTex, i.uv0); #if defined(_STRAIGHT_ALPHA_INPUT) tex.rgb *= tex.a; @@ -116,6 +149,12 @@ half4 frag(VertexOutput i) : SV_Target{ half shadowAttenuation = MainLightRealtimeShadow(i.shadowCoord); i.color.rgb = lerp(i.shadowedColor, i.color.rgb, shadowAttenuation); #endif + +#ifdef _WRITE_RENDERING_LAYERS + uint renderingLayers = GetMeshRenderingLayerBackwardsCompatible(); + outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); +#endif + return tex * i.color; } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl index e526d3574..47a67fd41 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl @@ -5,6 +5,7 @@ #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "SpineCoreShaders/SpriteLighting.cginc" #include "SpineCoreShaders/Spine-Common.cginc" +#include "Spine-Common-URP.hlsl" #if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS) #define NEEDS_POSITION_WS @@ -37,6 +38,10 @@ struct VertexOutputLWRP #if defined(NEEDS_POSITION_WS) float4 positionWS : TEXCOORD8; #endif +#if defined(_ADDITIONAL_LIGHTS) + float4 positionCS : TEXCOORD9; +#endif + UNITY_VERTEX_OUTPUT_STEREO }; @@ -90,6 +95,20 @@ half3 LightingLambertRamped(half3 lightColor, float attenuation, half3 lightDir, #if defined(SPECULAR) +half3 ProcessLightPBRSimplified(InputData inputData, BRDFData brdfData, half4 shadowMask, uint meshRenderingLayers, int lightIndex) +{ +#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) + Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask); +#else + Light light = GetAdditionalLight(lightIndex, inputData.positionWS); +#endif +#ifdef _LIGHT_LAYERS + if (!IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) + return half3(0, 0, 0); +#endif + return LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS); +} + half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha, half metallic, half3 specular, half smoothness, half3 emission, half4 vertexColor) { @@ -114,18 +133,26 @@ half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha #endif // _MAIN_LIGHT_VERTEX #ifdef _ADDITIONAL_LIGHTS - int pixelLightCount = GetAdditionalLightsCount(); - for (int i = 0; i < pixelLightCount; ++i) - { + uint meshRenderingLayers = GetMeshRenderingLayerBackwardsCompatible(); + #if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) - half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); - Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask); + half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); #else - Light light = GetAdditionalLight(i, inputData.positionWS); + half4 shadowMask = half4(1, 1, 1, 1); #endif - finalColor += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS); + +#if USE_FORWARD_PLUS + for (uint lightIndex = 0; lightIndex < min(_AdditionalLightsDirectionalCount, MAX_VISIBLE_LIGHTS); lightIndex++) + { + finalColor += ProcessLightPBRSimplified(inputData, brdfData, shadowMask, meshRenderingLayers, lightIndex); } #endif + int pixelLightCount = GetAdditionalLightsCount(); + LIGHT_LOOP_BEGIN_SPINE(pixelLightCount) + finalColor += ProcessLightPBRSimplified(inputData, brdfData, shadowMask, meshRenderingLayers, lightIndex); + LIGHT_LOOP_END_SPINE + +#endif // _ADDITIONAL_LIGHTS #ifdef _ADDITIONAL_LIGHTS_VERTEX finalColor += inputData.vertexLighting * brdfData.diffuse; @@ -136,6 +163,28 @@ half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha #else // !SPECULAR +half3 ProcessLightLambert(InputData inputData, half4 shadowMask, uint meshRenderingLayers, int lightIndex) +{ +#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) + Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask); +#else + Light light = GetAdditionalLight(lightIndex, inputData.positionWS); +#endif + +#ifdef _LIGHT_LAYERS + if (!IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) + return half3(0, 0, 0); +#endif + + half3 attenuation = (light.distanceAttenuation * light.shadowAttenuation); + half3 attenuatedLightColor = light.color * attenuation; +#ifndef _DIFFUSE_RAMP + return LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS); +#else + return LightingLambertRamped(light.color, attenuation, light.direction, inputData.normalWS); +#endif +} + half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiffuseAlpha, half3 emission, half4 vertexColor) { half4 diffuse = texDiffuseAlpha * vertexColor; @@ -160,23 +209,24 @@ half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiff #endif // _MAIN_LIGHT_VERTEX #ifdef _ADDITIONAL_LIGHTS - int pixelLightCount = GetAdditionalLightsCount(); - for (int i = 0; i < pixelLightCount; ++i) - { + uint meshRenderingLayers = GetMeshRenderingLayerBackwardsCompatible(); + #if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) - half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); - Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask); + half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); #else - Light light = GetAdditionalLight(i, inputData.positionWS); -#endif - half3 attenuation = (light.distanceAttenuation * light.shadowAttenuation); - half3 attenuatedLightColor = light.color * attenuation; -#ifndef _DIFFUSE_RAMP - diffuseLighting += LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS); -#else - diffuseLighting += LightingLambertRamped(light.color, attenuation, light.direction, inputData.normalWS); + half4 shadowMask = half4(1, 1, 1, 1); #endif +#if USE_FORWARD_PLUS + for (uint lightIndex = 0; lightIndex < min(_AdditionalLightsDirectionalCount, MAX_VISIBLE_LIGHTS); lightIndex++) + { + diffuseLighting += ProcessLightLambert(inputData, shadowMask, meshRenderingLayers, lightIndex); } +#endif + int pixelLightCount = GetAdditionalLightsCount(); + LIGHT_LOOP_BEGIN(pixelLightCount) + diffuseLighting += ProcessLightLambert(inputData, shadowMask, meshRenderingLayers, lightIndex); + LIGHT_LOOP_END + #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX diffuseLighting += inputData.vertexLighting; @@ -209,6 +259,9 @@ VertexOutputLWRP ForwardPassVertexSprite(VertexInput input) #if defined(NEEDS_POSITION_WS) output.positionWS = float4(positionWS, 1); #endif +#if defined(_ADDITIONAL_LIGHTS) + output.positionCS = output.pos; +#endif half3 normalWS = calculateSpriteWorldNormal(input, -backFaceSign); output.normalWorld.xyz = normalWS; @@ -235,7 +288,11 @@ VertexOutputLWRP ForwardPassVertexSprite(VertexInput input) return output; } -half4 ForwardPassFragmentSprite(VertexOutputLWRP input) : SV_Target +half4 ForwardPassFragmentSprite(VertexOutputLWRP input +#ifdef _WRITE_RENDERING_LAYERS + , out float4 outRenderingLayers : SV_Target1 +#endif +) : SV_Target0 { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); @@ -271,6 +328,11 @@ half4 ForwardPassFragmentSprite(VertexOutputLWRP input) : SV_Target #if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS) inputData.positionWS = input.positionWS.rgb; #endif +#if defined(_ADDITIONAL_LIGHTS) && USE_FORWARD_PLUS + inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS); +#else + inputData.normalizedScreenSpaceUV = 0; +#endif #if defined(SPECULAR) half2 metallicGloss = getMetallicGloss(input.texcoord.xy); @@ -294,6 +356,11 @@ half4 ForwardPassFragmentSprite(VertexOutputLWRP input) : SV_Target COLORISE(pixel) APPLY_FOG_LWRP(pixel, input.fogFactorAndVertexLight.x) +#ifdef _WRITE_RENDERING_LAYERS + uint renderingLayers = GetMeshRenderingLayerBackwardsCompatible(); + outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); +#endif + return pixel; } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-SkeletonLit-URP.shader b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-SkeletonLit-URP.shader index 348a15287..132aa0e8a 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-SkeletonLit-URP.shader +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-SkeletonLit-URP.shader @@ -47,6 +47,9 @@ #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _LIGHT_AFFECTS_ADDITIVE + // Farward+ renderer keywords + #pragma multi_compile _ _FORWARD_PLUS + #pragma multi_compile_fragment _ _WRITE_RENDERING_LAYERS // ------------------------------------- // Unity defined keywords diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader index 79118a0f1..c83084eaf 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader @@ -107,6 +107,9 @@ Shader "Universal Render Pipeline/Spine/Sprite" #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE + // Farward+ renderer keywords + #pragma multi_compile _ _FORWARD_PLUS + #pragma multi_compile_fragment _ _WRITE_RENDERING_LAYERS // ------------------------------------- // Unity defined keywords diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json index ac2360380..da528fef7 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.urp-shaders", "displayName": "Spine Universal RP Shaders", "description": "This plugin provides universal render pipeline (URP) shaders for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.1.5", + "version": "4.1.6", "unity": "2019.3", "author": { "name": "Esoteric Software",