Files
UnrealEngine/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.inl
2025-05-18 13:04:45 +08:00

220 lines
8.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
template<int32 SHOrder>
TGatheredLightSample<SHOrder> FGatheredLightSampleUtil::AmbientLight(const FLinearColor& Color)
{
TGatheredLightSample<SHOrder> Result;
Result.SHVector.AddAmbient(Color);
// Compute SHCorrection as if all the lighting was coming in along the normal
FVector4f TangentDirection(0, 0, 1);
FSHVector2 SH = FSHVector2::SHBasisFunction(FVector4(TangentDirection));
Result.SHCorrection = Color.GetLuminance() * (0.282095f * SH.V[0] + 0.325735f * SH.V[2]);
Result.IncidentLighting = Color;
checkSlow(Result.SHCorrection >= 0 && Result.IncidentLighting.GetMin() >= 0);
return Result;
}
template<int32 SHOrder>
TGatheredLightSample<SHOrder> FGatheredLightSampleUtil::PointLightWorldSpace(const FLinearColor& Color, const FVector4f& TangentDirection, const FVector4f& WorldDirection)
{
TGatheredLightSample<SHOrder> Result;
if (TangentDirection.Z >= 0.0f)
{
Result.SHVector.AddIncomingRadiance(Color, 1, FVector4(WorldDirection));
FSHVector2 SH = FSHVector2::SHBasisFunction(FVector4(TangentDirection));
// Evaluate lighting along the smoothed vertex normal direction, so that later we can guarantee an SH intensity of 1 along the normal
// These scaling coefficients are SHBasisFunction and CalcDiffuseTransferSH baked down
// 0.325735f = 0.488603f from SHBasisFunction * 2/3 from CalcDiffuseTransferSH
// Only using V[2] which is the tangent space Z
Result.SHCorrection = Color.GetLuminance() * (0.282095f * SH.V[0] + 0.325735f * SH.V[2]);
Result.IncidentLighting = Color * FMath::Max(0.0f, TangentDirection.Z);
checkSlow(Result.SHCorrection >= 0 && Result.IncidentLighting.GetMin() >= 0);
}
return Result;
}
template<int32 SHOrder>
void TGatheredLightSample<SHOrder>::AddWeighted(const TGatheredLightSample<SHOrder>& OtherSample, float Weight)
{
SHVector += OtherSample.SHVector * Weight;
SHCorrection += OtherSample.SHCorrection * Weight;
IncidentLighting += OtherSample.IncidentLighting * Weight;
SkyOcclusion += OtherSample.SkyOcclusion * Weight;
AOMaterialMask += OtherSample.AOMaterialMask * Weight;
}
template void TGatheredLightSample<2>::AddWeighted(const TGatheredLightSample<2>& OtherSample, float Weight);
template void TGatheredLightSample<3>::AddWeighted(const TGatheredLightSample<3>& OtherSample, float Weight);
template<int32 SHOrder>
void TGatheredLightSample<SHOrder>::ApplyOcclusion(float Occlusion)
{
SHVector *= Occlusion;
SHCorrection *= Occlusion;
IncidentLighting *= Occlusion;
}
template void TGatheredLightSample<2>::ApplyOcclusion(float Occlusion);
template void TGatheredLightSample<3>::ApplyOcclusion(float Occlusion);
template<int32 SHOrder>
bool TGatheredLightSample<SHOrder>::AreFloatsValid() const
{
return SHVector.AreFloatsValid()
&& FMath::IsFinite(SHCorrection) && !FMath::IsNaN(SHCorrection)
&& FLinearColorUtils::AreFloatsValid(IncidentLighting);
}
template bool TGatheredLightSample<2>::AreFloatsValid() const;
template bool TGatheredLightSample<3>::AreFloatsValid() const;
template<int32 SHOrder>
void TFinalGatherSample<SHOrder>::AddWeighted(const TFinalGatherSample<SHOrder>& OtherSample, float Weight)
{
TGatheredLightSample<SHOrder>::AddWeighted(OtherSample, Weight);
Occlusion += OtherSample.Occlusion * Weight;
StationarySkyLighting = StationarySkyLighting + OtherSample.StationarySkyLighting * Weight;
}
template<int32 SHOrder>
bool TFinalGatherSample<SHOrder>::AreFloatsValid() const
{
return TGatheredLightSample<SHOrder>::AreFloatsValid() && FMath::IsFinite(Occlusion) && !FMath::IsNaN(Occlusion);
}
template<int32 SHOrder>
void FStaticLightingSystem::CalculateApproximateDirectLighting(
const FStaticLightingVertex& Vertex,
float SampleRadius,
const TArray<FVector3f, TInlineAllocator<1>>& VertexOffsets,
float LightSampleFraction,
bool bCompositeAllLights,
bool bCalculateForIndirectLighting,
bool bDebugThisSample,
FStaticLightingMappingContext& MappingContext,
TGatheredLightSample<SHOrder>& OutStaticDirectLighting,
float& OutToggleableDirectionalLightShadowing) const
{
check(VertexOffsets.Num() > 0);
for (int32 LightIndex = 0; LightIndex < Lights.Num(); LightIndex++)
{
const FLight* Light = Lights[LightIndex];
if (Light->AffectsBounds(FBoxSphereBounds3f(FSphere3f(Vertex.WorldPosition, SampleRadius))))
{
FLinearColor LightIntensity(0, 0, 0, 0);
for (int32 OffsetIndex = 0; OffsetIndex < VertexOffsets.Num(); OffsetIndex++)
{
LightIntensity += Light->GetDirectIntensity(Vertex.WorldPosition + VertexOffsets[OffsetIndex] * SampleRadius, bCalculateForIndirectLighting);
}
LightIntensity /= (float)VertexOffsets.Num();
FLinearColor Transmission = FLinearColor::Black;
if ((Light->LightFlags & GI_LIGHT_CASTSHADOWS) && (Light->LightFlags & GI_LIGHT_CASTSTATICSHADOWS))
{
int32 UnShadowedRays = 0;
FLinearColor UnnormalizedTransmission = FLinearColor::Black;
const TArray<FLightSurfaceSample>& LightSurfaceSamples = Light->GetCachedSurfaceSamples(0, false);
const int32 NumSamplesToTrace = FMath::Max(FMath::TruncToInt(LightSurfaceSamples.Num() * LightSampleFraction), 1);
for (int32 RayIndex = 0; RayIndex < NumSamplesToTrace; RayIndex++)
{
FLightSurfaceSample CurrentSample = LightSurfaceSamples[RayIndex];
// Allow the light to modify the surface position for this receiving position
Light->ValidateSurfaceSample(Vertex.WorldPosition, CurrentSample);
// Construct a line segment between the light and the volume point.
const FVector4f LightVector = CurrentSample.Position - Vertex.WorldPosition;
FVector4f NormalForOffset = Vertex.WorldTangentZ;
const FVector4f StartOffset = LightVector.GetSafeNormal() * SceneConstants.VisibilityRayOffsetDistance
+ NormalForOffset * SampleRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale;
const FLightRay LightRay(
// Offset the start of the ray by some fraction along the direction of the ray and some fraction along the vertex normal.
Vertex.WorldPosition
+ StartOffset,
Vertex.WorldPosition + LightVector,
NULL,
Light
);
// Check the line segment for intersection with the static lighting meshes.
FLightRayIntersection Intersection;
//@todo - change this back to request boolean visibility once transmission is supported with boolean visibility ray intersections
AggregateMesh->IntersectLightRay(LightRay, true, true, true, MappingContext.RayCache, Intersection);
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
if (bDebugThisSample)
{
FDebugStaticLightingRay DebugRay(LightRay.Start, LightRay.End, Intersection.bIntersects);
if (Intersection.bIntersects)
{
DebugRay.End = Intersection.IntersectionVertex.WorldPosition;
}
MappingContext.DebugOutput->ShadowRays.Add(DebugRay);
}
#endif
if (!Intersection.bIntersects)
{
UnnormalizedTransmission += Intersection.Transmission;
UnShadowedRays++;
}
}
if (UnShadowedRays > 0)
{
Transmission = UnnormalizedTransmission / UnShadowedRays;
}
}
else
{
// Shadow casting disabled on this light
Transmission = FLinearColor::White;
}
{
// Calculate the direction from the vertex to the light.
const FVector4f WorldLightVector = Light->GetDirectLightingDirection(Vertex.WorldPosition, Vertex.WorldTangentZ);
// Transform the light vector to tangent space.
const FVector4f TangentLightVector =
FVector4f(
Dot3(WorldLightVector, Vertex.WorldTangentX),
Dot3(WorldLightVector, Vertex.WorldTangentY),
Dot3(WorldLightVector, Vertex.WorldTangentZ),
0
).GetSafeNormal();
// Compute the incident lighting of the light on the vertex.
const FLinearColor FinalIntensity = LightIntensity * Transmission;
// Compute the light-map sample for the front-face of the vertex.
TGatheredLightSample<SHOrder> Lighting = FGatheredLightSampleUtil::PointLightWorldSpace<SHOrder>(FinalIntensity, TangentLightVector, WorldLightVector.GetSafeNormal());
if (Light->UseStaticLighting() || bCompositeAllLights)
{
OutStaticDirectLighting = OutStaticDirectLighting + Lighting;
}
else if (Light->GetDirectionalLight() != NULL)
{
OutToggleableDirectionalLightShadowing = Transmission.GetLuminance();
}
}
}
}
}