331 lines
14 KiB
C++
331 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RayTracingInstanceMask.h"
|
|
|
|
#if RHI_RAYTRACING
|
|
|
|
#include "DataDrivenShaderPlatformInfo.h"
|
|
#include "RayTracingDefinitions.h"
|
|
#include "PathTracingDefinitions.h"
|
|
#include "PrimitiveSceneProxy.h"
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
#include "MeshPassProcessor.h"
|
|
#include "ScenePrivate.h"
|
|
|
|
namespace {
|
|
|
|
// Helper class so we can refer to both RAY_TRACING_MASK_* and PATH_TRACING_MASK_* in a unified way within this file
|
|
enum class ERayTracingInstanceMaskType : uint8
|
|
{
|
|
// General mask type for primary and secondary rays
|
|
Opaque,
|
|
Translucent,
|
|
|
|
// Mask types for shadow rays
|
|
OpaqueShadow,
|
|
TranslucentShadow,
|
|
ThinShadow,
|
|
|
|
// Geometry specific ray types
|
|
HairStrands,
|
|
|
|
// Special purpose ray types
|
|
FirstPersonWorldSpaceRepresentation,
|
|
|
|
// path tracing specific mask type
|
|
VisibleInPrimaryRay,
|
|
VisibleInIndirectRay
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
static uint8 ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType MaskType, ERayTracingType RayTracingType)
|
|
{
|
|
if (RayTracingType == ERayTracingType::RayTracing)
|
|
{
|
|
switch (MaskType)
|
|
{
|
|
case ERayTracingInstanceMaskType::Opaque:
|
|
return RAY_TRACING_MASK_OPAQUE;
|
|
case ERayTracingInstanceMaskType::Translucent:
|
|
return RAY_TRACING_MASK_TRANSLUCENT;
|
|
case ERayTracingInstanceMaskType::OpaqueShadow:
|
|
return RAY_TRACING_MASK_OPAQUE_SHADOW;
|
|
case ERayTracingInstanceMaskType::TranslucentShadow:
|
|
return RAY_TRACING_MASK_TRANSLUCENT_SHADOW;
|
|
case ERayTracingInstanceMaskType::ThinShadow:
|
|
return RAY_TRACING_MASK_THIN_SHADOW;
|
|
case ERayTracingInstanceMaskType::HairStrands:
|
|
return RAY_TRACING_MASK_HAIR_STRANDS | RAY_TRACING_MASK_THIN_SHADOW;
|
|
case ERayTracingInstanceMaskType::FirstPersonWorldSpaceRepresentation:
|
|
return RAY_TRACING_MASK_OPAQUE_FP_WORLD_SPACE;
|
|
case ERayTracingInstanceMaskType::VisibleInPrimaryRay:
|
|
return 0; // There is no distinct notion of primary ray visibility for ray tracing
|
|
case ERayTracingInstanceMaskType::VisibleInIndirectRay:
|
|
return RAY_TRACING_MASK_OPAQUE | RAY_TRACING_MASK_TRANSLUCENT | RAY_TRACING_MASK_HAIR_STRANDS;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (RayTracingType == ERayTracingType::PathTracing ||
|
|
RayTracingType == ERayTracingType::LightMapTracing)
|
|
{
|
|
switch (MaskType)
|
|
{
|
|
case ERayTracingInstanceMaskType::Opaque:
|
|
return PATHTRACER_MASK_CAMERA | PATHTRACER_MASK_INDIRECT;
|
|
case ERayTracingInstanceMaskType::Translucent:
|
|
return PATHTRACER_MASK_CAMERA_TRANSLUCENT | PATHTRACER_MASK_INDIRECT_TRANSLUCENT;
|
|
case ERayTracingInstanceMaskType::OpaqueShadow:
|
|
return PATHTRACER_MASK_SHADOW;
|
|
case ERayTracingInstanceMaskType::TranslucentShadow:
|
|
return PATHTRACER_MASK_SHADOW;
|
|
case ERayTracingInstanceMaskType::ThinShadow:
|
|
return PATHTRACER_MASK_HAIR_SHADOW;
|
|
case ERayTracingInstanceMaskType::FirstPersonWorldSpaceRepresentation:
|
|
return PATHTRACER_MASK_IGNORE;
|
|
case ERayTracingInstanceMaskType::HairStrands:
|
|
return PATHTRACER_MASK_HAIR_CAMERA | PATHTRACER_MASK_HAIR_SHADOW | PATHTRACER_MASK_HAIR_INDIRECT;
|
|
case ERayTracingInstanceMaskType::VisibleInPrimaryRay:
|
|
return PATHTRACER_MASK_CAMERA | PATHTRACER_MASK_HAIR_CAMERA | PATHTRACER_MASK_CAMERA_TRANSLUCENT;
|
|
case ERayTracingInstanceMaskType::VisibleInIndirectRay:
|
|
return PATHTRACER_MASK_INDIRECT | PATHTRACER_MASK_HAIR_INDIRECT | PATHTRACER_MASK_INDIRECT_TRANSLUCENT;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
checkNoEntry();
|
|
return 0;
|
|
}
|
|
|
|
uint8 BlendModeToRayTracingInstanceMask(const EBlendMode BlendMode, bool bIsDitherMasked, bool bCastShadow, ERayTracingType RayTracingType)
|
|
{
|
|
ERayTracingInstanceMaskType MaskTypePrimary = ERayTracingInstanceMaskType::Opaque;
|
|
ERayTracingInstanceMaskType MaskTypeShadows = ERayTracingInstanceMaskType::OpaqueShadow;
|
|
|
|
const bool bIsOpaqueOrMasked = IsOpaqueOrMaskedBlendMode(BlendMode);
|
|
|
|
// RayTracing treats dithered masked materials the same as regular masked materials for speed
|
|
// PathTracing/LightmapTracing both upgrade dithered masking to translucent internally and therefore need to take them with those bits
|
|
if ((RayTracingType == ERayTracingType::RayTracing && !bIsOpaqueOrMasked) ||
|
|
(RayTracingType != ERayTracingType::RayTracing && (!bIsOpaqueOrMasked || bIsDitherMasked)))
|
|
{
|
|
MaskTypePrimary = ERayTracingInstanceMaskType::Translucent;
|
|
MaskTypeShadows = ERayTracingInstanceMaskType::TranslucentShadow;
|
|
}
|
|
|
|
return ComputeRayTracingInstanceMask(MaskTypePrimary, RayTracingType) | (bCastShadow ? ComputeRayTracingInstanceMask(MaskTypeShadows, RayTracingType) : 0);
|
|
}
|
|
|
|
namespace {
|
|
|
|
/** Util struct and function to derive mask related info from scene proxy*/
|
|
struct FSceneProxyRayTracingMaskInfo
|
|
{
|
|
bool bVisibleToCamera = false;
|
|
bool bVisibleToShadow = false;
|
|
bool bVisibleToIndirect = false;
|
|
bool bIsFirstPersonWorldSpaceRepresentation = false;
|
|
ERayTracingType RayTracingType = ERayTracingType::RayTracing;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
static FSceneProxyRayTracingMaskInfo GetSceneProxyRayTracingMaskInfo(const FPrimitiveSceneProxy& PrimitiveSceneProxy)
|
|
{
|
|
FSceneProxyRayTracingMaskInfo MaskInfo = {};
|
|
|
|
const FScene* RenderScene = PrimitiveSceneProxy.GetScene().GetRenderScene();
|
|
MaskInfo.RayTracingType = RenderScene->CachedRayTracingMeshCommandsType;
|
|
|
|
if (PrimitiveSceneProxy.IsRayTracingFarField())
|
|
{
|
|
MaskInfo.bVisibleToCamera = true;
|
|
MaskInfo.bVisibleToShadow = true;
|
|
MaskInfo.bVisibleToIndirect = true;
|
|
}
|
|
else if (PrimitiveSceneProxy.IsDrawnInGame())
|
|
{
|
|
MaskInfo.bVisibleToCamera = true;
|
|
MaskInfo.bVisibleToShadow = true;
|
|
// NOTE: For backwards compatibility, only path tracing obeys the AffectsDynamicIndirectLighting flag
|
|
MaskInfo.bVisibleToIndirect = MaskInfo.RayTracingType == ERayTracingType::RayTracing ? true : PrimitiveSceneProxy.AffectsDynamicIndirectLighting();
|
|
}
|
|
else
|
|
{
|
|
MaskInfo.bVisibleToCamera = false;
|
|
MaskInfo.bVisibleToShadow = PrimitiveSceneProxy.CastsHiddenShadow();
|
|
MaskInfo.bVisibleToIndirect = PrimitiveSceneProxy.AffectsIndirectLightingWhileHidden();
|
|
}
|
|
|
|
MaskInfo.bIsFirstPersonWorldSpaceRepresentation = PrimitiveSceneProxy.IsFirstPersonWorldSpaceRepresentation();
|
|
|
|
return MaskInfo;
|
|
}
|
|
|
|
static uint8 ApplyFirstPersonRayTracingInstanceMaskFlag(uint8 Mask, ERayTracingType RayTracingType, bool bIsFirstPersonWorldSpaceRepresentation)
|
|
{
|
|
if (bIsFirstPersonWorldSpaceRepresentation)
|
|
{
|
|
const bool bIsOpaque = (Mask & (ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::Opaque, RayTracingType) | ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::OpaqueShadow, RayTracingType))) != 0;
|
|
|
|
// Tag world space representations of first person meshes so rays originating from first person meshes can skip them.
|
|
// We currently only support opaque world space representations of first person objects, so set the mask to 0 otherwise.
|
|
Mask = bIsOpaque ? ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::FirstPersonWorldSpaceRepresentation, RayTracingType) : 0;
|
|
}
|
|
return Mask;
|
|
}
|
|
|
|
FRayTracingMaskAndFlags BuildRayTracingInstanceMaskAndFlags(const FRayTracingInstance& Instance, const FPrimitiveSceneProxy& PrimitiveSceneProxy)
|
|
{
|
|
FSceneProxyRayTracingMaskInfo MaskInfo = GetSceneProxyRayTracingMaskInfo(PrimitiveSceneProxy);
|
|
const ERHIFeatureLevel::Type FeatureLevel = PrimitiveSceneProxy.GetScene().GetFeatureLevel();
|
|
const ERayTracingType RayTracingType = MaskInfo.RayTracingType;
|
|
|
|
FRayTracingMaskAndFlags Result;
|
|
|
|
ensureMsgf(Instance.GetMaterials().Num() > 0, TEXT("You need to add MeshBatches first for instance mask and flags to build upon."));
|
|
|
|
bool bAllSegmentsOpaque = true;
|
|
bool bAnySegmentsCastShadow = false;
|
|
bool bAllSegmentsCastShadow = true;
|
|
bool bAnySegmentsDecal = false;
|
|
bool bAllSegmentsDecal = true;
|
|
bool bDoubleSided = false;
|
|
bool bAllSegmentsReverseCulling = true;
|
|
|
|
Result.Mask = 0;
|
|
for (const FMeshBatch& MeshBatch : Instance.GetMaterials())
|
|
{
|
|
// Mesh Batches can "null" when they have zero triangles. Check the MaterialRenderProxy before accessing.
|
|
if (MeshBatch.bUseForMaterial && MeshBatch.MaterialRenderProxy)
|
|
{
|
|
const FMaterial& Material = MeshBatch.MaterialRenderProxy->GetIncompleteMaterialWithFallback(FeatureLevel);
|
|
const EBlendMode BlendMode = Material.GetBlendMode();
|
|
const bool bSegmentCastsShadow = MaskInfo.bVisibleToShadow && MeshBatch.CastRayTracedShadow && Material.CastsRayTracedShadows() && BlendMode != BLEND_Additive;
|
|
|
|
Result.Mask |= BlendModeToRayTracingInstanceMask(BlendMode, Material.IsDitherMasked(), bSegmentCastsShadow, RayTracingType);
|
|
bAllSegmentsOpaque &= BlendMode == EBlendMode::BLEND_Opaque;
|
|
bAnySegmentsCastShadow |= bSegmentCastsShadow;
|
|
bAllSegmentsCastShadow &= bSegmentCastsShadow;
|
|
bAnySegmentsDecal |= Material.IsDeferredDecal();
|
|
bAllSegmentsDecal &= Material.IsDeferredDecal();
|
|
bDoubleSided |= MeshBatch.bDisableBackfaceCulling || Material.IsTwoSided();
|
|
bAllSegmentsReverseCulling &= MeshBatch.ReverseCulling;
|
|
}
|
|
}
|
|
|
|
// Run AHS for alpha masked and meshes with only some sections casting shadows, which require per mesh section filtering in AHS
|
|
Result.bForceOpaque = bAllSegmentsOpaque && (bAllSegmentsCastShadow || !bAnySegmentsCastShadow);
|
|
// Consider that all segments are translucent if none of the mask bits contain Opaque or OpaqueShadow
|
|
const uint8 OpaqueMask =
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::Opaque , RayTracingType) |
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::OpaqueShadow, RayTracingType);
|
|
Result.bAllSegmentsTranslucent = Result.Mask != 0 && (Result.Mask & OpaqueMask) == 0;
|
|
|
|
Result.bDoubleSided = bDoubleSided;
|
|
Result.bAnySegmentsDecal = bAnySegmentsDecal;
|
|
Result.bAllSegmentsDecal = bAllSegmentsDecal;
|
|
Result.bReverseCulling = bAllSegmentsReverseCulling;
|
|
|
|
const bool bIsHairStrands = Instance.bThinGeometry;
|
|
if (bIsHairStrands)
|
|
{
|
|
// Reset all hair strands bits "on"
|
|
Result.Mask = ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::HairStrands, RayTracingType);
|
|
Result.bForceOpaque = true;
|
|
Result.bAllSegmentsTranslucent = false;
|
|
}
|
|
|
|
if (!MaskInfo.bVisibleToCamera)
|
|
{
|
|
// If the object is not visible to camera, remove all direct visibility bits.
|
|
Result.Mask &= ~ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::VisibleInPrimaryRay, RayTracingType);
|
|
}
|
|
|
|
if (!MaskInfo.bVisibleToIndirect)
|
|
{
|
|
// If the object does not affect indirect lighting, remove all indirect bits.
|
|
Result.Mask &= ~ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::VisibleInIndirectRay, RayTracingType);
|
|
}
|
|
|
|
if (!bAnySegmentsCastShadow)
|
|
{
|
|
// Not casting shadows, remove any set shadow flags
|
|
Result.Mask &= ~(
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::OpaqueShadow, RayTracingType) |
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::TranslucentShadow, RayTracingType) |
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::ThinShadow, RayTracingType)
|
|
);
|
|
}
|
|
|
|
Result.Mask = ApplyFirstPersonRayTracingInstanceMaskFlag(Result.Mask, RayTracingType, MaskInfo.bIsFirstPersonWorldSpaceRepresentation);
|
|
|
|
return Result;
|
|
}
|
|
|
|
void SetupRayTracingMeshCommandMaskAndStatus(FRayTracingMeshCommand& MeshCommand, const FMeshBatch& MeshBatch, const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterial& MaterialResource, ERayTracingType RayTracingType)
|
|
{
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
|
|
MeshCommand.bCastRayTracedShadows = MeshBatch.CastRayTracedShadow && MaterialResource.CastsRayTracedShadows() && MaterialResource.GetBlendMode() != BLEND_Additive;
|
|
MeshCommand.bOpaque = MaterialResource.GetBlendMode() == EBlendMode::BLEND_Opaque && !(VertexFactory->GetType()->SupportsRayTracingProceduralPrimitive() && FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(GMaxRHIShaderPlatform));
|
|
MeshCommand.bAlphaMasked = MaterialResource.GetBlendMode() == EBlendMode::BLEND_Masked; // Used by Lumen only
|
|
MeshCommand.bDecal = MaterialResource.IsDeferredDecal();
|
|
MeshCommand.bIsSky = MaterialResource.IsSky();
|
|
MeshCommand.bTwoSided = MaterialResource.IsTwoSided();
|
|
MeshCommand.bIsTranslucent = MaterialResource.GetBlendMode() == EBlendMode::BLEND_Translucent;
|
|
MeshCommand.bReverseCulling = MeshBatch.ReverseCulling;
|
|
|
|
MeshCommand.InstanceMask = BlendModeToRayTracingInstanceMask(MaterialResource.GetBlendMode(), MaterialResource.IsDitherMasked(), MeshCommand.bCastRayTracedShadows, RayTracingType);
|
|
|
|
if (!PrimitiveSceneProxy)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// MeshBatch.ReverseCulling is generally not what we want as the value could be set including the transform's orientation.
|
|
// This is because cached mesh commands are shared with rasterization.
|
|
// For ray tracing, only the user decision of wanting reversed culling matters, so query this directly here.
|
|
// In the case that that this mesh command is not associated with a primitive, the mesh batch value will still apply.
|
|
MeshCommand.bReverseCulling = PrimitiveSceneProxy->IsCullingReversedByComponent();
|
|
|
|
MeshCommand.bNaniteRayTracing = (Nanite::GetRayTracingMode() != Nanite::ERayTracingMode::Fallback) && PrimitiveSceneProxy->IsNaniteMesh();
|
|
|
|
FSceneProxyRayTracingMaskInfo MaskInfo = GetSceneProxyRayTracingMaskInfo(*PrimitiveSceneProxy);
|
|
|
|
MeshCommand.InstanceMask = ApplyFirstPersonRayTracingInstanceMaskFlag(MeshCommand.InstanceMask, RayTracingType, MaskInfo.bIsFirstPersonWorldSpaceRepresentation);
|
|
|
|
// TODO: This should be done once all mesh commands for a mesh are combined (similar to BuildRayTracingInstanceMaskAndFlags above)
|
|
// TODO: Why is this logic not applied in the ray tracing case?
|
|
if (RayTracingType == ERayTracingType::PathTracing ||
|
|
RayTracingType == ERayTracingType::LightMapTracing)
|
|
{
|
|
|
|
if (!MaskInfo.bVisibleToCamera)
|
|
{
|
|
// If the object is not visible to camera, remove all direct visibility bits.
|
|
MeshCommand.InstanceMask &= ~ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::VisibleInPrimaryRay, RayTracingType);
|
|
}
|
|
|
|
if (!MaskInfo.bVisibleToIndirect)
|
|
{
|
|
// If the object does not affect indirect lighting, remove all indirect bits.
|
|
MeshCommand.InstanceMask &= ~ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::VisibleInIndirectRay, RayTracingType);
|
|
}
|
|
|
|
if (!MaskInfo.bVisibleToShadow || !MeshCommand.bCastRayTracedShadows)
|
|
{
|
|
// Not casting shadows, remove any set shadow flags
|
|
MeshCommand.InstanceMask &= ~(
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::OpaqueShadow, RayTracingType) |
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::TranslucentShadow, RayTracingType) |
|
|
ComputeRayTracingInstanceMask(ERayTracingInstanceMaskType::ThinShadow, RayTracingType));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|