IVSmoke 1.0
Loading...
Searching...
No Matches
IVSmokeCSMRenderer.h
1// Copyright (c) 2026, Team SDB. All rights reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "TextureResource.h"
7#include "Engine/TextureRenderTarget2D.h"
8
9class USceneCaptureComponent2D;
10class AActor;
11class UWorld;
12
13//~==============================================================================
14// Cascade Data Structure
15
16/**
17 * Data for a single shadow cascade.
18 * Contains render targets, matrices, and update state.
19 *
20 * TIMING MODEL (Synchronous Capture):
21 * ============================================================================
22 * We use manual CaptureScene() calls instead of bCaptureEveryFrame to ensure
23 * VP matrix and depth texture are always synchronized within the same frame.
24 *
25 * Frame N timeline:
26 * 1. Game Thread: Update() calculates VP_N and calls CaptureScene()
27 * 2. Render Thread: Pre-pass ray march uses VP_N with Depth_N ✓
28 * 3. Render Thread: Post-process composite
29 * ============================================================================
30 */
31struct IVSMOKE_API FIVSmokeCascadeData
32{
33 /** Cascade index (0 = nearest, N-1 = farthest). */
34 int32 CascadeIndex = 0;
35
36 /** View distance range for this cascade. */
37 float NearPlane = 0.0f;
38 float FarPlane = 0.0f;
39
40 //~==============================================================================
41 // Current Frame Values (used for both capture and shader sampling)
42
43 /** Orthographic projection width for this cascade. */
44 float OrthoWidth = 0.0f;
45
46 /** View-projection matrix for world-to-light-clip transform. */
47 FMatrix ViewProjectionMatrix = FMatrix::Identity;
48
49 /** Light camera position for this cascade (after texel snapping). */
50 FVector LightCameraPosition = FVector::ZeroVector;
51
52 /** Light camera forward direction (light travel direction, opposite of light source). */
53 FVector LightCameraForward = FVector(0.0f, 0.0f, -1.0f);
54
55 //~==============================================================================
56 // Resources
57
58 /** Depth render target (R32F). */
59 TObjectPtr<UTextureRenderTarget2D> DepthRT = nullptr;
60
61 /** Variance Shadow Map render target (RG32F). */
62 TObjectPtr<UTextureRenderTarget2D> VSMRT = nullptr;
63
64 /** Scene capture component for this cascade. */
65 TObjectPtr<USceneCaptureComponent2D> CaptureComponent = nullptr;
66
67 /** Whether this cascade needs capture this frame. */
68 bool bNeedsCapture = true;
69
70 /** Frame number when last captured. */
71 uint32 LastCaptureFrame = 0;
72};
73
74/**
75 * GPU-side cascade data for shader access.
76 * Packed for efficient GPU transfer.
77 */
78struct IVSMOKE_API FIVSmokeCSMGPUData
79{
80 /** View-projection matrix for this cascade. */
82
83 /** Split distance (far plane of this cascade). */
85
86 /** Padding for 16-byte alignment. */
87 float Padding[3];
88};
89
90static_assert(sizeof(FIVSmokeCSMGPUData) == 80, "FIVSmokeCSMGPUData size must be 80 bytes");
91
92//~==============================================================================
93// CSM Renderer
94
95/**
96 * Cascaded Shadow Map renderer for volumetric smoke.
97 * Manages multiple shadow cascades with priority-based updates.
98 *
99 * Features:
100 * - Configurable cascade count (1-8)
101 * - Log/Linear split distribution
102 * - Texel snapping for shimmer prevention
103 * - Priority-based update (near cascades update more frequently)
104 * - VSM support for soft shadows
105 */
106class IVSMOKE_API FIVSmokeCSMRenderer
107{
108public:
111
112 //~==============================================================================
113 // Lifecycle
114
115 /**
116 * Initialize CSM renderer with specified settings.
117 * Creates cascade render targets and capture components.
118 *
119 * @param World World to create capture components in.
120 * @param NumCascades Number of shadow cascades (1-8).
121 * @param Resolution Shadow map resolution per cascade.
122 * @param MaxDistance Maximum shadow distance in world units.
123 */
124 void Initialize(UWorld* World, int32 NumCascades, int32 Resolution, float MaxDistance);
125
126 /** Release all resources. */
127 void Shutdown();
128
129 /** Check if renderer is initialized. */
130 bool IsInitialized() const { return bIsInitialized; }
131
132 //~==============================================================================
133 // Update
134
135 /**
136 * Update shadow cascades for current frame.
137 * Calculates cascade splits, applies texel snapping, and triggers captures.
138 *
139 * @param CameraPosition Current camera world position.
140 * @param CameraForward Camera forward direction.
141 * @param LightDirection Direction TOWARD the light source (opposite of light travel).
142 * @param FrameNumber Current frame number for priority update.
143 */
144 void Update(
145 const FVector& CameraPosition,
146 const FVector& CameraForward,
147 const FVector& LightDirection,
148 uint32 FrameNumber
149 );
150
151 //~==============================================================================
152 // Accessors
153
154 /** Get number of active cascades. */
155 int32 GetNumCascades() const { return Cascades.Num(); }
156
157 /** Get cascade data by index. */
158 const FIVSmokeCascadeData& GetCascade(int32 Index) const { return Cascades[Index]; }
159
160 /** Get all cascade data. */
161 const TArray<FIVSmokeCascadeData>& GetCascades() const { return Cascades; }
162
163 /** Get cascade split distances (for shader). */
164 TArray<float> GetSplitDistances() const;
165
166 /** Get VSM texture for a cascade (nullptr if VSM disabled). */
167 FTextureRHIRef GetVSMTexture(int32 CascadeIndex) const;
168
169 /** Get depth texture for a cascade. */
170 FTextureRHIRef GetDepthTexture(int32 CascadeIndex) const;
171
172 /** Check if any cascade has valid shadow data. */
173 bool HasValidShadowData() const;
174
175 /**
176 * Get light camera position for a cascade.
177 *
178 * @param CascadeIndex Index of cascade.
179 * @return Light camera world position for the specified cascade.
180 */
181 FVector GetLightCameraPosition(int32 CascadeIndex) const;
182
183 /** Get main camera position. */
184 FVector GetMainCameraPosition() const { return MainCameraPosition; }
185
186private:
187 //~==============================================================================
188 // Internal Methods
189
190 /**
191 * Calculate cascade split distances using log/linear blend.
192 *
193 * @param NearPlane Near plane distance.
194 * @param FarPlane Far plane distance (max shadow distance).
195 * @param LogLinearBlend Blend factor (0=linear, 1=logarithmic).
196 */
197 void CalculateCascadeSplits(float NearPlane, float FarPlane, float LogLinearBlend);
198
199 /**
200 * Apply texel snapping to prevent shadow shimmer.
201 *
202 * @param LightViewOrigin Original light view origin.
203 * @param OrthoWidth Orthographic projection width.
204 * @param Resolution Shadow map resolution.
205 * @param LightRight Light's right vector.
206 * @param LightUp Light's up vector.
207 * @return Snapped position.
208 */
209 FVector ApplyTexelSnapping(
210 const FVector& LightViewOrigin,
211 float OrthoWidth,
212 int32 Resolution,
213 const FVector& LightRight,
214 const FVector& LightUp
215 ) const;
216
217 /**
218 * Determine which cascades need update based on priority.
219 *
220 * @param FrameNumber Current frame number.
221 */
222 void UpdateCascadePriorities(uint32 FrameNumber);
223
224 /**
225 * Update a single cascade's capture settings.
226 *
227 * @param CascadeIndex Index of cascade to update.
228 * @param CameraPosition Camera world position.
229 * @param LightDirection Light direction (toward light).
230 */
231 void UpdateCascadeCapture(
232 int32 CascadeIndex,
233 const FVector& CameraPosition,
234 const FVector& LightDirection
235 );
236
237 /**
238 * Calculate view-projection matrix for a cascade.
239 * Uses current cascade values (LightCameraPosition, LightCameraForward, OrthoWidth)
240 * and stores result in ViewProjectionMatrix.
241 *
242 * @param Cascade Cascade data to update.
243 */
244 void CalculateViewProjectionMatrix(FIVSmokeCascadeData& Cascade);
245
246 /**
247 * Create render targets for a cascade.
248 *
249 * @param Cascade Cascade data to initialize.
250 * @param Resolution Shadow map resolution.
251 */
252 void CreateCascadeRenderTargets(FIVSmokeCascadeData& Cascade, int32 Resolution);
253
254 /**
255 * Configure scene capture component settings.
256 *
257 * @param CaptureComponent Component to configure.
258 */
259 void ConfigureCaptureComponent(USceneCaptureComponent2D* CaptureComponent);
260
261 //~==============================================================================
262 // Member Variables
263
264 /** All cascade data. */
265 TArray<FIVSmokeCascadeData> Cascades;
266
267 /** Owner actor for capture components. */
268 TWeakObjectPtr<AActor> CaptureOwner;
269
270 /** Current shadow map resolution. */
271 int32 CurrentResolution = 512;
272
273 /** Maximum shadow distance. */
274 float MaxShadowDistance = 100000.0f;
275
276 /** Log/Linear blend factor for cascade splits. */
277 float LogLinearBlend = 0.7f;
278
279 /** Near plane for cascade 0. */
280 float NearPlaneDistance = 10.0f;
281
282 /** Initialization state. */
283 bool bIsInitialized = false;
284
285 /** Enable priority-based updates. */
286 bool bEnablePriorityUpdate = true;
287
288 /** Near cascade update interval (frames). */
289 int32 NearCascadeUpdateInterval = 1;
290
291 /** Far cascade update interval (frames). */
292 int32 FarCascadeUpdateInterval = 4;
293
294 /** Main camera position (stored for camera-relative calculations). */
295 FVector MainCameraPosition = FVector::ZeroVector;
296};
FVector GetMainCameraPosition() const
const FIVSmokeCascadeData & GetCascade(int32 Index) const
const TArray< FIVSmokeCascadeData > & GetCascades() const
FMatrix44f ViewProjectionMatrix