You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
14 KiB
350 lines
14 KiB
|
|
#ifndef UNIVERSAL_REALTIME_LIGHTS_INCLUDED |
|
#define UNIVERSAL_REALTIME_LIGHTS_INCLUDED |
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/AmbientOcclusion.hlsl" |
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl" |
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" |
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LightCookie/LightCookie.hlsl" |
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Clustering.hlsl" |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Light Layers / |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
// Note: we need to mask out only 8bits of the layer mask before encoding it as otherwise any value > 255 will map to all layers active if save in a buffer |
|
uint GetMeshRenderingLightLayer() |
|
{ |
|
#ifdef _LIGHT_LAYERS |
|
return (asuint(unity_RenderingLayer.x) & RENDERING_LIGHT_LAYERS_MASK) >> RENDERING_LIGHT_LAYERS_MASK_SHIFT; |
|
#else |
|
return DEFAULT_LIGHT_LAYERS; |
|
#endif |
|
} |
|
|
|
// Abstraction over Light shading data. |
|
struct Light |
|
{ |
|
half3 direction; |
|
half3 color; |
|
float distanceAttenuation; // full-float precision required on some platforms |
|
half shadowAttenuation; |
|
uint layerMask; |
|
}; |
|
|
|
// WebGL1 does not support the variable conditioned for loops used for additional lights |
|
#if !defined(_USE_WEBGL1_LIGHTS) && defined(UNITY_PLATFORM_WEBGL) && !defined(SHADER_API_GLES3) |
|
#define _USE_WEBGL1_LIGHTS 1 |
|
#define _WEBGL1_MAX_LIGHTS 8 |
|
#else |
|
#define _USE_WEBGL1_LIGHTS 0 |
|
#endif |
|
|
|
#if USE_CLUSTERED_LIGHTING |
|
#define LIGHT_LOOP_BEGIN(lightCount) \ |
|
ClusteredLightLoop cll = ClusteredLightLoopInit(inputData.normalizedScreenSpaceUV, inputData.positionWS); \ |
|
while (ClusteredLightLoopNextWord(cll)) { while (ClusteredLightLoopNextLight(cll)) { \ |
|
uint lightIndex = ClusteredLightLoopGetLightIndex(cll); |
|
#define LIGHT_LOOP_END } } |
|
#elif !_USE_WEBGL1_LIGHTS |
|
#define LIGHT_LOOP_BEGIN(lightCount) \ |
|
for (uint lightIndex = 0u; lightIndex < lightCount; ++lightIndex) { |
|
|
|
#define LIGHT_LOOP_END } |
|
#else |
|
// WebGL 1 doesn't support variable for loop conditions |
|
#define LIGHT_LOOP_BEGIN(lightCount) \ |
|
for (int lightIndex = 0; lightIndex < _WEBGL1_MAX_LIGHTS; ++lightIndex) { \ |
|
if (lightIndex >= (int)lightCount) break; |
|
|
|
#define LIGHT_LOOP_END } |
|
#endif |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Attenuation Functions / |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
// 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); |
|
float2 distanceAttenuationFloat = float2(distanceAttenuation); |
|
|
|
#if SHADER_HINT_NICE_QUALITY |
|
// Use the smoothing factor also used in the Unity lightmapper. |
|
half factor = half(distanceSqr * distanceAttenuationFloat.x); |
|
half smoothFactor = saturate(half(1.0) - 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 = half(saturate(distanceSqr * distanceAttenuationFloat.x + distanceAttenuationFloat.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; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// Light Abstraction // |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
Light GetMainLight() |
|
{ |
|
Light light; |
|
light.direction = half3(_MainLightPosition.xyz); |
|
#if USE_CLUSTERED_LIGHTING |
|
light.distanceAttenuation = 1.0; |
|
#else |
|
light.distanceAttenuation = unity_LightData.z; // unity_LightData.z is 1 when not culled by the culling mask, otherwise 0. |
|
#endif |
|
light.shadowAttenuation = 1.0; |
|
light.color = _MainLightColor.rgb; |
|
|
|
#ifdef _LIGHT_LAYERS |
|
light.layerMask = _MainLightLayerMask; |
|
#else |
|
light.layerMask = DEFAULT_LIGHT_LAYERS; |
|
#endif |
|
|
|
return light; |
|
} |
|
|
|
Light GetMainLight(float4 shadowCoord) |
|
{ |
|
Light light = GetMainLight(); |
|
light.shadowAttenuation = MainLightRealtimeShadow(shadowCoord); |
|
return light; |
|
} |
|
|
|
Light GetMainLight(float4 shadowCoord, float3 positionWS, half4 shadowMask) |
|
{ |
|
Light light = GetMainLight(); |
|
light.shadowAttenuation = MainLightShadow(shadowCoord, positionWS, shadowMask, _MainLightOcclusionProbes); |
|
|
|
#if defined(_LIGHT_COOKIES) |
|
real3 cookieColor = SampleMainLightCookie(positionWS); |
|
light.color *= cookieColor; |
|
#endif |
|
|
|
return light; |
|
} |
|
|
|
Light GetMainLight(InputData inputData, half4 shadowMask, AmbientOcclusionFactor aoFactor) |
|
{ |
|
Light light = GetMainLight(inputData.shadowCoord, inputData.positionWS, shadowMask); |
|
|
|
#if defined(_SCREEN_SPACE_OCCLUSION) && !defined(_SURFACE_TYPE_TRANSPARENT) |
|
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_AMBIENT_OCCLUSION)) |
|
{ |
|
light.color *= aoFactor.directAmbientOcclusion; |
|
} |
|
#endif |
|
|
|
return light; |
|
} |
|
|
|
// 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; |
|
#ifdef _LIGHT_LAYERS |
|
uint lightLayerMask = _AdditionalLightsBuffer[perObjectLightIndex].layerMask; |
|
#else |
|
uint lightLayerMask = DEFAULT_LIGHT_LAYERS; |
|
#endif |
|
|
|
#else |
|
float4 lightPositionWS = _AdditionalLightsPosition[perObjectLightIndex]; |
|
half3 color = _AdditionalLightsColor[perObjectLightIndex].rgb; |
|
half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[perObjectLightIndex]; |
|
half4 spotDirection = _AdditionalLightsSpotDir[perObjectLightIndex]; |
|
#ifdef _LIGHT_LAYERS |
|
uint lightLayerMask = asuint(_AdditionalLightsLayerMasks[perObjectLightIndex]); |
|
#else |
|
uint lightLayerMask = DEFAULT_LIGHT_LAYERS; |
|
#endif |
|
|
|
#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)); |
|
// full-float precision required on some platforms |
|
float attenuation = half(DistanceAttenuation(distanceSqr, distanceAndSpotAttenuation.xy) * AngleAttenuation(spotDirection.xyz, lightDirection, distanceAndSpotAttenuation.zw)); |
|
|
|
Light light; |
|
light.direction = lightDirection; |
|
light.distanceAttenuation = attenuation; |
|
light.shadowAttenuation = 1.0; // This value can later be overridden in GetAdditionalLight(uint i, float3 positionWS, half4 shadowMask) |
|
light.color = color; |
|
light.layerMask = lightLayerMask; |
|
|
|
return light; |
|
} |
|
|
|
uint GetPerObjectLightIndexOffset() |
|
{ |
|
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA |
|
return uint(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 = uint(unity_LightData.x); |
|
return _AdditionalLightsIndices[offset + index]; |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////// |
|
// UBO path / |
|
// / |
|
// We store 8 light indices in half4 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. |
|
// |
|
// NOTE: min16float4 bug workaround. |
|
// Take the "vec4" part into float4 tmp variable in order to force float4 math. |
|
// It appears indexing half4 as min16float4 on DX11 can fail. (dp4 {min16f}) |
|
float4 tmp = unity_LightIndices[index / 4]; |
|
return int(tmp[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. |
|
half indexHalf = half(index); |
|
half2 lightIndex2 = (indexHalf < half(2.0)) ? unity_LightIndices[0].xy : unity_LightIndices[0].zw; |
|
half i_rem = (indexHalf < half(2.0)) ? indexHalf : indexHalf - half(2.0); |
|
return int((i_rem < half(1.0)) ? 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) |
|
{ |
|
#if USE_CLUSTERED_LIGHTING |
|
int lightIndex = i; |
|
#else |
|
int lightIndex = GetPerObjectLightIndex(i); |
|
#endif |
|
return GetAdditionalPerObjectLight(lightIndex, positionWS); |
|
} |
|
|
|
Light GetAdditionalLight(uint i, float3 positionWS, half4 shadowMask) |
|
{ |
|
#if USE_CLUSTERED_LIGHTING |
|
int lightIndex = i; |
|
#else |
|
int lightIndex = GetPerObjectLightIndex(i); |
|
#endif |
|
Light light = GetAdditionalPerObjectLight(lightIndex, positionWS); |
|
|
|
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA |
|
half4 occlusionProbeChannels = _AdditionalLightsBuffer[lightIndex].occlusionProbeChannels; |
|
#else |
|
half4 occlusionProbeChannels = _AdditionalLightsOcclusionProbes[lightIndex]; |
|
#endif |
|
light.shadowAttenuation = AdditionalLightShadow(lightIndex, positionWS, light.direction, shadowMask, occlusionProbeChannels); |
|
#if defined(_LIGHT_COOKIES) |
|
real3 cookieColor = SampleAdditionalLightCookie(lightIndex, positionWS); |
|
light.color *= cookieColor; |
|
#endif |
|
|
|
return light; |
|
} |
|
|
|
Light GetAdditionalLight(uint i, InputData inputData, half4 shadowMask, AmbientOcclusionFactor aoFactor) |
|
{ |
|
Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask); |
|
|
|
#if defined(_SCREEN_SPACE_OCCLUSION) && !defined(_SURFACE_TYPE_TRANSPARENT) |
|
if (IsLightingFeatureEnabled(DEBUGLIGHTINGFEATUREFLAGS_AMBIENT_OCCLUSION)) |
|
{ |
|
light.color *= aoFactor.directAmbientOcclusion; |
|
} |
|
#endif |
|
|
|
return light; |
|
} |
|
|
|
int GetAdditionalLightsCount() |
|
{ |
|
#if USE_CLUSTERED_LIGHTING |
|
// Counting the number of lights in clustered requires traversing the bit list, and is not needed up front. |
|
return 0; |
|
#else |
|
// 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 int(min(_AdditionalLightsCount.x, unity_LightData.y)); |
|
#endif |
|
} |
|
|
|
half4 CalculateShadowMask(InputData inputData) |
|
{ |
|
// To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders |
|
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON) |
|
half4 shadowMask = inputData.shadowMask; |
|
#elif !defined (LIGHTMAP_ON) |
|
half4 shadowMask = unity_ProbesOcclusion; |
|
#else |
|
half4 shadowMask = half4(1, 1, 1, 1); |
|
#endif |
|
|
|
return shadowMask; |
|
} |
|
|
|
#endif
|
|
|