Files
UnrealEngine/Engine/Shaders/Private/RayTracing/RayTracingCommon.ush
2025-05-18 13:04:45 +08:00

1445 lines
57 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
RayTracingCommon.ush: common header used in multiple ray generation shaders
=============================================================================*/
#pragma once
#ifndef RAYTRACINGCOMMON_USH_INCLUDED
#define RAYTRACINGCOMMON_USH_INCLUDED // Workarround for UE-66460
// Make sure we recompile ray tracing shaders if the main shader version changes
#include "/Engine/Public/ShaderVersion.ush"
// Update this GUID to invalidate RayTracing shaders and force a shader recompilation.
#pragma message("UESHADERMETADATA_VERSION 82EF3461-600F-45F4-94A7-F2D9EF032386")
#include "../ShadingCommon.ush"
#include "../OctahedralCommon.ush"
#include "../SceneData.ush"
#include "../Common.ush"
#include "/Engine/Shared/RayTracingDefinitions.h"
#include "/Engine/Shared/RayTracingPayloadType.h"
#include "RayTracingRayCone.ush"
#if SUBSTRATE_ENABLED && MATERIAL_IS_SUBSTRATE
#define SUBSTRATE_INLINE_SHADING 1
#endif
#include "../Substrate/Substrate.ush"
#ifdef OVERRIDE_RAYTRACING_USH
#include "/Platform/Private/RayTracing.ush"
#endif // OVERRIDE_RAYTRACING_USH
#if COMPILER_METAL
#include "/Engine/Private/RayTracing/RayTracingCommonMetal.ush"
#endif // COMPILER_METAL
// Certain DXR system value intrinsics are explicitly disallowed in Unreal Engine entirely.
// Supported cross-platform system value intrinsics:
// RayGen Intersection AnyHit ClosestHit Miss Callable
// uint3 DispatchRaysIndex() YES - - - - -
// uint3 DispatchRaysDimensions() YES - - - - -
// float3 WorldRayOrigin() - YES* YES YES YES -
// float3 WorldRayDirection() - YES* YES YES YES -
// float RayTMin() - - - - - -
// float RayTCurrent() - YES* YES YES YES -
// float RayFlags() - - - - - -
// uint InstanceIndex() - YES* YES YES - -
// uint InstanceID() - YES* YES YES - -
// uint GeometryIndex() - - - - - -
// uint PrimitiveIndex() - YES* YES YES - -
// float3 ObjectRayOrigin() - - - - - -
// float3 ObjectRayDirection() - - - - - -
// float3x4 ObjectToWorld3x4() - - - - - -
// float4x3 ObjectToWorld4x3() - - - - - -
// float3x4 WorldToObject3x4() - - - - - -
// float4x3 WorldToObject4x3() - - - - - -
// uint HitKind() - - YES YES - -
// * NOTE: Intersection shaders are only supported in DXR.
#define ALLOW_ALL_DXR_INTRINSICS 1
#if !ALLOW_ALL_DXR_INTRINSICS
#if !RAYGENSHADER
// These intrinsics are allowed by DXR in all shader types, but we only support them in RayGen.
// If values are required, they must be explicitly passed through the payload structure.
#define DispatchRaysIndex() DispatchRaysIndex_is_only_supported_in_raygen_shaders
#define DispatchRaysDimensions() DispatchRaysDimensions_is_only_supported_in_raygen_shaders
#endif // !RAYGENSHADER
// These DXR intrinsics are disallowed for better cross-platform compatibility and performance.
#define RayTMin() RayTMin_is_not_supported
#define RayFlags() RayFlags_is_not_supported
#define GeometryIndex() GeometryIndex_is_not_supported
#define ObjectRayOrigin() ObjectRayOrigin_is_not_supported
#define ObjectRayDirection() ObjectRayDirection_is_not_supported
#define ObjectToWorld3x4() ObjectToWorld3x4_is_not_supported
#define ObjectToWorld4x3() ObjectToWorld4x3_is_not_supported
#define WorldToObject3x4() WorldToObject3x4_is_not_supported
#define WorldToObject4x3() WorldToObject4x3_is_not_supported
#endif // !ALLOW_ALL_DXR_INTRINSICS
// Define generic wrappers for ray tracing shader entry points, if not already overriden
#ifndef RAY_TRACING_ENTRY_RAYGEN
#define RAY_TRACING_ENTRY_RAYGEN(name)\
[shader("raygeneration")] void name()
#endif // RAY_TRACING_ENTRY_RAYGEN
#ifndef RAY_TRACING_ENTRY_INTERSECTION
#define RAY_TRACING_ENTRY_INTERSECTION(name)\
[shader("intersection")] void name()
#endif //RAY_TRACING_ENTRY_INTERSECTION
#ifndef RAY_TRACING_ENTRY_CLOSEST_HIT
#define RAY_TRACING_ENTRY_CLOSEST_HIT(name, payload_type, payload_name, attributes_type, attributes_name)\
[shader("closesthit")] void name(inout payload_type payload_name, in attributes_type attributes_name)
#endif //RAY_TRACING_ENTRY_CLOSEST_HIT
#ifndef RAY_TRACING_ENTRY_ANY_HIT
#define RAY_TRACING_ENTRY_ANY_HIT(name, payload_type, payload_name, attributes_type, attributes_name)\
[shader("anyhit")] void name(inout payload_type payload_name, in attributes_type attributes_name)
#endif // RAY_TRACING_ENTRY_ANY_HIT
#ifndef RAY_TRACING_ENTRY_MISS
#define RAY_TRACING_ENTRY_MISS(name, payload_type, payload_name)\
[shader("miss")] void name(inout payload_type payload_name)
#endif //RAY_TRACING_ENTRY_MISS
#ifndef RAY_TRACING_ENTRY_CALLABLE
#define RAY_TRACING_ENTRY_CALLABLE(name, params_type, params_name)\
[shader("callable")] void name(inout params_type params_name)
#endif //RAY_TRACING_ENTRY_CALLABLE
// Helper macro to set common entry point for passes that have both inline (CS) and non-inline (RGS) version.
// Given "name" it will create "nameCS" entry point for compute and "nameRGS" for raygen shader.
#ifndef RAY_TRACING_ENTRY_RAYGEN_OR_INLINE
#if COMPUTESHADER && PLATFORM_SUPPORTS_INLINE_RAY_TRACING
#define RAY_TRACING_ENTRY_RAYGEN_OR_INLINE(name)\
void name##_INTERNAL(uint3 DispatchThreadIndex);\
[numthreads(INLINE_RAY_TRACING_THREAD_GROUP_SIZE_X, INLINE_RAY_TRACING_THREAD_GROUP_SIZE_Y, 1)]\
void name##CS(uint3 DispatchThreadIndex : SV_DispatchThreadID) {\
name##_INTERNAL(DispatchThreadIndex);}\
void name##_INTERNAL(uint3 DispatchThreadIndex)
#else // COMPUTESHADER && PLATFORM_SUPPORTS_INLINE_RAY_TRACING
#define RAY_TRACING_ENTRY_RAYGEN_OR_INLINE(name)\
void name##_INTERNAL(uint3 DispatchThreadIndex);\
RAY_TRACING_ENTRY_RAYGEN(name##RGS){\
name##_INTERNAL(DispatchRaysIndex());}\
void name##_INTERNAL(uint3 DispatchThreadIndex)
#endif // COMPUTESHADER && PLATFORM_SUPPORTS_INLINE_RAY_TRACING
#endif // RAY_TRACING_ENTRY_RAYGEN_OR_INLINE
#ifndef OVERRIDE_PLATFORM_RAYTRACING_INSTANCE_DESCRITOR
// Must match D3D12_RAYTRACING_INSTANCE_FLAGS / VkGeometryInstanceFlagBitsKHR
#define PLATFORM_RAYTRACING_INSTANCE_FLAG_NONE 0
#define PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE 0x1
#define PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_FRONT_COUNTERCLOCKWISE 0x2
#define PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE 0x4
#define PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE 0x8
#endif // OVERRIDE_PLATFORM_RAYTRACING_INSTANCE_DESCRITOR
// must match ERayTracingInstanceFlags
#define RAYTRACING_INSTANCE_FLAG_NONE 0
#define RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE (1 << 1)
#define RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_REVERSE (1 << 2)
#define RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE (1 << 3)
#define RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE (1 << 4)
#define RAY_TRACING_BLEND_MODE_OPAQUE 0
#define RAY_TRACING_BLEND_MODE_ALPHA_COMPOSITE 1
#define RAY_TRACING_BLEND_MODE_TRANSLUCENT 2
#define RAY_TRACING_BLEND_MODE_ADDITIVE 3
#define RAY_TRACING_BLEND_MODE_MODULATE 4
#define RAY_TRACING_BLEND_MODE_ALPHA_HOLDOUT 5
// Check how Flags are packed in the FPackedMaterialClosestHitPayload before adding to ensure bits are free (current packing gives us 6 bits for input and output)
// There are two sets of flags:
// - "inputs" are set before calling TraceRay
// - "outputs" are set during execution of the CHS and are valid after TraceRay returns
// These two sets of masks can overlap because they never co-exist
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_NONE 0
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE (1 << 0) // Indicates that ray has hit front face of a primitive. Set by closest hit shader.
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED (1 << 1) // Ray hit a two-sided material
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT (1 << 2) // We hit a primitive flagged as a holdout
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_HAS_INDIRECT_LIGHTING (1 << 3) // Payload contains valid indirect lighting
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_UNUSED4 (1 << 4) // Free for future use
#define RAY_TRACING_PAYLOAD_OUTPUT_FLAG_UNUSED5 (1 << 5) // Free for future use
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_NONE 0
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_MINIMAL_PAYLOAD (1 << 0) // Indicates that closest hit shader should only fill FMinimalPayload (HitT), skipping full material evaluation. Set by RayGen shader, before TraceRay().
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_ENABLE_SKY_LIGHT_CONTRIBUTION (1 << 1) // Indicates that hit shaders should add in the Sky Light contribution in their output. Set by RayGen shader, before TraceRay().
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_LUMEN_PAYLOAD (1 << 2) // Indicates that closest hit shader should evaluate geometry normal and instanceid.
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT (1 << 3) // Indicates that translucent materials should be ignored by IgnoreHit() in anyhit shaders.
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY (1 << 4) // Indicates this ray is being traced as part of a raytraced shadow calculation (see TraceVisibilityRay).
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_CAMERA_RAY (1 << 5) // Indicates this ray is being traced directly from the camera
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_INDIRECT_LIGHTING_RAY (1 << 6) // Indicates this ray is ray is a part of indirect lighting (GI or reflections) calculations
#define RAY_TRACING_PAYLOAD_INPUT_FLAG_UNUSED (1 << 7) // Free for future use
///////////////////////////////////////////////////////////////////////////////////////////////////
// Instance descriptor
#ifndef OVERRIDE_PLATFORM_RAYTRACING_INSTANCE_DESCRITOR
#define OVERRIDE_PLATFORM_RAYTRACING_INSTANCE_DESCRITOR
// Must match D3D12_RAYTRACING_INSTANCE_DESC / VkAccelerationStructureInstanceKHR
struct FPlatformRayTracingInstanceDescriptor
{
float3x4 Transform;
uint InstanceIdAndMask;
uint InstanceContributionToHitGroupIndexAndFlags;
uint2 AccelerationStructure; //uint64
uint GetInstanceId()
{
return InstanceIdAndMask & 0x00FFFFFF;
}
uint GetMask()
{
return (InstanceIdAndMask >> 24) & 0xFF;
}
uint GetInstanceContributionToHitGroupIndex()
{
return InstanceContributionToHitGroupIndexAndFlags & 0x00FFFFFF;
}
uint GetFlags()
{
return (InstanceContributionToHitGroupIndexAndFlags >> 24) & 0xFF;
}
};
void SetRayTracingInstanceTransform(inout FPlatformRayTracingInstanceDescriptor Desc, float3x4 LocalToWorld)
{
Desc.Transform = LocalToWorld;
}
uint GetRayTracingInstanceDescriptorInstanceId(FPlatformRayTracingInstanceDescriptor Desc)
{
return Desc.GetInstanceId();
}
uint GetRayTracingInstanceDescriptorMask(FPlatformRayTracingInstanceDescriptor Desc)
{
return Desc.GetMask();
}
uint GetRayTracingInstanceDescriptorInstanceContributionToHitGroupIndex(FPlatformRayTracingInstanceDescriptor Desc)
{
return Desc.GetInstanceContributionToHitGroupIndex();
}
uint GetRayTracingInstanceDescriptorFlags(FPlatformRayTracingInstanceDescriptor Desc)
{
return Desc.GetFlags();
}
FPlatformRayTracingInstanceDescriptor BuildPlatformRayTracingInstanceDesc(uint InstanceMask, uint InstanceId, uint InstanceFlags, uint InstanceContributionToHitGroupIndex, float3x4 LocalToWorld, uint2 AccelerationStructureAddress)
{
FPlatformRayTracingInstanceDescriptor Desc;
Desc.InstanceIdAndMask = (InstanceMask << 24) | (InstanceId & 0x00FFFFFF);
Desc.InstanceContributionToHitGroupIndexAndFlags = (InstanceFlags << 24) | (InstanceContributionToHitGroupIndex & 0x00FFFFFF);
Desc.AccelerationStructure = AccelerationStructureAddress;
SetRayTracingInstanceTransform(Desc, LocalToWorld);
return Desc;
}
#endif // !OVERRIDE_PLATFORM_RAYTRACING_INSTANCE_DESCRITOR
///////////////////////////////////////////////////////////////////////////////////////////////////
// Attributes and small payload
// Indices and vertex positions of the ray tracing geometry triangle.
// Used for loading and interpolation of per-vertex attributes and computing per-triangle normals.
struct FTriangleBaseAttributes
{
uint3 Indices;
float3 LocalPositions[3];
};
struct FBasicRayData
{
float3 Origin;
uint Mask;
float3 Direction;
float TFar;
};
struct FMinimalPayload
{
float HitT; // Distance from ray origin to the intersection point in the ray direction. Negative on miss.
bool IsMiss() { return HitT < 0; }
bool IsHit() { return !IsMiss(); }
void SetMiss() { HitT = -1; }
};
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_MINIMAL)
CHECK_RT_PAYLOAD_SIZE(FMinimalPayload)
#endif
struct FIntersectionPayload : FMinimalPayload
{
uint PrimitiveIndex; // Index of the primitive within the geometry inside the bottom-level acceleration structure instance. Undefined on miss.
uint InstanceIndex; // Index of the current instance in the top-level structure. Undefined on miss.
float2 Barycentrics; // Primitive barycentric coordinates of the intersection point. Undefined on miss.
};
struct FDefaultPayload : FIntersectionPayload
{
uint InstanceID; // Value of FRayTracingGeometryInstance::UserData. Undefined on miss.
};
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_DEFAULT)
CHECK_RT_PAYLOAD_SIZE(FDefaultPayload)
#endif
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_VFX)
struct FVFXTracePayload : FMinimalPayload
{
uint GPUSceneInstanceId;
float2 Barycentrics;
float3 TranslatedWorldPosition;
float3 WorldNormal;
};
CHECK_RT_PAYLOAD_SIZE(FVFXTracePayload)
#endif
#define RAY_TRACING_DEFERRED_MATERIAL_KEY_INVALID 0xFFFFFFFF
#define RAY_TRACING_DEFERRED_MATERIAL_KEY_RAY_MISS 0xFFFFFFFE
struct FDeferredMaterialPayload : FMinimalPayload
{
uint SortKey; // MaterialID by default
uint PixelCoordinates; // X in low 16 bits, Y in high 16 bits
};
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_DEFERRED)
CHECK_RT_PAYLOAD_SIZE(FDeferredMaterialPayload)
#endif
#ifndef RAY_TRACING_SUPPORT_CUSTOM_UINT_IN_INTERSECTION_ATTRIBUTES
# define RAY_TRACING_SUPPORT_CUSTOM_UINT_IN_INTERSECTION_ATTRIBUTES 0
#endif
struct FRayTracingIntersectionAttributes
{
uint2 PackedData;
float2 GetBarycentrics()
{
#if RAY_TRACING_SUPPORT_CUSTOM_UINT_IN_INTERSECTION_ATTRIBUTES
return UnpackUnorm2x16(PackedData.x);
#else
return asfloat(PackedData);
#endif
}
void SetBarycentrics(float2 Barycentrics)
{
#if RAY_TRACING_SUPPORT_CUSTOM_UINT_IN_INTERSECTION_ATTRIBUTES
PackedData.x = PackUnorm2x16(Barycentrics);
#else
PackedData = asuint(Barycentrics);
#endif
}
#if RAY_TRACING_SUPPORT_CUSTOM_UINT_IN_INTERSECTION_ATTRIBUTES
void SetCustomData(uint Data)
{
PackedData.y = Data;
}
#endif
};
// Include utility fonction for world position reconstruction
#include "../PositionReconstructionCommon.ush"
///////////////////////////////////////////////////////////////////////////////////////////////////
// Helpers
uint CalcLinearIndex(uint2 PixelCoord)
{
return PixelCoord.y * uint(View.BufferSizeAndInvSize.x) + PixelCoord.x;
}
uint2 GetPixelCoord(uint2 DispatchThreadId, uint UpscaleFactor)
{
uint UpscaleFactorPow2 = UpscaleFactor * UpscaleFactor;
// TODO(Denoiser): find a way to not interfer with TAA's jittering.
uint SubPixelId = View.StateFrameIndex & (UpscaleFactorPow2 - 1);
return DispatchThreadId * UpscaleFactor + uint2(SubPixelId & (UpscaleFactor - 1), SubPixelId / UpscaleFactor);
}
// WorldNormal is the vector towards which the ray position will be offseted.
void ApplyPositionBias(inout float3 RayOrigin, float3 RayDirection, const float3 WorldNormal, const float MaxNormalBias)
{
// Apply normal perturbation when defining ray to:
// * avoid self intersection with current underlying triangle
// * hide mismatch between shading surface & geometric surface
//
// While using shading normal is not correct (we should use the
// geometry normal, but it is not available atm/too costly to compute),
// it is good enough for a cheap solution
const float MinBias = 0.01f;
const float MaxBias = max(MinBias, MaxNormalBias);
const float NormalBias = lerp(MaxBias, MinBias, saturate(dot(WorldNormal, RayDirection)));
RayOrigin += WorldNormal * NormalBias;
}
void ApplyCameraRelativeDepthBias(inout float3 RayOrigin, float3 RayDirection, uint2 PixelCoord, float DeviceZ, const float3 WorldNormal, const float AbsoluteNormalBias)
{
float3 TranslatedWorldPosition = ReconstructTranslatedWorldPositionFromDeviceZ(PixelCoord, DeviceZ);
float3 CameraDirection = TranslatedWorldPosition - View.TranslatedWorldCameraOrigin;
float DistanceToCamera = GetDistanceToCameraFromViewVector(CameraDirection);
float Epsilon = 1.0e-4;
float RelativeBias = DistanceToCamera * Epsilon;
//float ProjectedBias = RelativeBias / dot(RayDirection, WorldNormal);
float RayBias = max(RelativeBias, AbsoluteNormalBias);
RayOrigin -= GetCameraVectorFromTranslatedWorldPosition(CameraDirection) * RayBias;
ApplyPositionBias(RayOrigin, RayDirection, WorldNormal, RayBias);
}
// This strategy is similar to ApplyPositionBias, but is the version used in the path tracer
void ApplyRayBias(inout float3 RayOrigin, float HitT, float3 GeoNormal)
{
// Take maximum of position or hit distance to determine "radius" of hit avoidance
const float RefValue = max(max(abs(RayOrigin.x), abs(RayOrigin.y)), max(abs(RayOrigin.z), HitT));
const uint UlpRadius = 16; // number of floating point ulps to skip around an intersection
const float RelativeOffset = asfloat(asuint(RefValue) + UlpRadius) - RefValue;
const float BaseOffset = 0.00390625; // 2^-8cm (roughly 39micro meters) (This avoids precision issues very close to the origin where the ulps become too small)
RayOrigin += max(BaseOffset, RelativeOffset) * GeoNormal;
}
float3 TranslatedWorldToTLASWorld(float3 TranslatedWorldPosition)
{
const FDFVector3 TLASPreViewTranslation = MakeDFVector3(View.TLASPreViewTranslationHigh, View.TLASPreViewTranslationLow);
const float3 TLASWorldPosition = TranslatedWorldPosition - DFFastSubtractDemote(PrimaryView.PreViewTranslation, TLASPreViewTranslation);
return TLASWorldPosition;
}
float3 TLASWorldToTranslatedWorld(float3 TLASWorldPosition)
{
const FDFVector3 TLASPreViewTranslation = MakeDFVector3(View.TLASPreViewTranslationHigh, View.TLASPreViewTranslationLow);
const float3 TranslatedWorldPosition = TLASWorldPosition + DFFastSubtractDemote(PrimaryView.PreViewTranslation, TLASPreViewTranslation);
return TranslatedWorldPosition;
}
float4 TranslatedWorldPositionToSvPosition(float3 TranslatedWorldPosition)
{
const float4 ClipSpacePosition = mul(float4(TranslatedWorldPosition, 1.0f), ResolvedView.TranslatedWorldToClip);
const float4 SvPosition = float4((ClipSpacePosition.xy / ClipSpacePosition.w * float2(.5f, - .5f) + .5f) * ResolvedView.ViewSizeAndInvSize.xy, ClipSpacePosition.zw);
return SvPosition;
}
// Pack normal vector into an uint32 using Octahedron encoding
uint PackNormalToUInt32(float3 Normal)
{
float2 NormalAsOctahedron = UnitVectorToOctahedron(Normal);
uint2 QuantizedOctahedron = clamp((NormalAsOctahedron * 0.5 + 0.5) * 65535.0 + 0.5, 0.0, 65535.0);
return ((QuantizedOctahedron.x & 0xFFFF) << 16) | (QuantizedOctahedron.y & 0xFFFF);
}
// Pack normal vector into from an uint32 using Octahedron encoding
float3 UnpackNormalFromUInt32(uint PackedNormal)
{
uint2 QuantizedOctahedron = uint2((PackedNormal >> 16) & 0xFFFF, PackedNormal & 0xFFFF);
float2 WorldNormalAsOctahedron = ((QuantizedOctahedron / 65535.0) - 0.5) * 2.0;
float3 WorldNormal = OctahedronToUnitVector(WorldNormalAsOctahedron);
return WorldNormal;
}
// Recommended default value for TMax for rays that should traverse the largest possible distance
#define RAY_DEFAULT_T_MAX (FLOAT_MAX)
#if !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
struct FRayDesc
{
float3 Origin;
float TMin;
float3 Direction;
float TMax;
RayDesc GetNativeDesc()
{
RayDesc NativeRay;
NativeRay.Origin = TranslatedWorldToTLASWorld(Origin);
NativeRay.TMin = TMin;
NativeRay.Direction = Direction;
NativeRay.TMax = TMax;
return NativeRay;
}
};
float3 TranslatedWorldRayOrigin()
{
return TLASWorldToTranslatedWorld(WorldRayOrigin());
}
void ApplyPositionBias(inout FRayDesc Ray, const float3 WorldNormal, const float MaxNormalBias)
{
ApplyPositionBias(Ray.Origin, Ray.Direction, WorldNormal, MaxNormalBias);
}
void ApplyCameraRelativeDepthBias(inout FRayDesc Ray, uint2 PixelCoord, float DeviceZ, const float3 WorldNormal, const float AbsoluteNormalBias)
{
ApplyCameraRelativeDepthBias(Ray.Origin, Ray.Direction, PixelCoord, DeviceZ, WorldNormal, AbsoluteNormalBias);
}
FRayDesc CreatePrimaryRay(float2 UV)
{
float2 ScreenPosition = (UV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
FRayDesc Ray;
float4 NearPoint = mul(float4(ScreenPosition, 1, 1), View.ClipToTranslatedWorld);
float4 FarPoint = mul(float4(ScreenPosition, 0, 1), View.ClipToTranslatedWorld);
Ray.Origin = NearPoint.xyz * rcp(NearPoint.w);
Ray.Direction = normalize(NearPoint.w * FarPoint.xyz - FarPoint.w * NearPoint.xyz);
// extract far plane (ortho only, so ignore distinction between T and Z)
float4 Back = mul(float4(0, 0, 0, 1), View.ClipToView);
Ray.TMin = 0;
Ray.TMax = Back.w > 0 ? (Back.z / Back.w - View.NearPlane) : RAY_DEFAULT_T_MAX;
return Ray;
}
#endif // !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
uint TranslateRayTracingInstanceFlags(uint InFlags)
{
uint Result = PLATFORM_RAYTRACING_INSTANCE_FLAG_NONE;
if (InFlags & RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE)
{
Result |= PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE;
}
if ((InFlags & RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_REVERSE) == 0)
{
// Counterclockwise is the default for UE. Reversing culling is achieved by *not* setting this flag.
Result |= PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_FRONT_COUNTERCLOCKWISE;
}
if (InFlags & RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE)
{
Result |= PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE;
}
if (InFlags & RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE)
{
Result |= PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE;
}
return Result;
}
uint TranslateRayTracingPlatformInstanceFlags(uint InPlatformFlags)
{
uint Result = RAYTRACING_INSTANCE_FLAG_NONE;
if (InPlatformFlags & PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE)
{
Result |= RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE;
}
if ((InPlatformFlags & PLATFORM_RAYTRACING_INSTANCE_FLAG_TRIANGLE_FRONT_COUNTERCLOCKWISE) == 0)
{
Result |= RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_REVERSE;
}
if (InPlatformFlags & PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE)
{
Result |= RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE;
}
if (InPlatformFlags & PLATFORM_RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE)
{
Result |= RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE;
}
return Result;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Material payload
#if !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
#if SUBTRATE_GBUFFER_FORMAT==1
struct FMaterialClosestHitPayload : FMinimalPayload
{
// float FMinimalPay // float FMinimalPayload::HitT
FRayCone RayCone;
float3 Radiance;
float IorOverride; // If >= 0, the material overridden IOR value
float3 WorldNormal; // Top Layer Normal
float Opacity;
float3 TransmittancePreCoverage;
uint BlendingMode;
uint PrimitiveLightingChannelMask;
float3 IndirectIrradiance;
uint ShadingModelID;
float3 RayDirection;
// Quite some code assume FRayHitInfo has a WorldPos
// So keep it here and force to re-construct it in Unpack call using ray information.
// It is not packed in FRayHitInfoPacked
float3 TranslatedWorldPos;
uint Flags;
FSubstrateRaytracingPayload SubstrateData;
void SetFrontFace() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE; }
bool IsFrontFace() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; }
void SetTwoSided() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED; }
bool IsTwoSided() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED) != 0; }
void SetHoldout() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT; }
bool IsHoldout() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT) != 0; }
void SetHasIndirectLighting() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_HAS_INDIRECT_LIGHTING; }
bool HasIndirectLighting() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_HAS_INDIRECT_LIGHTING) != 0; }
FRayCone GetRayCone() { return RayCone; }
void SetRayCone(FRayCone In) { RayCone = In; }
float3 GetRayDirection() { return RayDirection; }
};
struct FPackedMaterialClosestHitPayload : FMinimalPayload
{
// Flags layout
// 8bits | 8bits | 3bits | 3bits | 2bits | 8bits
// RAY_TRACING_PAYLOAD_INPUT | MIP Bias | BlendMode | LightChannelMask | Free | Opacity
// float FMinimalPayload::HitT // 4 bytes
uint PackedRayCone; // 4 bytes (fp16 width, fp16 spread) aliased with RayDirection for MS lighting payload
uint Flags; // 4 bytes (see layout above)
uint TransmittancePreCoverage; // 4 bytes
uint Radiance; // 4 bytes
uint PackedGeometryNormal; // 4 bytes
uint SceneInstanceIndex; // 4 bytes - GPUSceneInstanceIndex
//=28 bytes - must be in sync with InternalGetRayTracingMaterialPayloadSizeInBytes()
FSubstrateRaytracingPayload SubstrateData; // ? bytes - must be in sync with GetRaytracingMaterialPayloadSize (see FSubstrateRaytracingPayload)
//=28+? bytes total
void SetMinimalPayloadMode() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_MINIMAL_PAYLOAD; }
bool IsMinimalPayloadMode() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_MINIMAL_PAYLOAD) != 0; }
void SetEnableSkyLightContribution() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_ENABLE_SKY_LIGHT_CONTRIBUTION; }
bool IsEnableSkyLightContribution() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_ENABLE_SKY_LIGHT_CONTRIBUTION) != 0; }
void SetLumenPayload() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_LUMEN_PAYLOAD; }
bool IsLumenPayload() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_LUMEN_PAYLOAD) != 0; }
void SetIgnoreTranslucentMaterials() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT; }
bool IsIgnoreTranslucentMaterials() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT) != 0; }
void SetShadowRay() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY; }
bool IsShadowRay() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY) != 0; }
void SetCameraRay() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_CAMERA_RAY; }
bool IsCameraRay() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_CAMERA_RAY) != 0; }
void SetIndirectLightingRay() { Flags |= RAY_TRACING_PAYLOAD_INPUT_FLAG_INDIRECT_LIGHTING_RAY; }
bool IsIndirectLightingRay() { return (Flags & RAY_TRACING_PAYLOAD_INPUT_FLAG_INDIRECT_LIGHTING_RAY) != 0; }
FRayCone GetRayCone() { return UnpackRayCone(PackedRayCone); }
void SetRayCone(FRayCone NewRayCone) { PackedRayCone = PackRayCone(NewRayCone); }
float GetMipBias() { return UnpackR8(Flags>>8u); }
void SetMipBias(float NewMipBias) { Flags = (Flags & 0xFFFF00FFFu) | (PackR8(NewMipBias) << 8u); }
void SetRadiance(in float3 In) { Radiance = PackR11G11B10F(In); }
float3 GetRadiance() { return UnpackR11G11B10F(Radiance); }
// For now we use the last material data slot to store the indirect irradiance in order to not increase the payload over 64bytes
void SetIndirectIrradiance(float3 In) { if ((Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_HAS_INDIRECT_LIGHTING) != 0) { SubstrateData.Data[SUBSTRATE_RT_PAYLOAD_NUM_UINTS - 1] = PackR11G11B10F(In); } }
float3 GetIndirectIrradiance() { return ((Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_HAS_INDIRECT_LIGHTING) != 0) ? UnpackR11G11B10F(SubstrateData.Data[SUBSTRATE_RT_PAYLOAD_NUM_UINTS-1]) : 0.f; }
float3 GetWorldNormal() { return SubstrateUnpackTopLayerData(SubstrateData.PackedTopLayerData).WorldNormal; }
void SetGeometryNormal(float3 GeometryNormal)
{
PackedGeometryNormal = PackNormalToUInt32(GeometryNormal);
}
float3 GetGeometryNormal()
{
return UnpackNormalFromUInt32(PackedGeometryNormal);
}
float3 GetShadowVisibility()
{
return UnpackRGB111110(TransmittancePreCoverage); // TODO: Does this interfere with the BRDF evaluation in the lighting miss shader?
}
void SetShadowVisibility(float3 ShadowVisibility)
{
TransmittancePreCoverage = PackRGB111110(ShadowVisibility);
}
uint GetLightIndex()
{
return SceneInstanceIndex;
}
void SetLightIndex(uint LightIndex)
{
SceneInstanceIndex = LightIndex;
}
bool IsValid() { return any(SubstrateData.PackedTopLayerData != 0); }
uint GetShadingModelID() { return IsValid() ? SHADINGMODELID_SUBSTRATE : SHADINGMODELID_UNLIT; }
uint GetFlags() { return (Flags & 0xFF); }
bool IsTwoSided() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED) != 0; }
bool IsFrontFace() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; }
uint GetBlendingMode() { return (Flags >> 16u) & 0x7; }
uint GetPrimitiveLightingChannelMask() { return (Flags >> 19u) & 0x7; }
float GetOpacity() { return UnpackR8(Flags >> 24u); }
float3 GetTransmittancePreCoverage() { return UnpackR11G11B10F(TransmittancePreCoverage); }
void SetFlags(uint In) { Flags |= ((In & 0xFF)); }
void SetBlendingMode(uint In) { Flags = (Flags & 0xFFF8FFFF) | ((In & 0x7)<<16u); }
void SetPrimitiveLightingChannelMask(uint In) { Flags = (Flags & 0xFFC7FFFF) | ((In & 0x7)<<19u); }
void SetOpacity(float In) { Flags = (Flags & 0x00FFFFFF) | (PackR8(In) << 24u); }
void SetTransmittancePreCoverage(float3 In) { TransmittancePreCoverage = PackR11G11B10F(In); }
void SetRayDirection(float3 In) { PackedRayCone = PackNormalToUInt32(In); }
float3 GetRayDirection() { return UnpackNormalFromUInt32(PackedRayCone); }
// Member function for (IsLumenPayload() == 1)
void SetSceneInstanceIndex(uint In) { SceneInstanceIndex = In; }
int GetSceneInstanceIndex() { return SceneInstanceIndex; }
// SceneInstanceIndex is not used in hit group shaders so reuse it to pass a random number
void SetRandom(float E) { SetSceneInstanceIndex(asuint(E)); }
float GetRandom() { return asfloat(GetSceneInstanceIndex()); }
// (Placeholder) Accessors used by the legacy refleciton API
#if SUBSTRATE_LEGACY_REFLECTION_API
float3 GetWorldTangent() { return float3(1,0,0); }
float3 GetBaseColor() { return 1.f; }
float4 GetCustomData() { return 0.f; }
float GetMetallic() { return 1.f; }
float GetSpecular() { return 0.5f; }
float GetRoughness() { return 0.5f; }
float GetIor() { return 1.f; }
float GetAnisotropy() { return 0.f; }
float3 GetDiffuseColor() { return 1.f; }
float3 GetSpecularColor() { return 1.f; }
#endif
};
FPackedMaterialClosestHitPayload PackRayTracingPayload(FMaterialClosestHitPayload In, in FRayCone RayCone)
{
FPackedMaterialClosestHitPayload Out = (FPackedMaterialClosestHitPayload)0;
Out.HitT = In.HitT;
Out.SetRayCone(RayCone);
Out.SetFlags(In.Flags);
Out.SetBlendingMode(In.BlendingMode);
Out.SetPrimitiveLightingChannelMask(In.PrimitiveLightingChannelMask);
Out.SetRadiance(In.Radiance);
Out.SubstrateData = In.SubstrateData;
Out.SetIndirectIrradiance(In.IndirectIrradiance);
Out.SetOpacity(In.Opacity);
Out.SetTransmittancePreCoverage(In.TransmittancePreCoverage);
return Out;
}
FMaterialClosestHitPayload UnpackRayTracingPayload(FPackedMaterialClosestHitPayload In, FRayDesc Ray)
{
FMaterialClosestHitPayload Out = (FMaterialClosestHitPayload)0;
Out.HitT = In.HitT;
Out.RayCone = In.GetRayCone();
Out.BlendingMode = In.GetBlendingMode();
Out.PrimitiveLightingChannelMask = In.GetPrimitiveLightingChannelMask();
Out.Flags = In.GetFlags();
Out.Radiance = In.GetRadiance();
Out.WorldNormal = In.GetWorldNormal();
Out.ShadingModelID = In.GetShadingModelID();
Out.IndirectIrradiance = In.GetIndirectIrradiance();
Out.SubstrateData = In.SubstrateData;
Out.TranslatedWorldPos = Ray.Origin + Out.HitT * Ray.Direction;
Out.RayDirection = In.GetRayDirection();
Out.Opacity = In.GetOpacity();
Out.TransmittancePreCoverage = In.GetTransmittancePreCoverage();
return Out;
}
#else // SUBTRATE_GBUFFER_FORMAT==1
struct FMaterialClosestHitPayload : FMinimalPayload
{
// Unpacked Packed
// offset bytes
// float FMinimalPayload::HitT // 0 4 32bits
FRayCone RayCone; // 4 8 64bits
float3 Radiance; // 8 6 48bits
float3 WorldNormal; // 24 6 48bits
float3 BaseColor; // 36 6 48bits
float3 DiffuseColor; // 48 0 (derived)
float3 SpecularColor; // 60 0 (derived)
float Opacity; // 72 2 16bits
float Metallic; // 76 1 8bits
float Specular; // 80 1 8bits
float Roughness; // 84 2 16bits
float Ior; // 88 2 16bits
uint ShadingModelID; // 92 1 4bits
uint BlendingMode; // 96 0 3bits (packed with ShadingModelID)
uint PrimitiveLightingChannelMask; // 100 0 3bits (packed with ShadingModelID)
float4 CustomData; // 104 4 32bits
float GBufferAO; // 120 0 (removed)
float3 IndirectIrradiance; // 124 0 48bits -- gbuffer only has float payload and there are truncation HLSL warnings
// Quite some code assume FRayHitInfo has a WorldPos
// So keep it here and force to re-construct it in Unpack call using ray information.
// It is not packed in FRayHitInfoPacked
float3 TranslatedWorldPos; // 136 0 (derived)
uint Flags; // 148 0 6bits (packed with ShadingModelID)
float3 WorldTangent; // 152 6 48bits
float Anisotropy; // 164 2 16bits (packed with WorldTangent)
// 166 total
void SetFrontFace() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE; }
bool IsFrontFace() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; }
void SetTwoSided() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED; }
bool IsTwoSided() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED) != 0; }
void SetHoldout() { Flags |= RAY_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT; }
bool IsHoldout() { return (Flags & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT) != 0; }
FRayCone GetRayCone() { return RayCone; }
void SetRayCone(FRayCone NewRayCone) { RayCone = NewRayCone; }
bool HasAnisotropy() { return abs(Anisotropy) >= 0.001f; }
float3 GetRayDirection() { return IndirectIrradiance; /* Alias IndirectIrradiance/RayDirection */ }
};
struct FPackedMaterialClosestHitPayload : FMinimalPayload
{
// float FMinimalPayload::HitT // 4 bytes
uint PackedRayCone; // 4 bytes (fp16 width, fp16 spread)
// 8 bits - MipBias
// 8 bits - Flags
// 16 bits - Unused
uint FlagsAndMipBias;
uint RadianceAndNormal[3]; // 12 bytes
uint BaseColorAndOpacity[2]; // 8 bytes
uint MetallicAndSpecularAndRoughness; // 4 bytes
// 16 bits - IOR
// 4 bits - Shading Model Id
// 3 bits - Blending Mode
// 6 bits - Unused
// 3 bits - Lighting Channel Mask
uint IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask; // 4 bytes
uint PackedIndirectIrradiance[2]; // 8 bytes
uint PackedCustomData; // 4 bytes
uint WorldTangentAndAnisotropy[2]; // 8 bytes
uint PackedGeometryNormal; // 4 bytes
// 64 bytes total
void SetMinimalPayloadMode() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_MINIMAL_PAYLOAD; }
bool IsMinimalPayloadMode() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_MINIMAL_PAYLOAD) != 0; }
void SetEnableSkyLightContribution() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_ENABLE_SKY_LIGHT_CONTRIBUTION ; }
bool IsEnableSkyLightContribution() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_ENABLE_SKY_LIGHT_CONTRIBUTION) != 0; }
void SetLumenPayload() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_LUMEN_PAYLOAD; }
bool IsLumenPayload() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_LUMEN_PAYLOAD) != 0;}
void SetIgnoreTranslucentMaterials() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT; }
bool IsIgnoreTranslucentMaterials() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT) != 0; }
void SetShadowRay() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY; }
bool IsShadowRay() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY) != 0; }
void SetCameraRay() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_CAMERA_RAY; }
bool IsCameraRay() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_CAMERA_RAY) != 0; }
void SetIndirectLightingRay() { FlagsAndMipBias |= RAY_TRACING_PAYLOAD_INPUT_FLAG_INDIRECT_LIGHTING_RAY; }
bool IsIndirectLightingRay() { return (FlagsAndMipBias & RAY_TRACING_PAYLOAD_INPUT_FLAG_INDIRECT_LIGHTING_RAY) != 0; }
FRayCone GetRayCone() { return UnpackRayCone(PackedRayCone); }
void SetRayCone(FRayCone NewRayCone) { PackedRayCone = PackRayCone(NewRayCone); }
float GetMipBias() { return UnpackR8(FlagsAndMipBias >> 8u); }
void SetMipBias(float NewMipBias) { FlagsAndMipBias = (FlagsAndMipBias & 0xFFFF00FFu) | (PackR8(NewMipBias) << 8u); }
void SetRadiance(in float3 InRadiance)
{
RadianceAndNormal[0] = f32tof16(InRadiance.x) | (f32tof16(InRadiance.y) << 16);
RadianceAndNormal[1] &= 0xffff << 16; // Clear the radiance (low bits), keep packed normal component (high bits)
RadianceAndNormal[1] |= f32tof16(InRadiance.z);
}
float3 GetRadiance()
{
float3 Result;
Result.x = f16tof32(RadianceAndNormal[0]);
Result.y = f16tof32(RadianceAndNormal[0] >> 16);
Result.z = f16tof32(RadianceAndNormal[1]);
return Result;
}
void SetIndirectIrradiance(float3 InIndirectIrradiance)
{
PackedIndirectIrradiance[0] = f32tof16(InIndirectIrradiance.x);
PackedIndirectIrradiance[0] |= f32tof16(InIndirectIrradiance.y) << 16;
PackedIndirectIrradiance[1] = f32tof16(InIndirectIrradiance.z) | (PackedIndirectIrradiance[1] & 0xFFFF0000u);
}
float3 GetIndirectIrradiance()
{
float3 Result;
Result.x = f16tof32(PackedIndirectIrradiance[0]);
Result.y = f16tof32(PackedIndirectIrradiance[0] >> 16);
Result.z = f16tof32(PackedIndirectIrradiance[1]);
return Result;
}
// Alias IndirectIrradiance for storing ray direction
void SetRayDirection(float3 In) { PackedRayCone = PackNormalToUInt32(In); }
float3 GetRayDirection() { return UnpackNormalFromUInt32(PackedRayCone); }
float3 GetWorldNormal()
{
float3 Result;
Result.x = f16tof32(RadianceAndNormal[1] >> 16);
Result.y = f16tof32(RadianceAndNormal[2]);
Result.z = f16tof32(RadianceAndNormal[2] >> 16);
return Result;
}
float4 GetCustomData()
{
float4 Result;
Result.x = float(PackedCustomData & 0xFF);
Result.y = float((PackedCustomData >> 8) & 0xFF);
Result.z = float((PackedCustomData >> 16) & 0xFF);
Result.w = float((PackedCustomData >> 24) & 0xFF);
Result /= 255.0;
return Result;
}
float3 GetBaseColor()
{
float3 Result;
Result.x = f16tof32(BaseColorAndOpacity[0]);
Result.y = f16tof32(BaseColorAndOpacity[0] >> 16);
Result.z = f16tof32(BaseColorAndOpacity[1]);
return Result;
}
float3 GetWorldTangent()
{
float3 Result;
Result.x = f16tof32(WorldTangentAndAnisotropy[0]);
Result.y = f16tof32(WorldTangentAndAnisotropy[0] >> 16);
Result.z = f16tof32(WorldTangentAndAnisotropy[1]);
return Result;
}
float3 GetShadowVisibility()
{
return GetIndirectIrradiance();
}
void SetShadowVisibility(float3 ShadowVisibility)
{
SetIndirectIrradiance(ShadowVisibility);
}
uint GetLightIndex()
{
return PackedIndirectIrradiance[1] >> 16;
}
void SetLightIndex(uint LightIndex)
{
// NOTE: this assumes a maximum of 64k lights
PackedIndirectIrradiance[1] = (LightIndex << 16) | (PackedIndirectIrradiance[1] & 0xFFFFu);
}
float GetOpacity() { return f16tof32(BaseColorAndOpacity[1] >> 16); }
void SetOpacity(float Opacity) { BaseColorAndOpacity[1] = f32tof16(Opacity) << 16 | (BaseColorAndOpacity[1] & 0xFFFF); }
float GetMetallic() { return float(MetallicAndSpecularAndRoughness & 0xFF) / 255.0f; }
float GetSpecular() { return float((MetallicAndSpecularAndRoughness >> 8) & 0xFF) / 255.0f; }
float GetRoughness() { return f16tof32(MetallicAndSpecularAndRoughness >> 16); }
float GetIor() { return f16tof32(IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask); }
uint GetShadingModelID() { return (IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask >> 16) & 0xF; }
uint GetBlendingMode() { return (IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask >> 20) & 0x7; }
uint GetFlags() { return FlagsAndMipBias & 0xFF; }
void SetFlags(uint Flags) { FlagsAndMipBias |= (Flags & 0xFF); }
bool IsTwoSided() { return (GetFlags() & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED) != 0; }
bool IsFrontFace() { return (GetFlags() & RAY_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; }
uint GetPrimitiveLightingChannelMask() { return (IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask >> 29) & 0x7; }
float GetAnisotropy() { return f16tof32(WorldTangentAndAnisotropy[1] >> 16); }
bool IsValid() { return GetShadingModelID() != SHADINGMODELID_UNLIT; }
float3 GetDiffuseColor() { return GetBaseColor() - GetBaseColor() * GetMetallic(); }
float3 GetSpecularColor() { return ComputeF0(GetSpecular(), GetBaseColor(), GetMetallic()); }
// Member function for (IsLumenPayload() == 1)
void SetSceneInstanceIndex(uint GPUSceneInstanceIndex)
{
// Note: overwriting WorldTangentAndAnisotropy[1] so GetWorldTangent() won't work in Lumen
WorldTangentAndAnisotropy[1] = GPUSceneInstanceIndex;
}
void SetGeometryNormal(float3 GeometryNormal)
{
PackedGeometryNormal = PackNormalToUInt32(GeometryNormal);
}
float3 GetGeometryNormal()
{
return UnpackNormalFromUInt32(PackedGeometryNormal);
}
int GetSceneInstanceIndex() { return WorldTangentAndAnisotropy[1]; }
// SceneInstanceIndex is not used in hit group shaders so reuse it to pass a random number
void SetRandom(float E) { SetSceneInstanceIndex(asuint(E)); }
float GetRandom() { return asfloat(GetSceneInstanceIndex()); }
};
FPackedMaterialClosestHitPayload PackRayTracingPayload(FMaterialClosestHitPayload Input, in FRayCone RayCone)
{
FPackedMaterialClosestHitPayload Output = (FPackedMaterialClosestHitPayload)0;
Output.FlagsAndMipBias = Input.Flags;
Output.HitT = Input.HitT;
Output.SetRayCone(RayCone);
Output.RadianceAndNormal[0] = f32tof16(Input.Radiance.x);
Output.RadianceAndNormal[0] |= f32tof16(Input.Radiance.y) << 16;
Output.RadianceAndNormal[1] = f32tof16(Input.Radiance.z);
Output.RadianceAndNormal[1] |= f32tof16(Input.WorldNormal.x) << 16;
Output.RadianceAndNormal[2] = f32tof16(Input.WorldNormal.y);
Output.RadianceAndNormal[2] |= f32tof16(Input.WorldNormal.z) << 16;
Output.BaseColorAndOpacity[0] = f32tof16(Input.BaseColor.x);
Output.BaseColorAndOpacity[0] |= f32tof16(Input.BaseColor.y) << 16;
Output.BaseColorAndOpacity[1] = f32tof16(Input.BaseColor.z);
Output.BaseColorAndOpacity[1] |= f32tof16(Input.Opacity) << 16;
Output.MetallicAndSpecularAndRoughness = (uint(round(Input.Metallic * 255.0f)) & 0xFF);
Output.MetallicAndSpecularAndRoughness |= (uint(round(Input.Specular * 255.0f)) & 0xFF) << 8;
Output.MetallicAndSpecularAndRoughness |= f32tof16(Input.Roughness) << 16;
Output.IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask = f32tof16(Input.Ior); // 16 bits
Output.IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask |= (Input.ShadingModelID & 0xF) << 16; // 4 bits
Output.IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask |= (Input.BlendingMode & 0x7) << 20; // 3 bits
Output.IorAndShadingModelIDAndBlendingModeAndPrimitiveLightingChannelMask |= (Input.PrimitiveLightingChannelMask & 0x7) << 29; // 3 bits
Output.PackedIndirectIrradiance[0] = f32tof16(Input.IndirectIrradiance.x);
Output.PackedIndirectIrradiance[0] |= f32tof16(Input.IndirectIrradiance.y) << 16;
Output.PackedIndirectIrradiance[1] = f32tof16(Input.IndirectIrradiance.z);
int4 CustomData = round(Input.CustomData * 255);
Output.PackedCustomData = CustomData.x | (CustomData.y << 8) | (CustomData.z << 16) | (CustomData.w << 24);
Output.WorldTangentAndAnisotropy[0] = f32tof16(Input.WorldTangent.x);
Output.WorldTangentAndAnisotropy[0] |= f32tof16(Input.WorldTangent.y) << 16;
Output.WorldTangentAndAnisotropy[1] = f32tof16(Input.WorldTangent.z);
Output.WorldTangentAndAnisotropy[1] |= f32tof16(Input.Anisotropy) << 16;
return Output;
}
FMaterialClosestHitPayload UnpackRayTracingPayload(FPackedMaterialClosestHitPayload Input, FRayDesc Ray)
{
FMaterialClosestHitPayload Output = (FMaterialClosestHitPayload)0;
Output.HitT = Input.HitT;
Output.RayCone = Input.GetRayCone();
Output.Radiance = Input.GetRadiance();
Output.WorldNormal = Input.GetWorldNormal();
Output.BaseColor = Input.GetBaseColor();
Output.Opacity = Input.GetOpacity();
Output.Metallic = Input.GetMetallic();
Output.Specular = Input.GetSpecular();
Output.Roughness = Input.GetRoughness();
Output.Ior = Input.GetIor();
Output.ShadingModelID = Input.GetShadingModelID();
Output.BlendingMode = Input.GetBlendingMode();
Output.PrimitiveLightingChannelMask = Input.GetPrimitiveLightingChannelMask();
Output.Flags = Input.GetFlags();
Output.IndirectIrradiance = Input.GetIndirectIrradiance();
Output.CustomData = Input.GetCustomData();
Output.WorldTangent = Input.GetWorldTangent();
Output.Anisotropy = Input.GetAnisotropy();
Output.DiffuseColor = Output.BaseColor - Output.BaseColor * Output.Metallic;
Output.SpecularColor = ComputeF0(Output.Specular, Output.BaseColor, Output.Metallic);
Output.TranslatedWorldPos = Ray.Origin + Output.HitT * Ray.Direction;
return Output;
}
#endif // SUBTRATE_GBUFFER_FORMAT==1
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_RAYTRACING_MATERIAL)
CHECK_RT_PAYLOAD_SIZE(FPackedMaterialClosestHitPayload)
#endif
#endif // !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
///////////////////////////////////////////////////////////////////////////////////////////////////
// Decal parameters
// Must match EDecalsWriteFlags
// TODO: Move to a shared header?
#define DECAL_WRITE_BASE_COLOR_FLAG (1 << 0)
#define DECAL_WRITE_NORMAL_FLAG (1 << 1)
#define DECAL_WRITE_ROUGHNESS_SPECULAR_METALLIC_FLAG (1 << 2)
#define DECAL_WRITE_EMISSIVE_FLAG (1 << 3)
#define DECAL_USE_REVERSE_CULLING 1
#if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_DECALS)
struct FDecalShaderPayload : FMinimalPayload
{
// float HitT // 4 bytes
float3 TranslatedWorldPos; // 12 bytes
uint2 PackedBaseColor; // 8 bytes half4
uint2 PackedMetallicSpecularRoughness; // 8 bytes - half4
uint2 PackedWorldNormal; // 8 bytes - half4
uint2 PackedEmissive; // 8 bytes - half3 (alpha is not used)
// 48 bytes total
float4 GetBaseColor() {
return float4(UnpackFloat2FromUInt(PackedBaseColor.x),
UnpackFloat2FromUInt(PackedBaseColor.y));
}
void SetBaseColor(float4 BaseColor) {
PackedBaseColor.x = PackFloat2ToUInt(BaseColor.rg);
PackedBaseColor.y = PackFloat2ToUInt(BaseColor.ba);
}
float4 GetMetallicSpecularRoughness() {
return float4(UnpackFloat2FromUInt(PackedMetallicSpecularRoughness.x),
UnpackFloat2FromUInt(PackedMetallicSpecularRoughness.y));
}
void SetMetallicSpecularRoughness(float4 MetallicSpecularRoughness) {
PackedMetallicSpecularRoughness.x = PackFloat2ToUInt(MetallicSpecularRoughness.rg);
PackedMetallicSpecularRoughness.y = PackFloat2ToUInt(MetallicSpecularRoughness.ba);
}
float4 GetWorldNormal() {
return float4(UnpackFloat2FromUInt(PackedWorldNormal.x),
UnpackFloat2FromUInt(PackedWorldNormal.y));
}
void SetWorldNormal(float4 WorldNormal) {
PackedWorldNormal.x = PackFloat2ToUInt(WorldNormal.xy);
PackedWorldNormal.y = PackFloat2ToUInt(WorldNormal.zw);
}
float3 GetEmissive() {
return float3(UnpackFloat2FromUInt(PackedEmissive.x),
f16tof32(PackedEmissive.y));
}
void SetEmissive(float3 Emissive) {
PackedEmissive.x = PackFloat2ToUInt(Emissive.xy);
PackedEmissive.y = f32tof16(Emissive.z);
}
};
CHECK_RT_PAYLOAD_SIZE(FDecalShaderPayload)
FDecalShaderPayload InitDecalShaderPayload(float3 TranslatedWorldPos)
{
FDecalShaderPayload Payload = (FDecalShaderPayload)0;
Payload.TranslatedWorldPos = TranslatedWorldPos;
// set alpha=1 on all fields
Payload.PackedBaseColor.y = f32tof16(1.0) << 16;
Payload.PackedMetallicSpecularRoughness.y = f32tof16(1.0) << 16;
Payload.PackedWorldNormal.y = f32tof16(1.0) << 16;
return Payload;
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// Tracing util functions
#if !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
#if COMPUTESHADER
#include "TraceRayInline.ush"
#endif // COMPUTESHADER
void TraceVisibilityRayPacked(
inout FPackedMaterialClosestHitPayload PackedPayload,
in RaytracingAccelerationStructure TLAS,
in uint RayFlags,
in uint InstanceInclusionMask,
in FRayDesc Ray)
{
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_SHADOW;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
const uint MissShaderIndex = 0;
// By enabling minimal payload mode all other payload information is ignored, meaning these functions need no payload inputs
PackedPayload.SetMinimalPayloadMode();
PackedPayload.HitT = 0;
// Trace the ray
#if COMPUTESHADER
FTraceRayInlineContext TraceRayInlineContext = CreateTraceRayInlineContext();
FTraceRayInlineResult TraceRayResult = TraceRayInline(
TLAS,
RayFlags,
InstanceInclusionMask,
Ray.GetNativeDesc(),
TraceRayInlineContext
);
PackedPayload.HitT = TraceRayResult.HitT;
#else
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayload);
#endif // COMPUTESHADER
}
FMinimalPayload TraceVisibilityRay(
in RaytracingAccelerationStructure TLAS,
in uint RayFlags,
in uint InstanceInclusionMask,
in FRayDesc Ray)
{
FPackedMaterialClosestHitPayload PackedPayload = (FPackedMaterialClosestHitPayload)0;
PackedPayload.SetFlags(RAY_TRACING_PAYLOAD_INPUT_FLAG_SHADOW_RAY);
//Have shadow ray behaves the same to Lumen, not tracing translucent materials.
PackedPayload.SetIgnoreTranslucentMaterials();
TraceVisibilityRayPacked(PackedPayload, TLAS, RayFlags, InstanceInclusionMask, Ray);
// Unpack the payload
FMinimalPayload MinimalPayload = (FMinimalPayload)0;
// In theory this unpacking setp is not needed as FPackedMaterialClosestHitPayload derives from FMinimalPayload,
// but the compiler currently dislikes a direct cast between them. Additionally in the future if HitT is ever packed
// differently and the FMinimalPayload is not directly inherited from this will need to change.
MinimalPayload.HitT = PackedPayload.HitT;
return MinimalPayload;
}
#if !COMPUTESHADER
///////////////////////////////////////////////////////////////////////////////////////////////////
// Tracing translucent visibility ray functions
struct FTranslucentMinimalPayload : FMinimalPayload
{
float3 ShadowVisibility; // What fraction of the ray's radiance is visible? (1.0 - fully visible, 0.0 - fully shadowed)
};
FTranslucentMinimalPayload TraceTranslucentVisibilityRay(
in RaytracingAccelerationStructure TLAS,
in uint RayFlags,
in uint InstanceInclusionMask,
in FRayDesc Ray)
{
FPackedMaterialClosestHitPayload PackedPayload = (FPackedMaterialClosestHitPayload)0;
PackedPayload.SetShadowRay();
PackedPayload.SetMinimalPayloadMode();
PackedPayload.HitT = 0; // default value in case we don't run the miss shader
PackedPayload.SetShadowVisibility(1.0);
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_SHADOW;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
const uint MissShaderIndex = 0;
// Trace the ray
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayload);
// Unpack the payload
FTranslucentMinimalPayload MinimalPayload = (FTranslucentMinimalPayload)0;
MinimalPayload.HitT = PackedPayload.HitT;
// only read the throughput if we got a miss (ignored all hits and ran the miss shader which reset HitT)
// If we registered a hit, it means that either we ran the closest hit shader which set a valid distance,
// or we never ran the miss shader and kept the HitT value from above (or one set by the AHS before the ray was terminated)
// in both cases we know the shadow visibility should be counted as zero.
MinimalPayload.ShadowVisibility = PackedPayload.IsMiss() ? PackedPayload.GetShadowVisibility() : 0.0;
return MinimalPayload;
}
void TraceMaterialRayPacked(
inout FPackedMaterialClosestHitPayload Payload,
in RaytracingAccelerationStructure TLAS,
in uint RayFlags,
in uint InstanceInclusionMask,
in FRayDesc Ray,
// Payload Inputs
inout FRayCone RayCone,
in bool bEnableSkyLightContribution,
in bool bIsCameraRay)
{
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_MATERIAL;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
const uint MissShaderIndex = 0;
// Set payload inputs
Payload.SetRayCone(RayCone);
if (bEnableSkyLightContribution)
{
// Enable Sky Light contribution in the payload if requested
Payload.SetEnableSkyLightContribution();
}
if (bIsCameraRay)
{
Payload.SetCameraRay();
}
// Trace the ray
Payload.HitT = 0;
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
Payload);
RayCone = Payload.GetRayCone();
}
FMaterialClosestHitPayload TraceMaterialRay(
in RaytracingAccelerationStructure TLAS,
in uint RayFlags,
in uint InstanceInclusionMask,
in FRayDesc Ray,
// Payload Inputs
inout FRayCone RayCone,
in bool bEnableSkyLightContribution,
in bool bIgnoreTranslucentMaterials,
in bool bIsCameraRay)
{
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_MATERIAL;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
const uint MissShaderIndex = 0;
FPackedMaterialClosestHitPayload PackedPayload = (FPackedMaterialClosestHitPayload)0;
// Set payload inputs
PackedPayload.SetRayCone(RayCone);
if (bEnableSkyLightContribution)
{
// Enable Sky Light contribution in the payload if requested
PackedPayload.SetEnableSkyLightContribution();
}
if (bIgnoreTranslucentMaterials)
{
PackedPayload.SetIgnoreTranslucentMaterials();
}
if (bIsCameraRay)
{
PackedPayload.SetCameraRay();
}
// Trace the ray
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayload);
// Unpack the payload
FMaterialClosestHitPayload Payload = UnpackRayTracingPayload(PackedPayload, Ray);
RayCone = Payload.GetRayCone();
return Payload;
}
#endif // !COMPUTESHADER
#endif // !COMPUTESHADER || PLATFORM_SUPPORTS_INLINE_RAY_TRACING
uint GetGPUSceneInstanceIndex(uint PrimitiveIndex, uint InstanceIndex)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
FPrimitiveSceneData PrimData = GetPrimitiveData(PrimitiveIndex);
// Handle ray tracing auto instancing which can cause PrimitiveInstanceIndex to be above 0 for meshes with a single instance
if (PrimData.NumInstanceSceneDataEntries == 1)
{
InstanceIndex = 0;
}
return PrimData.InstanceSceneDataOffset + InstanceIndex;
#else
return 0;
#endif
}
#endif // RAYTRACINGCOMMON_USH_INCLUDED // Workarround for UE-66460