Path: blob/master/servers/rendering/rendering_light_culler.h
20957 views
/**************************************************************************/1/* rendering_light_culler.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#include "core/math/plane.h"33#include "core/math/vector3.h"34#include "renderer_scene_cull.h"3536struct Projection;37struct Transform3D;3839// For testing performance improvements from the LightCuller:40// Uncomment LIGHT_CULLER_DEBUG_FLASH and it will turn the culler41// on and off every LIGHT_CULLER_DEBUG_FLASH_FREQUENCY camera prepares.42// Uncomment LIGHT_CULLER_DEBUG_LOGGING to get periodic print of the number of casters culled before / after.43// Uncomment LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT to get periodic print of the number of casters culled for the directional light..4445// #define LIGHT_CULLER_DEBUG_LOGGING46// #define LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT47// #define LIGHT_CULLER_DEBUG_REGULAR_LIGHT48// #define LIGHT_CULLER_DEBUG_FLASH49#define LIGHT_CULLER_DEBUG_FLASH_FREQUENCY 102450////////////////////////////////////////////////////////////////////////////////////////////////5152// The code to generate the lookup table is included but commented out.53// This may be useful for debugging / regenerating the LUT in the future,54// especially if the order of planes changes.55// When this define is set, the generated lookup table will be printed to debug output.56// The generated lookup table can be copy pasted57// straight to LUT_entry_sizes and LUT_entries.58// See the referenced article for explanation.59// #define RENDERING_LIGHT_CULLER_CALCULATE_LUT6061////////////////////////////////////////////////////////////////////////////////////////////////62// This define will be set automatically depending on earlier defines, you can leave this as is.63#if defined(LIGHT_CULLER_DEBUG_LOGGING) || defined(RENDERING_LIGHT_CULLER_CALCULATE_LUT)64#define RENDERING_LIGHT_CULLER_DEBUG_STRINGS65#endif6667// Culls shadow casters that can't cast shadows into the camera frustum.68class RenderingLightCuller {69public:70RenderingLightCuller();7172private:73class LightSource {74public:75enum SourceType {76ST_UNKNOWN,77ST_DIRECTIONAL,78ST_SPOTLIGHT,79ST_OMNI,80};8182LightSource() {83type = ST_UNKNOWN;84angle = 0.0f;85range = FLT_MAX;86cascade_count = 0;87cascade_splits[0] = cascade_splits[1] = cascade_splits[2] = 0;88blend_splits = false;89}9091// All in world space, culling done in world space.92Vector3 pos;93Vector3 dir;94SourceType type;9596float angle; // For spotlight.97float range;9899int cascade_count;100float cascade_splits[3]; // Max 4 cascades, which only has 3 splits.101bool blend_splits;102};103104// Directional lights have separate cull frustums for each cascade, so this struct is needed to specify which one to use for each cull step.105struct CullFrustumData {106// Functions expect this to store a frustum, so it must be ALWAYS at least Plane[6]. Undefined behavior otherwise.107const Plane *frustum_planes = nullptr;108// Functions expect this to store frustum corners, so it must be ALWAYS at least Vector3[8]. UB otherwise.109const Vector3 *frustum_points = nullptr;110};111112// Same order as godot.113enum PlaneOrder {114PLANE_NEAR,115PLANE_FAR,116PLANE_LEFT,117PLANE_TOP,118PLANE_RIGHT,119PLANE_BOTTOM,120PLANE_TOTAL,121};122123// Same order as godot.124enum PointOrder {125PT_FAR_LEFT_TOP,126PT_FAR_LEFT_BOTTOM,127PT_FAR_RIGHT_TOP,128PT_FAR_RIGHT_BOTTOM,129PT_NEAR_LEFT_TOP,130PT_NEAR_LEFT_BOTTOM,131PT_NEAR_RIGHT_TOP,132PT_NEAR_RIGHT_BOTTOM,133};134135// 6 bits, 6 planes.136enum {137NUM_CAM_PLANES = 6,138NUM_CAM_POINTS = 8,139MAX_CULL_PLANES = 17,140LUT_SIZE = 64,141};142143public:144// Before each pass with a different camera, you must call this so the culler can pre-create145// the camera frustum planes and corner points in world space which are used for the culling.146bool prepare_camera(const Transform3D &p_cam_transform, const Projection &p_cam_matrix);147148// REGULAR LIGHTS (SPOT, OMNI).149// These are prepared then used for culling one by one, single threaded.150// prepare_regular_light() returns false if the entire light is culled (i.e. there is no intersection between the light and the view frustum).151bool prepare_regular_light(const RendererSceneCull::Instance &p_instance) { return _prepare_light(p_instance, -1); }152153// Cull according to the regular light planes that were setup in the previous call to prepare_regular_light.154void cull_regular_light(PagedArray<RendererSceneCull::Instance *> &r_instance_shadow_cull_result);155156// Directional lights are prepared in advance, and can be culled multithreaded chopping and changing between157// different directional_light_id.158void prepare_directional_light(const RendererSceneCull::Instance *p_instance, int32_t p_directional_light_id);159160// Return false if the instance is to be culled.161bool cull_directional_light(const RendererSceneCull::InstanceBounds &p_bound, int32_t p_directional_light_id, int32_t p_cascade);162163// Can turn on and off from the engine if desired.164void set_caster_culling_active(bool p_active) { data.caster_culling_active = p_active; }165void set_light_culling_active(bool p_active) { data.light_culling_active = p_active; }166167private:168struct LightCullPlanes {169void add_cull_plane(const Plane &p);170Plane cull_planes[MAX_CULL_PLANES];171int num_cull_planes = 0;172#ifdef LIGHT_CULLER_DEBUG_DIRECTIONAL_LIGHT173uint32_t rejected_count = 0;174#endif175};176177struct DirectionalCullPlanes {178LightCullPlanes planes[4]; // One set of cull planes per cascade179};180181bool _prepare_light(const RendererSceneCull::Instance &p_instance, int32_t p_directional_light_id = -1);182183// Avoid adding extra culling planes derived from near colinear triangles.184// The normals derived from these will be inaccurate, and can lead to false185// culling of objects that should be within the light volume.186// See:187// - issue GH-89702 "Tighter Shadow Caster Culling causes some object shadows to not render for a Frame"188// - issue GH-89560 "Directional Shadows disappear with large Camera Z Far values at some angles"189// - issue GH-91976 "SpotLight3D shadows exhibit flickering when moved around."190// - PR GH-92078 which gave it the current value.191bool _is_colinear_tri(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c) const {192// Lengths of sides a, b and c.193float la = (p_b - p_a).length();194float lb = (p_c - p_b).length();195float lc = (p_c - p_a).length();196197// Get longest side into lc.198if (lb < la) {199SWAP(la, lb);200}201if (lc < lb) {202SWAP(lb, lc);203}204205// Prevent divide by zero.206if (lc > 0.001f) {207// If the summed length of the smaller two208// sides is close to the length of the longest side,209// the points are colinear, and the triangle is near degenerate.210float ld = ((la + lb) - lc) / lc;211212// ld will be close to zero for colinear tris.213214// Long frustums are made out of significantly stretched-out triangles,215// so large threshold will produce large amounts of cullable but unculled meshes.216// For example: 0.001 fails to cull cullable meshes with camera FOV of 70 and ortho shadows at ~50m at certain view angles.217// 0.0001 fails to cull cullable meshes with camera FOV of 70 and ortho shadows at ~500m at certain view angles.218// ...These apply less to cascades since they have large near planes in comparison to far planes, unlike ortho lights.219// If you're reading this and the value for directional lights is still 0.001f,220// that is fine as is and it only means GH-115176 didn't make it.221return ld < 0.001f;222}223224// Don't create planes from tiny triangles,225// they won't be accurate.226return true;227}228229// Internal version uses LightSource.230bool _add_light_camera_planes(LightCullPlanes &r_cull_planes, const LightSource &p_light_source, const CullFrustumData &p_cull_frustum);231232// Directional light gives parallel culling planes (as opposed to point lights).233bool add_light_camera_planes_directional(LightCullPlanes &r_cull_planes, const LightSource &p_light_source, const CullFrustumData &p_cull_frustum);234235// Is the light culler active? maybe not in the editor...236bool is_caster_culling_active() const { return data.caster_culling_active; }237bool is_light_culling_active() const { return data.light_culling_active; }238239// Ensure result is at least a Plane[6] for the frustum input and Vector3[8] to store result before calling. If not, undefined behavior.240bool create_frustum_points(const Plane *p_frustum_planes, Vector3 *r_result) const;241242// Do we want to log some debug output?243bool is_logging() const { return data.debug_count == 0; }244245struct Data {246// Camera frustum planes (world space) - order ePlane.247Vector<Plane> frustum_planes;248249// Camera frustum corners (world space) - order ePoint.250Vector3 frustum_points[NUM_CAM_POINTS];251252// Master can have multiple directional lights.253// These need to store their own cull planes individually, as master254// chops and changes between culling different lights255// instead of doing one by one, and we don't want to prepare256// lights multiple times per frame.257LocalVector<DirectionalCullPlanes> directional_cull_planes;258259Transform3D camera_transform;260Projection camera_projection;261262// Single threaded cull planes for regular lights263// (OMNI, SPOT). These lights reuse the same set of cull plane data.264LightCullPlanes regular_cull_planes;265266#ifdef LIGHT_CULLER_DEBUG_REGULAR_LIGHT267uint32_t regular_rejected_count = 0;268#endif269// The whole regular light can be out of range of the view frustum, in which case all casters should be culled.270bool out_of_range = false;271272#ifdef RENDERING_LIGHT_CULLER_DEBUG_STRINGS273static String plane_bitfield_to_string(unsigned int BF);274// Names of the plane and point enums, useful for debugging.275static const char *string_planes[];276static const char *string_points[];277#endif278279// Precalculated look up table.280static uint8_t LUT_entry_sizes[LUT_SIZE];281static uint8_t LUT_entries[LUT_SIZE][8];282283bool caster_culling_active = true;284bool light_culling_active = true;285286// Light culling is a basic on / off switch.287// Caster culling only works if light culling is also on.288bool is_active() const { return light_culling_active; }289290// Ideally a frame counter, but for ease of implementation291// this is just incremented on each prepare_camera.292// used to turn on and off debugging features.293int debug_count = -1;294} data;295296// This functionality is not required in general use (and is compiled out),297// as the lookup table can normally be hard coded298// (provided order of planes etc does not change).299// It is provided for debugging / future maintenance.300#ifdef RENDERING_LIGHT_CULLER_CALCULATE_LUT301void get_neighbouring_planes(PlaneOrder p_plane, PlaneOrder r_neigh_planes[4]) const;302void get_corners_of_planes(PlaneOrder p_plane_a, PlaneOrder p_plane_b, PointOrder r_points[2]) const;303void create_LUT();304void compact_LUT_entry(uint32_t p_entry_id);305void debug_print_LUT();306void debug_print_LUT_as_table();307void add_LUT(int p_plane_0, int p_plane_1, PointOrder p_pts[2]);308void add_LUT_entry(uint32_t p_entry_id, PointOrder p_pts[2]);309String debug_string_LUT_entry(const LocalVector<uint8_t> &p_entry, bool p_pair = false);310String string_LUT_entry(const LocalVector<uint8_t> &p_entry);311312// Contains a list of points for each combination of plane facing directions.313LocalVector<uint8_t> _calculated_LUT[LUT_SIZE];314#endif315};316317318