#ifndef UTS_LIGHT_EVALUATION #define UTS_LIGHT_EVALUATION #if FP_BUFFER #define SATURATE_IF_SDR(x) (x) #define SATURATE_BASE_COLOR_IF_SDR(x) (x) #else #define SATURATE_IF_SDR(x) saturate(x) #define SATURATE_BASE_COLOR_IF_SDR(x) saturate(x) #endif const float rateR = 0.299; const float rateG = 0.587; const float rateB = 0.114; struct UTSLightData { float3 lightDirection; float3 lightColor; float diffuseDimmer; float specularDimmer; float3 shadowTint; float penumbraTint; SHADOW_TYPE shadowValue; }; float GetColorAttenuation(float3 lightColor) { float lightAttenuation = rateR * lightColor.r + rateG * lightColor.g + rateB * lightColor.b; return lightAttenuation; } float3 GetLimitedLightColor(float3 lightColor) { lightColor = ApplyCurrentExposureMultiplier(lightColor); float3 result = lerp(lightColor, saturate(lightColor), _Is_Filter_LightColor); return result; } DirectLighting UtsEvaluateBSDF_Directional(LightLoopContext lightLoopContext, PositionInputs posInput, BuiltinData builtinData, DirectionalLightData lightData, UtsBSDFData bsdfData, PreLightData preLightData, float3 V, float2 uv0) { DirectLighting lighting; ZERO_INITIALIZE(DirectLighting, lighting); float3 L = -lightData.forward; SHADOW_TYPE shadow = EvaluateShadow_Directional(lightLoopContext, posInput, lightData, builtinData, bsdfData.geomNormalWS); if (lightData.lightDimmer > 0.0) { // TODO: Colored shadow will overwrite the first and second shading diffuse color //float3 shadowColor = ComputeShadowColor(shadow, lightData.shadowTint, lightData.penumbraTint); float4 lightColor = EvaluateLight_Directional(lightLoopContext, posInput, lightData); lightColor.rgb = GetLimitedLightColor(lightColor.rgb * lightColor.a * _Light_Intensity_Multiplier); UtsClampRoughness(preLightData, bsdfData, lightData.minRoughness); lighting = UtsShadeSurface(posInput, bsdfData, preLightData, shadow, lightColor.rgb, V, L, uv0, lightData.diffuseDimmer, lightData.specularDimmer); } return lighting; } DirectLighting UtsEvaluateBSDF_Punctual(LightLoopContext lightLoopContext, PositionInputs posInput, BuiltinData builtinData, LightData lightData, UtsBSDFData bsdfData, PreLightData preLightData, float3 V, float2 uv0) { DirectLighting lighting; ZERO_INITIALIZE(DirectLighting, lighting); float3 L; float4 distances; // {d, d^2, 1/d, d_proj} GetPunctualLightVectors(posInput.positionWS, lightData, L, distances); PositionInputs shadowPositionInputs = posInput; shadowPositionInputs.positionWS = posInput.positionWS + L * _ShadowBias; SHADOW_TYPE shadow = EvaluateShadow_Punctual(lightLoopContext, shadowPositionInputs, lightData, builtinData, bsdfData.geomNormalWS, L, distances); if (lightData.lightDimmer > 0.0) { // TODO: Colored shadow will overwrite the first and second shading diffuse color //float3 shadowColor = ComputeShadowColor(shadow, lightData.shadowTint, lightData.penumbraTint); float4 lightColor = EvaluateLight_Punctual(lightLoopContext, posInput, lightData, L, distances); lightColor.rgb = GetLimitedLightColor(lightColor.rgb * lightColor.a * _Light_Intensity_Multiplier); UtsClampRoughness(preLightData, bsdfData, lightData.minRoughness); lighting = UtsShadeSurface(posInput, bsdfData, preLightData, shadow, lightColor.rgb, V, L, uv0, lightData.diffuseDimmer, lightData.specularDimmer); } return lighting; } IndirectLighting UtsEvaluateBSDF_ScreenSpaceReflection(PositionInputs posInput, PreLightData preLightData, inout float reflectionHierarchyWeight) { IndirectLighting lighting; ZERO_INITIALIZE(IndirectLighting, lighting); // TODO: this texture is sparse (mostly black). Can we avoid reading every texel? How about using Hi-S? float4 ssrLighting = LOAD_TEXTURE2D_X(_SsrLightingTexture, posInput.positionSS); InversePreExposeSsrLighting(ssrLighting); // Apply the weight on the ssr contribution (if required) ApplyScreenSpaceReflectionWeight(ssrLighting); reflectionHierarchyWeight = ssrLighting.a; lighting.specularReflected = ssrLighting.rgb * preLightData.specularFGD; return lighting; } void UtsEvaluateBSDF_BakeDiffuse(PositionInputs posInput, PreLightData preLightData, UtsBSDFData bsdfData, float3 V, inout BuiltinData builtinData, out float3 lightInReflDir) { lightInReflDir = 0.0; #if defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2) lightInReflDir = float3(-1, -1, -1); // This variable is used with APV for reflection probe normalization - see code for LIGHTFEATUREFLAGS_ENV #endif #if !defined(_SURFACE_TYPE_TRANSPARENT) && !defined(SCREEN_SPACE_INDIRECT_DIFFUSE_DISABLED) if (_IndirectDiffuseMode != INDIRECTDIFFUSEMODE_OFF) { builtinData.bakeDiffuseLighting = LOAD_TEXTURE2D_X(_IndirectDiffuseTexture, posInput.positionSS).xyz * GetInverseCurrentExposureMultiplier(); } else #endif { #if defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2) if (_EnableProbeVolumes) { // Reflect normal to get lighting for reflection probe tinting float3 R = reflect(-V, bsdfData.normalWS); #if defined(_PBR_Mode_OFF) || defined(_PBR_Mode_TOON) float3 normalWS = 0.0; float3 backNormalWS = 0.0; #else float3 normalWS = bsdfData.normalWS; float3 backNormalWS = -bsdfData.normalWS; #endif EvaluateAdaptiveProbeVolume(GetAbsolutePositionWS(posInput.positionWS), bsdfData.normalWS, -bsdfData.normalWS, R, V, posInput.positionSS, builtinData.renderingLayers, builtinData.bakeDiffuseLighting, builtinData.backBakeDiffuseLighting, lightInReflDir); } else // If probe volume is disabled we fallback on the ambient probes { builtinData.bakeDiffuseLighting = EvaluateAmbientProbe(bsdfData.normalWS); builtinData.backBakeDiffuseLighting = EvaluateAmbientProbe(-bsdfData.normalWS); } #endif } } void UtsEvaluateBSDF_MatCapDiffuse(float3 positionWS, float3 normalWS, inout BuiltinData builtinData) { float3 positionVS = mul(UNITY_MATRIX_V, float4(positionWS, 1.0)).xyz; float3 normalVS = mul(UNITY_MATRIX_V, float4(normalWS, 1.0)).xyz; float3 PcrossN = cross(normalize(positionVS), normalVS); float2 uv = PcrossN.yx; uv.x *= -1; uv = uv * 0.5 + 0.5; builtinData.bakeDiffuseLighting = SAMPLE_TEXTURE2D_LOD(_MatCapMap, s_linear_clamp_sampler, uv, UNITY_SPECCUBE_LOD_STEPS).rgb * GetInverseCurrentExposureMultiplier(); } IndirectLighting UtsEvaluateBSDF_MatCapSpecular(float3 positionWS, UtsBSDFData bsdfData, PreLightData preLightData) { IndirectLighting lighting; ZERO_INITIALIZE(IndirectLighting, lighting); float3 positionVS = mul(UNITY_MATRIX_V, float4(positionWS, 1.0)).xyz; float3 normalVS = mul(UNITY_MATRIX_V, float4(bsdfData.normalWS, 1.0)).xyz; float3 PcrossN = cross(normalize(positionVS), normalVS); float2 uv = PcrossN.yx; uv.x *= -1; uv = uv * 0.5 + 0.5; lighting.specularReflected = SAMPLE_TEXTURE2D_LOD(_MatCapMap, s_linear_clamp_sampler, uv, PerceptualRoughnessToMipmapLevel(bsdfData.perceptualRoughness)).rgb; lighting.specularReflected *= preLightData.specularFGD * GetInverseCurrentExposureMultiplier(); return lighting; } void UtsEvaluateBSDF_Ramp(PositionInputs posInput, UtsBSDFData bsdfData, float3 L, inout BuiltinData builtinData) { // TODO } IndirectLighting UtsEvaluateBSDF_Env(LightLoopContext lightLoopContext, PositionInputs posInput, PreLightData preLightData, EnvLightData lightData, UtsBSDFData bsdfData, int influenceShapeType, int GPUImageBasedLightingType, inout float hierarchyWeight) { IndirectLighting lighting; ZERO_INITIALIZE(IndirectLighting, lighting); if (GPUImageBasedLightingType == GPUIMAGEBASEDLIGHTINGTYPE_REFRACTION) { return lighting; } float3 envLighting; float3 positionWS = posInput.positionWS; float weight = 1.0; float3 R = preLightData.iblR; if (!IsEnvIndexTexture2D(lightData.envIndex)) // ENVCACHETYPE_CUBEMAP { R = GetSpecularDominantDir(bsdfData.normalWS, R, preLightData.iblPerceptualRoughness, ClampNdotV(preLightData.NdotV)); // When we are rough, we tend to see outward shifting of the reflection when at the boundary of the projection volume // Also it appear like more sharp. To avoid these artifact and at the same time get better match to reference we lerp to original unmodified reflection. // Formula is empirical. float roughness = PerceptualRoughnessToRoughness(preLightData.iblPerceptualRoughness); R = lerp(R, preLightData.iblR, saturate(smoothstep(0, 1, roughness * roughness))); } // Note: using influenceShapeType and projectionShapeType instead of (lightData|proxyData).shapeType allow to make compiler optimization in case the type is know (like for sky) float intersectionDistance = EvaluateLight_EnvIntersection(positionWS, bsdfData.normalWS, lightData, influenceShapeType, R, weight); float3 F = preLightData.specularFGD; float4 preLD = SampleEnvWithDistanceBaseRoughness(lightLoopContext, posInput, lightData, R, preLightData.iblPerceptualRoughness, intersectionDistance); weight *= preLD.a; // Used by planar reflection to discard pixel if (GPUImageBasedLightingType == GPUIMAGEBASEDLIGHTINGTYPE_REFLECTION) { envLighting = F * preLD.rgb; // Apply the main lobe weight and update main reflection hierarchyWeight: UpdateLightingHierarchyWeights(hierarchyWeight, weight); envLighting *= weight; } envLighting *= lightData.multiplier; if (GPUImageBasedLightingType == GPUIMAGEBASEDLIGHTINGTYPE_REFLECTION) { lighting.specularReflected = envLighting; } return lighting; } void UtsPostEvaluateBSDF(PositionInputs posInput, PreLightData preLightData, UtsBSDFData bsdfData, BuiltinData builtinData, AggregateLighting lighting, out LightLoopOutput lightLoopOutput) { AmbientOcclusionFactor aoFactor; GetScreenSpaceAmbientOcclusionMultibounce(posInput.positionSS, preLightData.NdotV, bsdfData.perceptualRoughness, bsdfData.ambientOcclusion, bsdfData.specularOcclusion, bsdfData.diffuseColor, bsdfData.fresnel0, aoFactor); builtinData.bakeDiffuseLighting = APPLY_WEIGHT(builtinData.bakeDiffuseLighting, aoFactor.indirectAmbientOcclusion, _AO_Factor); lighting.indirect.specularReflected = APPLY_WEIGHT(lighting.indirect.specularReflected, aoFactor.indirectSpecularOcclusion, _AO_Factor); lighting.direct.diffuse = APPLY_WEIGHT(lighting.direct.diffuse, aoFactor.directAmbientOcclusion, _AO_Factor); lighting.direct.specular = APPLY_WEIGHT(lighting.direct.specular, aoFactor.directSpecularOcclusion, _AO_Factor); builtinData.bakeDiffuseLighting = ApplyCurrentExposureMultiplier(builtinData.bakeDiffuseLighting * bsdfData.diffuseColor * preLightData.diffuseFGD * _ID_Intensity); lighting.indirect.specularReflected = ApplyCurrentExposureMultiplier(lighting.indirect.specularReflected * bsdfData.fresnel0 * _IR_Intensity); lightLoopOutput.diffuseLighting = lighting.direct.diffuse + builtinData.bakeDiffuseLighting; lightLoopOutput.specularLighting = lighting.direct.specular + lighting.indirect.specularReflected; // Rescale the GGX to account for the multiple scattering. lightLoopOutput.specularLighting *= 1.0 + bsdfData.fresnel0 * preLightData.energyCompensation; ApplyExposureAdjustment(lightLoopOutput.diffuseLighting); ApplyExposureAdjustment(lightLoopOutput.specularLighting); lightLoopOutput.diffuseLighting += builtinData.emissiveColor; } #endif