2021-09-09 20:42:29 -04:00

605 lines
29 KiB
HLSL

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl"
#if SHADEROPTIONS_ENABLE_PROBE_VOLUMES == 1
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinUtilities.hlsl"
#else
// Required to have access to the indirectDiffuseMode enum in forward pass where we don't include BuiltinUtilities
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.cs.hlsl"
#endif
#ifndef SCALARIZE_LIGHT_LOOP
// We perform scalarization only for forward rendering as for deferred loads will already be scalar since tiles will match waves and therefore all threads will read from the same tile.
// More info on scalarization: https://flashypixels.wordpress.com/2018/11/10/intro-to-gpu-scalarization-part-2-scalarize-all-the-lights/
#define SCALARIZE_LIGHT_LOOP (defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS) && !defined(LIGHTLOOP_DISABLE_TILE_AND_CLUSTER) && !defined(SHADER_API_GAMECORE) && SHADERPASS == SHADERPASS_FORWARD)
#endif
//-----------------------------------------------------------------------------
// LightLoop
// ----------------------------------------------------------------------------
void ApplyDebugToLighting(LightLoopContext context, inout BuiltinData builtinData, inout AggregateLighting aggregateLighting)
{
#ifdef DEBUG_DISPLAY
if (_DebugLightingMode >= DEBUGLIGHTINGMODE_DIFFUSE_LIGHTING && _DebugLightingMode <= DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING)
{
if (_DebugLightingMode == DEBUGLIGHTINGMODE_SPECULAR_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_SPECULAR_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_INDIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFRACTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING)
{
aggregateLighting.direct.diffuse = real3(0.0, 0.0, 0.0);
}
if (_DebugLightingMode == DEBUGLIGHTINGMODE_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_INDIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFRACTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING)
{
aggregateLighting.direct.specular = real3(0.0, 0.0, 0.0);
}
if (_DebugLightingMode == DEBUGLIGHTINGMODE_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_SPECULAR_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_INDIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFRACTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING)
{
aggregateLighting.indirect.specularReflected = real3(0.0, 0.0, 0.0);
}
// Note: specular transmission is the refraction and as it reflect lighting behind the object it
// must be displayed for both diffuse and specular mode, except if we ask for direct lighting only
if (_DebugLightingMode != DEBUGLIGHTINGMODE_REFRACTION_LIGHTING)
{
aggregateLighting.indirect.specularTransmitted = real3(0.0, 0.0, 0.0);
}
if (_DebugLightingMode == DEBUGLIGHTINGMODE_SPECULAR_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_DIFFUSE_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_DIRECT_SPECULAR_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTION_LIGHTING ||
_DebugLightingMode == DEBUGLIGHTINGMODE_REFRACTION_LIGHTING
#if (SHADERPASS != SHADERPASS_DEFERRED_LIGHTING)
|| _DebugLightingMode == DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING // With deferred, Emissive is store in builtinData.bakeDiffuseLighting
#endif
)
{
builtinData.bakeDiffuseLighting = real3(0.0, 0.0, 0.0);
}
if (_DebugLightingMode != DEBUGLIGHTINGMODE_EMISSIVE_LIGHTING)
{
builtinData.emissiveColor = real3(0.0, 0.0, 0.0);
}
}
#endif
}
bool UseScreenSpaceShadow(DirectionalLightData light, float3 normalWS)
{
// Two different options are possible here
// - We have a ray trace shadow in which case we have no valid signal for a transmission and we need to fallback on the rasterized shadow
// - We have a screen space shadow and it already contains the transmission shadow and we can use it straight away
bool visibleLight = dot(normalWS, -light.forward) > 0.0;
bool validScreenSpaceShadow = (light.screenSpaceShadowIndex & SCREEN_SPACE_SHADOW_INDEX_MASK) != INVALID_SCREEN_SPACE_SHADOW;
bool rayTracedShadow = (light.screenSpaceShadowIndex & RAY_TRACED_SCREEN_SPACE_SHADOW_FLAG) != 0;
return (validScreenSpaceShadow && ((rayTracedShadow && visibleLight) || !rayTracedShadow));
}
void ApplyDebug(LightLoopContext context, PositionInputs posInput, BSDFData bsdfData, inout LightLoopOutput lightLoopOutput)
{
#ifdef DEBUG_DISPLAY
if (_DebugLightingMode == DEBUGLIGHTINGMODE_PROBE_VOLUME)
{
// Debug info is written to diffuseColor inside of light loop.
lightLoopOutput.specularLighting = float3(0.0, 0.0, 0.0);
}
else if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER)
{
lightLoopOutput.specularLighting = float3(0.0, 0.0, 0.0); // Disable specular lighting
// Take the luminance
lightLoopOutput.diffuseLighting = Luminance(lightLoopOutput.diffuseLighting).xxx;
}
else if (_DebugLightingMode == DEBUGLIGHTINGMODE_VISUALIZE_CASCADE)
{
lightLoopOutput.specularLighting = float3(0.0, 0.0, 0.0);
const float3 s_CascadeColors[] = {
float3(0.5, 0.5, 0.7),
float3(0.5, 0.7, 0.5),
float3(0.7, 0.7, 0.5),
float3(0.7, 0.5, 0.5),
float3(1.0, 1.0, 1.0)
};
lightLoopOutput.diffuseLighting = Luminance(lightLoopOutput.diffuseLighting);
if (_DirectionalShadowIndex >= 0)
{
real alpha;
int cascadeCount;
int shadowSplitIndex = EvalShadow_GetSplitIndex(context.shadowContext, _DirectionalShadowIndex, posInput.positionWS, alpha, cascadeCount);
if (shadowSplitIndex >= 0)
{
SHADOW_TYPE shadow = 1.0;
if (_DirectionalShadowIndex >= 0)
{
DirectionalLightData light = _DirectionalLightDatas[_DirectionalShadowIndex];
#if defined(SCREEN_SPACE_SHADOWS_ON) && !defined(_SURFACE_TYPE_TRANSPARENT)
if (UseScreenSpaceShadow(light, bsdfData.normalWS))
{
shadow = GetScreenSpaceColorShadow(posInput, light.screenSpaceShadowIndex).SHADOW_TYPE_SWIZZLE;
}
else
#endif
{
float3 L = -light.forward;
shadow = GetDirectionalShadowAttenuation(context.shadowContext,
posInput.positionSS, posInput.positionWS, GetNormalForShadowBias(bsdfData),
light.shadowIndex, L);
}
}
float3 cascadeShadowColor = lerp(s_CascadeColors[shadowSplitIndex], s_CascadeColors[shadowSplitIndex + 1], alpha);
// We can't mix with the lighting as it can be HDR and it is hard to find a good lerp operation for this case that is still compliant with
// exposure. So disable exposure instead and replace color.
lightLoopOutput.diffuseLighting = cascadeShadowColor * Luminance(lightLoopOutput.diffuseLighting) * shadow;
}
}
}
else if (_DebugLightingMode == DEBUGLIGHTINGMODE_MATCAP_VIEW)
{
lightLoopOutput.specularLighting = float3(0.0, 0.0, 0.0);
float3 normalVS = mul((float3x3)UNITY_MATRIX_V, bsdfData.normalWS).xyz;
float3 V = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
float3 R = reflect(V, bsdfData.normalWS);
float2 UV = saturate(normalVS.xy * 0.5f + 0.5f);
float4 defaultColor = GetDiffuseOrDefaultColor(bsdfData, 1.0);
if (defaultColor.a == 1.0)
{
UV = saturate(R.xy * 0.5f + 0.5f);
}
lightLoopOutput.diffuseLighting = SAMPLE_TEXTURE2D_LOD(_DebugMatCapTexture, s_linear_repeat_sampler, UV, 0).rgb * (_MatcapMixAlbedo > 0 ? defaultColor.rgb * _MatcapViewScale : 1.0f);
#ifdef OUTPUT_SPLIT_LIGHTING // Work as matcap view is only call in forward, OUTPUT_SPLIT_LIGHTING isn't define in deferred.compute
if (_EnableSubsurfaceScattering != 0 && ShouldOutputSplitLighting(bsdfData))
{
lightLoopOutput.specularLighting = lightLoopOutput.diffuseLighting;
}
#endif
}
#endif
}
void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BSDFData bsdfData, BuiltinData builtinData, uint featureFlags,
out LightLoopOutput lightLoopOutput)
{
// Init LightLoop output structure
ZERO_INITIALIZE(LightLoopOutput, lightLoopOutput);
LightLoopContext context;
context.shadowContext = InitShadowContext();
context.shadowValue = 1;
context.sampleReflection = 0;
// With XR single-pass and camera-relative: offset position to do lighting computations from the combined center view (original camera matrix).
// This is required because there is only one list of lights generated on the CPU. Shadows are also generated once and shared between the instanced views.
ApplyCameraRelativeXR(posInput.positionWS);
// Initialize the contactShadow and contactShadowFade fields
InitContactShadow(posInput, context);
// First of all we compute the shadow value of the directional light to reduce the VGPR pressure
if (featureFlags & LIGHTFEATUREFLAGS_DIRECTIONAL)
{
// Evaluate sun shadows.
if (_DirectionalShadowIndex >= 0)
{
DirectionalLightData light = _DirectionalLightDatas[_DirectionalShadowIndex];
#if defined(SCREEN_SPACE_SHADOWS_ON) && !defined(_SURFACE_TYPE_TRANSPARENT)
if (UseScreenSpaceShadow(light, bsdfData.normalWS))
{
context.shadowValue = GetScreenSpaceColorShadow(posInput, light.screenSpaceShadowIndex).SHADOW_TYPE_SWIZZLE;
}
else
#endif
{
// TODO: this will cause us to load from the normal buffer first. Does this cause a performance problem?
float3 L = -light.forward;
// Is it worth sampling the shadow map?
if ((light.lightDimmer > 0) && (light.shadowDimmer > 0) && // Note: Volumetric can have different dimmer, thus why we test it here
IsNonZeroBSDF(V, L, preLightData, bsdfData) &&
!ShouldEvaluateThickObjectTransmission(V, L, preLightData, bsdfData, light.shadowIndex))
{
context.shadowValue = GetDirectionalShadowAttenuation(context.shadowContext,
posInput.positionSS, posInput.positionWS, GetNormalForShadowBias(bsdfData),
light.shadowIndex, L);
}
}
}
}
// This struct is define in the material. the Lightloop must not access it
// PostEvaluateBSDF call at the end will convert Lighting to diffuse and specular lighting
AggregateLighting aggregateLighting;
ZERO_INITIALIZE(AggregateLighting, aggregateLighting); // LightLoop is in charge of initializing the struct
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
{
uint lightCount, lightStart;
#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
GetCountAndStart(posInput, LIGHTCATEGORY_PUNCTUAL, lightStart, lightCount);
#else // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
lightCount = _PunctualLightCount;
lightStart = 0;
#endif
bool fastPath = false;
#if SCALARIZE_LIGHT_LOOP
uint lightStartLane0;
fastPath = IsFastPath(lightStart, lightStartLane0);
if (fastPath)
{
lightStart = lightStartLane0;
}
#endif
// Scalarized loop. All lights that are in a tile/cluster touched by any pixel in the wave are loaded (scalar load), only the one relevant to current thread/pixel are processed.
// For clarity, the following code will follow the convention: variables starting with s_ are meant to be wave uniform (meant for scalar register),
// v_ are variables that might have different value for each thread in the wave (meant for vector registers).
// This will perform more loads than it is supposed to, however, the benefits should offset the downside, especially given that light data accessed should be largely coherent.
// Note that the above is valid only if wave intriniscs are supported.
uint v_lightListOffset = 0;
uint v_lightIdx = lightStart;
while (v_lightListOffset < lightCount)
{
v_lightIdx = FetchIndex(lightStart, v_lightListOffset);
#if SCALARIZE_LIGHT_LOOP
uint s_lightIdx = ScalarizeElementIndex(v_lightIdx, fastPath);
#else
uint s_lightIdx = v_lightIdx;
#endif
if (s_lightIdx == -1)
break;
LightData s_lightData = FetchLight(s_lightIdx);
// If current scalar and vector light index match, we process the light. The v_lightListOffset for current thread is increased.
// Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could
// end up with a unique v_lightIdx value that is smaller than s_lightIdx hence being stuck in a loop. All the active lanes will not have this problem.
if (s_lightIdx >= v_lightIdx)
{
v_lightListOffset++;
if (IsMatchingLightLayer(s_lightData.lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = EvaluateBSDF_Punctual(context, V, posInput, preLightData, s_lightData, bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}
}
}
}
// Define macro for a better understanding of the loop
// TODO: this code is now much harder to understand...
#define EVALUATE_BSDF_ENV_SKY(envLightData, TYPE, type) \
IndirectLighting lighting = EvaluateBSDF_Env(context, V, posInput, preLightData, envLightData, bsdfData, envLightData.influenceShapeType, MERGE_NAME(GPUIMAGEBASEDLIGHTINGTYPE_, TYPE), MERGE_NAME(type, HierarchyWeight)); \
AccumulateIndirectLighting(lighting, aggregateLighting);
// Environment cubemap test lightlayers, sky don't test it
#define EVALUATE_BSDF_ENV(envLightData, TYPE, type) if (IsMatchingLightLayer(envLightData.lightLayers, builtinData.renderingLayers)) { EVALUATE_BSDF_ENV_SKY(envLightData, TYPE, type) }
// First loop iteration
if (featureFlags & (LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_SSREFRACTION | LIGHTFEATUREFLAGS_SSREFLECTION))
{
float reflectionHierarchyWeight = 0.0; // Max: 1.0
float refractionHierarchyWeight = _EnableSSRefraction ? 0.0 : 1.0; // Max: 1.0
uint envLightStart, envLightCount;
// Fetch first env light to provide the scene proxy for screen space computation
#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
GetCountAndStart(posInput, LIGHTCATEGORY_ENV, envLightStart, envLightCount);
#else // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
envLightCount = _EnvLightCount;
envLightStart = 0;
#endif
bool fastPath = false;
#if SCALARIZE_LIGHT_LOOP
uint envStartFirstLane;
fastPath = IsFastPath(envLightStart, envStartFirstLane);
#endif
// Reflection / Refraction hierarchy is
// 1. Screen Space Refraction / Reflection
// 2. Environment Reflection / Refraction
// 3. Sky Reflection / Refraction
// Apply SSR.
#if (defined(_SURFACE_TYPE_TRANSPARENT) && !defined(_DISABLE_SSR_TRANSPARENT)) || (!defined(_SURFACE_TYPE_TRANSPARENT) && !defined(_DISABLE_SSR))
{
IndirectLighting indirect = EvaluateBSDF_ScreenSpaceReflection(posInput, preLightData, bsdfData,
reflectionHierarchyWeight);
AccumulateIndirectLighting(indirect, aggregateLighting);
}
#endif
EnvLightData envLightData;
if (envLightCount > 0)
{
envLightData = FetchEnvLight(envLightStart, 0);
}
else
{
envLightData = InitSkyEnvLightData(0);
}
if ((featureFlags & LIGHTFEATUREFLAGS_SSREFRACTION) && (_EnableSSRefraction > 0))
{
IndirectLighting lighting = EvaluateBSDF_ScreenspaceRefraction(context, V, posInput, preLightData, bsdfData, envLightData, refractionHierarchyWeight);
AccumulateIndirectLighting(lighting, aggregateLighting);
}
// Reflection probes are sorted by volume (in the increasing order).
if (featureFlags & LIGHTFEATUREFLAGS_ENV)
{
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_REFLECTION_PROBES;
#if SCALARIZE_LIGHT_LOOP
if (fastPath)
{
envLightStart = envStartFirstLane;
}
#endif
// Scalarized loop, same rationale of the punctual light version
uint v_envLightListOffset = 0;
uint v_envLightIdx = envLightStart;
while (v_envLightListOffset < envLightCount)
{
v_envLightIdx = FetchIndex(envLightStart, v_envLightListOffset);
#if SCALARIZE_LIGHT_LOOP
uint s_envLightIdx = ScalarizeElementIndex(v_envLightIdx, fastPath);
#else
uint s_envLightIdx = v_envLightIdx;
#endif
if (s_envLightIdx == -1)
break;
EnvLightData s_envLightData = FetchEnvLight(s_envLightIdx); // Scalar load.
// If current scalar and vector light index match, we process the light. The v_envLightListOffset for current thread is increased.
// Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could
// end up with a unique v_envLightIdx value that is smaller than s_envLightIdx hence being stuck in a loop. All the active lanes will not have this problem.
if (s_envLightIdx >= v_envLightIdx)
{
v_envLightListOffset++;
if (reflectionHierarchyWeight < 1.0)
{
EVALUATE_BSDF_ENV(s_envLightData, REFLECTION, reflection);
}
// Refraction probe and reflection probe will process exactly the same weight. It will be good for performance to be able to share this computation
// However it is hard to deal with the fact that reflectionHierarchyWeight and refractionHierarchyWeight have not the same values, they are independent
// The refraction probe is rarely used and happen only with sphere shape and high IOR. So we accept the slow path that use more simple code and
// doesn't affect the performance of the reflection which is more important.
// We reuse LIGHTFEATUREFLAGS_SSREFRACTION flag as refraction is mainly base on the screen. Would be a waste to not use screen and only cubemap.
if ((featureFlags & LIGHTFEATUREFLAGS_SSREFRACTION) && (refractionHierarchyWeight < 1.0))
{
EVALUATE_BSDF_ENV(s_envLightData, REFRACTION, refraction);
}
}
}
}
// Only apply the sky IBL if the sky texture is available
if ((featureFlags & LIGHTFEATUREFLAGS_SKY) && _EnvLightSkyEnabled)
{
// The sky is a single cubemap texture separate from the reflection probe texture array (different resolution and compression)
context.sampleReflection = SINGLE_PASS_CONTEXT_SAMPLE_SKY;
// The sky data are generated on the fly so the compiler can optimize the code
EnvLightData envLightSky = InitSkyEnvLightData(0);
// Only apply the sky if we haven't yet accumulated enough IBL lighting.
if (reflectionHierarchyWeight < 1.0)
{
EVALUATE_BSDF_ENV_SKY(envLightSky, REFLECTION, reflection);
}
if ((featureFlags & LIGHTFEATUREFLAGS_SSREFRACTION) && (refractionHierarchyWeight < 1.0))
{
EVALUATE_BSDF_ENV_SKY(envLightSky, REFRACTION, refraction);
}
}
}
#undef EVALUATE_BSDF_ENV
#undef EVALUATE_BSDF_ENV_SKY
uint i = 0; // Declare once to avoid the D3D11 compiler warning.
if (featureFlags & LIGHTFEATUREFLAGS_DIRECTIONAL)
{
for (i = 0; i < _DirectionalLightCount; ++i)
{
if (IsMatchingLightLayer(_DirectionalLightDatas[i].lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = EvaluateBSDF_Directional(context, V, posInput, preLightData, _DirectionalLightDatas[i], bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}
}
}
#if SHADEROPTIONS_AREA_LIGHTS
if (featureFlags & LIGHTFEATUREFLAGS_AREA)
{
uint lightCount, lightStart;
#ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
GetCountAndStart(posInput, LIGHTCATEGORY_AREA, lightStart, lightCount);
#else
lightCount = _AreaLightCount;
lightStart = _PunctualLightCount;
#endif
// COMPILER BEHAVIOR WARNING!
// If rectangle lights are before line lights, the compiler will duplicate light matrices in VGPR because they are used differently between the two types of lights.
// By keeping line lights first we avoid this behavior and save substantial register pressure.
// TODO: This is based on the current Lit.shader and can be different for any other way of implementing area lights, how to be generic and ensure performance ?
if (lightCount > 0)
{
i = 0;
uint last = lightCount - 1;
LightData lightData = FetchLight(lightStart, i);
while (i <= last && lightData.lightType == GPULIGHTTYPE_TUBE)
{
lightData.lightType = GPULIGHTTYPE_TUBE; // Enforce constant propagation
lightData.cookieMode = COOKIEMODE_NONE; // Enforce constant propagation
if (IsMatchingLightLayer(lightData.lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = EvaluateBSDF_Area(context, V, posInput, preLightData, lightData, bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}
lightData = FetchLight(lightStart, min(++i, last));
}
while (i <= last) // GPULIGHTTYPE_RECTANGLE
{
lightData.lightType = GPULIGHTTYPE_RECTANGLE; // Enforce constant propagation
if (IsMatchingLightLayer(lightData.lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = EvaluateBSDF_Area(context, V, posInput, preLightData, lightData, bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}
lightData = FetchLight(lightStart, min(++i, last));
}
}
}
#endif
#if SHADEROPTIONS_ENABLE_PROBE_VOLUMES == 1
bool uninitialized = IsUninitializedGI(builtinData.bakeDiffuseLighting);
builtinData.bakeDiffuseLighting = uninitialized ? float3(0.0, 0.0, 0.0) : builtinData.bakeDiffuseLighting;
// If probe volume feature is enabled, this bit is enabled for all tiles to handle ambient probe fallback.
// No need to branch internally on _EnableProbeVolumes uniform.
if (featureFlags & LIGHTFEATUREFLAGS_PROBE_VOLUME)
{
#if !SHADEROPTIONS_PROBE_VOLUMES_ADDITIVE_BLENDING
if (uninitialized)
#endif
{
// Need to make sure not to apply ModifyBakedDiffuseLighting() twice to our bakeDiffuseLighting data, which could happen if we are dealing with initialized data (light maps).
// Create a local BuiltinData variable here, and then add results to builtinData.bakeDiffuseLighting at the end.
BuiltinData builtinDataProbeVolumes;
ZERO_INITIALIZE(BuiltinData, builtinDataProbeVolumes);
float probeVolumeHierarchyWeight = uninitialized ? 0.0f : 1.0f;
// Note: we aren't suppose to access normalWS in lightloop, but bsdfData.normalWS is always define for any material. So this is safe.
ProbeVolumeEvaluateSphericalHarmonics(
posInput,
bsdfData.normalWS,
-bsdfData.normalWS,
builtinData.renderingLayers,
probeVolumeHierarchyWeight,
builtinDataProbeVolumes.bakeDiffuseLighting,
builtinDataProbeVolumes.backBakeDiffuseLighting
);
// Apply control from the indirect lighting volume settings (Remember there is no emissive here at this step)
float indirectDiffuseMultiplier = GetIndirectDiffuseMultiplier(builtinData.renderingLayers);
builtinDataProbeVolumes.bakeDiffuseLighting *= indirectDiffuseMultiplier;
builtinDataProbeVolumes.backBakeDiffuseLighting *= indirectDiffuseMultiplier;
#ifdef MODIFY_BAKED_DIFFUSE_LIGHTING
#ifdef DEBUG_DISPLAY
// When the lux meter is enabled, we don't want the albedo of the material to modify the diffuse baked lighting
if (_DebugLightingMode != DEBUGLIGHTINGMODE_LUX_METER)
#endif
ModifyBakedDiffuseLighting(V, posInput, preLightData, bsdfData, builtinDataProbeVolumes);
#endif
#if (SHADERPASS == SHADERPASS_DEFERRED_LIGHTING)
// If we are deferred we should apply baked AO here as it was already apply for lightmap.
// But in deferred ambientOcclusion is white so we should use specularOcclusion instead. It is the
// same case than for Microshadow so we can reuse this function. It should not be apply in forward
// as in this case the baked AO is correctly apply in PostBSDF()
// This is apply only on bakeDiffuseLighting as ModifyBakedDiffuseLighting combine both bakeDiffuseLighting and backBakeDiffuseLighting
builtinDataProbeVolumes.bakeDiffuseLighting *= GetAmbientOcclusionForMicroShadowing(bsdfData);
#endif
ApplyDebugToBuiltinData(builtinDataProbeVolumes);
// Note: builtinDataProbeVolumes.bakeDiffuseLighting and builtinDataProbeVolumes.backBakeDiffuseLighting were combine inside of ModifyBakedDiffuseLighting().
builtinData.bakeDiffuseLighting += builtinDataProbeVolumes.bakeDiffuseLighting;
}
}
#endif
#if !defined(_SURFACE_TYPE_TRANSPARENT)
// If we use the texture ssgi for ssgi or rtgi, we want to combine it with the value in the bake diffuse lighting value
if (_IndirectDiffuseMode != INDIRECTDIFFUSEMODE_OFF)
{
BuiltinData builtinDataSSGI;
ZERO_INITIALIZE(BuiltinData, builtinDataSSGI);
builtinDataSSGI.bakeDiffuseLighting = LOAD_TEXTURE2D_X(_IndirectDiffuseTexture, posInput.positionSS).xyz * GetInverseCurrentExposureMultiplier();
builtinDataSSGI.bakeDiffuseLighting *= GetIndirectDiffuseMultiplier(builtinData.renderingLayers);
// TODO: try to see if we can share code with probe volume
#ifdef MODIFY_BAKED_DIFFUSE_LIGHTING
#ifdef DEBUG_DISPLAY
// When the lux meter is enabled, we don't want the albedo of the material to modify the diffuse baked lighting
if (_DebugLightingMode != DEBUGLIGHTINGMODE_LUX_METER)
#endif
ModifyBakedDiffuseLighting(V, posInput, preLightData, bsdfData, builtinDataSSGI);
#endif
// In the alpha channel, we have the interpolation value that we use to blend the result of SSGI/RTGI with the other GI thechnique
builtinData.bakeDiffuseLighting = lerp(builtinData.bakeDiffuseLighting,
builtinDataSSGI.bakeDiffuseLighting,
LOAD_TEXTURE2D_X(_IndirectDiffuseTexture, posInput.positionSS).w);
}
#endif
ApplyDebugToLighting(context, builtinData, aggregateLighting);
// Note: We can't apply the IndirectDiffuseMultiplier here as with GBuffer, Emissive is part of the bakeDiffuseLighting.
// so IndirectDiffuseMultiplier is apply in PostInitBuiltinData or related location (like for probe volume)
aggregateLighting.indirect.specularReflected *= GetIndirectSpecularMultiplier(builtinData.renderingLayers);
// Also Apply indiret diffuse (GI)
// PostEvaluateBSDF will perform any operation wanted by the material and sum everything into diffuseLighting and specularLighting
PostEvaluateBSDF( context, V, posInput, preLightData, bsdfData, builtinData, aggregateLighting, lightLoopOutput);
ApplyDebug(context, posInput, bsdfData, lightLoopOutput);
}