Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/servers/rendering/rendering_light_culler.h
20957 views
1
/**************************************************************************/
2
/* rendering_light_culler.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/math/plane.h"
34
#include "core/math/vector3.h"
35
#include "renderer_scene_cull.h"
36
37
struct Projection;
38
struct Transform3D;
39
40
// For testing performance improvements from the LightCuller:
41
// Uncomment LIGHT_CULLER_DEBUG_FLASH and it will turn the culler
42
// on and off every LIGHT_CULLER_DEBUG_FLASH_FREQUENCY camera prepares.
43
// Uncomment LIGHT_CULLER_DEBUG_LOGGING to get periodic print of the number of casters culled before / after.
44
// Uncomment LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT to get periodic print of the number of casters culled for the directional light..
45
46
// #define LIGHT_CULLER_DEBUG_LOGGING
47
// #define LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT
48
// #define LIGHT_CULLER_DEBUG_REGULAR_LIGHT
49
// #define LIGHT_CULLER_DEBUG_FLASH
50
#define LIGHT_CULLER_DEBUG_FLASH_FREQUENCY 1024
51
////////////////////////////////////////////////////////////////////////////////////////////////
52
53
// The code to generate the lookup table is included but commented out.
54
// This may be useful for debugging / regenerating the LUT in the future,
55
// especially if the order of planes changes.
56
// When this define is set, the generated lookup table will be printed to debug output.
57
// The generated lookup table can be copy pasted
58
// straight to LUT_entry_sizes and LUT_entries.
59
// See the referenced article for explanation.
60
// #define RENDERING_LIGHT_CULLER_CALCULATE_LUT
61
62
////////////////////////////////////////////////////////////////////////////////////////////////
63
// This define will be set automatically depending on earlier defines, you can leave this as is.
64
#if defined(LIGHT_CULLER_DEBUG_LOGGING) || defined(RENDERING_LIGHT_CULLER_CALCULATE_LUT)
65
#define RENDERING_LIGHT_CULLER_DEBUG_STRINGS
66
#endif
67
68
// Culls shadow casters that can't cast shadows into the camera frustum.
69
class RenderingLightCuller {
70
public:
71
RenderingLightCuller();
72
73
private:
74
class LightSource {
75
public:
76
enum SourceType {
77
ST_UNKNOWN,
78
ST_DIRECTIONAL,
79
ST_SPOTLIGHT,
80
ST_OMNI,
81
};
82
83
LightSource() {
84
type = ST_UNKNOWN;
85
angle = 0.0f;
86
range = FLT_MAX;
87
cascade_count = 0;
88
cascade_splits[0] = cascade_splits[1] = cascade_splits[2] = 0;
89
blend_splits = false;
90
}
91
92
// All in world space, culling done in world space.
93
Vector3 pos;
94
Vector3 dir;
95
SourceType type;
96
97
float angle; // For spotlight.
98
float range;
99
100
int cascade_count;
101
float cascade_splits[3]; // Max 4 cascades, which only has 3 splits.
102
bool blend_splits;
103
};
104
105
// Directional lights have separate cull frustums for each cascade, so this struct is needed to specify which one to use for each cull step.
106
struct CullFrustumData {
107
// Functions expect this to store a frustum, so it must be ALWAYS at least Plane[6]. Undefined behavior otherwise.
108
const Plane *frustum_planes = nullptr;
109
// Functions expect this to store frustum corners, so it must be ALWAYS at least Vector3[8]. UB otherwise.
110
const Vector3 *frustum_points = nullptr;
111
};
112
113
// Same order as godot.
114
enum PlaneOrder {
115
PLANE_NEAR,
116
PLANE_FAR,
117
PLANE_LEFT,
118
PLANE_TOP,
119
PLANE_RIGHT,
120
PLANE_BOTTOM,
121
PLANE_TOTAL,
122
};
123
124
// Same order as godot.
125
enum PointOrder {
126
PT_FAR_LEFT_TOP,
127
PT_FAR_LEFT_BOTTOM,
128
PT_FAR_RIGHT_TOP,
129
PT_FAR_RIGHT_BOTTOM,
130
PT_NEAR_LEFT_TOP,
131
PT_NEAR_LEFT_BOTTOM,
132
PT_NEAR_RIGHT_TOP,
133
PT_NEAR_RIGHT_BOTTOM,
134
};
135
136
// 6 bits, 6 planes.
137
enum {
138
NUM_CAM_PLANES = 6,
139
NUM_CAM_POINTS = 8,
140
MAX_CULL_PLANES = 17,
141
LUT_SIZE = 64,
142
};
143
144
public:
145
// Before each pass with a different camera, you must call this so the culler can pre-create
146
// the camera frustum planes and corner points in world space which are used for the culling.
147
bool prepare_camera(const Transform3D &p_cam_transform, const Projection &p_cam_matrix);
148
149
// REGULAR LIGHTS (SPOT, OMNI).
150
// These are prepared then used for culling one by one, single threaded.
151
// prepare_regular_light() returns false if the entire light is culled (i.e. there is no intersection between the light and the view frustum).
152
bool prepare_regular_light(const RendererSceneCull::Instance &p_instance) { return _prepare_light(p_instance, -1); }
153
154
// Cull according to the regular light planes that were setup in the previous call to prepare_regular_light.
155
void cull_regular_light(PagedArray<RendererSceneCull::Instance *> &r_instance_shadow_cull_result);
156
157
// Directional lights are prepared in advance, and can be culled multithreaded chopping and changing between
158
// different directional_light_id.
159
void prepare_directional_light(const RendererSceneCull::Instance *p_instance, int32_t p_directional_light_id);
160
161
// Return false if the instance is to be culled.
162
bool cull_directional_light(const RendererSceneCull::InstanceBounds &p_bound, int32_t p_directional_light_id, int32_t p_cascade);
163
164
// Can turn on and off from the engine if desired.
165
void set_caster_culling_active(bool p_active) { data.caster_culling_active = p_active; }
166
void set_light_culling_active(bool p_active) { data.light_culling_active = p_active; }
167
168
private:
169
struct LightCullPlanes {
170
void add_cull_plane(const Plane &p);
171
Plane cull_planes[MAX_CULL_PLANES];
172
int num_cull_planes = 0;
173
#ifdef LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT
174
uint32_t rejected_count = 0;
175
#endif
176
};
177
178
struct DirectionalCullPlanes {
179
LightCullPlanes planes[4]; // One set of cull planes per cascade
180
};
181
182
bool _prepare_light(const RendererSceneCull::Instance &p_instance, int32_t p_directional_light_id = -1);
183
184
// Avoid adding extra culling planes derived from near colinear triangles.
185
// The normals derived from these will be inaccurate, and can lead to false
186
// culling of objects that should be within the light volume.
187
// See:
188
// - issue GH-89702 "Tighter Shadow Caster Culling causes some object shadows to not render for a Frame"
189
// - issue GH-89560 "Directional Shadows disappear with large Camera Z Far values at some angles"
190
// - issue GH-91976 "SpotLight3D shadows exhibit flickering when moved around."
191
// - PR GH-92078 which gave it the current value.
192
bool _is_colinear_tri(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c) const {
193
// Lengths of sides a, b and c.
194
float la = (p_b - p_a).length();
195
float lb = (p_c - p_b).length();
196
float lc = (p_c - p_a).length();
197
198
// Get longest side into lc.
199
if (lb < la) {
200
SWAP(la, lb);
201
}
202
if (lc < lb) {
203
SWAP(lb, lc);
204
}
205
206
// Prevent divide by zero.
207
if (lc > 0.001f) {
208
// If the summed length of the smaller two
209
// sides is close to the length of the longest side,
210
// the points are colinear, and the triangle is near degenerate.
211
float ld = ((la + lb) - lc) / lc;
212
213
// ld will be close to zero for colinear tris.
214
215
// Long frustums are made out of significantly stretched-out triangles,
216
// so large threshold will produce large amounts of cullable but unculled meshes.
217
// For example: 0.001 fails to cull cullable meshes with camera FOV of 70 and ortho shadows at ~50m at certain view angles.
218
// 0.0001 fails to cull cullable meshes with camera FOV of 70 and ortho shadows at ~500m at certain view angles.
219
// ...These apply less to cascades since they have large near planes in comparison to far planes, unlike ortho lights.
220
// If you're reading this and the value for directional lights is still 0.001f,
221
// that is fine as is and it only means GH-115176 didn't make it.
222
return ld < 0.001f;
223
}
224
225
// Don't create planes from tiny triangles,
226
// they won't be accurate.
227
return true;
228
}
229
230
// Internal version uses LightSource.
231
bool _add_light_camera_planes(LightCullPlanes &r_cull_planes, const LightSource &p_light_source, const CullFrustumData &p_cull_frustum);
232
233
// Directional light gives parallel culling planes (as opposed to point lights).
234
bool add_light_camera_planes_directional(LightCullPlanes &r_cull_planes, const LightSource &p_light_source, const CullFrustumData &p_cull_frustum);
235
236
// Is the light culler active? maybe not in the editor...
237
bool is_caster_culling_active() const { return data.caster_culling_active; }
238
bool is_light_culling_active() const { return data.light_culling_active; }
239
240
// Ensure result is at least a Plane[6] for the frustum input and Vector3[8] to store result before calling. If not, undefined behavior.
241
bool create_frustum_points(const Plane *p_frustum_planes, Vector3 *r_result) const;
242
243
// Do we want to log some debug output?
244
bool is_logging() const { return data.debug_count == 0; }
245
246
struct Data {
247
// Camera frustum planes (world space) - order ePlane.
248
Vector<Plane> frustum_planes;
249
250
// Camera frustum corners (world space) - order ePoint.
251
Vector3 frustum_points[NUM_CAM_POINTS];
252
253
// Master can have multiple directional lights.
254
// These need to store their own cull planes individually, as master
255
// chops and changes between culling different lights
256
// instead of doing one by one, and we don't want to prepare
257
// lights multiple times per frame.
258
LocalVector<DirectionalCullPlanes> directional_cull_planes;
259
260
Transform3D camera_transform;
261
Projection camera_projection;
262
263
// Single threaded cull planes for regular lights
264
// (OMNI, SPOT). These lights reuse the same set of cull plane data.
265
LightCullPlanes regular_cull_planes;
266
267
#ifdef LIGHT_CULLER_DEBUG_REGULAR_LIGHT
268
uint32_t regular_rejected_count = 0;
269
#endif
270
// The whole regular light can be out of range of the view frustum, in which case all casters should be culled.
271
bool out_of_range = false;
272
273
#ifdef RENDERING_LIGHT_CULLER_DEBUG_STRINGS
274
static String plane_bitfield_to_string(unsigned int BF);
275
// Names of the plane and point enums, useful for debugging.
276
static const char *string_planes[];
277
static const char *string_points[];
278
#endif
279
280
// Precalculated look up table.
281
static uint8_t LUT_entry_sizes[LUT_SIZE];
282
static uint8_t LUT_entries[LUT_SIZE][8];
283
284
bool caster_culling_active = true;
285
bool light_culling_active = true;
286
287
// Light culling is a basic on / off switch.
288
// Caster culling only works if light culling is also on.
289
bool is_active() const { return light_culling_active; }
290
291
// Ideally a frame counter, but for ease of implementation
292
// this is just incremented on each prepare_camera.
293
// used to turn on and off debugging features.
294
int debug_count = -1;
295
} data;
296
297
// This functionality is not required in general use (and is compiled out),
298
// as the lookup table can normally be hard coded
299
// (provided order of planes etc does not change).
300
// It is provided for debugging / future maintenance.
301
#ifdef RENDERING_LIGHT_CULLER_CALCULATE_LUT
302
void get_neighbouring_planes(PlaneOrder p_plane, PlaneOrder r_neigh_planes[4]) const;
303
void get_corners_of_planes(PlaneOrder p_plane_a, PlaneOrder p_plane_b, PointOrder r_points[2]) const;
304
void create_LUT();
305
void compact_LUT_entry(uint32_t p_entry_id);
306
void debug_print_LUT();
307
void debug_print_LUT_as_table();
308
void add_LUT(int p_plane_0, int p_plane_1, PointOrder p_pts[2]);
309
void add_LUT_entry(uint32_t p_entry_id, PointOrder p_pts[2]);
310
String debug_string_LUT_entry(const LocalVector<uint8_t> &p_entry, bool p_pair = false);
311
String string_LUT_entry(const LocalVector<uint8_t> &p_entry);
312
313
// Contains a list of points for each combination of plane facing directions.
314
LocalVector<uint8_t> _calculated_LUT[LUT_SIZE];
315
#endif
316
};
317
318