// Copyright Epic Games, Inc. All Rights Reserved. #include "../Common.ush" #define SUPPORT_CONTACT_SHADOWS 0 #include "/Engine/Generated/Material.ush" #include "HeterogeneousVolumesStochasticFiltering.ush" #include "HeterogeneousVolumesLiveShadingUtils.ush" #include "HeterogeneousVolumesTracingUtils.ush" #include "HeterogeneousVolumesLightingUtils.ush" #include "../BlueNoise.ush" #ifndef THREADGROUP_SIZE_1D #define THREADGROUP_SIZE_1D 1 #endif // THREADGROUP_SIZE_1D #ifndef THREADGROUP_SIZE_2D #define THREADGROUP_SIZE_2D 1 #endif // THREADGROUP_SIZE_2D #ifndef THREADGROUP_SIZE_3D #define THREADGROUP_SIZE_3D 1 #endif // THREADGROUP_SIZE_3D #ifndef USE_EXISTENCE_MASK #define USE_EXISTENCE_MASK 1 #endif // // Object data float4x4 LocalToWorld; float4x4 WorldToLocal; float3 LocalBoundsOrigin; float3 LocalBoundsExtent; int PrimitiveId; // Volume data int3 VoxelResolution; int3 VoxelMin; int3 VoxelMax; // Shadowing float ShadowStepSize; float ShadowStepFactor; // Ray data float MaxTraceDistance; float MaxShadowTraceDistance; float StepSize; float StepFactor; int MaxStepCount; int bJitter; int StochasticFilteringMode; Texture3D ExistenceMaskTexture; // Optional cinematic features int bIsOfflineRender; // AO data int2 NumRays; FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters) { return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent); } FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent); } float4x4 GetLocalToWorld() { return LocalToWorld; } float4x4 GetWorldToLocal() { return WorldToLocal; } float3 GetLocalBoundsOrigin() { return LocalBoundsOrigin; } float3 GetLocalBoundsExtent() { return LocalBoundsExtent; } int3 GetVolumeResolution() { return VoxelResolution; } float GetStepSize() { return StepSize; } float GetShadowStepSize() { return ShadowStepSize; } float GetStepFactor() { return StepFactor; } float GetShadowStepFactor() { return ShadowStepFactor; } float GetMaxTraceDistance() { return MaxTraceDistance; } float GetMaxShadowTraceDistance() { return MaxShadowTraceDistance; } int GetStochasticFilteringMode() { return StochasticFilteringMode; } float EvalAmbientOcclusion(float3 WorldPosition) { return 0.0; } float GetIndirectInscatteringFactor() { return 0.0; } // Material sampling hooks.. struct FVolumeSampleContext { FMaterialPixelParameters MaterialParameters; FPixelMaterialInputs PixelMaterialInputs; }; FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float3 WorldPosition_DDX, float3 WorldPosition_DDY, float MipLevel) { FVolumeSampleContext VolumeSampleContext; VolumeSampleContext.MaterialParameters = MakeInitializedMaterialPixelParameters(); //VolumeSampleContext.MaterialParameters.PrimitiveId = PrimitiveId; VolumeSampleContext.MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition); VolumeSampleContext.MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition); // TODO: Add object centroid to LWC.ObjectWorldPosition VolumeSampleContext.MaterialParameters.LWCData.LocalToWorld = WSPromote(GetLocalToWorld()); VolumeSampleContext.MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(GetWorldToLocal()); #if USE_ANALYTIC_DERIVATIVES VolumeSampleContext.MaterialParameters.WorldPosition_DDX = WorldPosition_DDX; VolumeSampleContext.MaterialParameters.WorldPosition_DDY = WorldPosition_DDY; CalcPixelMaterialInputsAnalyticDerivatives(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs); #else CalcPixelMaterialInputs(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs); #endif return VolumeSampleContext; } FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float MipLevel) { float3 WorldPosition_DDX = 0.0; float3 WorldPosition_DDY = 0.0; return CreateVolumeSampleContext( LocalPosition, WorldPosition, WorldPosition_DDX, WorldPosition_DDY, MipLevel ); } FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float3 FilterWidth, float MipLevel, float Rand, int StochasticFilteringMode) { // TODO: Use FilterWidth determine MipLevel //float3 WorldPosition_DDX = mul(float4(LocalPosition + float3(1.0, 0.0, 0.0), 1.0), GetLocalToWorld()).xyz; //float3 WorldPosition_DDY = mul(float4(LocalPosition + float3(0.0, 1.0, 0.0), 1.0), GetLocalToWorld()).xyz; //float3 WorldPosition_DDZ = mul(float4(LocalPosition + float3(0.0, 0.0, 1.0), 1.0), GetLocalToWorld()).xyz; if (StochasticFilteringMode == STOCHASTIC_FILTERING_CONSTANT) { LocalPosition = StochasticFilteringConstant(LocalPosition, Rand); } else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRILINEAR) { LocalPosition = StochasticFilteringTrilinear(LocalPosition, Rand); } else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRICUBIC) { LocalPosition = StochasticFilteringTricubic(LocalPosition, Rand); } WorldPosition = mul(float4(LocalPosition, 1.0), GetLocalToWorld()).xyz; return CreateVolumeSampleContext(LocalPosition, WorldPosition, MipLevel); } float3 SampleExtinction(inout FVolumeSampleContext Context) { float3 Extinction = SampleExtinctionCoefficients(Context.PixelMaterialInputs); return Extinction; } float3 SampleEmission(inout FVolumeSampleContext Context) { float3 Emission = SampleEmissive(Context.PixelMaterialInputs); return Emission; } float3 SampleAlbedo(inout FVolumeSampleContext Context) { float3 Albedo = SampleAlbedo(Context.PixelMaterialInputs); return Albedo; } // Cinematic feature options #define INDIRECT_LIGHTING_MODE_OFF 0 #define INDIRECT_LIGHTING_MODE_LIGHTING_CACHE 1 #define INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING 2 int GetIndirectLightingMode() { return INDIRECT_LIGHTING_MODE_OFF; } bool IsOfflineRender() { #if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC return bIsOfflineRender; #else return false; #endif } int ShouldApplyHeightFog() { return false; } int ShouldApplyVolumetricFog() { return false; } #define FOG_INSCATTERING_MODE_OFF 0 #define FOG_INSCATTERING_MODE_REFERENCE 1 #define FOG_INSCATTERING_MODE_LINEAR_APPROX 2 int GetFogInscatteringMode() { return FOG_INSCATTERING_MODE_OFF; } #include "HeterogeneousVolumesRayMarchingUtils.ush" // Output RWTexture3D RWAmbientOcclusionUIntTexture; FRayMarchingContext CreateShadowRayMarchingContext( float3 WorldRayOrigin, float3 WorldToLight, uint MaxStepCount ) { float3 WorldRayEnd = WorldRayOrigin + WorldToLight; float3 WorldRayDirection = normalize(WorldToLight); float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), GetWorldToLocal()).xyz; float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), GetWorldToLocal()).xyz; float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin; float LocalRayTMin = 0.0; float LocalRayTMax = length(LocalRayDirection); LocalRayDirection /= LocalRayTMax; float ShadowBias = 0.5; float ShadowStepSize = CalcShadowStepSize(LocalRayDirection); int bApplyEmissionAndTransmittance = 0; int bApplyDirectLighting = 0; int bApplyShadowTransmittance = 0; FRayMarchingContext ShadowRayMarchingContext = CreateRayMarchingContext( LocalRayOrigin, LocalRayDirection, LocalRayTMin, LocalRayTMax, WorldRayOrigin, WorldRayDirection, ShadowBias, ShadowStepSize, MaxStepCount, bApplyEmissionAndTransmittance, bApplyDirectLighting, bApplyShadowTransmittance //RayMarchingContext.MaxShadowTraceDistance ); return ShadowRayMarchingContext; } [numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)] void RenderAmbientOcclusionWithLiveShadingCS( uint3 DispatchThreadId : SV_DispatchThreadID ) { int3 VoxelCount = VoxelMax - VoxelMin + 1; int3 VoxelIndex = float3(DispatchThreadId.x, DispatchThreadId.y, DispatchThreadId.z % VoxelCount.z); if (any(VoxelIndex >= VoxelCount)) { return; } VoxelIndex += VoxelMin; int LinearRayIndex = DispatchThreadId.z / VoxelCount.z; int2 RayIndex = int2(LinearRayIndex % NumRays.x, LinearRayIndex / NumRays.x); if (any(RayIndex >= NumRays)) { return; } float3 VoxelJitter = 0.5; if (bJitter) { uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz; VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff)); } float3 UVW = (VoxelIndex + VoxelJitter) / float3(GetLightingCacheResolution()); float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent(); float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent(); float3 LocalRayOrigin = UVW * GetLocalBoundsExtent() * 2.0 + LocalBoundsMin; float3 WorldRayOrigin = mul(float4(LocalRayOrigin, 1.0), GetLocalToWorld()).xyz; // Construct ray direction float2 RayUV = float2(RayIndex + 0.5) / NumRays; float3 WorldRayDirection = EquiAreaSphericalMapping(RayUV); // Evaluate transmittance based on MaxShadowDistance FRayMarchingContext RayMarchingContext = CreateShadowRayMarchingContext(WorldRayOrigin, WorldRayDirection * MaxShadowTraceDistance, MaxStepCount); float AmbientOcclusion = 1.0; #if USE_EXISTENCE_MASK float Existence = ExistenceMaskTexture.SampleLevel(View.SharedBilinearClampedSampler, UVW, 0).r; if (Existence > 0.0) #endif { float3 Transmittance = ComputeTransmittance(RayMarchingContext); AmbientOcclusion = Luminance(Transmittance) * rcp(NumRays.x * NumRays.y); } // Convert to fixed-point and accumulate int AmbientOcclusionFixedPoint = AmbientOcclusion * 65535.0; InterlockedAdd(RWAmbientOcclusionUIntTexture[VoxelIndex], AmbientOcclusionFixedPoint); } RWTexture3D RWExistenceMaskTexture; [numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)] void RenderExistenceMaskWithLiveShadingCS( uint3 DispatchThreadId : SV_DispatchThreadID ) { int3 VoxelCount = VoxelMax - VoxelMin + 1; int3 VoxelIndex = DispatchThreadId; if (any(VoxelIndex > VoxelCount)) { return; } VoxelIndex += VoxelMin; float3 VoxelJitter = 0.5; if (bJitter) { uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz; VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff)); } float3 UVW = (VoxelIndex + VoxelJitter) / float3(VoxelResolution); float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent(); float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent(); float3 LocalPosition = UVW * GetLocalBoundsExtent() * 2.0 + LocalBoundsMin; float3 WorldPosition = mul(float4(LocalPosition, 1.0), GetLocalToWorld()).xyz; int MipLevel = 0; FVolumeSampleContext SampleContext; if (IsOfflineRender()) { float3 FilterWidth = 1.0; RandomSequence RandSequence; int LinearIndex = VoxelIndex.x * (VoxelResolution.y * VoxelResolution.z) + VoxelIndex.y * VoxelResolution.z + VoxelIndex.z; RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex); float Rand = RandomSequence_GenerateSample1D(RandSequence); int StochasticFilteringMode = GetStochasticFilteringMode(); SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, FilterWidth, MipLevel, Rand, StochasticFilteringMode); } else { SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, MipLevel); } float3 Extinction = SampleExtinction(SampleContext); RWExistenceMaskTexture[VoxelIndex] = any(Extinction > 0.0) ? 1.0 : 0.0; }