//-------------------------------------------------------------------------------------------------------------------------------- // Cartoon FX // (c) 2012-2020 Jean Moreno //-------------------------------------------------------------------------------------------------------------------------------- // Copy of URP specific variables needed for lighting // ================================================================================================================================ // Input.hlsl: // ================================================================================================================================ #if defined(SHADER_API_MOBILE) || (defined(SHADER_API_GLCORE) && !defined(SHADER_API_SWITCH)) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) // Workaround for bug on Nintendo Switch where SHADER_API_GLCORE is mistakenly defined #define MAX_VISIBLE_LIGHTS 32 #else #define MAX_VISIBLE_LIGHTS 256 #endif // -------------------------------- float4 _MainLightPosition; half4 _MainLightColor; // -------------------------------- half4 _AdditionalLightsCount; #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA StructuredBuffer _AdditionalLightsBuffer; StructuredBuffer _AdditionalLightsIndices; #else // GLES3 causes a performance regression in some devices when using CBUFFER. #ifndef SHADER_API_GLES3 CBUFFER_START(AdditionalLights) #endif float4 _AdditionalLightsPosition[MAX_VISIBLE_LIGHTS]; half4 _AdditionalLightsColor[MAX_VISIBLE_LIGHTS]; half4 _AdditionalLightsAttenuation[MAX_VISIBLE_LIGHTS]; half4 _AdditionalLightsSpotDir[MAX_VISIBLE_LIGHTS]; half4 _AdditionalLightsOcclusionProbes[MAX_VISIBLE_LIGHTS]; #ifndef SHADER_API_GLES3 CBUFFER_END #endif #endif // ================================================================================================================================ // UnityInput.hlsl: // ================================================================================================================================ half4 unity_LightData; half4 unity_LightIndices[2]; // -------------------------------- // ================================================================================================================================ // Macros.hlsl // ================================================================================================================================ #define HALF_MIN 6.103515625e-5 // 2^-14, the same value for 10, 11 and 16-bit: https://www.khronos.org/opengl/wiki/Small_Float_Formats // ================================================================================================================================ // Lighting.hlsl // ================================================================================================================================ // Abstraction over Light shading data. struct Light { half3 direction; half3 color; half distanceAttenuation; half shadowAttenuation; }; // Matches Unity Vanila attenuation // Attenuation smoothly decreases to light range. float DistanceAttenuation(float distanceSqr, half2 distanceAttenuation) { // We use a shared distance attenuation for additional directional and puctual lights // for directional lights attenuation will be 1 float lightAtten = rcp(distanceSqr); #if SHADER_HINT_NICE_QUALITY // Use the smoothing factor also used in the Unity lightmapper. half factor = distanceSqr * distanceAttenuation.x; half smoothFactor = saturate(1.0h - factor * factor); smoothFactor = smoothFactor * smoothFactor; #else // We need to smoothly fade attenuation to light range. We start fading linearly at 80% of light range // Therefore: // fadeDistance = (0.8 * 0.8 * lightRangeSq) // smoothFactor = (lightRangeSqr - distanceSqr) / (lightRangeSqr - fadeDistance) // We can rewrite that to fit a MAD by doing // distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr) // distanceSqr * distanceAttenuation.y + distanceAttenuation.z half smoothFactor = saturate(distanceSqr * distanceAttenuation.x + distanceAttenuation.y); #endif return lightAtten * smoothFactor; } half AngleAttenuation(half3 spotDirection, half3 lightDirection, half2 spotAttenuation) { // Spot Attenuation with a linear falloff can be defined as // (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle) // This can be rewritten as // invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle) // SdotL * invAngleRange + (-cosOuterAngle * invAngleRange) // SdotL * spotAttenuation.x + spotAttenuation.y // If we precompute the terms in a MAD instruction half SdotL = dot(spotDirection, lightDirection); half atten = saturate(SdotL * spotAttenuation.x + spotAttenuation.y); return atten * atten; } // Fills a light struct given a perObjectLightIndex Light GetAdditionalPerObjectLight(int perObjectLightIndex, float3 positionWS) { // Abstraction over Light input constants #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA float4 lightPositionWS = _AdditionalLightsBuffer[perObjectLightIndex].position; half3 color = _AdditionalLightsBuffer[perObjectLightIndex].color.rgb; half4 distanceAndSpotAttenuation = _AdditionalLightsBuffer[perObjectLightIndex].attenuation; half4 spotDirection = _AdditionalLightsBuffer[perObjectLightIndex].spotDirection; half4 lightOcclusionProbeInfo = _AdditionalLightsBuffer[perObjectLightIndex].occlusionProbeChannels; #else float4 lightPositionWS = _AdditionalLightsPosition[perObjectLightIndex]; half3 color = _AdditionalLightsColor[perObjectLightIndex].rgb; half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[perObjectLightIndex]; half4 spotDirection = _AdditionalLightsSpotDir[perObjectLightIndex]; half4 lightOcclusionProbeInfo = _AdditionalLightsOcclusionProbes[perObjectLightIndex]; #endif // Directional lights store direction in lightPosition.xyz and have .w set to 0.0. // This way the following code will work for both directional and punctual lights. float3 lightVector = lightPositionWS.xyz - positionWS * lightPositionWS.w; float distanceSqr = max(dot(lightVector, lightVector), HALF_MIN); half3 lightDirection = half3(lightVector * rsqrt(distanceSqr)); half attenuation = DistanceAttenuation(distanceSqr, distanceAndSpotAttenuation.xy) * AngleAttenuation(spotDirection.xyz, lightDirection, distanceAndSpotAttenuation.zw); Light light; light.direction = lightDirection; light.distanceAttenuation = attenuation; /// light.shadowAttenuation = AdditionalLightRealtimeShadow(perObjectLightIndex, positionWS); light.shadowAttenuation = 1; light.color = color; // In case we're using light probes, we can sample the attenuation from the `unity_ProbesOcclusion` #if defined(LIGHTMAP_ON) || defined(_MIXED_LIGHTING_SUBTRACTIVE) // First find the probe channel from the light. // Then sample `unity_ProbesOcclusion` for the baked occlusion. // If the light is not baked, the channel is -1, and we need to apply no occlusion. // probeChannel is the index in 'unity_ProbesOcclusion' that holds the proper occlusion value. int probeChannel = lightOcclusionProbeInfo.x; // lightProbeContribution is set to 0 if we are indeed using a probe, otherwise set to 1. half lightProbeContribution = lightOcclusionProbeInfo.y; half probeOcclusionValue = unity_ProbesOcclusion[probeChannel]; light.distanceAttenuation *= max(probeOcclusionValue, lightProbeContribution); #endif return light; } uint GetPerObjectLightIndexOffset() { #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA return unity_LightData.x; #else return 0; #endif } // Returns a per-object index given a loop index. // This abstract the underlying data implementation for storing lights/light indices int GetPerObjectLightIndex(uint index) { ///////////////////////////////////////////////////////////////////////////////////////////// // Structured Buffer Path / // / // Lights and light indices are stored in StructuredBuffer. We can just index them. / // Currently all non-mobile platforms take this path :( / // There are limitation in mobile GPUs to use SSBO (performance / no vertex shader support) / ///////////////////////////////////////////////////////////////////////////////////////////// #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA uint offset = unity_LightData.x; return _AdditionalLightsIndices[offset + index]; ///////////////////////////////////////////////////////////////////////////////////////////// // UBO path / // / // We store 8 light indices in float4 unity_LightIndices[2]; / // Due to memory alignment unity doesn't support int[] or float[] / // Even trying to reinterpret cast the unity_LightIndices to float[] won't work / // it will cast to float4[] and create extra register pressure. :( / ///////////////////////////////////////////////////////////////////////////////////////////// #elif !defined(SHADER_API_GLES) // since index is uint shader compiler will implement // div & mod as bitfield ops (shift and mask). // TODO: Can we index a float4? Currently compiler is // replacing unity_LightIndicesX[i] with a dp4 with identity matrix. // u_xlat16_40 = dot(unity_LightIndices[int(u_xlatu13)], ImmCB_0_0_0[u_xlati1]); // This increases both arithmetic and register pressure. return unity_LightIndices[index / 4][index % 4]; #else // Fallback to GLES2. No bitfield magic here :(. // We limit to 4 indices per object and only sample unity_4LightIndices0. // Conditional moves are branch free even on mali-400 // small arithmetic cost but no extra register pressure from ImmCB_0_0_0 matrix. half2 lightIndex2 = (index < 2.0h) ? unity_LightIndices[0].xy : unity_LightIndices[0].zw; half i_rem = (index < 2.0h) ? index : index - 2.0h; return (i_rem < 1.0h) ? lightIndex2.x : lightIndex2.y; #endif } // Fills a light struct given a loop i index. This will convert the i // index to a perObjectLightIndex Light GetAdditionalLight(uint i, float3 positionWS) { int perObjectLightIndex = GetPerObjectLightIndex(i); return GetAdditionalPerObjectLight(perObjectLightIndex, positionWS); } int GetAdditionalLightsCount() { // TODO: we need to expose in SRP api an ability for the pipeline cap the amount of lights // in the culling. This way we could do the loop branch with an uniform // This would be helpful to support baking exceeding lights in SH as well return min(_AdditionalLightsCount.x, unity_LightData.y); }