IVSmoke 1.0
Loading...
Searching...
No Matches
IVSmokeRayMarchPipeline.cpp
1// Copyright (c) 2026, Team SDB. All rights reserved.
2
3#include "IVSmokeRayMarchPipeline.h"
4#include "IVSmokeShaders.h"
5#include "IVSmoke.h"
6#include "RenderGraphBuilder.h"
7#include "RenderGraphUtils.h"
8#include "SceneTexturesConfig.h"
9#include "SceneRenderTargetParameters.h"
10#include "SystemTextures.h"
11
12//~==============================================================================
13// Shader Implementations
14
15IMPLEMENT_GLOBAL_SHADER(FIVSmokeTileSetupCS, "/Plugin/IVSmoke/IVSmokeTileSetupCS.usf", "MainCS", SF_Compute);
16IMPLEMENT_GLOBAL_SHADER(FIVSmokeOccupancyBuildCS, "/Plugin/IVSmoke/IVSmokeOccupancyBuildCS.usf", "MainCS", SF_Compute);
17IMPLEMENT_GLOBAL_SHADER(FIVSmokeMultiVolumeRayMarchCS, "/Plugin/IVSmoke/IVSmokeMultiVolumeRayMarch.usf", "MainCS", SF_Compute);
18
19//~==============================================================================
20// Occupancy Renderer Implementation
21
22namespace IVSmokeOccupancy
23{
24 /**
25 * Compute tile count from viewport size.
26 */
27 FIntPoint ComputeTileCount(const FIntPoint& ViewportSize)
28 {
29 return FIntPoint(
30 FMath::DivideAndRoundUp(ViewportSize.X, (int32)FIVSmokeOccupancyConfig::TileSizeX),
31 FMath::DivideAndRoundUp(ViewportSize.Y, (int32)FIVSmokeOccupancyConfig::TileSizeY)
32 );
33 }
34
35 /**
36 * Compute step slice count from max steps.
37 */
38 uint32 ComputeStepSliceCount(int32 MaxSteps)
39 {
40 return FMath::DivideAndRoundUp((uint32)MaxSteps, FIVSmokeOccupancyConfig::StepDivisor);
41 }
42
43 /**
44 * Add Pass 0: Tile Setup.
45 * Computes per-tile depth range and quick volume mask.
46 */
47 void AddTileSetupPass(
48 FRDGBuilder& GraphBuilder,
49 const FSceneView& View,
50 FRDGBufferRef VolumeDataBuffer,
51 uint32 NumActiveVolumes,
52 FRDGBufferRef OutTileDataBuffer,
53 const FIntPoint& TileCount,
54 uint32 StepSliceCount,
55 float MaxRayDistance,
56 const FIntPoint& ViewportSize,
57 const FIntPoint& ViewRectMin)
58 {
59 FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(View.FeatureLevel);
60 TShaderMapRef<FIVSmokeTileSetupCS> ComputeShader(ShaderMap);
61
62 auto* Parameters = GraphBuilder.AllocParameters<FIVSmokeTileSetupCS::FParameters>();
63
64 // Output
65 Parameters->TileDataBufferRW = GraphBuilder.CreateUAV(OutTileDataBuffer);
66
67 // Scene textures
68 Parameters->SceneTexturesStruct = GetSceneTextureShaderParameters(View).SceneTextures;
69
70 // Volume data
71 Parameters->VolumeDataBuffer = GraphBuilder.CreateSRV(VolumeDataBuffer);
72 Parameters->NumActiveVolumes = NumActiveVolumes;
73
74 // Tile configuration
75 Parameters->TileCount = TileCount;
76 Parameters->StepSliceCount = StepSliceCount;
77 Parameters->MaxRayDistance = MaxRayDistance;
78
79 // Viewport
80 Parameters->ViewportSize = ViewportSize;
81 Parameters->ViewRectMin = ViewRectMin;
82
83 // Camera
84 const FViewMatrices& ViewMatrices = View.ViewMatrices;
85 Parameters->CameraPosition = FVector3f(ViewMatrices.GetViewOrigin());
86 Parameters->CameraForward = FVector3f(View.GetViewDirection());
87 Parameters->CameraRight = FVector3f(View.GetViewRight());
88 Parameters->CameraUp = FVector3f(View.GetViewUp());
89
90 const FMatrix& ProjMatrix = ViewMatrices.GetProjectionMatrix();
91 float ProjM11 = ProjMatrix.M[1][1];
92
93 // Validate projection matrix to prevent inf/NaN
94 if (FMath::IsNearlyZero(ProjM11, 1e-6f) || !FMath::IsFinite(ProjM11))
95 {
96 UE_LOG(LogIVSmoke, Error, TEXT("[TileSetup] Invalid ProjMatrix.M[1][1] = %f (likely orthographic viewport), skipping"), ProjM11);
97 return; // Skip rendering for orthographic/invalid views
98 }
99
100 Parameters->TanHalfFOV = 1.0f / ProjM11;
101 Parameters->AspectRatio = (float)ViewportSize.X / (float)ViewportSize.Y;
102
103 // Depth conversion
104 Parameters->InvDeviceZToWorldZTransform = FVector4f(View.InvDeviceZToWorldZTransform);
105
106 // Dispatch: One thread group per tile
107 FIntVector GroupCount(TileCount.X, TileCount.Y, 1);
108
109 FComputeShaderUtils::AddPass(
110 GraphBuilder,
111 RDG_EVENT_NAME("IVSmoke::TileSetup (%dx%d tiles)", TileCount.X, TileCount.Y),
112 ComputeShader,
113 Parameters,
114 GroupCount
115 );
116 }
117
118 /**
119 * Add Pass 1: Occupancy Build.
120 * Builds View and Light occupancy 3D textures.
121 */
122 void AddOccupancyBuildPass(
123 FRDGBuilder& GraphBuilder,
124 const FSceneView& View,
125 FRDGBufferRef TileDataBuffer,
126 FRDGBufferRef VolumeDataBuffer,
127 uint32 NumActiveVolumes,
128 FRDGTextureRef OutViewOccupancy,
129 FRDGTextureRef OutLightOccupancy,
130 const FIntPoint& TileCount,
131 uint32 StepSliceCount,
132 const FVector3f& LightDirection,
133 float MaxLightMarchDistance,
134 const FIntPoint& ViewportSize)
135 {
136 FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(View.FeatureLevel);
137 TShaderMapRef<FIVSmokeOccupancyBuildCS> ComputeShader(ShaderMap);
138
139 auto* Parameters = GraphBuilder.AllocParameters<FIVSmokeOccupancyBuildCS::FParameters>();
140
141 // Input
142 Parameters->TileDataBuffer = GraphBuilder.CreateSRV(TileDataBuffer);
143 Parameters->VolumeDataBuffer = GraphBuilder.CreateSRV(VolumeDataBuffer);
144 Parameters->NumActiveVolumes = NumActiveVolumes;
145
146 // Output
147 Parameters->ViewOccupancyRW = GraphBuilder.CreateUAV(OutViewOccupancy);
148 Parameters->LightOccupancyRW = GraphBuilder.CreateUAV(OutLightOccupancy);
149
150 // Configuration
151 Parameters->TileCount = TileCount;
152 Parameters->StepSliceCount = StepSliceCount;
153 Parameters->StepDivisor = FIVSmokeOccupancyConfig::StepDivisor;
154
155 // Camera
156 const FViewMatrices& ViewMatrices = View.ViewMatrices;
157 Parameters->CameraPosition = FVector3f(ViewMatrices.GetViewOrigin());
158 Parameters->CameraForward = FVector3f(View.GetViewDirection());
159 Parameters->CameraRight = FVector3f(View.GetViewRight());
160 Parameters->CameraUp = FVector3f(View.GetViewUp());
161
162 const FMatrix& ProjMatrix = ViewMatrices.GetProjectionMatrix();
163 float ProjM11 = ProjMatrix.M[1][1];
164
165 // Validate projection matrix to prevent inf/NaN
166 if (FMath::IsNearlyZero(ProjM11, 1e-6f) || !FMath::IsFinite(ProjM11))
167 {
168 UE_LOG(LogIVSmoke, Error, TEXT("[OccupancyBuild] Invalid ProjMatrix.M[1][1] = %f (likely orthographic viewport), skipping"), ProjM11);
169 return; // Skip rendering for orthographic/invalid views
170 }
171
172 Parameters->TanHalfFOV = 1.0f / ProjM11;
173 Parameters->AspectRatio = (float)ViewportSize.X / (float)ViewportSize.Y;
174
175 // Light
176 Parameters->LightDirection = LightDirection;
177 Parameters->MaxLightMarchDistance = MaxLightMarchDistance;
178
179 // Dispatch: ceil(TileCount/8) × ceil(StepSliceCount/4)
180 FIntVector GroupCount(
181 FMath::DivideAndRoundUp((uint32)TileCount.X, FIVSmokeOccupancyBuildCS::ThreadGroupSizeX),
182 FMath::DivideAndRoundUp((uint32)TileCount.Y, FIVSmokeOccupancyBuildCS::ThreadGroupSizeY),
183 FMath::DivideAndRoundUp(StepSliceCount, FIVSmokeOccupancyBuildCS::ThreadGroupSizeZ)
184 );
185
186 FComputeShaderUtils::AddPass(
187 GraphBuilder,
188 RDG_EVENT_NAME("IVSmoke::OccupancyBuild (%dx%dx%d)", TileCount.X, TileCount.Y, StepSliceCount),
189 ComputeShader,
190 Parameters,
191 GroupCount
192 );
193 }
194
195 /**
196 * Create occupancy resources for a frame.
197 */
198 FIVSmokeOccupancyResources CreateOccupancyResources(
199 FRDGBuilder& GraphBuilder,
200 const FIntPoint& TileCount,
201 uint32 StepSliceCount)
202 {
204
205 // Tile Data Buffer
206 const uint32 TileDataCount = TileCount.X * TileCount.Y;
207 FRDGBufferDesc TileDataDesc = FRDGBufferDesc::CreateStructuredDesc(sizeof(FIVSmokeTileData), TileDataCount);
208 Resources.TileDataBuffer = GraphBuilder.CreateBuffer(TileDataDesc, TEXT("IVSmoke.TileData"));
209
210 // View Occupancy 3D Texture (uint4 = 128 bits per texel)
211 FRDGTextureDesc OccupancyDesc = FRDGTextureDesc::Create3D(
212 FIntVector(TileCount.X, TileCount.Y, StepSliceCount),
213 PF_R32G32B32A32_UINT,
214 FClearValueBinding::Black,
215 ETextureCreateFlags::UAV | ETextureCreateFlags::ShaderResource
216 );
217 Resources.ViewOccupancy = GraphBuilder.CreateTexture(OccupancyDesc, TEXT("IVSmoke.ViewOccupancy"));
218 Resources.LightOccupancy = GraphBuilder.CreateTexture(OccupancyDesc, TEXT("IVSmoke.LightOccupancy"));
219
220 // Store configuration
221 Resources.TileCount = TileCount;
222 Resources.StepSliceCount = StepSliceCount;
223
224 return Resources;
225 }
226
227} // namespace IVSmokeOccupancy
228
229//~==============================================================================
230// Occupancy Resources Structure
231
232FIVSmokeOccupancyResources::FIVSmokeOccupancyResources()
233 : TileDataBuffer(nullptr)
234 , ViewOccupancy(nullptr)
235 , LightOccupancy(nullptr)
236 , TileCount(0, 0)
237 , StepSliceCount(0)
238{
239}
240
241bool FIVSmokeOccupancyResources::IsValid() const
242{
243 return TileDataBuffer != nullptr && ViewOccupancy != nullptr && LightOccupancy != nullptr;
244}
static constexpr uint32 TileSizeX
static constexpr uint32 StepDivisor