// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= PathTracingCommon.ush: path tracing payload structures =============================================================================*/ #pragma once #include "Material/PathTracingMaterialCommon.ush" #include "Utilities/PathTracingRandomSequence.ush" #include "Utilities/PathTracingRIS.ush" #include "../RayTracing/RayTracingCommon.ush" #include "../OctahedralCommon.ush" #include "../DoubleFloat.ush" #include "/Engine/Shared/PathTracingDefinitions.h" // These flags are analogous to RAY_TRACING_PAYLOAD_* flags. There are currently 9 bits available. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE (1 << 0) // Indicates that ray has hit the front face of a primitive. Set by closest hit shader. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED_MATERIAL (1 << 1) // Indicates that ray has hit a primitive surface with a two sided material. Set by closest hit shader. Used by GPU Lightmass to detect invalid surfaces. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT (1 << 2) // Indicates that ray has hit a primitive which should be held out from alpha. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_MASK_SHIFT (3) #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_COLOR (1 << 3) // Indicates that ray has hit a primitive that receives decals color. Set by closest hit shader. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_NORMAL (1 << 4) // Indicates that ray has hit a primitive that receives decals normal. Set by closest hit shader. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_ROUGHNESS (1 << 5) // Indicates that ray has hit a primitive that receives decals metallic/specular/roughness. Set by closest hit shader. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_USE_DBUFFER_LOOKUP (1 << 6) // Indicates that ray has hit a primitive that uses DBufferTextureLookup. Set by closest hit shader. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_OUTOUT_DEPTH (1 << 7) // Indicates that ray has hit a primitive that needs to output depth. Set by closest hit shader and used in primary ray only. #define PATH_TRACING_PAYLOAD_OUTPUT_FLAG_UNUSED8 (1 << 8) // Free for future use #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_MASK (7 << 0) // Reserve 3 bits for ray-mask #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_CAMERA 0 // Indicates that this is a camera ray which can trigger a number of special behaviors in the closest hit shader #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_SHADOW 1 // Indicates that this is a path tracer visibility ray (which supports material evaluation for transparent shadows and fake caustics) #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_DIFFUSE 2 #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_SPECULAR 3 #define PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_VOLUME 4 #define PATH_TRACING_PAYLOAD_INPUT_FLAG_HAS_SCENE_DEPTH (1 << 4) // Indicates the payload contains a scene depth value (exclusive with dbuffer usage) #define PATH_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT (1 << 5) // Indicates this ray should not process any translucent geometry (so we can skip them in our opaque traces) #define PATH_TRACING_PAYLOAD_NORMAL_BITS 32 #define PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS 16 // Could be dropped to 12 or even 10 if we need more room in the future // shading model extensions beyond the default count #define SHADINGMODELID_MEDIUM (SHADINGMODELID_NUM + 1) #define SHADINGMODELID_SOLID_GLASS (SHADINGMODELID_NUM + 2) HLSL_STATIC_ASSERT(SHADINGMODELID_MEDIUM <= SHADINGMODELID_MASK, ""); HLSL_STATIC_ASSERT(SHADINGMODELID_SOLID_GLASS <= SHADINGMODELID_MASK, ""); #define PATH_TRACING_ABSORPTION_SCALE 0.01 // inverse distance at which we hit the transmission color (Beer's law) // Given a ray with origin O in direction D, assuming we have found a valid hit at THit, compute a new TMin value that we can use to retrace // the same ray and find more hits. This is used for transparency, mesh decals and SSS tracing. float ComputeNewTMin(float3 O, float3 D, float THit) { #if NEED_TMIN_WORKAROUND // Some hardware shifts the ray origin to TMin which can lead to erroneous hits against TMin when shifted back // This code tries to find to smallest increment that causes the shifted origin to change uint Inc = 1; float TMin = asfloat(asuint(THit) + Inc); while (all((mad(D, TMin, O) == mad(D, THit, O)))) { Inc *= 2; TMin = asfloat(asuint(THit) + Inc); } return TMin; #else // On conformant hardware, the following is sufficient to avoid the previous hit return asfloat(asuint(THit) + 1); #endif } // Utilities to help weight the contribution of the albedo/normal to the denoising buffers // We want cases like low roughness mirrors to "pass through" the signal static const float DenoiserMaxRoughness = 0.15f; // don't bother past this level static const float DenoiserMinRoughness = 0.10f; // capture anything below this level // Given the current path roughness, how much contribution should the current hit make to the denoiser AOVs? float DenoiserAOVWeight(float PathRoughness) { return 1 - saturate((PathRoughness - DenoiserMinRoughness) / (DenoiserMaxRoughness - DenoiserMinRoughness)); } // For a given surface of roughness R, how much should its own contribution be counted // This heuristic lets us skip the contribution a mirror's albedo would normally make because we will be seeing "through" it to the next hit float DenoiserRoughnessWeight(float Roughness, float PathRoughness) { float MinRoughness = max(DenoiserMinRoughness, PathRoughness); return saturate((Roughness - MinRoughness) / (DenoiserMaxRoughness - MinRoughness)); } // This function is meant for the solid glass case so we can store the transmittance as a color in [0,1] float3 PathTracingGlassTransmittanceToExtinction(float3 TransmittanceColor) { return -log(max(TransmittanceColor, 1e-8f)) * PATH_TRACING_ABSORPTION_SCALE; } uint PayloadEncodeUnitVector(float3 Normal) { const int BITS = PATH_TRACING_PAYLOAD_NORMAL_BITS; const float Scale = float((1u << (BITS / 2)) - 2) / 2; // largest representable even number (so we can encode 0) float2 OctF = UnitVectorToOctahedron(Normal); int2 Oct = int2(round(OctF * Scale + Scale)); return Oct.x + (1u << (BITS / 2)) * Oct.y; } float3 PayloadDecodeUnitVector(uint Encoded) { const int BITS = PATH_TRACING_PAYLOAD_NORMAL_BITS; const uint Mask = (1u << (BITS / 2)) - 1; const float Scale = float((1u << (BITS / 2)) - 2); int2 Oct = int2(Encoded & Mask, (Encoded >> (BITS / 2)) & Mask); float2 OctF = saturate(float2(Oct) / Scale); return OctahedronToUnitVector(2 * OctF - 1); } // Encode a tangent vector relative to a given Normal uint PayloadEncodeTangentAngle(float3 Normal, float3 Tangent) { const uint Mask = (1u << (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 2)) - 1; const uint SignShiftX = 31 - (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 2); const uint SignShiftY = 31 - (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 1); const uint SignMaskX = 0x80000000u >> SignShiftX; const uint SignMaskY = 0x80000000u >> SignShiftY; const float Scale = float(Mask); float3x3 Basis = GetTangentBasis(Normal); float X = dot(Basis[0], Tangent); float Y = dot(Basis[1], Tangent); // NOTE: We use 'Y' to define the angle so that if Tangent happens to be (0,0,0) it will be decoded back as the "x" axis in the reference basis float PseudoAngle = abs(Y) / (abs(X) + abs(Y)); // Coarse ArcTan, returns value in [0,1] uint QuantizedBits = uint(round(PseudoAngle * Scale)); // Merge with sign bits of X and Y return QuantizedBits | ((asuint(X) >> SignShiftX) & SignMaskX) | ((asuint(Y) >> SignShiftY) & SignMaskY); } // Decode a tangent vector relative to a given Normal float3 PayloadDecodeTangentAngle(float3 Normal, uint Bits) { const uint Mask = (1u << (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 2)) - 1; const uint SignShiftX = 31 - (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 2); const uint SignShiftY = 31 - (PATH_TRACING_PAYLOAD_TANGENT_ANGLE_BITS - 1); const uint SignMaskX = 0x80000000u >> SignShiftX; const uint SignMaskY = 0x80000000u >> SignShiftY; const float Scale = float(Mask); float3x3 Basis = GetTangentBasis(Normal); float Y = float(Bits & Mask) / Scale; float2 P = float2(1.0f - Y, Y); // restore sign bits P.x = asfloat(asuint(P.x) | ((Bits & SignMaskX) << SignShiftX)); P.y = asfloat(asuint(P.y) | ((Bits & SignMaskY) << SignShiftY)); P = normalize(P); return Basis[0] * P.x + Basis[1] * P.y; } uint PayloadEncodeHDRColor(float3 rgb) { return PackRGB998E6(rgb); } float3 PayloadDecodeHDRColor(uint rgb) { return UnpackRGB998E6(rgb); } uint PayloadEncodeLDRColor(float3 rgb) { return PackRGB111110(rgb); } float3 PayloadDecodeLDRColor(uint rgb) { return UnpackRGB111110(rgb); } uint PayloadEncodeRoughnessAniso(float3 RoughnessData, float Anisotropy) { uint r = uint(floor(saturate(RoughnessData.x) * 255.0 + 0.5)); uint g = uint(floor(saturate(RoughnessData.y) * 255.0 + 0.5)); uint b = uint(floor(saturate(RoughnessData.z) * 255.0 + 0.5)); uint a = uint(floor(clamp(Anisotropy, -1.0, 1.0) * 127.0 + 127.0)); // Range is [-1,1] so use an SNorm encoding here so we can represent 0.0 exactly return (r << 24) | (g << 16) | (b << 8) | a; } float4 PayloadDecodeRoughnessAniso(uint rgba) { float r = float((rgba >> 24) ) * (1.0 / 255.0); float g = float((rgba >> 16) & 255) * (1.0 / 255.0); float b = float((rgba >> 8) & 255) * (1.0 / 255.0); float a = float((rgba ) & 255) * (1.0 / 127.0) - 1.0; return float4(r, g, b, a); } uint PayloadEncodeFuzzAndBottomNormal(float FuzzAmount, float FuzzRoughness, uint BottomNormalOct16bits) { uint r = uint(floor(saturate(FuzzAmount) * 255.0 + 0.5)); uint g = uint(floor(saturate(FuzzRoughness) * 255.0 + 0.5)); return (r << 24) | (g << 16) | (BottomNormalOct16bits & 0xFFFF); } void PayloadDecodeFuzzAndBottomNormal(uint rgba, inout float FuzzAmount, inout float FuzzRoughness, inout uint BottomNormalOct16bits) { FuzzAmount = float((rgba >> 24)) * (1.0 / 255.0); FuzzRoughness = float((rgba >> 16) & 255) * (1.0 / 255.0); BottomNormalOct16bits = rgba & 0xFFFF; } float3 AdjustShadingNormal(float3 ShadingNormal, float3 GeoNormal, float3 RayDirection) { // Clip shading normal in a view dependent way such that the reflection stays above the geometric normal // This introduces a bit of view-dependency to the shading normal but fixes dark artifacts around grazing angles float3 D = RayDirection; float3 R = reflect(D, ShadingNormal); // https://iquilezles.org/www/articles/dontflip/dontflip.htm float k = dot(R, GeoNormal); if (k < 0.0) { return normalize(normalize(R - k * GeoNormal) - D); } return ShadingNormal; } #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_PATH_TRACING_MATERIAL) && IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) #error "Path tracing and GPULightmass payloads are mutually exclusive. They should not be both enabled at once." #endif #if SUBSTRATE_ENABLED && !IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) #define PATHTRACING_SUBSTRATE_PAYLOAD 1 #else #define PATHTRACING_SUBSTRATE_PAYLOAD 0 #endif // This payload structure is what we transport between RGS/CHS/AHS programs struct FPackedPathTracingPayload : FMinimalPayload { // float FMinimalPayload::HitT // 4 bytes #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) uint PackedData[7]; // 28 bytes (encoded data, depends on shading model and ray type) // 32 bytes total #elif PATHTRACING_SUBSTRATE_PAYLOAD // Need a bit more space for Substrate slab data uint PackedData[18]; // 72 bytes // 76 bytes total #else uint PackedData[15]; // 60 bytes (encoded data, depends on shading model and ray type) // 64 bytes total #endif float UnpackBSDFOpacity() { return f16tof32(PackedData[0]); } uint GetShadingModelID() { return (PackedData[0] >> 16) & 0xF; } uint GetFlags() { return (PackedData[0] >> 20) & 0x1FF; } void SetFlag(uint Flag) { PackedData[0] |= Flag << 20; } void RemoveFlag(uint Flag) { PackedData[0] &= ~(Flag << 20); } uint GetPrimitiveLightingChannelMask() { return (PackedData[0] >> 29) & 0x7; } // Flag methods void SetCameraRay() { SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_CAMERA); } bool IsCameraRay() { return (GetFlags() & PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_MASK) == PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_CAMERA; } void SetVisibilityRay() { SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_SHADOW); } bool IsVisibilityRay() { return (GetFlags() & PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_MASK) == PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_SHADOW; } bool IsFrontFace() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; } bool IsDecalReceiver() { const uint Mask = PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_COLOR | PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_NORMAL | PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_ROUGHNESS | PATH_TRACING_PAYLOAD_OUTPUT_FLAG_USE_DBUFFER_LOOKUP; return (GetFlags() & Mask) != 0; } bool HasDecalResponseColor() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_COLOR ) != 0; } bool HasDecalResponseNormal() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_NORMAL ) != 0; } bool HasDecalResponseRoughness() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_ROUGHNESS) != 0; } bool UsesDBufferLookup() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_USE_DBUFFER_LOOKUP ) != 0; } void SetOutputDepth() { SetFlag(PATH_TRACING_PAYLOAD_OUTPUT_FLAG_OUTOUT_DEPTH); } bool ShouldOutputDepth() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_OUTOUT_DEPTH) != 0; } bool IsHoldout() { return (GetFlags() & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT) != 0; } // These method are meant to be used only when IsVisibilityRay() is true float3 GetRayThroughput() { return float3( asfloat(PackedData[1]), asfloat(PackedData[2]), asfloat(PackedData[3]) ); } void SetRayThroughput(float3 RayThroughput) { PackedData[1] = asuint(RayThroughput.x); PackedData[2] = asuint(RayThroughput.y); PackedData[3] = asuint(RayThroughput.z); } float GetPathRoughness() { return asfloat(PackedData[4]); } void SetPathRoughness(float PathRoughness) { PackedData[4] = asuint(PathRoughness); } #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) float3 UnpackWorldGeoNormal() { return PayloadDecodeUnitVector(PackedData[1]); } float3 UnpackWorldNormal() { return PayloadDecodeUnitVector(PackedData[2]); } float3 UnpackRadiance() { return PayloadDecodeHDRColor(PackedData[3]); } float3 UnpackBaseColor() { return PayloadDecodeLDRColor(PackedData[4]); } float3 UnpackTransparencyColor() { return PayloadDecodeLDRColor(PackedData[5]); } float3 UnpackSubsurfaceColor() { return PayloadDecodeLDRColor(PackedData[6]); } #else float3 UnpackWorldGeoNormal() { return PayloadDecodeUnitVector(PackedData[1]); } float3 UnpackWorldSmoothNormal() { return PayloadDecodeUnitVector(PackedData[2]); } float3 UnpackWorldNormal() { return PayloadDecodeUnitVector(PackedData[3]); } float3 UnpackRadiance() { return PayloadDecodeHDRColor(PackedData[4]); } void PackWorldNormal(float3 N) { PackedData[3] = PayloadEncodeUnitVector(N); } void PackRadiance(float3 R) { PackedData[4] = PayloadEncodeHDRColor(R); } float3 UnpackWorldTangent() { return PayloadDecodeTangentAngle(UnpackWorldNormal(), PackedData[5]); } float UnpackIor() { return f16tof32(PackedData[5] >> 16); } #if PATHTRACING_SUBSTRATE_PAYLOAD float3 UnpackDiffuseColor() { return PayloadDecodeLDRColor(PackedData[6]); } float3 UnpackSpecularColor() { return PayloadDecodeLDRColor(PackedData[7]); } float3 UnpackSpecularEdgeColor() { return PayloadDecodeLDRColor(PackedData[8]); } float4 UnpackRoughnessAniso() { return PayloadDecodeRoughnessAniso(PackedData[9]); } float3 UnpackTransparencyColor() { return PayloadDecodeLDRColor(PackedData[10]); } float3 UnpackMeanFreePath() { return float3(f16tof32(PackedData[11]), f16tof32(PackedData[11] >> 16), f16tof32(PackedData[12])); } float UnpackPhaseG() { return f16tof32(PackedData[12] >> 16); } float3 UnpackFuzzColor() { return PayloadDecodeLDRColor(PackedData[13]); } float3 UnpackWeightV() { return PayloadDecodeLDRColor(PackedData[15]); } float3 UnpackTransmittanceN() { return PayloadDecodeLDRColor(PackedData[16]); } float UnpackCoverageAboveAlongN() { return abs(f16tof32(PackedData[17] >> 16)); } float UnpackSpecularProfileId() { return f16tof32(PackedData[17]); } void PackDiffuseColor(float3 C) { PackedData[6] = PayloadEncodeLDRColor(C); } void PackSpecularColor(float3 C) { PackedData[7] = PayloadEncodeLDRColor(C); } void PackRoughnessAniso(float4 D) { PackedData[9] = PayloadEncodeRoughnessAniso(D.xyz, D.w); } #else float3 UnpackBaseColor() { return PayloadDecodeLDRColor(PackedData[6]); } float3 UnpackTransparencyColor() { return PayloadDecodeLDRColor(PackedData[7]); } float UnpackMetallic() { return f16tof32(PackedData[8]); } float UnpackSpecular() { return f16tof32(PackedData[8] >> 16); } float UnpackRoughness() { return f16tof32(PackedData[9]); } float UnpackAnisotropy() { return f16tof32(PackedData[9] >> 16); } float4 UnpackCustomData0() { return float4(UnpackFloat2FromUInt(PackedData[10]), UnpackFloat2FromUInt(PackedData[11])); } float4 UnpackCustomData1() { return float4(UnpackFloat2FromUInt(PackedData[12]), UnpackFloat2FromUInt(PackedData[13])); } void PackBaseColor(float3 C) { PackedData[6] = PayloadEncodeLDRColor(C); } void PackMetallicSpecular(float M, float S) { PackedData[8] = PackFloat2ToUInt(float2(M, S)); } void PackRoughnessAniso(float R, float A) { PackedData[9] = PackFloat2ToUInt(float2(R, A)); } #endif // tau: optical depth along shadow ray, this is the integral of the extinction coefficient float3 GetTau() { return float3( asfloat(PackedData[5]), asfloat(PackedData[6]), asfloat(PackedData[7]) ); } void SetTau(float3 Tau) { PackedData[5] = asuint(Tau.x); PackedData[6] = asuint(Tau.y); PackedData[7] = asuint(Tau.z); } float4 GetDBufferA() { return float4( UnpackFloat2FromUInt(PackedData[8]), UnpackFloat2FromUInt(PackedData[9]) ); } void SetDBufferA(float4 DBufferA) { PackedData[8] = PackFloat2ToUInt(DBufferA.xy); PackedData[9] = PackFloat2ToUInt(DBufferA.zw); } float4 GetDBufferB() { return float4( UnpackFloat2FromUInt(PackedData[10]), UnpackFloat2FromUInt(PackedData[11]) ); } void SetDBufferB(float4 DBufferB) { PackedData[10] = PackFloat2ToUInt(DBufferB.xy); PackedData[11] = PackFloat2ToUInt(DBufferB.zw); } float4 GetDBufferC() { return float4( UnpackFloat2FromUInt(PackedData[12]), UnpackFloat2FromUInt(PackedData[13]) ); } void SetDBufferC(float4 DBufferC) { PackedData[12] = PackFloat2ToUInt(DBufferC.xy); PackedData[13] = PackFloat2ToUInt(DBufferC.zw); } void SetStochasticSlabRand(float SlabRand) { PackedData[14] = asuint(SlabRand); } float GetStochasticSlabRand() { return asfloat(PackedData[14]); } float GetSceneDepth() { // NOTE: This is aliased with GetDBufferA, but this is ok as the two features may not be used together return asfloat(PackedData[8]); } void SetSceneDepth(float SceneZ) { PackedData[8] = asuint(SceneZ); } #if PATH_TRACER_USE_CLOUD_SHADER // Volumetric callable shaders void SetVolumetricCallableShaderInput(float3 Origin, float3 Direction, float TMin, float TMax, RandomSequence RandSeq, uint Flags, float3 Throughput, float CloudDensity) { HitT = TMin; PackedData[0] = asuint(TMax); PackedData[1] = asuint(Origin.x); PackedData[2] = asuint(Origin.y); PackedData[3] = asuint(Origin.z); PackedData[4] = asuint(Direction.x); PackedData[5] = asuint(Direction.y); PackedData[6] = asuint(Direction.z); PackedData[7] = RandSeq.SampleIndex; PackedData[8] = RandSeq.SampleSeed; PackedData[9] = (Flags & 0xFFFF) | (f32tof16(CloudDensity) << 16); PackedData[10] = asuint(Throughput.x); PackedData[11] = asuint(Throughput.y); PackedData[12] = asuint(Throughput.z); } void SetVolumetricCallableShaderInputCloudFactor(float CloudFactor) { PackedData[13] = asuint(CloudFactor); } void SetVolumetricCallableShaderRISContext(FRISContext SurfaceContext) { PackedData[13] = asuint(SurfaceContext.WeightSum); PackedData[14] = asuint(SurfaceContext.RandSample); } FRayDesc GetVolumetricCallableShaderInputRay() { FRayDesc Ray; Ray.Origin = asfloat(uint3(PackedData[1], PackedData[2], PackedData[3])); Ray.Direction = asfloat(uint3(PackedData[4], PackedData[5], PackedData[6])); Ray.TMin = HitT; Ray.TMax = asfloat(PackedData[0]); return Ray; } RandomSequence GetVolumetricCallableShaderInputRandSequence() { RandomSequence RandSeq; RandSeq.SampleIndex = PackedData[7]; RandSeq.SampleSeed = PackedData[8]; return RandSeq; } uint GetVolumetricCallableShaderInputFlags() { return PackedData[9] & 0xFFFF; } float3 GetVolumetricCallableShaderInputThroughput() { return asfloat(uint3(PackedData[10], PackedData[11], PackedData[12])); } float GetVolumetricCallableShaderInputCloudFactor() { return asfloat(PackedData[13]); } float GetVolumetricCallableShaderInputCloudDensity() { return f16tof32(PackedData[9] >> 16); } FRISContext GetVolumetricCallableShaderRISContext() { FRISContext Context; Context.WeightSum = asfloat(PackedData[13]); Context.RandSample = asfloat(PackedData[14]); return Context; } void SetVolumetricCallableShaderOutput(RandomSequence RandSeq, float3 Throughput, float3 Albedo) { PackedData[0] = RandSeq.SampleIndex; PackedData[1] = RandSeq.SampleSeed; // sampling mode: need to pack transmittance and albedo together PackedData[2] = PackFloat2ToUInt(Throughput.xy); PackedData[3] = PackFloat2ToUInt(float2(Throughput.z, Albedo.x)); PackedData[4] = PackFloat2ToUInt(Albedo.yz); } void SetVolumetricCallableShaderOutput(RandomSequence RandSeq, float3 Throughput) { // Increased precision for Transmittance mode PackedData[0] = RandSeq.SampleIndex; PackedData[1] = RandSeq.SampleSeed; PackedData[2] = asuint(Throughput.x); PackedData[3] = asuint(Throughput.y); PackedData[4] = asuint(Throughput.z); } void SetVolumetricCallableShaderOutputSample(float SampleT, float3 AlbedoRayleigh, float3 AlbedoHG1, float PhaseG, float3 AlbedoHG2, float3 DualPhaseData, float3 PayloadThroughput) { HitT = SampleT; PackedData[5] = PackFloat2ToUInt(PayloadThroughput.xy); PackedData[6] = PackFloat2ToUInt(float2(PayloadThroughput.z, PhaseG)); PackedData[7] = PayloadEncodeLDRColor(AlbedoRayleigh); PackedData[8] = PayloadEncodeLDRColor(AlbedoHG1); PackedData[9] = PayloadEncodeLDRColor(AlbedoHG2); PackedData[10] = PayloadEncodeLDRColor(DualPhaseData * float3(0.5, 0.5, 1.0) + float3(0.5, 0.5, 0.0)); } void SetVolumetricCallableShaderOutputCloudDensityFactor(float Factor) { PackedData[12] &= 0xFFFFu; // clear upper bits PackedData[12] |= f32tof16(Factor) << 16; } void SetVolumetricCallableShaderOutputRadiance(float3 Radiance, float Alpha) { PackedData[11] = PayloadEncodeHDRColor(Radiance); PackedData[12] &= 0xFFFF0000u; // clear lower bits PackedData[12] |= f32tof16(Alpha); } RandomSequence GetVolumetricCallableShaderOutputRandSeq() { RandomSequence RandSeq; RandSeq.SampleIndex = PackedData[0]; RandSeq.SampleSeed = PackedData[1]; return RandSeq; } float3 GetVolumetricCallableShaderOutputThroughput() { return float3(UnpackFloat2FromUInt(PackedData[2]), UnpackFloat2FromUInt(PackedData[3]).x); } float3 GetVolumetricCallableShaderOutputAlbedo() { return float3(UnpackFloat2FromUInt(PackedData[3]).y, UnpackFloat2FromUInt(PackedData[4])); } float3 GetVolumetricCallableShaderOutputTransmittanceThroughput() { // Increased precision for Transmittance mode return asfloat(uint3(PackedData[2], PackedData[3], PackedData[4])); } float3 GetVolumetricCallableShaderOutputPayloadThroughput() { return float3(UnpackFloat2FromUInt(PackedData[5]), UnpackFloat2FromUInt(PackedData[6]).x); } float3 GetVolumetricCallableShaderOutputRayleighWeight() { return PayloadDecodeLDRColor(PackedData[7]); } float4 GetVolumetricCallableShaderOutputHG() { return float4(PayloadDecodeLDRColor(PackedData[8]), UnpackFloat2FromUInt(PackedData[6]).y); } float3 GetVolumetricCallableShaderOutputDualHGWeight() { return PayloadDecodeLDRColor(PackedData[9]); } float3 GetVolumetricCallableShaderOutputDualHGPhaseData() { return PayloadDecodeLDRColor(PackedData[10]) * float3(2, 2, 1) - float3(1, 1, 0); } float3 GetVolumetricCallableShaderOutputRadiance() { return PayloadDecodeHDRColor(PackedData[11]); } float GetVolumetricCallableShaderOutputAlpha() { return f16tof32(PackedData[12]); } float GetVolumetricCallableShaderOutputCloudDensityFactor() { return f16tof32(PackedData[12] >> 16); } #endif // PATH_TRACER_USE_CLOUD_SHADER #endif // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) }; #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_PATH_TRACING_MATERIAL) CHECK_RT_PAYLOAD_SIZE(FPackedPathTracingPayload) #endif #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) CHECK_RT_PAYLOAD_SIZE(FPackedPathTracingPayload) #endif // This payload structure is the expanded version of the above which is more convenient to work with struct FPathTracingPayload : FMinimalPayload { float3 Radiance; float3 WorldGeoNormal; // normal of the actual triangle (faceted) float3 WorldNormal; // normal output of the material (includes normal/bump) #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) // skip smooth normal for GPULightmass #else float3 WorldSmoothNormal; // smooth normal before normal/bump is applied #endif float BSDFOpacity; // how much should we weigh down the bsdf by? (for glass, controls the blend between solid and glass lobes) float3 TransparencyColor; // how much do we see straight through the surface? uint ShadingModelID; uint Flags; uint PrimitiveLightingChannelMask; #if PATHTRACING_SUBSTRATE_PAYLOAD float3 DiffuseColor; float3 SpecularColor; float3 SpecularEdgeColor; float3 RoughnessData; // r0,r1,rblend uint BottomNormalOct16bits; float Anisotropy; float3 WorldTangent; float3 MeanFreePath; float PhaseG; float Ior; float FuzzAmount; float FuzzRoughness; float3 FuzzColor; float3 WeightV; float3 TransmittanceN; float CoverageAboveAlongN; float SpecularProfileId; float GlintValue; float2 GlintUV; float2 GlintUVdx; float2 GlintUVdy; // not transported through packed payload, but used in raygen float3 SubsurfaceColor; #else // PATHTRACING_SUBSTRATE_PAYLOAD float3 BaseColor; #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) float3 SubsurfaceColor; #else float3 DiffuseColor; float3 SpecularColor; float Metallic; float Specular; float Roughness; float Anisotropy; float Ior; float4 CustomData0; float4 CustomData1; float3 WorldTangent; float3 SubsurfaceColor; float3 SubsurfaceRadius; #endif // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) #endif // PATHTRACING_SUBSTRATE_PAYLOAD float3 TranslatedWorldPos; void SetFrontFace() { Flags |= PATH_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE; } bool IsFrontFace() { return (Flags & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_FRONT_FACE) != 0; } void SetDecalReceiver(uint DecalResponseMask) { Flags |= (DecalResponseMask & 0x07) << PATH_TRACING_PAYLOAD_OUTPUT_FLAG_DECAL_RESPONSE_MASK_SHIFT; } void SetUseDBufferLookup() { Flags |= PATH_TRACING_PAYLOAD_OUTPUT_FLAG_USE_DBUFFER_LOOKUP; } bool UsesDBufferLookup() { return (Flags & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_USE_DBUFFER_LOOKUP) != 0; } void SetHoldout() { Flags |= PATH_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT; } bool IsHoldout() { return (Flags & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_IS_HOLDOUT) != 0; } void SetMaterialTwoSided() { Flags |= PATH_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED_MATERIAL; } bool IsMaterialTwoSided() { return (Flags & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_TWO_SIDED_MATERIAL) != 0; } void SetOutputDepth() { Flags |= PATH_TRACING_PAYLOAD_OUTPUT_FLAG_OUTOUT_DEPTH; } bool ShouldOutputDepth() { return Flags & PATH_TRACING_PAYLOAD_OUTPUT_FLAG_OUTOUT_DEPTH; } #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) float3 GetBaseColor() { return BaseColor; } float3 GetSubsurfaceColor() { return SubsurfaceColor; } bool IsMaterialTransmissive() { return ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE; } #else // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) #if PATHTRACING_SUBSTRATE_PAYLOAD // SHADINGMODELID_SUBSTRATE float3 GetMaxLobeWeight() { return WeightV * lerp(1.0f, TransmittanceN, CoverageAboveAlongN); // largest value LobeWeight() could return } // SHADINGMODELID_NUM (Lambert diffuse) and SHADINGMODELID_MEDIUM float3 GetBaseColor() { return DiffuseColor; } void SetBaseColor(float3 BaseColor) { DiffuseColor = BaseColor; } // SHADINGMODELID_MEDIUM float3 GetHGWeight() { return SpecularColor; } float GetHGPhaseG() { return PhaseG; } void SetHG(float3 Weight, float G) { SpecularColor = Weight; PhaseG = G; } float3 GetDualHGWeight() { return SpecularEdgeColor; } float3 GetDualHGPhaseData() { return RoughnessData; } void SetDualHG(float3 Weight, float3 PhaseData) { SpecularEdgeColor = Weight; RoughnessData = PhaseData; } float GetCloudFactor() { return Anisotropy; } void SetCloudFactor(float Factor) { Anisotropy = Factor; } // SHADINGMODELID_EYE float GetEyeRoughness() { return RoughnessData.x; } void SetEyeRoughness(float R) { RoughnessData.x = R; } float3 GetEyeCausticNormal() { return normalize(2.0 * FuzzColor - 1.0); } // SUBSTRATE_TODO: not optimal since packed as color void SetEyeCausticNormal(float3 CausticNormal) { FuzzColor = 0.5 * CausticNormal + 0.5; } float GetEyeIrisMask() { return Anisotropy; } void SetEyeIrisMask(float IrisMask) { Anisotropy = IrisMask; } float3 GetEyeIrisNormal() { return normalize(2.0 * SpecularEdgeColor - 1.0); } // SUBSTRATE_TODO: not optimal since packed as color void SetEyeIrisNormal(float3 IrisNormal) { SpecularEdgeColor = IrisNormal * 0.5 + 0.5; } // SHADINGMODELID_HAIR float GetHairLongitudinalRoughness() { return RoughnessData.x; } void SetHairLongitudinalRoughness(float Roughness) { RoughnessData.x = Roughness;} float GetHairAzimuthalRoughness() { return RoughnessData.y; } void SetHairAzimuthalRoughness(float AzimuthalRoughness) { RoughnessData.y = AzimuthalRoughness; } float GetHairSpecular() { return RoughnessData.z; } void SetHairSpecular(float Specular) { RoughnessData.z = Specular; } float2 GetHairPrimitiveUV() { return SpecularEdgeColor.xy; } void SetHairPrimitiveUV(float2 UV) { SpecularEdgeColor.xy = UV; } float3 GetExtinction() { return rcp(max(MeanFreePath, 1e-5)); } // SHADINGMODELID_THIN_TRANSLUCENT float3 GetTransmittanceColor() { return exp(-GetExtinction() * SUBSTRATE_SIMPLEVOLUME_THICKNESS_CM); } // SHADINGMODELID_TWOSIDED_FOLIAGE float3 GetFoliageTransmissionColor() { return MeanFreePath; } void SetFoliageTransmissionColor(float3 C) { MeanFreePath = C; } #else // PATHTRACING_SUBSTRATE_PAYLOAD // Various ways to interpret CustomData (depending on ShadingModelID) // NOTE: This is not always following the same conventions as GetMaterialCustomData0,1() // SHADINGMODELID_CLOTH float3 GetClothColor() { return CustomData0.xyz; } void SetClothColor(float3 ClothColor) { CustomData0.xyz = ClothColor; } float GetClothAmount() { return CustomData0.w; } void SetClothAmount(float ClothAmount) { CustomData0.w = ClothAmount; } // SHADINGMODELID_CLEAR_COAT float GetClearCoat() { return CustomData0.x; } float GetClearCoatRoughness() { return CustomData0.y; } void SetClearCoat(float ClearCoat) { CustomData0.x = ClearCoat; } void SetClearCoatRoughness(float ClearCoatRoughness) { CustomData0.y = ClearCoatRoughness; } float3 GetClearCoatBottomNormal() { return CustomData1.xyz; } void SetClearCoatBottomNormal(float3 BottomNormal) { CustomData1.xyz = BottomNormal; } // SHADINGMODELID_SUBSURFACE, SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE float3 GetSubsurfaceRadius() { return CustomData0.rgb; } void SetSubsurfaceRadius(float3 Radius) { CustomData0.rgb = Radius; } float GetSubsurfacePhaseFunction() { return CustomData0.a; } void SetSubsurfacePhaseFunction(float G) { CustomData0.a = G; } // SHADINGMODELID_SUBSURFACE, SHADINGMODELID_TWOSIDED_FOLIAGE (note that SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE don't use this as they use DiffuseColor directly) float3 GetSubsurfaceColor() { return CustomData1.xyz; } void SetSubsurfaceColor(float3 SubsurfaceColor) { CustomData1.xyz = SubsurfaceColor; } // SHADINGMODELID_SUBSURFACE_PROFILE float3 GetDualRoughnessSpecular() { return CustomData1.xyz; } void SetDualRoughnessSpecular(float Roughness0, float Roughness1, float LobeMix) { CustomData1.xyz = float3(Roughness0, Roughness1, LobeMix); } // SHADINGMODELID_EYE float GetEyeRoughness() { return Roughness; } void SetEyeRoughness(float R) { Roughness = R; } float3 GetEyeCausticNormal() { return OctahedronToUnitVector(CustomData1.xy); } void SetEyeCausticNormal(float3 CausticNormal) { CustomData1.xy = UnitVectorToOctahedron(CausticNormal); } float GetEyeIrisMask() { return Anisotropy; } void SetEyeIrisMask(float IrisMask) { Anisotropy = IrisMask; } float3 GetEyeIrisNormal() { return OctahedronToUnitVector(CustomData1.zw); } void SetEyeIrisNormal(float3 IrisNormal) { CustomData1.zw = UnitVectorToOctahedron(IrisNormal); } // SHADINGMODELID_HAIR float GetHairLongitudinalRoughness() { return Roughness; } void SetHairLongitudinalRoughness(float R) { Roughness = R; } float GetHairAzimuthalRoughness() { return Metallic; } void SetHairAzimuthalRoughness(float AzimuthalRoughness) { Metallic = AzimuthalRoughness; } float GetHairSpecular() { return Specular; } void SetHairSpecular(float S) { Specular = S; } float2 GetHairPrimitiveUV() { return CustomData0.xy; } void SetHairPrimitiveUV(float2 UV) { CustomData0.xy = UV; } // SHADINGMODELID_THIN_TRANSLUCENT float3 GetTransmittanceColor() { return CustomData0.xyz; } void SetTransmittanceColor(float3 TransmittanceColor) { CustomData0.xyz = TransmittanceColor; } // SHADINGMODELID_DEFAULT_LIT float3 GetExtinction() { return CustomData0.xyz; } void SetExtinction(float3 SigmaT) { CustomData0.xyz = SigmaT; } // SHADINGMODELID_NUM (Lambert diffuse) and SHADINGMODELID_MEDIUM float3 GetBaseColor() { return BaseColor; } void SetBaseColor(float3 C) { BaseColor = C; } // SHADINGMODELID_MEDIUM float3 GetHGWeight() { return CustomData0.xyz; } float GetHGPhaseG() { return CustomData0.w; } void SetHG(float3 Weight, float G) { CustomData0 = float4(Weight, G); } float3 GetDualHGWeight() { return CustomData1.xyz; } float3 GetDualHGPhaseData() { return float3(Metallic, Specular, Roughness); } void SetDualHG(float3 Weight, float3 PhaseData) { CustomData1.xyz = Weight; Metallic = PhaseData.x; Specular = PhaseData.y; Roughness = PhaseData.z; } float GetCloudFactor() { return CustomData1.w; } void SetCloudFactor(float Factor) { CustomData1.w = Factor; } #endif // PATHTRACING_SUBSTRATE_PAYLOAD // Methods used by the integrator bool HasRefraction() { return Ior > 0.0; } bool IsMaterialSolidGlass() { return ShadingModelID == SHADINGMODELID_SOLID_GLASS; } bool IsMaterialThinGlass() { return ShadingModelID == SHADINGMODELID_THIN_TRANSLUCENT && HasRefraction(); } bool IsMaterialTransmissive() { return ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE || ShadingModelID == SHADINGMODELID_HAIR || ShadingModelID == SHADINGMODELID_MEDIUM || ShadingModelID == SHADINGMODELID_UNLIT || // Volumetric Directional/Non-Directional IsMaterialSolidGlass() || IsMaterialThinGlass(); } bool IsSubsurfaceMaterial() { #if PATHTRACING_SUBSTRATE_PAYLOAD return ShadingModelID == SHADINGMODELID_SUBSTRATE || ShadingModelID == SHADINGMODELID_EYE; #else return ShadingModelID == SHADINGMODELID_SUBSURFACE || ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN || ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE || ShadingModelID == SHADINGMODELID_EYE; #endif } #endif // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) }; FPackedPathTracingPayload InitPathTracingPayload(uint ScatterType, float PathRoughness) { FPackedPathTracingPayload Output = (FPackedPathTracingPayload)0; Output.SetPathRoughness(PathRoughness); Output.SetMiss(); switch (ScatterType) { case PATHTRACER_SCATTER_DIFFUSE: Output.SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_DIFFUSE); break; case PATHTRACER_SCATTER_SPECULAR: case PATHTRACER_SCATTER_REFRACT: Output.SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_SPECULAR); break; case PATHTRACER_SCATTER_VOLUME: Output.SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_INDIRECT_VOLUME); break; case PATHTRACER_SCATTER_CAMERA: default: Output.SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_RAY_TYPE_CAMERA); break; } return Output; } FPackedPathTracingPayload InitPathTracingVisibilityPayload(float PathRoughness) { FPackedPathTracingPayload Output = (FPackedPathTracingPayload)0; // Signal to the AHS we want to evaluate the opacity // The payload is used to carry the path throughput (for transparent shadows) // and current path roughness (for approximate caustics) Output.SetVisibilityRay(); Output.SetPathRoughness(PathRoughness); Output.SetRayThroughput(1.0); // NOTE: We start with HitT = 0 which counts as "IsHit()". This will be changed by the miss shader if we don't register any hits (or the AHS writes don't accumulate all the way to 0) // This is because we want to be able to skip the CHS on shadow rays. Therefore the miss shader is the one that will be responsible for telling us a ray did not hit anything opaque. // If after tracing the ray we still have IsHit() returning true, this means we must have hit something opaque (without having run either CHS or AHS). // This also has the benefit of preparing the "mailbox" used for detecting duplicate invocations of AHS with an invalid value. Output.HitT = 0.0; return Output; } FPackedPathTracingPayload PackPathTracingPayload(FPathTracingPayload Input) { FPackedPathTracingPayload Output = (FPackedPathTracingPayload)0; Output.HitT = Input.HitT; Output.PackedData[0] = f32tof16(Input.BSDFOpacity); Output.PackedData[0] |= (Input.ShadingModelID & 0xF) << 16; // 4 bits Output.PackedData[0] |= (Input.Flags & 0x1FF) << 20; // 9 bits Output.PackedData[0] |= (Input.PrimitiveLightingChannelMask & 0x7) << 29; // 3 bits // total 32 bits #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) Output.PackedData[1] = PayloadEncodeUnitVector(Input.WorldGeoNormal); Output.PackedData[2] = PayloadEncodeUnitVector(Input.WorldNormal); Output.PackedData[3] = PayloadEncodeHDRColor(Input.Radiance); Output.PackedData[4] = PayloadEncodeLDRColor(Input.BaseColor); Output.PackedData[5] = PayloadEncodeLDRColor(Input.TransparencyColor); Output.PackedData[6] = PayloadEncodeLDRColor(Input.SubsurfaceColor); #else Output.PackedData[1] = PayloadEncodeUnitVector(Input.WorldGeoNormal); Output.PackedData[2] = PayloadEncodeUnitVector(Input.WorldSmoothNormal); Output.PackedData[3] = PayloadEncodeUnitVector(Input.WorldNormal); Output.PackedData[4] = PayloadEncodeHDRColor(Input.Radiance); Output.PackedData[5] = PayloadEncodeTangentAngle(Output.UnpackWorldNormal(), Input.WorldTangent); Output.PackedData[5]|= f32tof16(Input.Ior) << 16; #if PATHTRACING_SUBSTRATE_PAYLOAD Output.PackedData[6] = PayloadEncodeLDRColor(Input.DiffuseColor); Output.PackedData[7] = PayloadEncodeLDRColor(Input.SpecularColor); Output.PackedData[8] = PayloadEncodeLDRColor(Input.SpecularEdgeColor); Output.PackedData[9] = PayloadEncodeRoughnessAniso(Input.RoughnessData, Input.Anisotropy); Output.PackedData[10] = PayloadEncodeLDRColor(Input.TransparencyColor); Output.PackedData[11] = PackFloat2ToUInt(float2(Input.MeanFreePath.xy)); Output.PackedData[12] = PackFloat2ToUInt(float2(Input.MeanFreePath.z, Input.PhaseG)); Output.PackedData[13] = PayloadEncodeLDRColor(Input.FuzzColor); Output.PackedData[14] = PayloadEncodeFuzzAndBottomNormal(Input.FuzzAmount, Input.FuzzRoughness, Input.BottomNormalOct16bits); Output.PackedData[15] = PayloadEncodeLDRColor(Input.WeightV); Output.PackedData[16] = PayloadEncodeLDRColor(Input.TransmittanceN); Output.PackedData[17] = PackFloat2ToUInt(float2(Input.SpecularProfileId, abs(Input.CoverageAboveAlongN))); // In order to not increase the data payload size, we alias SSS/Fuzz parameters when glint are enabled if (Input.GlintValue < 1) { const uint2 PackedGlintsData = PackGlints(Input.GlintValue, Input.GlintUV); Output.PackedData[11] = PackedGlintsData.x; Output.PackedData[12] = PackedGlintsData.y; Output.PackedData[13] = PackFloat2ToUInt(Input.GlintUVdx); Output.PackedData[14] = PackFloat2ToUInt(Input.GlintUVdx); Output.PackedData[17] |= 0x80000000u; // Encode presence of glints in sign bit of CoverageAboveAlongN } #else // PATHTRACING_SUBSTRATE_PAYLOAD Output.PackedData[ 6] = PayloadEncodeLDRColor(Input.BaseColor); Output.PackedData[ 7] = PayloadEncodeLDRColor(Input.TransparencyColor); Output.PackedData[ 8] = PackFloat2ToUInt(float2(Input.Metallic , Input.Specular )); Output.PackedData[ 9] = PackFloat2ToUInt(float2(Input.Roughness, Input.Anisotropy)); Output.PackedData[10] = PackFloat2ToUInt(Input.CustomData0.xy); Output.PackedData[11] = PackFloat2ToUInt(Input.CustomData0.zw); Output.PackedData[12] = PackFloat2ToUInt(Input.CustomData1.xy); Output.PackedData[13] = PackFloat2ToUInt(Input.CustomData1.zw); #endif // PATHTRACING_SUBSTRATE_PAYLOAD #endif // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) return Output; } #if RAYGENSHADER FPathTracingPayload UnpackPathTracingPayload(FPackedPathTracingPayload Input, FRayDesc Ray) { FPathTracingPayload Output = (FPathTracingPayload)0; Output.HitT = Input.HitT; Output.TranslatedWorldPos = Ray.Origin + Output.HitT * Ray.Direction; Output.BSDFOpacity = Input.UnpackBSDFOpacity(); Output.ShadingModelID = Input.GetShadingModelID(); Output.Flags = Input.GetFlags(); Output.PrimitiveLightingChannelMask = Input.GetPrimitiveLightingChannelMask(); #if IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) Output.WorldGeoNormal = Input.UnpackWorldGeoNormal(); Output.WorldNormal = Input.UnpackWorldNormal(); Output.Radiance = Input.UnpackRadiance(); Output.BaseColor = Input.UnpackBaseColor(); Output.TransparencyColor = Input.UnpackTransparencyColor(); Output.SubsurfaceColor = Input.UnpackSubsurfaceColor(); #else Output.WorldGeoNormal = Input.UnpackWorldGeoNormal(); Output.WorldSmoothNormal = Input.UnpackWorldSmoothNormal(); Output.WorldNormal = Input.UnpackWorldNormal(); Output.Radiance = Input.UnpackRadiance(); Output.WorldTangent = Input.UnpackWorldTangent(); Output.Ior = Input.UnpackIor(); #if PATHTRACING_SUBSTRATE_PAYLOAD Output.DiffuseColor = Input.UnpackDiffuseColor(); Output.SpecularColor = Input.UnpackSpecularColor(); Output.SpecularEdgeColor = Input.UnpackSpecularEdgeColor(); float4 RoughnessAniso = Input.UnpackRoughnessAniso(); Output.RoughnessData = RoughnessAniso.xyz; Output.Anisotropy = RoughnessAniso.w; Output.TransparencyColor = Input.UnpackTransparencyColor(); Output.MeanFreePath = Input.UnpackMeanFreePath(); Output.PhaseG = Input.UnpackPhaseG(); Output.FuzzColor = Input.UnpackFuzzColor(); PayloadDecodeFuzzAndBottomNormal(Input.PackedData[14], Output.FuzzAmount, Output.FuzzRoughness, Output.BottomNormalOct16bits); Output.WeightV = Input.UnpackWeightV(); Output.TransmittanceN = Input.UnpackTransmittanceN(); Output.CoverageAboveAlongN = Input.UnpackCoverageAboveAlongN(); Output.SpecularProfileId = Input.UnpackSpecularProfileId(); // TODO: figure out how to make glint unpacking closer to functions above // In order to not increase the data payload size, we alias SSS/Fuzz parameters when glint are enabled const bool bHasGlints = Input.PackedData[17] >> 31u; if (bHasGlints) { Output.MeanFreePath.x = 0.f; Output.MeanFreePath.y = 0.f; Output.MeanFreePath.z = 0.f; Output.PhaseG = 0.f; Output.FuzzColor = 0.f; Output.FuzzAmount = 0.f; Output.FuzzRoughness = 0.f; UnpackGlints(uint2(Input.PackedData[11], Input.PackedData[12]), Output.GlintValue, Output.GlintUV); Output.GlintUVdx = UnpackFloat2FromUInt(Input.PackedData[13]); Output.GlintUVdy = UnpackFloat2FromUInt(Input.PackedData[14]); } else { // When glints are not used, default to full density so we get the regular specular highlight Output.GlintValue = 1.0f; } #else // !PATHTRACING_SUBSTRATE_PAYLOAD Output.BaseColor = Input.UnpackBaseColor(); Output.TransparencyColor = Input.UnpackTransparencyColor(); Output.Metallic = Input.UnpackMetallic(); Output.Specular = Input.UnpackSpecular(); Output.Roughness = Input.UnpackRoughness(); Output.Anisotropy = Input.UnpackAnisotropy(); Output.CustomData0 = Input.UnpackCustomData0(); Output.CustomData1 = Input.UnpackCustomData1(); // Output.DiffuseColor = Output.BaseColor - Output.BaseColor * Output.Metallic; Output.SpecularColor = ComputeF0(Output.Specular, Output.BaseColor, Output.Metallic); #endif // PATHTRACING_SUBSTRATE_PAYLOAD #endif // IS_PAYLOAD_ENABLED(RT_PAYLOAD_TYPE_GPULIGHTMASS) return Output; } #endif