float4 GetSphereCaptureVector(float3 ReflectionVector, float3 WorldPosition, float4 CapturePositionAndRadius) { float4 ProjectedCaptureVector; ProjectedCaptureVector.w = 0; ProjectedCaptureVector.xyz = ReflectionVector; float3 RayDirection = ReflectionVector; float ProjectionSphereRadius = CapturePositionAndRadius.w * 1.2f; float SphereRadiusSquared = ProjectionSphereRadius * ProjectionSphereRadius; float3 ReceiverToSphereCenter = WorldPosition - CapturePositionAndRadius.xyz; float ReceiverToSphereCenterSq = dot(ReceiverToSphereCenter, ReceiverToSphereCenter); float3 CaptureVector = WorldPosition - CapturePositionAndRadius.xyz; float CaptureVectorLength = sqrt(dot(CaptureVector, CaptureVector)); float NormalizedDistanceToCapture = saturate(CaptureVectorLength / CapturePositionAndRadius.w); // Find the intersection between the ray along the reflection vector and the capture's sphere float3 QuadraticCoef; QuadraticCoef.x = 1; QuadraticCoef.y = 2 * dot(RayDirection, ReceiverToSphereCenter); QuadraticCoef.z = ReceiverToSphereCenterSq - SphereRadiusSquared; float Determinant = QuadraticCoef.y * QuadraticCoef.y - 4 * QuadraticCoef.z; BRANCH // Only continue if the ray intersects the sphere if (Determinant >= 0) { float FarIntersection = (sqrt(Determinant) - QuadraticCoef.y) * 0.5; float3 IntersectPosition = WorldPosition + FarIntersection * RayDirection; ProjectedCaptureVector.xyz = IntersectPosition - CapturePositionAndRadius.xyz; // Fade out based on distance to capture ProjectedCaptureVector.w = 1.0 - smoothstep(.6, 1, NormalizedDistanceToCapture); } return ProjectedCaptureVector; }
MobileBasePassPixelShader.usf文件里 SpecularIBL的计算:
// Environment map has been prenormalized, scale by lightmap luminance half3 SpecularIBL = GetImageBasedReflectionLighting(MaterialParameters, Roughness) * IndirectIrradiance;
half3 GetImageBasedReflectionLighting(FMaterialPixelParameters MaterialParameters, half Roughness) { #if HQ_REFLECTIONS half3 SpecularIBL = BlendReflectionCaptures(MaterialParameters, Roughness); #else half3 ProjectedCaptureVector = MaterialParameters.ReflectionVector; half UsingSkyReflection = MobileReflectionParams.w > 0; half CubemapMaxMip = UsingSkyReflection ? MobileReflectionParams.w : ResolvedView.ReflectionCubemapMaxMip; // Compute fractional mip from roughness half AbsoluteSpecularMip = ComputeReflectionCaptureMipFromRoughness(Roughness, CubemapMaxMip); // Fetch from cubemap and convert to linear HDR half3 SpecularIBL; half4 SpecularIBLSample = ReflectionCubemap.SampleLevel(ReflectionCubemapSampler, ProjectedCaptureVector, AbsoluteSpecularMip); if (UsingSkyReflection) { SpecularIBL = SpecularIBLSample.rgb; // Apply sky colour if the reflection map is the sky. SpecularIBL *= ResolvedView.SkyLightColor.rgb; } else { SpecularIBL = RGBMDecode(SpecularIBLSample, 16.0); SpecularIBL = SpecularIBL * SpecularIBL; #if LQ_TEXTURE_LIGHTMAP || CACHED_POINT_INDIRECT_LIGHTING // divide by average brightness for lightmap use cases only. SpecularIBL *= MobileReflectionParams.x; #endif } #endif #if WEBGL // need a rgb swizzle instead of the existing rgba swizzle, we should add it if another use case comes up. return SpecularIBL.bgr; #else return SpecularIBL; #endif }
half3 BlendReflectionCaptures(FMaterialPixelParameters MaterialParameters, half Roughness) { // Compute fractional mip from roughness half AbsoluteSpecularMip = ComputeReflectionCaptureMipFromRoughness(Roughness, ResolvedView.ReflectionCubemapMaxMip); half3 SpecularIBL = half3(0, 0, 0); half BlendFactor = 1; SpecularIBL += BlendReflectionCapture(MaterialParameters, Roughness, ReflectionCubemap0, ReflectionCubemapSampler0, AbsoluteSpecularMip, 0, ReflectionInvAverageBrigtness.x, BlendFactor); SpecularIBL += BlendReflectionCapture(MaterialParameters, Roughness, ReflectionCubemap1, ReflectionCubemapSampler1, AbsoluteSpecularMip, 1, ReflectionInvAverageBrigtness.y, BlendFactor); SpecularIBL += BlendReflectionCapture(MaterialParameters, Roughness, ReflectionCubemap2, ReflectionCubemapSampler2, AbsoluteSpecularMip, 2, ReflectionInvAverageBrigtness.z, BlendFactor); return SpecularIBL; }
half3 BlendReflectionCapture(FMaterialPixelParameters MaterialParameters, half Roughness, TextureCube ReflectionCube, SamplerState ReflectionSampler, half MipLevel, int ReflectionIndex, half InvReflectionCubemapAverageBrightness, inout half BlendFactor) { half4 ProjectedCaptureVector; #if ALLOW_CUBE_REFLECTIONS if (CaptureBoxScalesArray[ReflectionIndex].w > 0) { ProjectedCaptureVector = GetBoxCaptureVector(MaterialParameters.ReflectionVector, MaterialParameters.AbsoluteWorldPosition, ReflectionPositionsAndRadii[ReflectionIndex], CaptureBoxTransformArray[ReflectionIndex], CaptureBoxScalesArray[ReflectionIndex]); } else #endif { ProjectedCaptureVector = GetSphereCaptureVector(MaterialParameters.ReflectionVector, MaterialParameters.AbsoluteWorldPosition, ReflectionPositionsAndRadii[ReflectionIndex]); } // Fetch from cubemap and convert to linear HDR float4 Reflection = ReflectionCube.SampleLevel(ReflectionSampler, ProjectedCaptureVector.xyz, MipLevel); half3 SpecularIBL = RGBMDecode(Reflection, 16.0f); #if !LQ_TEXTURE_LIGHTMAP && !CACHED_POINT_INDIRECT_LIGHTING // ignore average brightness for non-lightmap use case. InvReflectionCubemapAverageBrightness = 1.0f; #endif SpecularIBL = SpecularIBL * SpecularIBL * (InvReflectionCubemapAverageBrightness * ProjectedCaptureVector.w * BlendFactor); BlendFactor = BlendFactor * (1.0 - ProjectedCaptureVector.w); return SpecularIBL; }