// _preIntegratedFGD and _CubemapLD are unique for each BRDF IndirectLighting EvaluateBSDF_Env(LightLoopContext lightLoopContext, float3 V, PositionInputs posInput, PreLightData preLightData, EnvLightData lightData, BSDFData bsdfData, int influenceShapeType, inout float hierarchyWeight) { IndirectLighting lighting; ZERO_INITIALIZE(IndirectLighting, lighting); float3 envLighting; float weight = 1.0; float3 R = reflect(-V, bsdfData.normalWS); EvaluateLight_EnvIntersection(posInput.positionWS, bsdfData.normalWS, lightData, influenceShapeType, R, weight); // 31 bit index, 1 bit cache type uint cacheType = IsEnvIndexCubemap(lightData.envIndex) ? ENVCACHETYPE_CUBEMAP : ENVCACHETYPE_TEXTURE2D; // Index start at 1, because -0 == 0, so we can't known which cache to sample for that index. Thus it is invalid. int index = abs(lightData.envIndex) - 1; float lod = PerceptualRoughnessToMipmapLevel(preLightData.iblPerceptualRoughness) * lightData.roughReflections; float2 atlasCoords = GetReflectionAtlasCoordsCube(CUBE_SCALE_OFFSET[index], R, lod); // No distance based roughness for simple lit float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, R, PerceptualRoughnessToMipmapLevel(preLightData.iblPerceptualRoughness) * lightData.roughReflections, lightData.rangeCompressionFactorCompensation, posInput.positionNDC); weight *= preLD.a; // Used by planar reflection to discard pixel //envLighting = F_Schlick(bsdfData.fresnel0, dot(bsdfData.normalWS, V)) * preLD.rgb; envLighting = preLD.rgb; UpdateLightingHierarchyWeights(hierarchyWeight, weight); envLighting *= weight * lightData.multiplier; lighting.specularReflected = envLighting; return lighting; } float4 ComputeReflection(LightLoopContext context, PositionInputs posInput, PreLightData preLightData, BuiltinData builtinData, float3 V, float lod, BSDFData bsdfData) { float3 refcolor = 0; float reflectionHierarchyWeight = 0.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 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; #if NEED_TO_CHECK_HELPER_LANE // On some platform helper lanes don't behave as we'd expect, therefore we prevent them from entering the loop altogether. // IMPORTANT! This has implications if ddx/ddy is used on results derived from lighting, however given Lightloop is called in compute we should be // sure it will not happen. bool isHelperLane = WaveIsHelperLane(); while (!isHelperLane && v_envLightListOffset < envLightCount) #else while (v_envLightListOffset < envLightCount) #endif { 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) { if (IsMatchingLightLayer(s_envLightData.lightLayers, builtinData.renderingLayers)) { IndirectLighting lighting = EvaluateBSDF_Env(context, V, posInput, preLightData, s_envLightData, bsdfData, s_envLightData.influenceShapeType, reflectionHierarchyWeight); #if defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2) float3 lightInReflDir = float3(-1, -1, -1); if (s_envLightData.normalizeWithAPV > 0 && all(lightInReflDir >= 0)) { float factor = GetReflectionProbeNormalizationFactor(lightInReflDir, bsdfData.normalWS, s_envLightData.L0L1, s_envLightData.L2_1, s_envLightData.L2_2); lighting.specularReflected *= factor; } #endif refcolor += lighting.specularReflected; } } } } return float4(refcolor.r, refcolor.g, refcolor.b, reflectionHierarchyWeight); } float3 ComputeFresnelLerp(float3 c0, float3 c1, float cosA) { float t = pow(1 - cosA, 5); return lerp(c0, c1, t); } float3 ComputeIndirectDiffuse(PositionInputs posInput, BSDFData bsdfData, float3 V) { float3 indirectDiffuse = 0.0; if(_ID_Intensity > 0) { #ifdef _PBR_Mode_ANISO GetGGXAnisotropicModifiedNormalAndRoughness(bsdfData.bitangentWS, bsdfData.tangentWS , bsdfData.normalWS, V, bsdfData.anisotropy, bsdfData.perceptualRoughness, bsdfData.normalWS, bsdfData.perceptualRoughness); #endif float NdotV = saturate(dot(bsdfData.normalWS, V)); #if defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2) BuiltinData apvBuiltinData; ZERO_INITIALIZE(BuiltinData, apvBuiltinData); #if defined(_PBR_Mode_OFF) || defined(_PBR_Mode_TOON) EvaluateAdaptiveProbeVolume(GetAbsolutePositionWS(posInput.positionWS), 0.0, 0.0, V, posInput.positionSS, apvBuiltinData.bakeDiffuseLighting, apvBuiltinData.backBakeDiffuseLighting); #else EvaluateAdaptiveProbeVolume(GetAbsolutePositionWS(posInput.positionWS), bsdfData.normalWS, -bsdfData.normalWS, V, posInput.positionSS, apvBuiltinData.bakeDiffuseLighting, apvBuiltinData.backBakeDiffuseLighting); #endif float3 probeDiffuse = apvBuiltinData.bakeDiffuseLighting * GetCurrentExposureMultiplier(); indirectDiffuse = probeDiffuse; #else #if defined(_PBR_Mode_OFF) || defined(_PBR_Mode_TOON) indirectDiffuse = EvaluateAmbientProbe(0.0) * GetCurrentExposureMultiplier(); #else indirectDiffuse = EvaluateAmbientProbe(bsdfData.normalWS) * GetCurrentExposureMultiplier(); #endif #endif //SSGI if(_ReceivesSSGI == 1) { float4 ssgiLighting = LOAD_TEXTURE2D_X(_IndirectDiffuseTexture, posInput.positionSS); ssgiLighting *= _GIMultiplier; indirectDiffuse = lerp(indirectDiffuse, ssgiLighting.rgb, ssgiLighting.a); } //Compelete the indirect lighting indirectDiffuse = indirectDiffuse * bsdfData.diffuseColor.rgb * _BaseColor.rgb; //SSAO if(_ReceivesSSAO == 1) { AmbientOcclusionFactor aoFactor; GetScreenSpaceAmbientOcclusionMultibounce(posInput.positionSS, NdotV, bsdfData.perceptualRoughness, bsdfData.ambientOcclusion, bsdfData.specularOcclusion, bsdfData.diffuseColor, bsdfData.fresnel0, aoFactor); indirectDiffuse *= lerp(_AOMin, 1, aoFactor.indirectAmbientOcclusion); } indirectDiffuse = indirectDiffuse * bsdfData.ambientOcclusion; } return indirectDiffuse; } float3 ComputeIndirectSpecular(LightLoopContext lightLoopContext, PositionInputs posInput, PreLightData preLightData, BSDFData bsdfData, SurfaceData surfaceData, BuiltinData builtinData, float3 V) { #if defined(_PBR_Mode_OFF) || defined(_PBR_Mode_TOON) return 0; #else float3 indirectSpecular = 0; if(_IR_Intensity > 0) { #ifdef _PBR_Mode_ANISO GetGGXAnisotropicModifiedNormalAndRoughness(bsdfData.bitangentWS, bsdfData.tangentWS , bsdfData.normalWS, V, bsdfData.anisotropy, bsdfData.perceptualRoughness, bsdfData.normalWS, bsdfData.perceptualRoughness); #endif float3 albedo = _BaseColor.rgb * surfaceData.baseColor; float mip = PerceptualRoughnessToMipmapLevel(bsdfData.perceptualRoughness); float NdotV = saturate(dot(bsdfData.normalWS, V)); indirectSpecular = SampleSkyTexture(reflect(-V, bsdfData.normalWS), mip, 0).rgb; float3 specColor = lerp(ColorSpaceDielectricSpec.rgb, albedo, surfaceData.metallic); float oneMinusReflectivity = ColorSpaceDielectricSpec.a * (1 - surfaceData.metallic); float grazingTerm = saturate((1 - bsdfData.perceptualRoughness) + (1 - oneMinusReflectivity)); //Reflection Probe float4 refProbe = ComputeReflection(lightLoopContext, posInput, preLightData, builtinData, V, mip, bsdfData); indirectSpecular = lerp(indirectSpecular, refProbe.rgb, refProbe.a); //SSR if(_ReceivesSSR == 1) { float4 ssrLighting = LOAD_TEXTURE2D_X(_SsrLightingTexture, posInput.positionSS); InversePreExposeSsrLighting(ssrLighting); ApplyScreenSpaceReflectionWeight(ssrLighting); indirectSpecular = lerp(indirectSpecular, ssrLighting.rgb, ssrLighting.a); } //Compelete the indirect lighting indirectSpecular = indirectSpecular * ComputeFresnelLerp(specColor, grazingTerm, NdotV) * GetCurrentExposureMultiplier(); // Occlusion if(_ReceivesSSAO == 1) { AmbientOcclusionFactor aoFactor; GetScreenSpaceAmbientOcclusionMultibounce(posInput.positionSS, NdotV, bsdfData.perceptualRoughness, bsdfData.ambientOcclusion, bsdfData.specularOcclusion, bsdfData.diffuseColor, bsdfData.fresnel0, aoFactor); indirectSpecular *= lerp(_AOMin, 1, aoFactor.indirectSpecularOcclusion); } indirectSpecular = indirectSpecular * bsdfData.specularOcclusion; } return indirectSpecular; #endif }