diff --git a/core/math/vector2i.cpp b/core/math/vector2i.cpp index 790f56473492..7836dc3d98c5 100644 --- a/core/math/vector2i.cpp +++ b/core/math/vector2i.cpp @@ -141,3 +141,73 @@ Vector2i::operator String() const { Vector2i::operator Vector2() const { return Vector2((int32_t)x, (int32_t)y); } + +Vector2i &Vector2i::operator>>=(const Vector2i &p_v) { + x >>= p_v.x; + y >>= p_v.y; + return *this; +} + +Vector2i Vector2i::operator>>(const Vector2i &p_v) const { + return Vector2i(x >> p_v.x, y >> p_v.y); +} + +Vector2i &Vector2i::operator<<=(const Vector2i &p_v) { + x <<= p_v.x; + y <<= p_v.y; + return *this; +} + +Vector2i Vector2i::operator<<(const Vector2i &p_v) const { + return Vector2i(x << p_v.x, y << p_v.y); +} + +Vector2i &Vector2i::operator<<=(const int32_t p_scalar) { + x <<= p_scalar; + y <<= p_scalar; + return *this; +} + +Vector2i Vector2i::operator<<(const int32_t p_scalar) const { + return Vector2i(x << p_scalar, y << p_scalar); +} + +Vector2i &Vector2i::operator>>=(const int32_t p_scalar) { + x >>= p_scalar; + y >>= p_scalar; + return *this; +} + +Vector2i Vector2i::operator>>(const int32_t p_scalar) const { + return Vector2i(x >> p_scalar, y >> p_scalar); +} + +Vector2i Vector2i::operator|(const Vector2i &p_v) const { + return Vector2i(x | p_v.x, y | p_v.y); +} + +Vector2i &Vector2i::operator|=(const Vector2i &p_v) { + x |= p_v.x; + y |= p_v.y; + return *this; +} + +Vector2i Vector2i::operator&(const Vector2i &p_v) const { + return Vector2i(x & p_v.x, y & p_v.y); +} + +Vector2i &Vector2i::operator&=(const Vector2i &p_v) { + x &= p_v.x; + y &= p_v.y; + return *this; +} + +Vector2i Vector2i::operator^(const Vector2i &p_v) const { + return Vector2i(x ^ p_v.x, y ^ p_v.y); +} + +Vector2i &Vector2i::operator^=(const Vector2i &p_v) { + x ^= p_v.x; + y ^= p_v.y; + return *this; +} diff --git a/core/math/vector2i.h b/core/math/vector2i.h index fff9b0a658fb..83c4ef3ad657 100644 --- a/core/math/vector2i.h +++ b/core/math/vector2i.h @@ -128,6 +128,23 @@ struct [[nodiscard]] Vector2i { bool operator==(const Vector2i &p_vec2) const; bool operator!=(const Vector2i &p_vec2) const; + Vector2i operator>>(const Vector2i &p_v) const; + Vector2i &operator>>=(const Vector2i &p_v); + Vector2i operator<<(const Vector2i &p_v) const; + Vector2i &operator<<=(const Vector2i &p_v); + + Vector2i &operator>>=(const int32_t p_scalar); + Vector2i operator>>(const int32_t p_scalar) const; + Vector2i &operator<<=(const int32_t p_scalar); + Vector2i operator<<(const int32_t p_scalar) const; + + Vector2i operator|(const Vector2i &p_v) const; + Vector2i &operator|=(const Vector2i &p_v); + Vector2i operator&(const Vector2i &p_v) const; + Vector2i &operator&=(const Vector2i &p_v); + Vector2i operator^(const Vector2i &p_v) const; + Vector2i &operator^=(const Vector2i &p_v); + int64_t length_squared() const; double length() const; diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 40d0700bf730..31a24bb59fef 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -113,6 +113,7 @@ struct [[nodiscard]] Vector3i { _FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v); _FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar); _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const; _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar); @@ -120,6 +121,23 @@ struct [[nodiscard]] Vector3i { _FORCE_INLINE_ Vector3i &operator%=(int32_t p_scalar); _FORCE_INLINE_ Vector3i operator%(int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i operator>>(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator>>=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator<<(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator<<=(const Vector3i &p_v); + + _FORCE_INLINE_ Vector3i &operator>>=(const int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator>>(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator<<=(const int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator<<(const int32_t p_scalar) const; + + _FORCE_INLINE_ Vector3i operator|(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator|=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator&(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator&=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator^(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator^=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator-() const; _FORCE_INLINE_ bool operator==(const Vector3i &p_v) const; @@ -232,6 +250,61 @@ Vector3i Vector3i::operator*(int32_t p_scalar) const { return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); } +Vector3i &Vector3i::operator>>=(const Vector3i &p_v) { + x >>= p_v.x; + y >>= p_v.y; + z >>= p_v.z; + return *this; +} + +Vector3i Vector3i::operator>>(const Vector3i &p_v) const { + return Vector3i(x >> p_v.x, y >> p_v.y, z >> p_v.z); +} + +Vector3i &Vector3i::operator<<=(const Vector3i &p_v) { + x <<= p_v.x; + y <<= p_v.y; + z <<= p_v.z; + return *this; +} + +Vector3i Vector3i::operator<<(const Vector3i &p_v) const { + return Vector3i(x << p_v.x, y << p_v.y, z << p_v.z); +} + +Vector3i &Vector3i::operator&=(const Vector3i &p_v) { + x &= p_v.x; + y &= p_v.y; + z &= p_v.z; + return *this; +} + +Vector3i Vector3i::operator&(const Vector3i &p_v) const { + return Vector3i(x & p_v.x, y & p_v.y, z & p_v.z); +} + +Vector3i &Vector3i::operator|=(const Vector3i &p_v) { + x |= p_v.x; + y |= p_v.y; + z |= p_v.z; + return *this; +} + +Vector3i Vector3i::operator|(const Vector3i &p_v) const { + return Vector3i(x | p_v.x, y | p_v.y, z | p_v.z); +} + +Vector3i &Vector3i::operator^=(const Vector3i &p_v) { + x ^= p_v.x; + y ^= p_v.y; + z ^= p_v.z; + return *this; +} + +Vector3i Vector3i::operator^(const Vector3i &p_v) const { + return Vector3i(x ^ p_v.x, y ^ p_v.y, z ^ p_v.z); +} + // Multiplication operators required to workaround issues with LLVM using implicit conversion. _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vector) { @@ -272,6 +345,28 @@ Vector3i Vector3i::operator%(int32_t p_scalar) const { return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar); } +Vector3i &Vector3i::operator<<=(const int32_t p_scalar) { + x <<= p_scalar; + y <<= p_scalar; + z <<= p_scalar; + return *this; +} + +Vector3i Vector3i::operator<<(const int32_t p_scalar) const { + return Vector3i(x << p_scalar, y << p_scalar, z << p_scalar); +} + +Vector3i &Vector3i::operator>>=(const int32_t p_scalar) { + x >>= p_scalar; + y >>= p_scalar; + z >>= p_scalar; + return *this; +} + +Vector3i Vector3i::operator>>(const int32_t p_scalar) const { + return Vector3i(x >> p_scalar, y >> p_scalar, z >> p_scalar); +} + Vector3i Vector3i::operator-() const { return Vector3i(-x, -y, -z); } diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 1f306d3536a5..645a125b508d 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -515,6 +515,24 @@ void Variant::_register_variant_operators() { register_op>(Variant::OP_POSITIVE, Variant::PLANE, Variant::NIL); register_op>(Variant::OP_POSITIVE, Variant::COLOR, Variant::NIL); + register_op>(Variant::OP_SHIFT_LEFT, Variant::VECTOR2I, Variant::INT); + register_op>(Variant::OP_SHIFT_RIGHT, Variant::VECTOR2I, Variant::INT); + register_op>(Variant::OP_SHIFT_LEFT, Variant::VECTOR2I, Variant::VECTOR2I); + register_op>(Variant::OP_SHIFT_RIGHT, Variant::VECTOR2I, Variant::VECTOR2I); + + register_op>(Variant::OP_SHIFT_LEFT, Variant::VECTOR3I, Variant::INT); + register_op>(Variant::OP_SHIFT_RIGHT, Variant::VECTOR3I, Variant::INT); + register_op>(Variant::OP_SHIFT_LEFT, Variant::VECTOR3I, Variant::VECTOR3I); + register_op>(Variant::OP_SHIFT_RIGHT, Variant::VECTOR3I, Variant::VECTOR3I); + + register_op>(Variant::OP_BIT_OR, Variant::VECTOR2I, Variant::VECTOR2I); + register_op>(Variant::OP_BIT_AND, Variant::VECTOR2I, Variant::VECTOR2I); + register_op>(Variant::OP_BIT_XOR, Variant::VECTOR2I, Variant::VECTOR2I); + + register_op>(Variant::OP_BIT_OR, Variant::VECTOR3I, Variant::VECTOR3I); + register_op>(Variant::OP_BIT_AND, Variant::VECTOR3I, Variant::VECTOR3I); + register_op>(Variant::OP_BIT_XOR, Variant::VECTOR3I, Variant::VECTOR3I); + register_op>(Variant::OP_SHIFT_LEFT, Variant::INT, Variant::INT); register_op>(Variant::OP_SHIFT_RIGHT, Variant::INT, Variant::INT); register_op>(Variant::OP_BIT_OR, Variant::INT, Variant::INT); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index ac39a4135fca..ba5352a0cc44 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -412,8 +412,8 @@ class OperatorEvaluatorShiftLeft { const B &b = *VariantGetInternalPtr::get_ptr(&p_right); #if defined(DEBUG_ENABLED) - if (b < 0 || a < 0) { - *r_ret = "Invalid operands for bit shifting. Only positive operands are supported."; + if (b < 0) { + *r_ret = "Invalid operands for bit shifting. Only positive shifts are supported."; r_valid = false; return; } @@ -438,8 +438,8 @@ class OperatorEvaluatorShiftRight { const B &b = *VariantGetInternalPtr::get_ptr(&p_right); #if defined(DEBUG_ENABLED) - if (b < 0 || a < 0) { - *r_ret = "Invalid operands for bit shifting. Only positive operands are supported."; + if (b < 0) { + *r_ret = "Invalid operands for bit shifting. Only positive shifts are supported."; r_valid = false; return; } @@ -456,6 +456,44 @@ class OperatorEvaluatorShiftRight { static Variant::Type get_return_type() { return GetTypeInfo::VARIANT_TYPE; } }; +template +class OperatorEvaluatorShiftRightVector { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr::get_ptr(&p_right); + + *r_ret = a >> b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr::get_ptr(r_ret) = *VariantGetInternalPtr::get_ptr(left) >> *VariantGetInternalPtr::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg::encode(PtrToArg::convert(left) >> PtrToArg::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo::VARIANT_TYPE; } +}; + +template +class OperatorEvaluatorShiftLeftVector { +public: + static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { + const A &a = *VariantGetInternalPtr::get_ptr(&p_left); + const B &b = *VariantGetInternalPtr::get_ptr(&p_right); + + *r_ret = a << b; + r_valid = true; + } + static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { + *VariantGetInternalPtr::get_ptr(r_ret) = *VariantGetInternalPtr::get_ptr(left) << *VariantGetInternalPtr::get_ptr(right); + } + static void ptr_evaluate(const void *left, const void *right, void *r_ret) { + PtrToArg::encode(PtrToArg::convert(left) << PtrToArg::convert(right), r_ret); + } + static Variant::Type get_return_type() { return GetTypeInfo::VARIANT_TYPE; } +}; + template class OperatorEvaluatorBitOr { public: diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index f7469610439b..c7ea7456572a 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -80,6 +80,54 @@ The background mode. See [enum BGMode] for possible values. + + How much light bounces back to the probes. This increases the amount of indirect light received on surfaces. + + + Maximum distance for the first cascade. Use mostly as reference, as changing this also changes cell size. + + + Chooses the format for the cascades (amount of probes in each axis). Currently only full size and half height sizes are supported. + + + The amount of cascades used for global illumination. More cascades allows the global illumination to reach further away, but at the same time it costs more memory and GPU performance. Adjust this value to what you find necessary in your game. + + + Turns on Dynamic GI. This provides global illumination (indirect light and reflections) for the whole scene. Only static objects contribute to GI while dynamic objects can also recieve it (check whether your object is static, dynamic or disabled with [member GeometryInstance3D.gi_mode]. + + + Adjust the amount of energy that geometry recieved from GI. Use this only as a last resort because it affects everything uniformly and decreases the quality. If needed, consider using [member Environment.dynamic_gi_bounce_feedback] or [member Light3D.light_indirect_energy] to inject more energy into the system. + + + Filter the ambient light, this results in higher quality transitions between the cascades. + + + Filter the probes (averaging probes with neighbouring probes) to smooth out the light transitions. This option can be used safely, as occlusion between probes is considered when filtering, but it may also result on lower light frequency. + + + Applies a gaussian filter to reflections. If you find that the voxelish look of the reflections is not to your taste, this filter will smooth it out. + + + Total maximum distance for Dynamic GI, considering cell size and cascades. Changing this value also changes [member dynamic_gi_cascade0_distance] and [member dynamic_gi_min_cell_size]. + + + The minimum size of a voxel cell when processing Dynamic GI (a probe is placed every 8 voxel cells). It is recommended to have the cascade 0 probes visible when adjusting this value, which you can do via the viewport menu (Advanced -> Dynamic GI Probes). + + + Used for unocclusion. If unsure, leave this value unmodified. + + + This value can strengthen probe occlusion when closer to zero, but may also cause unwanted dark patterns. It loosens occlusion when closer to 1, but leaks light. Adjust depending on your needs, leave unmodified if unsure. + + + Bias used for probe raytracing. + + + If enabled, the probes will read from the sky light when rays hit nothing. + + + Bias used for reflections. The current value is safe to avoid occlusion with nearby voxels, but change if you find too much light is being leaked or reflections have too much offset. + If set above [code]0.0[/code] (exclusive), blends between the fog's color and the color of the background [Sky]. This has a small performance cost when set above [code]0.0[/code]. Must have [member background_mode] set to [constant BG_SKY]. This is useful to simulate [url=https://en.wikipedia.org/wiki/Aerial_perspective]aerial perspective[/url] in large scenes with low density fog. However, it is not very useful for high-density fog, as the sky will shine through. When set to [code]1.0[/code], the fog color comes completely from the [Sky]. If set to [code]0.0[/code], aerial perspective is disabled. @@ -199,49 +247,6 @@ The reflected (specular) light source. - - The energy multiplier applied to light every time it bounces from a surface when using SDFGI. Values greater than [code]0.0[/code] will simulate multiple bounces, resulting in a more realistic appearance. Increasing [member sdfgi_bounce_feedback] generally has no performance impact. See also [member sdfgi_energy]. - [b]Note:[/b] Values greater than [code]0.5[/code] can cause infinite feedback loops and should be avoided in scenes with bright materials. - [b]Note:[/b] If [member sdfgi_bounce_feedback] is [code]0.0[/code], indirect lighting will not be represented in reflections as light will only bounce one time. - - - [b]Note:[/b] This property is linked to [member sdfgi_min_cell_size] and [member sdfgi_max_distance]. Changing its value will automatically change those properties as well. - - - The number of cascades to use for SDFGI (between 1 and 8). A higher number of cascades allows displaying SDFGI further away while preserving detail up close, at the cost of performance. When using SDFGI on small-scale levels, [member sdfgi_cascades] can often be decreased between [code]1[/code] and [code]4[/code] to improve performance. - - - If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not. - [b]Note:[/b] SDFGI is only supported in the Forward+ rendering method, not Mobile or Compatibility. - [b]Performance:[/b] SDFGI is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve SDFGI performance, enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings. - [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. - - - The energy multiplier to use for SDFGI. Higher values will result in brighter indirect lighting and reflections. See also [member sdfgi_bounce_feedback]. - - - The maximum distance at which SDFGI is visible. Beyond this distance, environment lighting or other sources of GI such as [ReflectionProbe] will be used as a fallback. - [b]Note:[/b] This property is linked to [member sdfgi_min_cell_size] and [member sdfgi_cascade0_distance]. Changing its value will automatically change those properties as well. - - - The cell size to use for the closest SDFGI cascade (in 3D units). Lower values allow SDFGI to be more precise up close, at the cost of making SDFGI updates more demanding. This can cause stuttering when the camera moves fast. Higher values allow SDFGI to cover more ground, while also reducing the performance impact of SDFGI updates. - [b]Note:[/b] This property is linked to [member sdfgi_max_distance] and [member sdfgi_cascade0_distance]. Changing its value will automatically change those properties as well. - - - The normal bias to use for SDFGI probes. Increasing this value can reduce visible streaking artifacts on sloped surfaces, at the cost of increased light leaking. - - - The constant bias to use for SDFGI probes. Increasing this value can reduce visible streaking artifacts on sloped surfaces, at the cost of increased light leaking. - - - If [code]true[/code], SDFGI takes the environment lighting into account. This should be set to [code]false[/code] for interior scenes. - - - If [code]true[/code], SDFGI uses an occlusion detection approach to reduce light leaking. Occlusion may however introduce dark blotches in certain spots, which may be undesired in mostly outdoor scenes. [member sdfgi_use_occlusion] has a performance impact and should only be enabled when needed. - - - The Y scale to use for SDFGI cells. Lower values will result in SDFGI cells being packed together more closely on the Y axis. This is used to balance between quality and covering a lot of vertical ground. [member sdfgi_y_scale] should be set depending on how vertical your scene is (and how fast your camera may move on the Y axis). - The [Sky] resource used for this [Environment]. @@ -280,7 +285,7 @@ The amount that the screen-space ambient occlusion effect is allowed to blur over the edges of objects. Setting too high will result in aliasing around the edges of objects. Setting too low will make object edges appear blurry. - If [code]true[/code], the screen-space indirect lighting effect is enabled. Screen space indirect lighting is a form of indirect lighting that allows diffuse light to bounce between nearby objects. Screen-space indirect lighting works very similarly to screen-space ambient occlusion, in that it only affects a limited range. It is intended to be used along with a form of proper global illumination like SDFGI or [VoxelGI]. Screen-space indirect lighting is not affected by individual light's [member Light3D.light_indirect_energy]. + If [code]true[/code], the screen-space indirect lighting effect is enabled. Screen space indirect lighting is a form of indirect lighting that allows diffuse light to bounce between nearby objects. Screen-space indirect lighting works very similarly to screen-space ambient occlusion, in that it only affects a limited range. It is intended to be used along with a form of proper global illumination like Dynamic GI or [VoxelGI]. Screen-space indirect lighting is not affected by individual light's [member Light3D.light_indirect_energy]. [b]Note:[/b] SSIL is only supported in the Forward+ rendering method, not Mobile or Compatibility. @@ -351,7 +356,7 @@ Scales the strength of Global Illumination used in the volumetric fog's albedo color. A value of [code]0.0[/code] means that Global Illumination will not impact the volumetric fog. [member volumetric_fog_gi_inject] has a small performance cost when set above [code]0.0[/code]. [b]Note:[/b] This has no visible effect if [member volumetric_fog_density] is [code]0.0[/code] or if [member volumetric_fog_albedo] is a fully black color. - [b]Note:[/b] Only [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) are taken into account when using [member volumetric_fog_gi_inject]. Global illumination from [LightmapGI], [ReflectionProbe] and SSIL (see [member ssil_enabled]) will be ignored by volumetric fog. + [b]Note:[/b] Only [VoxelGI] and Dynamic GI ([member Environment.dynamic_gi_enabled]) are taken into account when using [member volumetric_fog_gi_inject]. Global illumination from [LightmapGI], [ReflectionProbe] and SSIL (see [member ssil_enabled]) will be ignored by volumetric fog. The distance over which the volumetric fog is computed. Increase to compute fog over a greater range, decrease to add more detail when a long range is not needed. For best quality fog, keep this as low as possible. See also [member ProjectSettings.rendering/environment/volumetric_fog/volume_depth]. @@ -450,14 +455,14 @@ Use a simple fog model defined by start and end positions and a custom curve. While not physically accurate, this model can be useful when you need more artistic control. - - Use 50% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be twice as short as they are wide. This allows providing increased GI detail and reduced light leaking with thin floors and ceilings. This is usually the best choice for scenes that don't feature much verticality. + + Uses a cascade format with half the vertical resolution. This is the default, as most level geometry is generally fine with this. - - Use 75% scale for SDFGI on the Y (vertical) axis. This is a balance between the 50% and 100% SDFGI Y scales. + + Full vertical resolution cascade format. Ensures cascades are the same in all directions. - - Use 100% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be as tall as they are wide. This is usually the best choice for highly vertical scenes. The downside is that light leaking may become more noticeable with thin floors and ceilings. + + Maximum amount of cascade formats. diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index bda5fb69de7d..7fe46a2f304f 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -73,7 +73,7 @@ The light's strength multiplier (this is not a physical unit). For [OmniLight3D] and [SpotLight3D], changing this value will only change the light color's intensity, not the light's radius. - Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). + Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI] and DynamicGI (see [member Environment.dynamic_gi_enabled]). [b]Note:[/b] This property is ignored if [member light_energy] is equal to [code]0.0[/code], as the light won't be present at all in the GI shader. @@ -199,14 +199,14 @@ Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights. - [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). + [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and DynamicGI (see [member Environment.dynamic_gi_enabled). - Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. + Light is taken into account in static baking ([VoxelGI], [LightmapGI], DynamicGI ([member Environment.dynamic_gi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. [b]Note:[/b] The light is not baked in [LightmapGI] if [member editor_only] is [code]true[/code]. - Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC]. When using SDFGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights]. + Light is taken into account in dynamic baking ([VoxelGI] and DynamicGI ([member Environment.dynamic_gi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC]. When using DynamicGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/hddagi/frames_to_update_lights]. diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index e7d44411efd4..f9906088c026 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -5,7 +5,7 @@ The [LightmapGI] node is used to compute and store baked lightmaps. Lightmaps are used to provide high-quality indirect lighting with very little light leaking. [LightmapGI] can also provide rough reflections using spherical harmonics if [member directional] is enabled. Dynamic objects can receive indirect lighting thanks to [i]light probes[/i], which can be automatically placed by setting [member generate_probes_subdiv] to a value other than [constant GENERATE_PROBES_DISABLED]. Additional lightmap probes can also be added by creating [LightmapProbe] nodes. The downside is that lightmaps are fully static and cannot be baked in an exported project. Baking a [LightmapGI] node is also slower compared to [VoxelGI]. - [b]Procedural generation:[/b] Lightmap baking functionality is only available in the editor. This means [LightmapGI] is not suited to procedurally generated or user-built levels. For procedurally generated or user-built levels, use [VoxelGI] or SDFGI instead (see [member Environment.sdfgi_enabled]). + [b]Procedural generation:[/b] Lightmap baking functionality is only available in the editor. This means [LightmapGI] is not suited to procedurally generated or user-built levels. For procedurally generated or user-built levels, use [VoxelGI] or DynamicGI instead (see [member Environment.dynamic_gi_enabled]). [b]Performance:[/b] [LightmapGI] provides the best possible run-time performance for global illumination. It is suitable for low-end hardware including integrated graphics and mobile devices. [b]Note:[/b] Due to how lightmaps work, most properties only have a visible effect once lightmaps are baked again. [b]Note:[/b] Lightmap baking on [CSGShape3D]s and [PrimitiveMesh]es is not supported, as these cannot store UV2 data required for baking. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index c10908ba9a66..38c608d790d6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2532,22 +2532,15 @@ If [code]true[/code], disables the threaded optimization feature from the NVIDIA drivers, which are known to cause stuttering in most OpenGL applications. [b]Note:[/b] This setting only works on Windows, as threaded optimization is disabled by default on other platforms. - - If [code]true[/code], renders [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) buffers at halved resolution (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or SDFGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. + + If [code]true[/code], renders [VoxelGI] and DynamicGI ([member Environment.dynamic_gi_enabled]) buffers at halved resolution (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or DynamicGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. [b]Note:[/b] This property is only read when the project starts. To set half-resolution GI at run-time, call [method RenderingServer.gi_set_use_half_resolution] instead. - - The number of frames to use for converging signed distance field global illumination. Higher values lead to a less noisy result, at the cost of taking a longer time to fully converge. This means the scene's global illumination will be too dark for a longer period of time, especially when the camera moves fast. The actual convergence speed depends on rendered framerate. For example, with the default setting of 30 frames, rendering at 60 FPS will make SDFGI fully converge after 0.5 seconds. See also [member rendering/global_illumination/sdfgi/frames_to_update_lights] and [member rendering/global_illumination/sdfgi/probe_ray_count]. - [b]Note:[/b] This property is only read when the project starts. To control SDFGI convergence speed at runtime, call [method RenderingServer.environment_set_sdfgi_frames_to_converge] instead. + - - The number of frames over which dynamic lights should be updated in signed distance field global illumination. Higher values take more time to update indirect lighting coming from dynamic lights, but result in better performance when many dynamic lights are present. See also [member rendering/global_illumination/sdfgi/frames_to_converge] and [member rendering/global_illumination/sdfgi/probe_ray_count]. - [b]Note:[/b] This only affects [Light3D] nodes whose [member Light3D.light_bake_mode] is [constant Light3D.BAKE_DYNAMIC] (which is the default). Consider making non-moving lights use the [constant Light3D.BAKE_STATIC] bake mode to improve performance. - [b]Note:[/b] This property is only read when the project starts. To control SDFGI light update speed at runtime, call [method RenderingServer.environment_set_sdfgi_frames_to_update_light] instead. + - - The number of rays to throw per frame when computing signed distance field global illumination. Higher values lead to a less noisy result, at the cost of performance. See also [member rendering/global_illumination/sdfgi/frames_to_converge] and [member rendering/global_illumination/sdfgi/frames_to_update_lights]. - [b]Note:[/b] This property is only read when the project starts. To control SDFGI quality at runtime, call [method RenderingServer.environment_set_sdfgi_ray_count] instead. + The VoxelGI quality to use. High quality leads to more precise lighting and better reflections, but is slower to render. This setting does not affect the baked data and doesn't require baking the [VoxelGI] again to apply. diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 003cd5354586..b309140b3259 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -5,8 +5,8 @@ Captures its surroundings as a cubemap, and stores versions of it with increasing levels of blur to simulate different material roughnesses. - The [ReflectionProbe] is used to create high-quality reflections at a low performance cost (when [member update_mode] is [constant UPDATE_ONCE]). [ReflectionProbe]s can be blended together and with the rest of the scene smoothly. [ReflectionProbe]s can also be combined with [VoxelGI], SDFGI ([member Environment.sdfgi_enabled]) and screen-space reflections ([member Environment.ssr_enabled]) to get more accurate reflections in specific areas. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them as-is. - [b]Note:[/b] Unlike [VoxelGI] and SDFGI, [ReflectionProbe]s only source their environment from a [WorldEnvironment] node. If you specify an [Environment] resource within a [Camera3D] node, it will be ignored by the [ReflectionProbe]. This can lead to incorrect lighting within the [ReflectionProbe]. + The [ReflectionProbe] is used to create high-quality reflections at a low performance cost (when [member update_mode] is [constant UPDATE_ONCE]). [ReflectionProbe]s can be blended together and with the rest of the scene smoothly. [ReflectionProbe]s can also be combined with [VoxelGI], DynamicGI ([member Environment.dynamic_gi_enabled]) and screen-space reflections ([member Environment.ssr_enabled]) to get more accurate reflections in specific areas. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them as-is. + [b]Note:[/b] Unlike [VoxelGI] and DynamicGI, [ReflectionProbe]s only source their environment from a [WorldEnvironment] node. If you specify an [Environment] resource within a [Camera3D] node, it will be ignored by the [ReflectionProbe]. This can lead to incorrect lighting within the [ReflectionProbe]. [b]Note:[/b] Reflection probes are only supported in the Forward+ and Mobile rendering methods, not Compatibility. When using the Mobile rendering method, only 8 reflection probes can be displayed on each mesh resource. Attempting to display more than 8 reflection probes on a single mesh resource will result in reflection probes flickering in and out as the camera moves. [b]Note:[/b] When using the Mobile rendering method, reflection probes will only correctly affect meshes whose visibility AABB intersects with the reflection probe's AABB. If using a shader to deform the mesh in a way that makes it go outside its AABB, [member GeometryInstance3D.extra_cull_margin] must be increased on the mesh. Otherwise, the reflection probe may not be visible on the mesh. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index be0b50d78ed4..2d6a1bbcb796 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1312,42 +1312,46 @@ Configures glow for the specified environment RID. See [code]glow_*[/code] properties in [Environment] for more information. - + - - - + + + - + + + + + - Configures signed distance field global illumination for the specified environment RID. See [code]sdfgi_*[/code] properties in [Environment] for more information. + Configure DynamicGI (HDDAGI). See the [Environment] class for more detail on what each argument is. - + - + - Sets the number of frames to use for converging signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_converge]. + Set the frames to converge for DynamicGI (HDDAGI). A higher value results in higher quality, but takes longer to converge. - + - + - Sets the update speed for dynamic lights' indirect lighting when computing signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights]. + Set the frames to update for DynamicGI (HDDAGI). Higher values take longer, but use significantly less GPU resources. - + - + - Sets the number of rays to throw per frame when computing signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/probe_ray_count]. + Set the amount of frames it takes to process inactive probes (inactive probes are those not connected to any geometry and not visible by the camera). They process (raytrace) at a lower framerate in order to save GPU resouces. @@ -1635,7 +1639,7 @@ - If [param half_resolution] is [code]true[/code], renders [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) buffers at halved resolution on each axis (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or SDFGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. Equivalent to [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution]. + If [param half_resolution] is [code]true[/code], renders [VoxelGI] and DynamicGI ([member Environment.dynamic_gi_enabled]) buffers at halved resolution on each axis (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or DynamicGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. Equivalent to [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution]. @@ -2062,12 +2066,12 @@ Sets the distance fade for this 3D light. This acts as a form of level of detail (LOD) and can be used to improve performance. Equivalent to [member Light3D.distance_fade_enabled], [member Light3D.distance_fade_begin], [member Light3D.distance_fade_shadow], and [member Light3D.distance_fade_length]. - + - Sets the maximum SDFGI cascade in which the 3D light's indirect lighting is rendered. Higher values allow the light to be rendered in SDFGI further away from the camera. + Set the maximum amount of cascades used by DynamicGI (HDDAGI). @@ -3929,7 +3933,7 @@ - Sets the viewport's 2D signed distance field [member ProjectSettings.rendering/2d/sdf/oversize] and [member ProjectSettings.rendering/2d/sdf/scale]. This is used when sampling the signed distance field in [CanvasItem] shaders as well as [GPUParticles2D] collision. This is [i]not[/i] used by SDFGI in 3D rendering. + Sets the viewport's 2D signed distance field [member ProjectSettings.rendering/2d/sdf/oversize] and [member ProjectSettings.rendering/2d/sdf/scale]. This is used when sampling the signed distance field in [CanvasItem] shaders as well as [GPUParticles2D] collision. This is [i]not[/i] used by DynamicGI in 3D rendering. @@ -4580,10 +4584,10 @@ Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights. - Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. + Light is taken into account in static baking ([VoxelGI], [LightmapGI], DynamicGI ([member Environment.dynamic_gi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off. - Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant LIGHT_BAKE_STATIC]. This has a greater performance cost compared to [constant LIGHT_BAKE_STATIC]. When using SDFGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights]. + Light is taken into account in dynamic baking ([VoxelGI] and DynamicGI ([member Environment.dynamic_gi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant LIGHT_BAKE_STATIC]. This has a greater performance cost compared to [constant LIGHT_BAKE_STATIC]. When using DynamicGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/hddagi/frames_to_update_lights]. Use a dual paraboloid shadow map for omni lights. @@ -4951,14 +4955,14 @@ Draws the decal atlas that stores decal textures from [Decal]s. - - Draws SDFGI cascade data. This is the data structure that is used to bounce lighting against and create reflections. + + Draw the voxel world as seen by DynamicGI (HDDAGI). - - Draws SDFGI probe data. This is the data structure that is used to give indirect lighting dynamic objects moving within the scene. + + Draw the probes of the first DynamicGI cascade (HDDAGI). - Draws the global illumination buffer ([VoxelGI] or SDFGI). + Draws the global illumination buffer ([VoxelGI] or DynamicGI). Disable mesh LOD. All meshes are drawn with full detail, which can be used to compare performance. @@ -5177,77 +5181,58 @@ Highest quality screen-space indirect lighting. Uses the adaptive target setting which can be dynamically adjusted to smoothly balance performance and visual quality. - - Use 50% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be twice as short as they are wide. This allows providing increased GI detail and reduced light leaking with thin floors and ceilings. This is usually the best choice for scenes that don't feature much verticality. + + Half resolution cascade format for DynamicGI (HDDAGI). - - Use 75% scale for SDFGI on the Y (vertical) axis. This is a balance between the 50% and 100% SDFGI Y scales. + + Full resolution cascade format for DynamicGI (HDDAGI). - - Use 100% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be as tall as they are wide. This is usually the best choice for highly vertical scenes. The downside is that light leaking may become more noticeable with thin floors and ceilings. + - - Throw 4 rays per frame when converging SDFGI. This has the lowest GPU requirements, but creates the most noisy result. + + Converge the DynamicGI (HDDAGI) in 6 frames. This means it will take 6 frames for light changes to update, but the lighting quality will be lower. + This is the default, as well as enabling filter probes. - - Throw 8 rays per frame when converging SDFGI. + + Converge the DynamicGI (HDDAGI) in 12 frames. - - Throw 16 rays per frame when converging SDFGI. + + Converge the DynamicGI (HDDAGI) in 18 frames. - - Throw 32 rays per frame when converging SDFGI. + + Converge the DynamicGI (HDDAGI) in 24 frames. - - Throw 64 rays per frame when converging SDFGI. + + Converge the DynamicGI (HDDAGI) in 32 frames. This means it will take 32 frames to update the light changes, but quality will be much higher. - - Throw 96 rays per frame when converging SDFGI. This has high GPU requirements. + - - Throw 128 rays per frame when converging SDFGI. This has very high GPU requirements, but creates the least noisy result. + + Update lights (directional, spot, omni) in 1 frame. Changes are instant. - - Represents the size of the [enum EnvironmentSDFGIRayCount] enum. + + Update lights (directional, spot, omni) in 2 frames. - - Converge SDFGI over 5 frames. This is the most responsive, but creates the most noisy result with a given ray count. + + Update lights (directional, spot, omni) in 4 frames. This is the default. - - Configure SDFGI to fully converge over 10 frames. + + Update lights (directional, spot, omni) in 8 frames. - - Configure SDFGI to fully converge over 15 frames. + + Update lights (directional, spot, omni) in 16 frames. - - Configure SDFGI to fully converge over 20 frames. + - - Configure SDFGI to fully converge over 25 frames. + - - Configure SDFGI to fully converge over 30 frames. This is the least responsive, but creates the least noisy result with a given ray count. + - - Represents the size of the [enum EnvironmentSDFGIFramesToConverge] enum. + - - Update indirect light from dynamic lights in SDFGI over 1 frame. This is the most responsive, but has the highest GPU requirements. + - - Update indirect light from dynamic lights in SDFGI over 2 frames. - - - Update indirect light from dynamic lights in SDFGI over 4 frames. - - - Update indirect light from dynamic lights in SDFGI over 8 frames. - - - Update indirect light from dynamic lights in SDFGI over 16 frames. This is the least responsive, but has the lowest GPU requirements. - - - Represents the size of the [enum EnvironmentSDFGIFramesToUpdateLight] enum. + Disables subsurface scattering entirely, even on materials that have [member BaseMaterial3D.subsurf_scatter_enabled] set to [code]true[/code]. This has the lowest GPU requirements. diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml index 4afc62e03878..e7a9e2bb4fd5 100644 --- a/doc/classes/Vector2i.xml +++ b/doc/classes/Vector2i.xml @@ -229,6 +229,12 @@ [/codeblock] + + + + + + @@ -310,6 +316,18 @@ Compares two [Vector2i] vectors by first checking if the X value of the left vector is less than the X value of the [param right] vector. If the X values are exactly equal, then it repeats this check with the Y values of the two vectors. This operator is useful for sorting vectors. + + + + + + + + + + + + @@ -338,6 +356,18 @@ Compares two [Vector2i] vectors by first checking if the X value of the left vector is greater than or equal to the X value of the [param right] vector. If the X values are exactly equal, then it repeats this check with the Y values of the two vectors. This operator is useful for sorting vectors. + + + + + + + + + + + + @@ -345,6 +375,12 @@ Access vector components using their [param index]. [code]v[0][/code] is equivalent to [code]v.x[/code], and [code]v[1][/code] is equivalent to [code]v.y[/code]. + + + + + + @@ -357,5 +393,11 @@ Returns the negative value of the [Vector2i]. This is the same as writing [code]Vector2i(-v.x, -v.y)[/code]. This operation flips the direction of the vector while keeping the same magnitude. + + + + + + diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml index df4624dbb100..1d6a58bc14a2 100644 --- a/doc/classes/Vector3i.xml +++ b/doc/classes/Vector3i.xml @@ -236,6 +236,12 @@ [/codeblock] + + + + + + @@ -317,6 +323,18 @@ Compares two [Vector3i] vectors by first checking if the X value of the left vector is less than the X value of the [param right] vector. If the X values are exactly equal, then it repeats this check with the Y values of the two vectors, and then with the Z values. This operator is useful for sorting vectors. + + + + + + + + + + + + @@ -345,6 +363,18 @@ Compares two [Vector3i] vectors by first checking if the X value of the left vector is greater than or equal to the X value of the [param right] vector. If the X values are exactly equal, then it repeats this check with the Y values of the two vectors, and then with the Z values. This operator is useful for sorting vectors. + + + + + + + + + + + + @@ -352,6 +382,12 @@ Access vector components using their [param index]. [code]v[0][/code] is equivalent to [code]v.x[/code], [code]v[1][/code] is equivalent to [code]v.y[/code], and [code]v[2][/code] is equivalent to [code]v.z[/code]. + + + + + + @@ -364,5 +400,11 @@ Returns the negative value of the [Vector3i]. This is the same as writing [code]Vector3i(-v.x, -v.y, -v.z)[/code]. This operation flips the direction of the vector while keeping the same magnitude. + + + + + + diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 00ddee9d2002..58f3f585ffd3 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -566,13 +566,13 @@ Draws the decal atlas used by [Decal]s and light projector textures in the upper left quadrant of the [Viewport]. - - Draws the cascades used to render signed distance field global illumination (SDFGI). - Does nothing if the current environment's [member Environment.sdfgi_enabled] is [code]false[/code] or SDFGI is not supported on the platform. + + Draws the cascades used to render voxel volumes for global illumination (DynamicGI). + Does nothing if the current environment's [member Environment.dynamic_gi_enabled] is [code]false[/code] or Dynamic GI is not supported on the platform. - - Draws the probes used for signed distance field global illumination (SDFGI). - Does nothing if the current environment's [member Environment.sdfgi_enabled] is [code]false[/code] or SDFGI is not supported on the platform. + + Draws the probes used for DynamicGI. + Does nothing if the current environment's [member Environment.dynamic_gi_enabled] is [code]false[/code] or DynamicGI is not supported on the platform. Draws the buffer used for global illumination (GI). diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index 93679cccf6f3..3ec531886d57 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -6,7 +6,7 @@ [VoxelGI]s are used to provide high-quality real-time indirect light and reflections to scenes. They precompute the effect of objects that emit light and the effect of static geometry to simulate the behavior of complex light in real-time. [VoxelGI]s need to be baked before having a visible effect. However, once baked, dynamic objects will receive light from them. Furthermore, lights can be fully dynamic or baked. [b]Note:[/b] [VoxelGI] is only supported in the Forward+ rendering method, not Mobile or Compatibility. - [b]Procedural generation:[/b] [VoxelGI] can be baked in an exported project, which makes it suitable for procedurally generated or user-built levels as long as all the geometry is generated in advance. For games where geometry is generated at any time during gameplay, SDFGI is more suitable (see [member Environment.sdfgi_enabled]). + [b]Procedural generation:[/b] [VoxelGI] can be baked in an exported project, which makes it suitable for procedurally generated or user-built levels as long as all the geometry is generated in advance. For games where geometry is generated at any time during gameplay, DynamicGI is more suitable (see [member Environment.dynamic_gi_enabled]). [b]Performance:[/b] [VoxelGI] is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve performance, adjust [member ProjectSettings.rendering/global_illumination/voxel_gi/quality] and enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings. To provide a fallback for low-end hardware, consider adding an option to disable [VoxelGI] in your project's options menus. A [VoxelGI] node can be disabled by hiding it. [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. These temporary nodes can then be hidden after baking the [VoxelGI] node. diff --git a/drivers/gles3/environment/gi.cpp b/drivers/gles3/environment/gi.cpp index 6b33e0aa2287..408a3b999944 100644 --- a/drivers/gles3/environment/gi.cpp +++ b/drivers/gles3/environment/gi.cpp @@ -137,7 +137,7 @@ uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const { return 0; } -void GI::sdfgi_reset() { +void GI::hddagi_reset() { } #endif // GLES3_ENABLED diff --git a/drivers/gles3/environment/gi.h b/drivers/gles3/environment/gi.h index 3eaa687f4f6f..da70d95f263f 100644 --- a/drivers/gles3/environment/gi.h +++ b/drivers/gles3/environment/gi.h @@ -86,7 +86,7 @@ class GI : public RendererGI { virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override; - virtual void sdfgi_reset() override; + virtual void hddagi_reset() override; }; }; // namespace GLES3 diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 181cc61156aa..14188da36a79 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1091,13 +1091,10 @@ void RasterizerSceneGLES3::environment_set_ssao_quality(RS::EnvironmentSSAOQuali void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { } -void RasterizerSceneGLES3::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { +void RasterizerSceneGLES3::environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) { } -void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) { -} - -void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) { +void RasterizerSceneGLES3::environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) { } void RasterizerSceneGLES3::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) { @@ -1106,6 +1103,9 @@ void RasterizerSceneGLES3::environment_set_volumetric_fog_volume_size(int p_size void RasterizerSceneGLES3::environment_set_volumetric_fog_filter_active(bool p_enable) { } +void RasterizerSceneGLES3::environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) { +} + Ref RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) { ERR_FAIL_COND_V(p_env.is_null(), Ref()); @@ -2222,7 +2222,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); } -void RasterizerSceneGLES3::render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RenderingMethod::RenderInfo *r_render_info) { +void RasterizerSceneGLES3::render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data, RenderingMethod::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); @@ -4033,7 +4033,7 @@ void RasterizerSceneGLES3::update() { _update_dirty_skys(); } -void RasterizerSceneGLES3::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { +void RasterizerSceneGLES3::hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { } void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) { diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 2b3f863b891e..0b791aebc39b 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -300,7 +300,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { int32_t instance_count = 0; - bool can_sdfgi = false; + bool can_hddagi = false; bool using_projectors = false; bool using_softshadows = false; @@ -787,16 +787,16 @@ class RasterizerSceneGLES3 : public RendererSceneRender { uint32_t geometry_instance_get_pair_mask() override; - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - void sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(const Ref &p_render_buffers) const override { + void hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int hddagi_get_pending_region_count(const Ref &p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { + AABB hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { + uint32_t hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { return 0; } @@ -820,9 +820,10 @@ class RasterizerSceneGLES3 : public RendererSceneRender { void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; - void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; - void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; - void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override; + void environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) override; + void environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) override; + + void environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) override; void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override; void environment_set_volumetric_fog_filter_active(bool p_enable) override; @@ -849,7 +850,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { void voxel_gi_set_quality(RS::VoxelGIQuality) override; - void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; + void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) override; @@ -881,7 +882,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { bool free(RID p_rid) override; void update() override; - void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; + void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; void decals_set_filter(RS::DecalFilter p_filter) override; void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index b6e64c9492b2..6b88eb2e3d18 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -57,7 +57,7 @@ struct Light { bool negative = false; bool reverse_cull = false; RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC; - uint32_t max_sdfgi_cascade = 2; + uint32_t max_hddagi_cascade = 2; uint32_t cull_mask = 0xFFFFFFFF; bool distance_fade = false; real_t distance_fade_begin = 40.0; @@ -327,7 +327,7 @@ class LightStorage : public RendererLightStorage { virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override; virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override; virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override; - virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {} + virtual void light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) override {} virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override; @@ -424,7 +424,7 @@ class LightStorage : public RendererLightStorage { } virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; - virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } + virtual uint32_t light_get_max_hddagi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override; virtual uint32_t light_get_cull_mask(RID p_light) const override; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 9717c99dd1fd..7a46164b25f4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -395,10 +395,13 @@ void EditorNode::_update_from_settings() { RS::get_singleton()->directional_soft_shadow_filter_set_quality(directional_shadow_quality); float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); - RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge"))); - RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge); - RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count"))); - RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); + RS::EnvironmentHDDAGIFramesToConverge frames_to_converge = RS::EnvironmentHDDAGIFramesToConverge(int(GLOBAL_GET("rendering/global_illumination/hddagi/frames_to_converge"))); + RS::get_singleton()->environment_set_hddagi_frames_to_converge(frames_to_converge); + RS::EnvironmentHDDAGIFramesToUpdateLight frames_to_update_light = RS::EnvironmentHDDAGIFramesToUpdateLight(int(GLOBAL_GET("rendering/global_illumination/hddagi/frames_to_update_lights"))); + RS::get_singleton()->environment_set_hddagi_frames_to_update_light(frames_to_update_light); + RS::EnvironmentHDDAGIInactiveProbeFrames inactive_probe_frames = RS::EnvironmentHDDAGIInactiveProbeFrames(int(GLOBAL_GET("rendering/global_illumination/hddagi/frames_to_update_inactive_probes"))); + RS::get_singleton()->environment_set_hddagi_inactive_probe_frames(inactive_probe_frames); + RS::VoxelGIQuality voxel_gi_quality = RS::VoxelGIQuality(int(GLOBAL_GET("rendering/global_illumination/voxel_gi/quality"))); RS::get_singleton()->voxel_gi_set_quality(voxel_gi_quality); RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/environment/volumetric_fog/volume_size"), GLOBAL_GET("rendering/environment/volumetric_fog/volume_depth")); @@ -3857,8 +3860,8 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { editor_data.notify_edited_scene_changed(); emit_signal(SNAME("scene_changed")); - // Reset SDFGI after everything else so that any last-second scene modifications will be processed. - RenderingServer::get_singleton()->sdfgi_reset(); + // Reset HDDAGI after everything else so that any last-second scene modifications will be processed. + RenderingServer::get_singleton()->hddagi_reset(); } bool EditorNode::is_changing_scene() const { diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index f23cab676ccc..f3f852a226e7 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -267,7 +267,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["s3tc"] = "S3TC"; capitalize_string_remaps["scp"] = "SCP"; capitalize_string_remaps["sdf"] = "SDF"; - capitalize_string_remaps["sdfgi"] = "SDFGI"; + capitalize_string_remaps["hddagi"] = "HDDAGI"; capitalize_string_remaps["sdk"] = "SDK"; capitalize_string_remaps["sec"] = "(sec)"; // Unit. capitalize_string_remaps["signtool"] = "signtool"; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 59a4ac8075f9..cedcf1c42e9f 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -796,8 +796,8 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const { Vector3 pos = get_ray_pos(p_pos); Vector2 shrinked_pos = p_pos / subviewport_container->get_stretch_shrink(); - if (viewport->get_debug_draw() == Viewport::DEBUG_DRAW_SDFGI_PROBES) { - RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray); + if (viewport->get_debug_draw() == Viewport::DEBUG_DRAW_HDDAGI_PROBES) { + RS::get_singleton()->hddagi_set_debug_probe_select(pos, ray); } Vector instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario()); @@ -3574,8 +3574,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { case VIEW_DISPLAY_DEBUG_SSIL: case VIEW_DISPLAY_DEBUG_PSSM_SPLITS: case VIEW_DISPLAY_DEBUG_DECAL_ATLAS: - case VIEW_DISPLAY_DEBUG_SDFGI: - case VIEW_DISPLAY_DEBUG_SDFGI_PROBES: + case VIEW_DISPLAY_DEBUG_HDDAGI: + case VIEW_DISPLAY_DEBUG_HDDAGI_PROBES: case VIEW_DISPLAY_DEBUG_GI_BUFFER: case VIEW_DISPLAY_DEBUG_DISABLE_LOD: case VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS: @@ -3604,8 +3604,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { VIEW_DISPLAY_DEBUG_DISABLE_LOD, VIEW_DISPLAY_DEBUG_PSSM_SPLITS, VIEW_DISPLAY_DEBUG_DECAL_ATLAS, - VIEW_DISPLAY_DEBUG_SDFGI, - VIEW_DISPLAY_DEBUG_SDFGI_PROBES, + VIEW_DISPLAY_DEBUG_HDDAGI, + VIEW_DISPLAY_DEBUG_HDDAGI_PROBES, VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS, VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS, VIEW_DISPLAY_DEBUG_CLUSTER_DECALS, @@ -3634,8 +3634,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { Viewport::DEBUG_DRAW_DISABLE_LOD, Viewport::DEBUG_DRAW_PSSM_SPLITS, Viewport::DEBUG_DRAW_DECAL_ATLAS, - Viewport::DEBUG_DRAW_SDFGI, - Viewport::DEBUG_DRAW_SDFGI_PROBES, + Viewport::DEBUG_DRAW_HDDAGI, + Viewport::DEBUG_DRAW_HDDAGI_PROBES, Viewport::DEBUG_DRAW_CLUSTER_OMNI_LIGHTS, Viewport::DEBUG_DRAW_CLUSTER_SPOT_LIGHTS, Viewport::DEBUG_DRAW_CLUSTER_DECALS, @@ -5277,15 +5277,15 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p display_submenu->add_radio_check_item(TTR("VoxelGI Albedo"), VIEW_DISPLAY_DEBUG_VOXEL_GI_ALBEDO); display_submenu->add_radio_check_item(TTR("VoxelGI Emission"), VIEW_DISPLAY_DEBUG_VOXEL_GI_EMISSION); display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("SDFGI Cascades"), VIEW_DISPLAY_DEBUG_SDFGI); - display_submenu->add_radio_check_item(TTR("SDFGI Probes"), VIEW_DISPLAY_DEBUG_SDFGI_PROBES); + display_submenu->add_radio_check_item(TTR("Dynamic GI Cascades"), VIEW_DISPLAY_DEBUG_HDDAGI); + display_submenu->add_radio_check_item(TTR("Dynamic GI Probes"), VIEW_DISPLAY_DEBUG_HDDAGI_PROBES); display_submenu->add_separator(); display_submenu->add_radio_check_item(TTR("Scene Luminance"), VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE); display_submenu->add_separator(); display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO); display_submenu->add_radio_check_item(TTR("SSIL"), VIEW_DISPLAY_DEBUG_SSIL); display_submenu->add_separator(); - display_submenu->add_radio_check_item(TTR("VoxelGI/SDFGI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER); + display_submenu->add_radio_check_item(TTR("Voxel/Dynamic GI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER); display_submenu->add_separator(); display_submenu->add_radio_check_item(TTR("Disable Mesh LOD"), VIEW_DISPLAY_DEBUG_DISABLE_LOD); display_submenu->add_separator(); @@ -8346,7 +8346,7 @@ void Node3DEditor::_preview_settings_changed() { environment->set_ssao_enabled(environ_ao_button->is_pressed()); environment->set_glow_enabled(environ_glow_button->is_pressed()); - environment->set_sdfgi_enabled(environ_gi_button->is_pressed()); + environment->set_dynamic_gi_enabled(environ_gi_button->is_pressed()); environment->set_tonemapper(environ_tonemap_button->is_pressed() ? Environment::TONE_MAPPER_FILMIC : Environment::TONE_MAPPER_LINEAR); } } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 5bd14748c08d..9134d661f8ed 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -144,8 +144,8 @@ class Node3DEditorViewport : public Control { VIEW_DISPLAY_DEBUG_VOXEL_GI_ALBEDO, VIEW_DISPLAY_DEBUG_VOXEL_GI_LIGHTING, VIEW_DISPLAY_DEBUG_VOXEL_GI_EMISSION, - VIEW_DISPLAY_DEBUG_SDFGI, - VIEW_DISPLAY_DEBUG_SDFGI_PROBES, + VIEW_DISPLAY_DEBUG_HDDAGI, + VIEW_DISPLAY_DEBUG_HDDAGI_PROBES, VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, VIEW_DISPLAY_DEBUG_SSAO, VIEW_DISPLAY_DEBUG_SSIL, diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1302e3c53e66..0af695e5a2c6 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -4926,8 +4926,8 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(DEBUG_DRAW_SSIL); BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS); BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS); - BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI); - BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES); + BIND_ENUM_CONSTANT(DEBUG_DRAW_HDDAGI); + BIND_ENUM_CONSTANT(DEBUG_DRAW_HDDAGI_PROBES); BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER); BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD); BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_OMNI_LIGHTS); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0d31c07e57e2..0333c51b0859 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -159,8 +159,8 @@ class Viewport : public Node { DEBUG_DRAW_SSIL, DEBUG_DRAW_PSSM_SPLITS, DEBUG_DRAW_DECAL_ATLAS, - DEBUG_DRAW_SDFGI, - DEBUG_DRAW_SDFGI_PROBES, + DEBUG_DRAW_HDDAGI, + DEBUG_DRAW_HDDAGI_PROBES, DEBUG_DRAW_GI_BUFFER, DEBUG_DRAW_DISABLE_LOD, DEBUG_DRAW_CLUSTER_OMNI_LIGHTS, diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index ac04227b6490..03a384573c52 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -452,139 +452,179 @@ void Environment::_update_ssil() { ssil_normal_rejection); } -// SDFGI +// DynamicGI -void Environment::set_sdfgi_enabled(bool p_enabled) { - sdfgi_enabled = p_enabled; - _update_sdfgi(); +void Environment::set_dynamic_gi_enabled(bool p_enabled) { + dynamic_gi_enabled = p_enabled; + _update_dynamic_gi(); notify_property_list_changed(); } -bool Environment::is_sdfgi_enabled() const { - return sdfgi_enabled; +bool Environment::is_dynamic_gi_enabled() const { + return dynamic_gi_enabled; } -void Environment::set_sdfgi_cascades(int p_cascades) { - ERR_FAIL_COND_MSG(p_cascades < 1 || p_cascades > 8, "Invalid number of SDFGI cascades (must be between 1 and 8)."); - sdfgi_cascades = p_cascades; - _update_sdfgi(); +void Environment::set_dynamic_gi_cascades(int p_cascades) { + ERR_FAIL_COND_MSG(p_cascades < 1 || p_cascades > 8, "Invalid number of DynamicGI cascades (must be between 1 and 8)."); + dynamic_gi_cascades = p_cascades; + _update_dynamic_gi(); } -int Environment::get_sdfgi_cascades() const { - return sdfgi_cascades; +int Environment::get_dynamic_gi_cascades() const { + return dynamic_gi_cascades; } -void Environment::set_sdfgi_min_cell_size(float p_size) { - sdfgi_min_cell_size = p_size; - _update_sdfgi(); +void Environment::set_dynamic_gi_min_cell_size(float p_size) { + dynamic_gi_min_cell_size = p_size; + _update_dynamic_gi(); } -float Environment::get_sdfgi_min_cell_size() const { - return sdfgi_min_cell_size; +float Environment::get_dynamic_gi_min_cell_size() const { + return dynamic_gi_min_cell_size; } -void Environment::set_sdfgi_max_distance(float p_distance) { +void Environment::set_dynamic_gi_max_distance(float p_distance) { p_distance /= 64.0; - for (int i = 0; i < sdfgi_cascades; i++) { + for (int i = 0; i < dynamic_gi_cascades; i++) { p_distance *= 0.5; //halve for each cascade } - sdfgi_min_cell_size = p_distance; - _update_sdfgi(); + dynamic_gi_min_cell_size = p_distance; + _update_dynamic_gi(); } -float Environment::get_sdfgi_max_distance() const { - float md = sdfgi_min_cell_size; +float Environment::get_dynamic_gi_max_distance() const { + float md = dynamic_gi_min_cell_size; md *= 64.0; - for (int i = 0; i < sdfgi_cascades; i++) { + for (int i = 0; i < dynamic_gi_cascades; i++) { md *= 2.0; } return md; } -void Environment::set_sdfgi_cascade0_distance(float p_distance) { - sdfgi_min_cell_size = p_distance / 64.0; - _update_sdfgi(); +void Environment::set_dynamic_gi_cascade0_distance(float p_distance) { + dynamic_gi_min_cell_size = p_distance / 64.0; + _update_dynamic_gi(); } -float Environment::get_sdfgi_cascade0_distance() const { - return sdfgi_min_cell_size * 64.0; +float Environment::get_dynamic_gi_cascade0_distance() const { + return dynamic_gi_min_cell_size * 64.0; } -void Environment::set_sdfgi_y_scale(SDFGIYScale p_y_scale) { - sdfgi_y_scale = p_y_scale; - _update_sdfgi(); +void Environment::set_dynamic_gi_cascade_format(DynamicGICascadeFormat p_cascade_format) { + dynamic_gi_cascade_format = p_cascade_format; + _update_dynamic_gi(); } -Environment::SDFGIYScale Environment::get_sdfgi_y_scale() const { - return sdfgi_y_scale; +Environment::DynamicGICascadeFormat Environment::get_dynamic_gi_cascade_format() const { + return dynamic_gi_cascade_format; } -void Environment::set_sdfgi_use_occlusion(bool p_enabled) { - sdfgi_use_occlusion = p_enabled; - _update_sdfgi(); +void Environment::set_dynamic_gi_filter_probes(bool p_enabled) { + dynamic_gi_filter_probes = p_enabled; + _update_dynamic_gi(); } -bool Environment::is_sdfgi_using_occlusion() const { - return sdfgi_use_occlusion; +bool Environment::is_dynamic_gi_filtering_probes() const { + return dynamic_gi_filter_probes; } -void Environment::set_sdfgi_bounce_feedback(float p_amount) { - sdfgi_bounce_feedback = p_amount; - _update_sdfgi(); +void Environment::set_dynamic_gi_filter_ambient(bool p_enabled) { + dynamic_gi_filter_ambient = p_enabled; + _update_dynamic_gi(); } -float Environment::get_sdfgi_bounce_feedback() const { - return sdfgi_bounce_feedback; + +bool Environment::is_dynamic_gi_filtering_ambient() const { + return dynamic_gi_filter_ambient; } -void Environment::set_sdfgi_read_sky_light(bool p_enabled) { - sdfgi_read_sky_light = p_enabled; - _update_sdfgi(); +void Environment::set_dynamic_gi_filter_reflections(bool p_enabled) { + dynamic_gi_filter_reflections = p_enabled; + _update_dynamic_gi(); } -bool Environment::is_sdfgi_reading_sky_light() const { - return sdfgi_read_sky_light; +bool Environment::is_dynamic_gi_filtering_reflections() const { + return dynamic_gi_filter_reflections; } -void Environment::set_sdfgi_energy(float p_energy) { - sdfgi_energy = p_energy; - _update_sdfgi(); +void Environment::set_dynamic_gi_bounce_feedback(float p_amount) { + dynamic_gi_bounce_feedback = p_amount; + _update_dynamic_gi(); +} +float Environment::get_dynamic_gi_bounce_feedback() const { + return dynamic_gi_bounce_feedback; } -float Environment::get_sdfgi_energy() const { - return sdfgi_energy; +void Environment::set_dynamic_gi_read_sky_light(bool p_enabled) { + dynamic_gi_read_sky_light = p_enabled; + _update_dynamic_gi(); } -void Environment::set_sdfgi_normal_bias(float p_bias) { - sdfgi_normal_bias = p_bias; - _update_sdfgi(); +bool Environment::is_dynamic_gi_reading_sky_light() const { + return dynamic_gi_read_sky_light; } -float Environment::get_sdfgi_normal_bias() const { - return sdfgi_normal_bias; +void Environment::set_dynamic_gi_energy(float p_energy) { + dynamic_gi_energy = p_energy; + _update_dynamic_gi(); } -void Environment::set_sdfgi_probe_bias(float p_bias) { - sdfgi_probe_bias = p_bias; - _update_sdfgi(); +float Environment::get_dynamic_gi_energy() const { + return dynamic_gi_energy; } -float Environment::get_sdfgi_probe_bias() const { - return sdfgi_probe_bias; +void Environment::set_dynamic_gi_normal_bias(float p_bias) { + dynamic_gi_normal_bias = p_bias; + _update_dynamic_gi(); } -void Environment::_update_sdfgi() { - RS::get_singleton()->environment_set_sdfgi( +float Environment::get_dynamic_gi_normal_bias() const { + return dynamic_gi_normal_bias; +} + +void Environment::set_dynamic_gi_reflection_bias(float p_bias) { + dynamic_gi_reflection_bias = p_bias; + _update_dynamic_gi(); +} + +float Environment::get_dynamic_gi_reflection_bias() const { + return dynamic_gi_reflection_bias; +} + +void Environment::set_dynamic_gi_probe_bias(float p_bias) { + dynamic_gi_probe_bias = p_bias; + _update_dynamic_gi(); +} + +float Environment::get_dynamic_gi_probe_bias() const { + return dynamic_gi_probe_bias; +} + +void Environment::set_dynamic_gi_occlusion_bias(float p_bias) { + dynamic_gi_occlusion_bias = p_bias; + _update_dynamic_gi(); +} + +float Environment::get_dynamic_gi_occlusion_bias() const { + return dynamic_gi_occlusion_bias; +} + +void Environment::_update_dynamic_gi() { + RS::get_singleton()->environment_set_hddagi( environment, - sdfgi_enabled, - sdfgi_cascades, - sdfgi_min_cell_size, - RS::EnvironmentSDFGIYScale(sdfgi_y_scale), - sdfgi_use_occlusion, - sdfgi_bounce_feedback, - sdfgi_read_sky_light, - sdfgi_energy, - sdfgi_normal_bias, - sdfgi_probe_bias); + dynamic_gi_enabled, + dynamic_gi_cascades, + RS::EnvironmentHDDAGICascadeFormat(dynamic_gi_cascade_format), + dynamic_gi_min_cell_size, + dynamic_gi_filter_probes, + dynamic_gi_bounce_feedback, + dynamic_gi_read_sky_light, + dynamic_gi_energy, + dynamic_gi_normal_bias, + dynamic_gi_reflection_bias, + dynamic_gi_probe_bias, + dynamic_gi_occlusion_bias, + dynamic_gi_filter_reflections, + dynamic_gi_filter_ambient); } // Glow @@ -1170,7 +1210,7 @@ void Environment::_validate_property(PropertyInfo &p_property) const { "ssr_", "ssao_", "ssil_", - "sdfgi_", + "dynamic_gi_", "glow_", "adjustment_", nullptr @@ -1204,6 +1244,10 @@ bool Environment::_set(const StringName &p_name, const Variant &p_value) { Vector3 euler = p_value.operator Basis().get_euler(); set_sky_rotation(euler); return true; + } else if (p_name.operator String().begins_with("sdfgi")) { + StringName new_name = p_name.operator String().replace_first("sdfgi", "dynamic_gi"); + set(new_name, p_value); + return true; } else { return false; } @@ -1352,48 +1396,66 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_sharpness", "get_ssil_sharpness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_normal_rejection", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_normal_rejection", "get_ssil_normal_rejection"); - // SDFGI - - ClassDB::bind_method(D_METHOD("set_sdfgi_enabled", "enabled"), &Environment::set_sdfgi_enabled); - ClassDB::bind_method(D_METHOD("is_sdfgi_enabled"), &Environment::is_sdfgi_enabled); - ClassDB::bind_method(D_METHOD("set_sdfgi_cascades", "amount"), &Environment::set_sdfgi_cascades); - ClassDB::bind_method(D_METHOD("get_sdfgi_cascades"), &Environment::get_sdfgi_cascades); - ClassDB::bind_method(D_METHOD("set_sdfgi_min_cell_size", "size"), &Environment::set_sdfgi_min_cell_size); - ClassDB::bind_method(D_METHOD("get_sdfgi_min_cell_size"), &Environment::get_sdfgi_min_cell_size); - ClassDB::bind_method(D_METHOD("set_sdfgi_max_distance", "distance"), &Environment::set_sdfgi_max_distance); - ClassDB::bind_method(D_METHOD("get_sdfgi_max_distance"), &Environment::get_sdfgi_max_distance); - ClassDB::bind_method(D_METHOD("set_sdfgi_cascade0_distance", "distance"), &Environment::set_sdfgi_cascade0_distance); - ClassDB::bind_method(D_METHOD("get_sdfgi_cascade0_distance"), &Environment::get_sdfgi_cascade0_distance); - ClassDB::bind_method(D_METHOD("set_sdfgi_y_scale", "scale"), &Environment::set_sdfgi_y_scale); - ClassDB::bind_method(D_METHOD("get_sdfgi_y_scale"), &Environment::get_sdfgi_y_scale); - ClassDB::bind_method(D_METHOD("set_sdfgi_use_occlusion", "enable"), &Environment::set_sdfgi_use_occlusion); - ClassDB::bind_method(D_METHOD("is_sdfgi_using_occlusion"), &Environment::is_sdfgi_using_occlusion); - ClassDB::bind_method(D_METHOD("set_sdfgi_bounce_feedback", "amount"), &Environment::set_sdfgi_bounce_feedback); - ClassDB::bind_method(D_METHOD("get_sdfgi_bounce_feedback"), &Environment::get_sdfgi_bounce_feedback); - ClassDB::bind_method(D_METHOD("set_sdfgi_read_sky_light", "enable"), &Environment::set_sdfgi_read_sky_light); - ClassDB::bind_method(D_METHOD("is_sdfgi_reading_sky_light"), &Environment::is_sdfgi_reading_sky_light); - ClassDB::bind_method(D_METHOD("set_sdfgi_energy", "amount"), &Environment::set_sdfgi_energy); - ClassDB::bind_method(D_METHOD("get_sdfgi_energy"), &Environment::get_sdfgi_energy); - ClassDB::bind_method(D_METHOD("set_sdfgi_normal_bias", "bias"), &Environment::set_sdfgi_normal_bias); - ClassDB::bind_method(D_METHOD("get_sdfgi_normal_bias"), &Environment::get_sdfgi_normal_bias); - ClassDB::bind_method(D_METHOD("set_sdfgi_probe_bias", "bias"), &Environment::set_sdfgi_probe_bias); - ClassDB::bind_method(D_METHOD("get_sdfgi_probe_bias"), &Environment::get_sdfgi_probe_bias); - - ADD_GROUP("SDFGI", "sdfgi_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_enabled"), "set_sdfgi_enabled", "is_sdfgi_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_sdfgi_cascades", "get_sdfgi_cascades"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size"); - // Don't store the values of `sdfgi_cascade0_distance` and `sdfgi_max_distance` - // as they're derived from `sdfgi_min_cell_size`. - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_sdfgi_max_distance", "get_sdfgi_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_y_scale", PROPERTY_HINT_ENUM, "50% (Compact),75% (Balanced),100% (Sparse)"), "set_sdfgi_y_scale", "get_sdfgi_y_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_energy"), "set_sdfgi_energy", "get_sdfgi_energy"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_normal_bias"), "set_sdfgi_normal_bias", "get_sdfgi_normal_bias"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_probe_bias"), "set_sdfgi_probe_bias", "get_sdfgi_probe_bias"); + // DynamicGI + + ClassDB::bind_method(D_METHOD("set_dynamic_gi_enabled", "enabled"), &Environment::set_dynamic_gi_enabled); + ClassDB::bind_method(D_METHOD("is_dynamic_gi_enabled"), &Environment::is_dynamic_gi_enabled); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_cascades", "amount"), &Environment::set_dynamic_gi_cascades); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_cascades"), &Environment::get_dynamic_gi_cascades); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_min_cell_size", "size"), &Environment::set_dynamic_gi_min_cell_size); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_min_cell_size"), &Environment::get_dynamic_gi_min_cell_size); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_max_distance", "distance"), &Environment::set_dynamic_gi_max_distance); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_max_distance"), &Environment::get_dynamic_gi_max_distance); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_cascade0_distance", "distance"), &Environment::set_dynamic_gi_cascade0_distance); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_cascade0_distance"), &Environment::get_dynamic_gi_cascade0_distance); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_cascade_format", "scale"), &Environment::set_dynamic_gi_cascade_format); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_cascade_format"), &Environment::get_dynamic_gi_cascade_format); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_filter_probes", "enable"), &Environment::set_dynamic_gi_filter_probes); + ClassDB::bind_method(D_METHOD("is_dynamic_gi_filtering_probes"), &Environment::is_dynamic_gi_filtering_probes); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_bounce_feedback", "amount"), &Environment::set_dynamic_gi_bounce_feedback); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_bounce_feedback"), &Environment::get_dynamic_gi_bounce_feedback); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_read_sky_light", "enable"), &Environment::set_dynamic_gi_read_sky_light); + ClassDB::bind_method(D_METHOD("is_dynamic_gi_reading_sky_light"), &Environment::is_dynamic_gi_reading_sky_light); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_energy", "amount"), &Environment::set_dynamic_gi_energy); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_energy"), &Environment::get_dynamic_gi_energy); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_normal_bias", "bias"), &Environment::set_dynamic_gi_normal_bias); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_normal_bias"), &Environment::get_dynamic_gi_normal_bias); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_reflection_bias", "bias"), &Environment::set_dynamic_gi_reflection_bias); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_reflection_bias"), &Environment::get_dynamic_gi_reflection_bias); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_probe_bias", "bias"), &Environment::set_dynamic_gi_probe_bias); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_probe_bias"), &Environment::get_dynamic_gi_probe_bias); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_occlusion_bias", "bias"), &Environment::set_dynamic_gi_occlusion_bias); + ClassDB::bind_method(D_METHOD("get_dynamic_gi_occlusion_bias"), &Environment::get_dynamic_gi_occlusion_bias); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_filter_ambient", "enable"), &Environment::set_dynamic_gi_filter_ambient); + ClassDB::bind_method(D_METHOD("is_dynamic_gi_filtering_ambient"), &Environment::is_dynamic_gi_filtering_ambient); + ClassDB::bind_method(D_METHOD("set_dynamic_gi_filter_reflections", "enable"), &Environment::set_dynamic_gi_filter_reflections); + ClassDB::bind_method(D_METHOD("is_dynamic_gi_filtering_reflections"), &Environment::is_dynamic_gi_filtering_reflections); + + ADD_GROUP("DynamicGI", "dynamic_gi_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dynamic_gi_enabled"), "set_dynamic_gi_enabled", "is_dynamic_gi_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_gi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_dynamic_gi_cascades", "get_dynamic_gi_cascades"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_gi_cascade_format", PROPERTY_HINT_ENUM, "16x8x16,16x16x16"), "set_dynamic_gi_cascade_format", "get_dynamic_gi_cascade_format"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_dynamic_gi_min_cell_size", "get_dynamic_gi_min_cell_size"); + // Don't store the values of `dynamic_gi_cascade0_distance` and `dynamic_gi_max_distance` + // as they're derived from `dynamic_gi_min_cell_size`. + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_dynamic_gi_cascade0_distance", "get_dynamic_gi_cascade0_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_dynamic_gi_max_distance", "get_dynamic_gi_max_distance"); + + ADD_SUBGROUP("Light", "dynamic_gi_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dynamic_gi_read_sky_light"), "set_dynamic_gi_read_sky_light", "is_dynamic_gi_reading_sky_light"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_dynamic_gi_bounce_feedback", "get_dynamic_gi_bounce_feedback"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_energy"), "set_dynamic_gi_energy", "get_dynamic_gi_energy"); + + ADD_SUBGROUP("Filter", "dynamic_gi_filter_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dynamic_gi_filter_probes"), "set_dynamic_gi_filter_probes", "is_dynamic_gi_filtering_probes"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dynamic_gi_filter_ambient"), "set_dynamic_gi_filter_ambient", "is_dynamic_gi_filtering_ambient"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dynamic_gi_filter_reflection"), "set_dynamic_gi_filter_reflections", "is_dynamic_gi_filtering_reflections"); + + ADD_SUBGROUP("Bias", "dynamic_gi_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_normal_bias", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_dynamic_gi_normal_bias", "get_dynamic_gi_normal_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_reflection_bias", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_dynamic_gi_reflection_bias", "get_dynamic_gi_reflection_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_probe_bias", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_dynamic_gi_probe_bias", "get_dynamic_gi_probe_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_gi_occlusion_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_dynamic_gi_occlusion_bias", "get_dynamic_gi_occlusion_bias"); // Glow @@ -1595,9 +1657,9 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(FOG_MODE_EXPONENTIAL); BIND_ENUM_CONSTANT(FOG_MODE_DEPTH); - BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT); - BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT); - BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_100_PERCENT); + BIND_ENUM_CONSTANT(DYNAMIC_GI_CASCADE_FORMAT_16x8x16); + BIND_ENUM_CONSTANT(DYNAMIC_GI_CASCADE_FORMAT_16x16x16); + BIND_ENUM_CONSTANT(DYNAMIC_GI_CASCADE_FORMAT_MAX); } Environment::Environment() { @@ -1619,7 +1681,7 @@ Environment::Environment() { _update_ssr(); _update_ssao(); _update_ssil(); - _update_sdfgi(); + _update_dynamic_gi(); _update_glow(); _update_fog(); _update_adjustment(); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 9408e7d22311..a689b6845917 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -71,10 +71,10 @@ class Environment : public Resource { TONE_MAPPER_AGX_PUNCHY, }; - enum SDFGIYScale { - SDFGI_Y_SCALE_50_PERCENT, - SDFGI_Y_SCALE_75_PERCENT, - SDFGI_Y_SCALE_100_PERCENT, + enum DynamicGICascadeFormat { + DYNAMIC_GI_CASCADE_FORMAT_16x8x16, + DYNAMIC_GI_CASCADE_FORMAT_16x16x16, + DYNAMIC_GI_CASCADE_FORMAT_MAX, }; enum FogMode { @@ -148,18 +148,22 @@ class Environment : public Resource { void _update_ssil(); - // SDFGI - bool sdfgi_enabled = false; - int sdfgi_cascades = 4; - float sdfgi_min_cell_size = 0.2; - SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_75_PERCENT; - bool sdfgi_use_occlusion = false; - float sdfgi_bounce_feedback = 0.5; - bool sdfgi_read_sky_light = true; - float sdfgi_energy = 1.0; - float sdfgi_normal_bias = 1.1; - float sdfgi_probe_bias = 1.1; - void _update_sdfgi(); + // DynamicGI + bool dynamic_gi_enabled = false; + int dynamic_gi_cascades = 4; + float dynamic_gi_min_cell_size = 0.2; + DynamicGICascadeFormat dynamic_gi_cascade_format = DYNAMIC_GI_CASCADE_FORMAT_16x8x16; + bool dynamic_gi_filter_probes = true; + float dynamic_gi_bounce_feedback = 1.0; + bool dynamic_gi_read_sky_light = true; + float dynamic_gi_energy = 1.0; + float dynamic_gi_normal_bias = 1.1; + float dynamic_gi_reflection_bias = 2.0; + float dynamic_gi_probe_bias = 1.1; + float dynamic_gi_occlusion_bias = 0.1; + bool dynamic_gi_filter_ambient = true; + bool dynamic_gi_filter_reflections = false; + void _update_dynamic_gi(); // Glow bool glow_enabled = false; @@ -318,31 +322,39 @@ class Environment : public Resource { void set_ssil_normal_rejection(float p_normal_rejection); float get_ssil_normal_rejection() const; - // SDFGI - void set_sdfgi_enabled(bool p_enabled); - bool is_sdfgi_enabled() const; - void set_sdfgi_cascades(int p_cascades); - int get_sdfgi_cascades() const; - void set_sdfgi_min_cell_size(float p_size); - float get_sdfgi_min_cell_size() const; - void set_sdfgi_max_distance(float p_distance); - float get_sdfgi_max_distance() const; - void set_sdfgi_cascade0_distance(float p_distance); - float get_sdfgi_cascade0_distance() const; - void set_sdfgi_y_scale(SDFGIYScale p_y_scale); - SDFGIYScale get_sdfgi_y_scale() const; - void set_sdfgi_use_occlusion(bool p_enabled); - bool is_sdfgi_using_occlusion() const; - void set_sdfgi_bounce_feedback(float p_amount); - float get_sdfgi_bounce_feedback() const; - void set_sdfgi_read_sky_light(bool p_enabled); - bool is_sdfgi_reading_sky_light() const; - void set_sdfgi_energy(float p_energy); - float get_sdfgi_energy() const; - void set_sdfgi_normal_bias(float p_bias); - float get_sdfgi_normal_bias() const; - void set_sdfgi_probe_bias(float p_bias); - float get_sdfgi_probe_bias() const; + // DynamicGI + void set_dynamic_gi_enabled(bool p_enabled); + bool is_dynamic_gi_enabled() const; + void set_dynamic_gi_cascades(int p_cascades); + int get_dynamic_gi_cascades() const; + void set_dynamic_gi_min_cell_size(float p_size); + float get_dynamic_gi_min_cell_size() const; + void set_dynamic_gi_max_distance(float p_distance); + float get_dynamic_gi_max_distance() const; + void set_dynamic_gi_cascade0_distance(float p_distance); + float get_dynamic_gi_cascade0_distance() const; + void set_dynamic_gi_cascade_format(DynamicGICascadeFormat p_cascade_format); + DynamicGICascadeFormat get_dynamic_gi_cascade_format() const; + void set_dynamic_gi_filter_probes(bool p_enabled); + bool is_dynamic_gi_filtering_probes() const; + void set_dynamic_gi_bounce_feedback(float p_amount); + float get_dynamic_gi_bounce_feedback() const; + void set_dynamic_gi_read_sky_light(bool p_enabled); + bool is_dynamic_gi_reading_sky_light() const; + void set_dynamic_gi_energy(float p_energy); + float get_dynamic_gi_energy() const; + void set_dynamic_gi_normal_bias(float p_bias); + float get_dynamic_gi_normal_bias() const; + void set_dynamic_gi_reflection_bias(float p_bias); + float get_dynamic_gi_reflection_bias() const; + void set_dynamic_gi_probe_bias(float p_bias); + float get_dynamic_gi_probe_bias() const; + void set_dynamic_gi_occlusion_bias(float p_bias); + float get_dynamic_gi_occlusion_bias() const; + void set_dynamic_gi_filter_ambient(bool p_enabled); + bool is_dynamic_gi_filtering_ambient() const; + void set_dynamic_gi_filter_reflections(bool p_enabled); + bool is_dynamic_gi_filtering_reflections() const; // Glow void set_glow_enabled(bool p_enabled); @@ -452,7 +464,7 @@ VARIANT_ENUM_CAST(Environment::BGMode) VARIANT_ENUM_CAST(Environment::AmbientSource) VARIANT_ENUM_CAST(Environment::ReflectionSource) VARIANT_ENUM_CAST(Environment::ToneMapper) -VARIANT_ENUM_CAST(Environment::SDFGIYScale) +VARIANT_ENUM_CAST(Environment::DynamicGICascadeFormat) VARIANT_ENUM_CAST(Environment::GlowBlendMode) VARIANT_ENUM_CAST(Environment::FogMode) diff --git a/servers/rendering/dummy/environment/gi.h b/servers/rendering/dummy/environment/gi.h index 5d0e84ae4328..2b36f95a7f7a 100644 --- a/servers/rendering/dummy/environment/gi.h +++ b/servers/rendering/dummy/environment/gi.h @@ -79,7 +79,7 @@ class GI : public RendererGI { virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override { return 0; } - virtual void sdfgi_reset() override {} + virtual void hddagi_reset() override {} }; } // namespace RendererDummy diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 083493003f0b..934e395c28dd 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -94,12 +94,12 @@ class RasterizerSceneDummy : public RendererSceneRender { uint32_t geometry_instance_get_pair_mask() override { return 0; } - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - void sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(const Ref &p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { return 0; } + void hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int hddagi_get_pending_region_count(const Ref &p_render_buffers) const override { return 0; } + AABB hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { return AABB(); } + uint32_t hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { return 0; } /* SKY API */ @@ -120,9 +120,10 @@ class RasterizerSceneDummy : public RendererSceneRender { void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} - void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override {} - void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override {} - void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override {} + void environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) override {} + void environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) override {} + + void environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) override {} void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override {} void environment_set_volumetric_fog_filter_active(bool p_enable) override {} @@ -145,7 +146,7 @@ class RasterizerSceneDummy : public RendererSceneRender { void voxel_gi_set_quality(RS::VoxelGIQuality) override {} - void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_info = nullptr) override {} + void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data = nullptr, RenderingMethod::RenderInfo *r_info = nullptr) override {} void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) override {} @@ -182,7 +183,7 @@ class RasterizerSceneDummy : public RendererSceneRender { } } void update() override {} - void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override {} + void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override {} virtual void decals_set_filter(RS::DecalFilter p_filter) override {} virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {} diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index c3b63cdbf63f..a4e27306d240 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -79,7 +79,7 @@ class LightStorage : public RendererLightStorage { virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override {} virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override {} virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override {} - virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {} + virtual void light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) override {} virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override {} @@ -101,7 +101,7 @@ class LightStorage : public RendererLightStorage { virtual Color light_get_color(RID p_light) override { return Color(); } virtual bool light_get_reverse_cull_face_mode(RID p_light) const override { return false; } virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; } - virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } + virtual uint32_t light_get_max_hddagi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override { return 0; } virtual uint32_t light_get_cull_mask(RID p_light) const override { return 0; } diff --git a/servers/rendering/environment/renderer_gi.h b/servers/rendering/environment/renderer_gi.h index 94e2c1afda52..827cd1eb50c4 100644 --- a/servers/rendering/environment/renderer_gi.h +++ b/servers/rendering/environment/renderer_gi.h @@ -80,7 +80,7 @@ class RendererGI { virtual uint32_t voxel_gi_get_version(RID p_probe) const = 0; - virtual void sdfgi_reset() = 0; + virtual void hddagi_reset() = 0; }; #endif // RENDERER_GI_H diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 5eaec2315312..0f5db1e51941 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -277,7 +277,7 @@ ALBEDO = vec3(1.0); } Vector volumetric_fog_modes; volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n"); - volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n#define ENABLE_SDFGI\n"); + volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n#define ENABLE_HDDAGI\n"); volumetric_fog_modes.push_back("\n#define MODE_FILTER\n"); volumetric_fog_modes.push_back("\n#define MODE_FOG\n"); volumetric_fog_modes.push_back("\n#define MODE_COPY\n"); @@ -489,8 +489,8 @@ Fog::VolumetricFog::~VolumetricFog() { sync_gi_dependent_sets_validity(true); - if (sdfgi_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sdfgi_uniform_set)) { - RD::get_singleton()->free(sdfgi_uniform_set); + if (hddagi_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(hddagi_uniform_set)) { + RD::get_singleton()->free(hddagi_uniform_set); } if (sky_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_uniform_set)) { RD::get_singleton()->free(sky_uniform_set); @@ -951,17 +951,17 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P } } - bool using_sdfgi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_enabled(p_settings.env) && (p_settings.sdfgi.is_valid()); + bool using_hddagi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_hddagi_enabled(p_settings.env) && (p_settings.hddagi.is_valid()); - if (using_sdfgi) { - if (fog->sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->sdfgi_uniform_set)) { + if (using_hddagi) { + if (fog->hddagi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->hddagi_uniform_set)) { Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 0; - u.append_id(p_settings.gi->sdfgi_ubo); + u.append_id(p_settings.gi->hddagi_ubo); uniforms.push_back(u); } @@ -969,7 +969,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; - u.append_id(p_settings.sdfgi->ambient_texture); + u.append_id(p_settings.hddagi->lightprobe_ambient_tex); uniforms.push_back(u); } @@ -977,11 +977,13 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; - u.append_id(p_settings.sdfgi->occlusion_texture); + Vector occ = p_settings.hddagi->get_lightprobe_occlusion_textures(); + u.append_id(occ[0]); + u.append_id(occ[1]); uniforms.push_back(u); } - fog->sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI), 1); + fog->hddagi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_HDDAGI), 1); } } @@ -1089,12 +1091,12 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[using_sdfgi ? VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI : VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY]); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[using_hddagi ? VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_HDDAGI : VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set_density, 0); - if (using_sdfgi) { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->sdfgi_uniform_set, 1); + if (using_hddagi) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->hddagi_uniform_set, 1); } RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth); RD::get_singleton()->compute_list_add_barrier(compute_list); diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h index 25fb190f444c..f37b58124b83 100644 --- a/servers/rendering/renderer_rd/environment/fog.h +++ b/servers/rendering/renderer_rd/environment/fog.h @@ -129,7 +129,7 @@ class Fog : public RendererFog { enum { VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY, - VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI, + VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_HDDAGI, VOLUMETRIC_FOG_PROCESS_SHADER_FILTER, VOLUMETRIC_FOG_PROCESS_SHADER_FOG, VOLUMETRIC_FOG_PROCESS_SHADER_COPY, @@ -311,7 +311,7 @@ class Fog : public RendererFog { RID process_uniform_set2; } gi_dependent_sets; - RID sdfgi_uniform_set; + RID hddagi_uniform_set; RID sky_uniform_set; int last_shadow_filter = -1; @@ -346,7 +346,7 @@ class Fog : public RendererFog { Ref vfog; ClusterBuilderRD *cluster_builder; GI *gi; - Ref sdfgi; + Ref hddagi; Ref rbgi; RID env; SkyRD *sky; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 12ff28d7b054..d3c1be7070a3 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -36,11 +36,15 @@ #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" +#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" #include "servers/rendering/rendering_server_default.h" +// Debug recreating everything every frame. +//#define DIRTY_ALL_FRAMES + using namespace RendererRD; -const Vector3i GI::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF); +const Vector3i GI::HDDAGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF); GI *GI::singleton = nullptr; @@ -112,75 +116,6 @@ void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xfo voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView(), s); RD::get_singleton()->set_resource_name(voxel_gi->sdf_texture, "VoxelGI SDF Texture"); } -#if 0 - { - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R8_UNORM; - tf.width = voxel_gi->octree_size.x; - tf.height = voxel_gi->octree_size.y; - tf.depth = voxel_gi->octree_size.z; - tf.type = RD::TEXTURE_TYPE_3D; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM); - tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT); - voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - RD::get_singleton()->set_resource_name(voxel_gi->sdf_texture, "VoxelGI SDF Texture"); - } - RID shared_tex; - { - RD::TextureView tv; - tv.format_override = RD::DATA_FORMAT_R8_UINT; - shared_tex = RD::get_singleton()->texture_create_shared(tv, voxel_gi->sdf_texture); - } - //update SDF texture - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 1; - u.append_id(voxel_gi->octree_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 2; - u.append_id(voxel_gi->data_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 3; - u.append_id(shared_tex); - uniforms.push_back(u); - } - - RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, voxel_gi_sdf_shader_version_shader, 0); - - { - uint32_t push_constant[4] = { 0, 0, 0, 0 }; - - for (int i = 0; i < voxel_gi->level_counts.size() - 1; i++) { - push_constant[0] += voxel_gi->level_counts[i]; - } - push_constant[1] = push_constant[0] + voxel_gi->level_counts[voxel_gi->level_counts.size() - 1]; - - print_line("offset: " + itos(push_constant[0])); - print_line("size: " + itos(push_constant[1])); - //create SDF - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, voxel_gi_sdf_shader_pipeline); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4); - RD::get_singleton()->compute_list_dispatch(compute_list, voxel_gi->octree_size.x / 4, voxel_gi->octree_size.y / 4, voxel_gi->octree_size.z / 4); - RD::get_singleton()->compute_list_end(); - } - - RD::get_singleton()->free(uniform_set); - RD::get_singleton()->free(shared_tex); - } -#endif } voxel_gi->version++; @@ -392,12 +327,12 @@ Dependency *GI::voxel_gi_get_dependency(RID p_voxel_gi) const { return &voxel_gi->dependency; } -void GI::sdfgi_reset() { - sdfgi_current_version++; +void GI::hddagi_reset() { + hddagi_current_version++; } //////////////////////////////////////////////////////////////////////////////// -// SDFGI +// HDDAGI static RID create_clear_texture(const RD::TextureFormat &p_format, const String &p_name) { RID texture = RD::get_singleton()->texture_create(p_format, RD::TextureView()); @@ -409,770 +344,722 @@ static RID create_clear_texture(const RD::TextureFormat &p_format, const String return texture; } -void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); +void GI::HDDAGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi) { + //RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + //RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + + cascade_size.x = cascade_size.z = CASCADE_SIZE; + + cascade_format = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_cascade_format(p_env); + switch (cascade_format) { + case RS::ENV_HDDAGI_CASCADE_FORMAT_16x16x16: { + cascade_size.y = CASCADE_SIZE; + } break; + case RS::ENV_HDDAGI_CASCADE_FORMAT_16x8x16: { + cascade_size.y = CASCADE_SIZE / 2; + } break; + default: { + cascade_size.y = CASCADE_SIZE; + } break; + } gi = p_gi; - num_cascades = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_cascades(p_env); - min_cell_size = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_min_cell_size(p_env); - uses_occlusion = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_use_occlusion(p_env); - y_scale_mode = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_y_scale(p_env); - static const float y_scale[3] = { 2.0, 1.5, 1.0 }; - y_mult = y_scale[y_scale_mode]; - version = gi->sdfgi_current_version; + num_cascades = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_cascades(p_env); + min_cell_size = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_min_cell_size(p_env); + using_probe_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_probes(p_env); + using_reflection_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_reflection(p_env); + using_ambient_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_ambient(p_env); + reflection_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_reflection_bias(p_env); + probe_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_probe_bias(p_env); + occlusion_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_occlusion_bias(p_env); + normal_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_normal_bias(p_env); + frames_to_converge = p_requested_history_size; + //y_scale_mode = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_y_scale(p_env); + //static const float y_scale[3] = { 2.0, 1.5, 1.0 }; + //y_mult = y_scale[y_scale_mode]; + version = gi->hddagi_current_version; cascades.resize(num_cascades); - probe_axis_count = SDFGI::PROBE_DIVISOR + 1; - solid_cell_ratio = gi->sdfgi_solid_cell_ratio; - solid_cell_count = uint32_t(float(cascade_size * cascade_size * cascade_size) * solid_cell_ratio); + + solid_cell_ratio = gi->hddagi_solid_cell_ratio; + solid_cell_count = uint32_t(float(cascade_size.x * cascade_size.y * cascade_size.z) * solid_cell_ratio); float base_cell_size = min_cell_size; - RD::TextureFormat tf_sdf; - tf_sdf.format = RD::DATA_FORMAT_R8_UNORM; - tf_sdf.width = cascade_size; // Always 64x64 - tf_sdf.height = cascade_size; - tf_sdf.depth = cascade_size; - tf_sdf.texture_type = RD::TEXTURE_TYPE_3D; - tf_sdf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + RD::TextureFormat tf_base; + tf_base.format = RD::DATA_FORMAT_R8_UNORM; + tf_base.width = cascade_size.x; + tf_base.height = cascade_size.y; + tf_base.depth = cascade_size.z; + tf_base.texture_type = RD::TEXTURE_TYPE_3D; + tf_base.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; { - RD::TextureFormat tf_render = tf_sdf; - tf_render.format = RD::DATA_FORMAT_R16_UINT; - render_albedo = create_clear_texture(tf_render, "SDFGI Render Albedo"); + RD::TextureFormat tf_voxel = tf_base; + tf_voxel.format = RD::DATA_FORMAT_R32G32_UINT; // 4x4 region in a cache friendly format + tf_voxel.width /= 4; + tf_voxel.height /= 4; + tf_voxel.depth /= 4; + tf_voxel.texture_type = RD::TEXTURE_TYPE_3D; + tf_voxel.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - tf_render.format = RD::DATA_FORMAT_R32_UINT; - render_emission = create_clear_texture(tf_render, "SDFGI Render Emission"); - render_emission_aniso = create_clear_texture(tf_render, "SDFGI Render Emission Aniso"); + tf_voxel.height *= cascades.size(); + voxel_bits_tex = create_clear_texture(tf_voxel, "HDDAGI Voxel Field"); + } - tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize + { + RD::TextureFormat tf_voxel = tf_base; + tf_voxel.format = RD::DATA_FORMAT_R16_UINT; // 4x4 region in a cache friendly format + tf_voxel.width /= REGION_CELLS; + tf_voxel.height /= REGION_CELLS; + tf_voxel.depth /= REGION_CELLS; + tf_voxel.height *= cascades.size(); + tf_voxel.texture_type = RD::TEXTURE_TYPE_3D; + tf_voxel.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + region_version_data = create_clear_texture(tf_voxel, "HDDAGI Region Version"); + } - for (int i = 0; i < 8; i++) { - render_occlusion[i] = create_clear_texture(tf_render, String("SDFGI Render Occlusion ") + itos(i)); - } + { + RD::TextureFormat tf_disocc = tf_base; + tf_disocc.format = RD::DATA_FORMAT_R8_UINT; // 4x4 region in a cache friendly format + tf_disocc.texture_type = RD::TEXTURE_TYPE_3D; + tf_disocc.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf_disocc.height *= cascades.size(); + voxel_disocclusion_tex = create_clear_texture(tf_disocc, "HDDAGI Voxel Disocclusion"); + } - tf_render.format = RD::DATA_FORMAT_R32_UINT; - render_geom_facing = create_clear_texture(tf_render, "SDFGI Render Geometry Facing"); + { + RD::TextureFormat tf_voxel_region = tf_base; + tf_voxel_region.format = RD::DATA_FORMAT_R8_UINT; + tf_voxel_region.width /= REGION_CELLS; + tf_voxel_region.height /= REGION_CELLS; + tf_voxel_region.depth /= REGION_CELLS; + tf_voxel_region.texture_type = RD::TEXTURE_TYPE_3D; + tf_voxel_region.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + RD::TextureFormat tf_sdf = tf_base; - tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT; - render_sdf[0] = create_clear_texture(tf_render, "SDFGI Render SDF 0"); - render_sdf[1] = create_clear_texture(tf_render, "SDFGI Render SDF 1"); + tf_voxel_region.height *= cascades.size(); + voxel_region_tex = create_clear_texture(tf_voxel_region, "HDDAGI Voxel Regions"); + } - tf_render.width /= 2; - tf_render.height /= 2; - tf_render.depth /= 2; + { + { + RD::TextureFormat tf_light = tf_base; + tf_light.format = RD::DATA_FORMAT_R32_UINT; + tf_light.height *= cascades.size(); + tf_light.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + tf_light.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); - render_sdf_half[0] = create_clear_texture(tf_render, "SDFGI Render SDF Half 0"); - render_sdf_half[1] = create_clear_texture(tf_render, "SDFGI Render SDF Half 1"); - } + voxel_light_tex_data = create_clear_texture(tf_light, "HDDAGI Cascade Light Data"); - RD::TextureFormat tf_occlusion = tf_sdf; - tf_occlusion.format = RD::DATA_FORMAT_R16_UINT; - tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT); - tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16); - tf_occlusion.depth *= cascades.size(); //use depth for occlusion slices - tf_occlusion.width *= 2; //use width for the other half + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; + voxel_light_tex = RD::get_singleton()->texture_create_shared(tv, voxel_light_tex_data); + } - RD::TextureFormat tf_light = tf_sdf; - tf_light.format = RD::DATA_FORMAT_R32_UINT; - tf_light.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); - tf_light.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + { + RD::TextureFormat tf_neighbour = tf_base; + tf_neighbour.format = RD::DATA_FORMAT_R32_UINT; + tf_neighbour.height *= cascades.size(); - RD::TextureFormat tf_aniso0 = tf_sdf; - tf_aniso0.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - RD::TextureFormat tf_aniso1 = tf_sdf; - tf_aniso1.format = RD::DATA_FORMAT_R8G8_UNORM; + voxel_light_neighbour_data = create_clear_texture(tf_neighbour, "HDDAGI Cascade Light Neighbours"); + } - int passes = nearest_shift(cascade_size) - 1; + { // Albedo texture, this is anisotropic (x6). + RD::TextureFormat tf_render_albedo = tf_base; + tf_render_albedo.width /= 2; // Albedo is half size.. + tf_render_albedo.height /= 2; + tf_render_albedo.depth /= 2; + tf_render_albedo.depth *= 6; // ..but anisotropic. + tf_render_albedo.format = RD::DATA_FORMAT_R16_UINT; + render_albedo = create_clear_texture(tf_render_albedo, "HDDAGI Render Albedo"); + } - //store lightprobe SH - RD::TextureFormat tf_probes; - tf_probes.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - tf_probes.width = probe_axis_count * probe_axis_count; - tf_probes.height = probe_axis_count * SDFGI::SH_SIZE; - tf_probes.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - tf_probes.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + { // Emission texture, this is anisotropic but in a different way (main light, then x6 aniso weight) to save space. + RD::TextureFormat tf_render_emission = tf_base; + tf_render_emission.width /= 2; // Emission is half size.. + tf_render_emission.height /= 2; + tf_render_emission.depth /= 2; + tf_render_emission.format = RD::DATA_FORMAT_R32_UINT; + render_emission = create_clear_texture(tf_render_emission, "HDDAGI Render Emission"); + render_emission_aniso = create_clear_texture(tf_render_emission, "HDDAGI Render Emission Aniso"); + } - history_size = p_requested_history_size; + { // Aniso normals + RD::TextureFormat tf_render_aniso_normals = tf_base; + tf_render_aniso_normals.format = RD::DATA_FORMAT_R32_UINT; + render_aniso_normals = create_clear_texture(tf_render_aniso_normals, String("HDDAGI Render Solid Bits ")); + } + } - RD::TextureFormat tf_probe_history = tf_probes; - tf_probe_history.format = RD::DATA_FORMAT_R16G16B16A16_SINT; //signed integer because SH are signed - tf_probe_history.array_layers = history_size; + light_process_buffer_render = RD::get_singleton()->storage_buffer_create(sizeof(HDDAGI::Cascade::LightProcessCell) * solid_cell_count); + light_process_dispatch_buffer_render = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); - RD::TextureFormat tf_probe_average = tf_probes; - tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed - tf_probe_average.texture_type = RD::TEXTURE_TYPE_2D; + cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(HDDAGI::Cascade::UBO) * HDDAGI::MAX_CASCADES); - lightprobe_history_scroll = create_clear_texture(tf_probe_history, "SDFGI LightProbe History Scroll"); - lightprobe_average_scroll = create_clear_texture(tf_probe_average, "SDFGI LightProbe Average Scroll"); + // lightprobes + Vector3i PROBE_DIVISOR = cascade_size / REGION_CELLS; { - //octahedral lightprobes - RD::TextureFormat tf_octprobes = tf_probes; - tf_octprobes.array_layers = cascades.size() * 2; - tf_octprobes.format = RD::DATA_FORMAT_R32_UINT; //pack well with RGBE - tf_octprobes.width = probe_axis_count * probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2); - tf_octprobes.height = probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2); - tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); - tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); - //lightprobe texture is an octahedral texture - - lightprobe_data = create_clear_texture(tf_octprobes, "SDFGI LightProbe Data"); + RD::TextureFormat tf_lightprobes; + tf_lightprobes.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + tf_lightprobes.format = RD::DATA_FORMAT_R32_UINT; + tf_lightprobes.width = (PROBE_DIVISOR.x + 1) * (LIGHTPROBE_OCT_SIZE + 2); // Divisor +1 because need an extra one on the outside of the box for proper interpolation. This contains also the Z probes towards x+ + tf_lightprobes.height = (PROBE_DIVISOR.y + 1) * (PROBE_DIVISOR.z + 1) * (LIGHTPROBE_OCT_SIZE + 2); // OctSize +2 because it needs a border to interpolate properly + tf_lightprobes.depth = 1; + tf_lightprobes.array_layers = cascades.size(); + tf_lightprobes.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf_lightprobes.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); + tf_lightprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + + lightprobe_specular_data = create_clear_texture(tf_lightprobes, String("HDDAGI Lighprobe Specular")); + lightprobe_diffuse_data = create_clear_texture(tf_lightprobes, String("HDDAGI Lighprobe Diffuse")); + lightprobe_diffuse_filter_data = create_clear_texture(tf_lightprobes, String("HDDAGI Lighprobe Diffuse Filtered")); + RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; - lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, lightprobe_data); - - //texture handling ambient data, to integrate with volumetric foc - RD::TextureFormat tf_ambient = tf_probes; - tf_ambient.array_layers = cascades.size(); - tf_ambient.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; //pack well with RGBE - tf_ambient.width = probe_axis_count * probe_axis_count; - tf_ambient.height = probe_axis_count; - tf_ambient.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - //lightprobe texture is an octahedral texture - ambient_texture = create_clear_texture(tf_ambient, "SDFGI Ambient Texture"); - } + lightprobe_diffuse_tex = RD::get_singleton()->texture_create_shared(tv, lightprobe_diffuse_data); + lightprobe_diffuse_filter_tex = RD::get_singleton()->texture_create_shared(tv, lightprobe_diffuse_filter_data); + lightprobe_specular_tex = RD::get_singleton()->texture_create_shared(tv, lightprobe_specular_data); - cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES); + RD::TextureFormat tf_cache_data = tf_lightprobes; + tf_cache_data.format = RD::DATA_FORMAT_R32_UINT; + tf_cache_data.width = (PROBE_DIVISOR.x + 1) * LIGHTPROBE_OCT_SIZE; + tf_cache_data.height = (PROBE_DIVISOR.y + 1) * (PROBE_DIVISOR.z + 1) * LIGHTPROBE_OCT_SIZE; + tf_cache_data.array_layers *= frames_to_converge; + tf_cache_data.shareable_formats.clear(); + lightprobe_hit_cache_data = create_clear_texture(tf_cache_data, String("HDDAGI Lighprobe Hit Cache")); - occlusion_data = create_clear_texture(tf_occlusion, "SDFGI Occlusion Data"); - { - RD::TextureView tv; - tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16; - occlusion_texture = RD::get_singleton()->texture_create_shared(tv, occlusion_data); - } + tf_cache_data.format = RD::DATA_FORMAT_R16_UINT; + lightprobe_hit_cache_version_data = create_clear_texture(tf_cache_data, String("HDDAGI Lighprobe Hit Cache Version")); - for (SDFGI::Cascade &cascade : cascades) { - /* 3D Textures */ + { + tf_cache_data.format = RD::DATA_FORMAT_R32_UINT; - cascade.sdf_tex = create_clear_texture(tf_sdf, "SDFGI Cascade SDF Texture"); + lightprobe_moving_average_history = create_clear_texture(tf_cache_data, String("HDDAGI Lighprobe Moving Average History")); - cascade.light_data = create_clear_texture(tf_light, "SDFGI Cascade Light Data"); + RD::TextureFormat tf_moving_average = tf_cache_data; + tf_moving_average.width *= 3; // no RGB32 UI so.. + tf_moving_average.array_layers = cascades.size(); // Return to just cascades, no history. + lightprobe_moving_average = create_clear_texture(tf_moving_average, String("HDDAGI Lighprobe Moving Average")); + } - cascade.light_aniso_0_tex = create_clear_texture(tf_aniso0, "SDFGI Cascade Light Aniso 0 Texture"); - cascade.light_aniso_1_tex = create_clear_texture(tf_aniso1, "SDFGI Cascade Light Aniso 1 Texture"); + RD::TextureFormat tf_ambient = tf_lightprobes; + tf_ambient.width = (PROBE_DIVISOR.x + 1); + tf_ambient.height = (PROBE_DIVISOR.y + 1) * (PROBE_DIVISOR.z + 1); - { + lightprobe_ambient_data = create_clear_texture(tf_ambient, String("HDDAGI Lighprobe Ambient")); + lightprobe_ambient_tex = RD::get_singleton()->texture_create_shared(tv, lightprobe_ambient_data); + + RD::TextureFormat tf_neighbours; + tf_neighbours.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + tf_neighbours.format = RD::DATA_FORMAT_R32_UINT; + tf_neighbours.width = (PROBE_DIVISOR.x + 1); + tf_neighbours.height = (PROBE_DIVISOR.y + 1) * (PROBE_DIVISOR.z + 1); + tf_neighbours.depth = 1; + tf_neighbours.array_layers = cascades.size(); + tf_neighbours.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + lightprobe_neighbour_visibility_map = create_clear_texture(tf_neighbours, String("HDDAGI Neighbour Visibility Map")); + + RD::TextureFormat tf_geometry_proximity = tf_neighbours; + + tf_geometry_proximity.format = RD::DATA_FORMAT_R8_UNORM; + lightprobe_geometry_proximity_map = create_clear_texture(tf_geometry_proximity, String("HDDAGI Geometry Proximity Map")); + lightprobe_camera_visibility_map = create_clear_texture(tf_geometry_proximity, String("HDDAGI Camera Visibility Map")); + + for (uint32_t i = 0; i < cascades.size(); i++) { + for (int j = 0; j < 4; j++) { + lightprobe_camera_buffers.push_back(RD::get_singleton()->uniform_buffer_create(sizeof(HDDAGIShader::IntegrateCameraUBO))); + } + } + + RD::TextureFormat tf_process_frame = tf_neighbours; + tf_process_frame.format = RD::DATA_FORMAT_R32_UINT; + lightprobe_process_frame = create_clear_texture(tf_process_frame, String("HDDAGI Lightprobe Frame")); + } + + // Occlusion + + { + RD::TextureFormat tf_occlusion = tf_base; + tf_occlusion.format = RD::DATA_FORMAT_R16_UINT; + tf_occlusion.width += 2; // Extra border for proper blending. + tf_occlusion.height += 2; // Extra border for proper blending. + tf_occlusion.depth += 2; // Extra border for proper blending. + tf_occlusion.height *= cascades.size(); + tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16); + tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT); + tf_occlusion.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + + for (int i = 0; i < 2; i++) { + occlusion_data[i] = create_clear_texture(tf_occlusion, String("HDDAGI Occlusion ") + itos(i)); RD::TextureView tv; - tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; - cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data); + tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16; + occlusion_tex[i] = RD::get_singleton()->texture_create_shared(tv, occlusion_data[i]); } + } + + for (HDDAGI::Cascade &cascade : cascades) { + /* 3D Textures */ + + cascade.light_process_buffer = RD::get_singleton()->storage_buffer_create(sizeof(HDDAGI::Cascade::LightProcessCell) * solid_cell_count); + cascade.light_process_dispatch_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + cascade.light_process_dispatch_buffer_copy = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + + cascade.light_position_bufer = RD::get_singleton()->storage_buffer_create(sizeof(HDDAGIShader::Light) * MAX(HDDAGI::MAX_STATIC_LIGHTS, HDDAGI::MAX_DYNAMIC_LIGHTS)); cascade.cell_size = base_cell_size; Vector3 world_position = p_world_position; world_position.y *= y_mult; - int32_t probe_cells = cascade_size / SDFGI::PROBE_DIVISOR; - Vector3 probe_size = Vector3(1, 1, 1) * cascade.cell_size * probe_cells; + Vector3i probe_cells = cascade_size / REGION_CELLS; + Vector3 probe_size = Vector3(1, 1, 1) * cascade.cell_size * Vector3(probe_cells); Vector3i probe_pos = Vector3i((world_position / probe_size + Vector3(0.5, 0.5, 0.5)).floor()); cascade.position = probe_pos * probe_cells; - cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + cascade.dirty_regions = HDDAGI::Cascade::DIRTY_ALL; + + // lightprobes base_cell_size *= 2.0; + } - /* Probe History */ + bounce_feedback = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_bounce_feedback(p_env); + energy = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_energy(p_env); + normal_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_normal_bias(p_env); + reflection_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_reflection_bias(p_env); + probe_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_probe_bias(p_env); + occlusion_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_occlusion_bias(p_env); + reads_sky = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_read_sky_light(p_env); +} - cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.lightprobe_history_tex, "SDFGI Cascade LightProbe History Texture"); - RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work +void GI::HDDAGI::render_region(Ref p_render_buffers, int p_region, const PagedArray &p_instances, float p_exposure_normalization) { + //print_line("rendering region " + itos(p_region)); + ERR_FAIL_COND(p_render_buffers.is_null()); // we wouldn't be here if this failed but... + AABB bounds; + Vector3i from; + Vector3i size; + Vector3i scroll; + Vector3i region_ofs; - cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); - RD::get_singleton()->set_resource_name(cascade.lightprobe_average_tex, "SDFGI Cascade LightProbe Average Texture"); - RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work + int cascade = get_pending_region_data(p_region, from, size, bounds, scroll, region_ofs); + ERR_FAIL_COND(cascade < 0); - /* Buffers */ + //initialize render + //@TODO: Add a clear region to RenderingDevice to optimize this part + RD::get_singleton()->texture_clear(render_albedo, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->texture_clear(render_emission, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->texture_clear(render_emission_aniso, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->texture_clear(render_aniso_normals, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->buffer_clear(light_process_dispatch_buffer_render, 0, sizeof(uint32_t) * 4); - cascade.solid_cell_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGI::Cascade::SolidCell) * solid_cell_count); - cascade.solid_cell_dispatch_buffer_storage = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector()); - cascade.solid_cell_dispatch_buffer_call = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); - cascade.lights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGIShader::Light) * MAX(SDFGI::MAX_STATIC_LIGHTS, SDFGI::MAX_DYNAMIC_LIGHTS)); - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_sdf[(passes & 1) ? 1 : 0]); //if passes are even, we read from buffer 0, else we read from buffer 1 - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 3; - for (int j = 0; j < 8; j++) { - u.append_id(render_occlusion[j]); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 4; - u.append_id(render_emission); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 5; - u.append_id(render_emission_aniso); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 6; - u.append_id(render_geom_facing); - uniforms.push_back(u); - } + if (scroll == HDDAGI::Cascade::DIRTY_ALL) { + RD::get_singleton()->buffer_clear(light_process_buffer_render, 0, sizeof(HDDAGI::Cascade::LightProcessCell) * solid_cell_count); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 7; - u.append_id(cascade.sdf_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 8; - u.append_id(occlusion_data); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 10; - u.append_id(cascade.solid_cell_dispatch_buffer_storage); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 11; - u.append_id(cascade.solid_cell_buffer); - uniforms.push_back(u); - } + RD::get_singleton()->texture_clear(lightprobe_hit_cache_data, Color(0, 0, 0, 0), 0, 1, cascade * frames_to_converge, frames_to_converge); + RD::get_singleton()->texture_clear(lightprobe_hit_cache_version_data, Color(0, 0, 0, 0), 0, 1, cascade * frames_to_converge, frames_to_converge); + RD::get_singleton()->texture_clear(lightprobe_moving_average_history, Color(0, 0, 0, 0), 0, 1, cascade * frames_to_converge, frames_to_converge); + RD::get_singleton()->texture_clear(lightprobe_moving_average, Color(0, 0, 0, 0), 0, 1, cascade, 1); + RD::get_singleton()->texture_clear(lightprobe_specular_data, Color(0, 0, 0, 0), 0, 1, cascade, 1); + RD::get_singleton()->texture_clear(lightprobe_diffuse_data, Color(0, 0, 0, 0), 0, 1, cascade, 1); + RD::get_singleton()->texture_clear(lightprobe_process_frame, Color(0, 0, 0, 0), 0, 1, cascade, 1); + } - cascade.sdf_store_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_STORE), 0); - } + //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(cascades[cascade].cell_size)); - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_geom_facing); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 3; - u.append_id(render_emission); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 4; - u.append_id(render_emission_aniso); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 5; - u.append_id(cascade.solid_cell_dispatch_buffer_storage); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 6; - u.append_id(cascade.solid_cell_buffer); - uniforms.push_back(u); - } + RendererSceneRenderRD::get_singleton()->_render_hddagi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_aniso_normals, p_exposure_normalization); - cascade.scroll_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_SCROLL), 0); - } - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - for (int j = 0; j < 8; j++) { - u.append_id(render_occlusion[j]); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(occlusion_data); - uniforms.push_back(u); - } + RD::get_singleton()->draw_command_begin_label("HDDAGI Create Cascade SDF"); - cascade.scroll_occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_SCROLL_OCCLUSION), 0); - } - } + RENDER_TIMESTAMP("> HDDAGI Update SDF"); + //done rendering! must update SDF + //clear dispatch indirect data - //direct light - for (SDFGI::Cascade &cascade : cascades) { - Vector uniforms; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (j < cascades.size()) { - u.append_id(cascades[j].sdf_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } + HDDAGIShader::PreprocessPushConstant push_constant; + memset(&push_constant, 0, sizeof(HDDAGIShader::PreprocessPushConstant)); + + Vector3i dispatch_size; + //scroll + if (scroll != HDDAGI::Cascade::DIRTY_ALL) { + //for scroll + push_constant.scroll[0] = scroll.x; + push_constant.scroll[1] = scroll.y; + push_constant.scroll[2] = scroll.z; + + for (int i = 0; i < 3; i++) { + if (scroll[i] > 0) { + push_constant.offset[i] = 0; + push_constant.limit[i] = scroll[i]; + dispatch_size[i] = scroll[i]; + } else if (scroll[i] < 0) { + push_constant.offset[i] = cascade_size[i] + scroll[i]; + push_constant.limit[i] = cascade_size[i]; + dispatch_size[i] = -scroll[i]; + } else { + push_constant.offset[i] = 0; + push_constant.limit[i] = cascade_size[i]; + dispatch_size[i] = cascade_size[i]; } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.append_id(cascade.solid_cell_dispatch_buffer_storage); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.append_id(cascade.solid_cell_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 5; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.append_id(cascade.light_data); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 6; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.append_id(cascade.light_aniso_0_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 7; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.append_id(cascade.light_aniso_1_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 8; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.append_id(cascades_ubo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 9; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.append_id(cascade.lights_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 10; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(lightprobe_texture); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 11; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(occlusion_texture); - uniforms.push_back(u); } - cascade.sdf_direct_light_static_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.direct_light.version_get_shader(gi->sdfgi_shader.direct_light_shader, SDFGIShader::DIRECT_LIGHT_MODE_STATIC), 0); - cascade.sdf_direct_light_dynamic_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.direct_light.version_get_shader(gi->sdfgi_shader.direct_light_shader, SDFGIShader::DIRECT_LIGHT_MODE_DYNAMIC), 0); - } + } else { + //for no scroll + push_constant.scroll[0] = 0; + push_constant.scroll[1] = 0; + push_constant.scroll[2] = 0; - //preprocess initialize uniform set - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_sdf[0]); - uniforms.push_back(u); - } + push_constant.offset[0] = 0; + push_constant.offset[1] = 0; + push_constant.offset[2] = 0; + + push_constant.limit[0] = cascade_size[0]; + push_constant.limit[1] = cascade_size[1]; + push_constant.limit[2] = cascade_size[2]; - sdf_initialize_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE), 0); + dispatch_size.x = cascade_size[0]; + dispatch_size.y = cascade_size[1]; + dispatch_size.z = cascade_size[2]; } + push_constant.grid_size[0] = cascade_size[0]; + push_constant.grid_size[1] = cascade_size[1]; + push_constant.grid_size[2] = cascade_size[2]; - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_sdf_half[0]); - uniforms.push_back(u); - } + Vector3i probe_axis_count = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); - sdf_initialize_half_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF), 0); - } + push_constant.probe_axis_size[0] = probe_axis_count[0]; + push_constant.probe_axis_size[1] = probe_axis_count[1]; + push_constant.probe_axis_size[2] = probe_axis_count[2]; - //jump flood uniform set - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_sdf[0]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_sdf[1]); - uniforms.push_back(u); - } + push_constant.region_world_pos[0] = region_ofs[0]; + push_constant.region_world_pos[1] = region_ofs[1]; + push_constant.region_world_pos[2] = region_ofs[2]; - jump_flood_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD), 0); - RID aux0 = uniforms.write[0].get_id(0); - RID aux1 = uniforms.write[1].get_id(0); - uniforms.write[0].set_id(0, aux1); - uniforms.write[1].set_id(0, aux0); - jump_flood_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD), 0); - } - //jump flood half uniform set - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_sdf_half[0]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_sdf_half[1]); - uniforms.push_back(u); + if (cascade < int(cascades.size() - 1) && scroll != HDDAGI::Cascade::DIRTY_ALL) { + Vector3 upper_position = cascades[cascade + 1].position; + + // Get proper upper cascade position (before scroll) + for (int k = 0; k < 3; k++) { + if (cascades[cascade + 1].dirty_regions[k] != 0) { + upper_position[k] -= cascades[cascade + 1].dirty_regions[k]; + } } - jump_flood_half_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD), 0); - RID aux0 = uniforms.write[0].get_id(0); - RID aux1 = uniforms.write[1].get_id(0); - uniforms.write[0].set_id(0, aux1); - uniforms.write[1].set_id(0, aux0); - jump_flood_half_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD), 0); + push_constant.upper_region_world_pos[0] = upper_position[0] / REGION_CELLS; + push_constant.upper_region_world_pos[1] = upper_position[1] / REGION_CELLS; + push_constant.upper_region_world_pos[2] = upper_position[2] / REGION_CELLS; + + } else { + push_constant.upper_region_world_pos[0] = 0; + push_constant.upper_region_world_pos[1] = 0; + push_constant.upper_region_world_pos[2] = 0; } - //upscale half size sdf - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - u.append_id(render_sdf_half[(passes & 1) ? 0 : 1]); //reverse pass order because half size - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 3; - u.append_id(render_sdf[(passes & 1) ? 0 : 1]); //reverse pass order because it needs an extra JFA pass - uniforms.push_back(u); - } + push_constant.maximum_light_cells = solid_cell_count; + push_constant.cascade_count = cascades.size(); - upscale_jfa_uniform_set_index = (passes & 1) ? 0 : 1; - sdf_upscale_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE), 0); - } + push_constant.ray_hit_cache_frames = frames_to_converge; - //occlusion uniform set - { - Vector uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(render_albedo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 2; - for (int i = 0; i < 8; i++) { - u.append_id(render_occlusion[i]); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 3; - u.append_id(render_geom_facing); - uniforms.push_back(u); - } + static const uint32_t frames_to_update_table[RS::ENV_HDDAGI_INACTIVE_PROBE_MAX] = { + 1, 2, 4, 8 + }; - occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.preprocess.version_get_shader(gi->sdfgi_shader.preprocess_shader, SDFGIShader::PRE_PROCESS_OCCLUSION), 0); - } + push_constant.probe_update_frames = frames_to_update_table[gi->inactive_probe_frames]; - for (uint32_t i = 0; i < cascades.size(); i++) { - //integrate uniform + push_constant.cascade = cascade; + push_constant.occlusion_offset = 0; - Vector uniforms; + cascades[cascade].latest_version++; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (j < cascades.size()) { - u.append_id(cascades[j].sdf_tex); + push_constant.region_version = cascades[cascade].latest_version; + + //full size jumpflood + RENDER_TIMESTAMP("HDDAGI Scroll"); + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + if (scroll != HDDAGI::Cascade::DIRTY_ALL) { + // Scroll happened + + { // Light Scroll + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHT_SCROLL]); + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHT_SCROLL], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_light_tex_data), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 5, light_process_dispatch_buffer_render), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 6, light_process_buffer_render), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 7, cascades[cascade].light_process_dispatch_buffer_copy), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 8, cascades[cascade].light_process_buffer) + + ); + + HDDAGIShader::PreprocessPushConstant push_constant_scroll = push_constant; + + for (int i = 0; i < 3; i++) { + if (scroll[i] > 0) { + push_constant_scroll.limit[i] = cascade_size[i] - scroll[i]; + push_constant_scroll.offset[i] = 1; //+1 because one extra is rendered below for consistency with neighbouring voxels. + } else if (scroll[i] < 0) { + push_constant_scroll.limit[i] = cascade_size[i] - 1; // -1 because one extra is rendered below for consistency with neighbouring voxels. + push_constant_scroll.offset[i] = -scroll[i]; } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); + push_constant_scroll.limit[i] = cascade_size[i]; + push_constant_scroll.offset[i] = 0; } } - uniforms.push_back(u); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant_scroll, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascades[cascade].light_process_dispatch_buffer, 0); } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (j < cascades.size()) { - u.append_id(cascades[j].light_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); + + { // Probe Scroll + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_SCROLL]); + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_SCROLL], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, lightprobe_specular_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, lightprobe_diffuse_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, lightprobe_ambient_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, lightprobe_hit_cache_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 5, lightprobe_moving_average_history), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 6, lightprobe_moving_average), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 7, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 8, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED))); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + Vector3i dispatch_cells = dispatch_size / REGION_CELLS + Vector3i(1, 1, 1); + + HDDAGIShader::PreprocessPushConstant push_constant_scroll = push_constant; + + for (int i = 0; i < 3; i++) { + if (scroll[i] < 0) { + push_constant_scroll.offset[i] += REGION_CELLS; + dispatch_cells[i]--; + } else if (scroll[i] > 0) { + dispatch_cells[i]--; } } - uniforms.push_back(u); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant_scroll, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_cells.x, dispatch_cells.y, dispatch_cells.z); } - { - RD::Uniform u; - u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (j < cascades.size()) { - u.append_id(cascades[j].light_aniso_0_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + { // Occlusion + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_OCCLUSION]); + + for (int i = 0; i < 2; i++) { + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_OCCLUSION], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, render_aniso_normals), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, occlusion_data[i])); + + push_constant.occlusion_offset = (i - 1) * 4; // Turns out Z needs to be swapped. I have no idea why. If you figure it out, let me know. + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_size.x / REGION_CELLS, dispatch_size.y / REGION_CELLS, dispatch_size.z / REGION_CELLS); } - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (j < cascades.size()) { - u.append_id(cascades[j].light_aniso_1_tex); + } + + { // Region Store + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_REGION_STORE]); + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_REGION_STORE], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, render_aniso_normals), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, region_version_data)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, dispatch_size.x, dispatch_size.y, dispatch_size.z); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); // store needs another barrier + + RENDER_TIMESTAMP("HDDAGI Store SDF"); + + { + // Storing light happens last (after barrier) because it needs occlusion information. + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHT_STORE]); + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHT_STORE], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, render_albedo), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, render_emission), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, render_emission_aniso), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, render_aniso_normals), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 5, light_process_dispatch_buffer_render), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 6, light_process_buffer_render), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 7, lightprobe_neighbour_visibility_map), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 8, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 9, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 10, voxel_disocclusion_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 11, voxel_light_neighbour_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 12, voxel_light_tex_data)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + Vector3i store_size = dispatch_size; + + if (scroll != HDDAGI::Cascade::DIRTY_ALL) { + for (int i = 0; i < 3; i++) { + if (scroll[i] > 0) { + push_constant.offset[i] = 0; + push_constant.limit[i] = scroll[i] + 1; //extra voxel to properly store light + store_size[i] += 1; + } else if (scroll[i] < 0) { + push_constant.offset[i] = cascade_size[i] + scroll[i] - 1; + push_constant.limit[i] = cascade_size[i]; + store_size[i] += 1; } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); + push_constant.offset[i] = 0; + push_constant.limit[i] = cascade_size[i]; } } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 6; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 7; - u.append_id(cascades_ubo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 8; - u.append_id(lightprobe_data); - uniforms.push_back(u); - } + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, store_size.x, store_size.y, store_size.z); + // Store processed ones into cascade + SWAP(light_process_dispatch_buffer_render, cascades[cascade].light_process_dispatch_buffer); + SWAP(light_process_buffer_render, cascades[cascade].light_process_buffer); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 9; - u.append_id(cascades[i].lightprobe_history_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 10; - u.append_id(cascades[i].lightprobe_average_tex); - uniforms.push_back(u); - } + cascades[cascade].static_lights_dirty = true; + cascades[cascade].dynamic_lights_dirty = true; + } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 11; - u.append_id(lightprobe_history_scroll); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 12; - u.append_id(lightprobe_average_scroll); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 13; - RID parent_average; - if (cascades.size() == 1) { - // If there is only one SDFGI cascade, we can't use the previous cascade for blending. - parent_average = cascades[i].lightprobe_average_tex; - } else if (i < cascades.size() - 1) { - parent_average = cascades[i + 1].lightprobe_average_tex; - } else { - parent_average = cascades[i - 1].lightprobe_average_tex; //to use something, but it won't be used + { // Probe Neighbours (no barrier needed) + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_NEIGHBOURS]); + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_NEIGHBOURS], + 0, + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 1, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 2, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, lightprobe_neighbour_visibility_map) + + ); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + Vector3i dispatch_cells = dispatch_size / REGION_CELLS + Vector3i(1, 1, 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, dispatch_cells.x, dispatch_cells.y, dispatch_cells.z); + } + + { // Probe geometry proximity (no barrier needed) + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_GEOMETRY_PROXIMITY]); + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_GEOMETRY_PROXIMITY], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, lightprobe_geometry_proximity_map)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + Vector3i dispatch_cells = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, dispatch_cells.x, dispatch_cells.y, dispatch_cells.z); + } + + { // Probe frames + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.preprocess_pipeline[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_UPDATE_FRAMES]); + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.preprocess_shader_version[HDDAGIShader::PRE_PROCESS_LIGHTPROBE_UPDATE_FRAMES], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, lightprobe_process_frame)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + Vector3i dispatch_cells = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); + + HDDAGIShader::PreprocessPushConstant push_constant_scroll = push_constant; + + if (scroll != HDDAGI::Cascade::DIRTY_ALL) { + // Only edge if not all dirty + for (int i = 0; i < 3; i++) { + if (scroll[i] < 0) { + push_constant_scroll.offset[i] += REGION_CELLS; + dispatch_cells[i]--; + } else if (scroll[i] > 0) { + dispatch_cells[i]--; + } } - u.append_id(parent_average); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 14; - u.append_id(ambient_texture); - uniforms.push_back(u); } - cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.integrate.version_get_shader(gi->sdfgi_shader.integrate_shader, 0), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant_scroll, sizeof(HDDAGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_cells.x, dispatch_cells.y, dispatch_cells.z); } - bounce_feedback = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_bounce_feedback(p_env); - energy = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_energy(p_env); - normal_bias = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_normal_bias(p_env); - probe_bias = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_probe_bias(p_env); - reads_sky = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_read_sky_light(p_env); + RD::get_singleton()->compute_list_end(); + + RD::get_singleton()->buffer_copy(cascades[cascade].light_process_dispatch_buffer, cascades[cascade].light_process_dispatch_buffer_copy, 0, 0, sizeof(uint32_t) * 4); + + cascades[cascade].baked_exposure_normalization = p_exposure_normalization; + + RD::get_singleton()->draw_command_end_label(); + RENDER_TIMESTAMP("< HDDAGI Update SDF"); } -void GI::SDFGI::free_data() { - // we don't free things here, we handle SDFGI differently at the moment destructing the object when it needs to change. +void GI::HDDAGI::free_data() { + // we don't free things here, we handle HDDAGI differently at the moment destructing the object when it needs to change. } -GI::SDFGI::~SDFGI() { - for (const SDFGI::Cascade &c : cascades) { - RD::get_singleton()->free(c.light_data); - RD::get_singleton()->free(c.light_aniso_0_tex); - RD::get_singleton()->free(c.light_aniso_1_tex); - RD::get_singleton()->free(c.sdf_tex); - RD::get_singleton()->free(c.solid_cell_dispatch_buffer_storage); - RD::get_singleton()->free(c.solid_cell_dispatch_buffer_call); - RD::get_singleton()->free(c.solid_cell_buffer); - RD::get_singleton()->free(c.lightprobe_history_tex); - RD::get_singleton()->free(c.lightprobe_average_tex); - RD::get_singleton()->free(c.lights_buffer); +GI::HDDAGI::~HDDAGI() { + for (const HDDAGI::Cascade &c : cascades) { + RD::get_singleton()->free(c.light_process_buffer); + RD::get_singleton()->free(c.light_process_dispatch_buffer); + RD::get_singleton()->free(c.light_process_dispatch_buffer_copy); } RD::get_singleton()->free(render_albedo); + RD::get_singleton()->free(render_aniso_normals); RD::get_singleton()->free(render_emission); RD::get_singleton()->free(render_emission_aniso); - RD::get_singleton()->free(render_sdf[0]); - RD::get_singleton()->free(render_sdf[1]); + RD::get_singleton()->free(voxel_bits_tex); + RD::get_singleton()->free(voxel_region_tex); + RD::get_singleton()->free(voxel_light_tex_data); + RD::get_singleton()->free(voxel_light_neighbour_data); + RD::get_singleton()->free(region_version_data); - RD::get_singleton()->free(render_sdf_half[0]); - RD::get_singleton()->free(render_sdf_half[1]); + RD::get_singleton()->free(light_process_buffer_render); + RD::get_singleton()->free(light_process_dispatch_buffer_render); - for (int i = 0; i < 8; i++) { - RD::get_singleton()->free(render_occlusion[i]); - } - - RD::get_singleton()->free(render_geom_facing); + RD::get_singleton()->free(cascades_ubo); - RD::get_singleton()->free(lightprobe_data); - RD::get_singleton()->free(lightprobe_history_scroll); - RD::get_singleton()->free(lightprobe_average_scroll); - RD::get_singleton()->free(occlusion_data); - RD::get_singleton()->free(ambient_texture); + RD::get_singleton()->free(lightprobe_specular_data); + RD::get_singleton()->free(lightprobe_diffuse_data); + RD::get_singleton()->free(lightprobe_ambient_data); + RD::get_singleton()->free(lightprobe_diffuse_filter_data); + RD::get_singleton()->free(lightprobe_hit_cache_data); + RD::get_singleton()->free(lightprobe_hit_cache_version_data); - RD::get_singleton()->free(cascades_ubo); + RD::get_singleton()->free(lightprobe_moving_average); + RD::get_singleton()->free(lightprobe_moving_average_history); - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - if (RD::get_singleton()->uniform_set_is_valid(debug_uniform_set[v])) { - RD::get_singleton()->free(debug_uniform_set[v]); - } - debug_uniform_set[v] = RID(); + RD::get_singleton()->free(lightprobe_neighbour_visibility_map); + RD::get_singleton()->free(lightprobe_geometry_proximity_map); + RD::get_singleton()->free(lightprobe_camera_visibility_map); + for (int i = 0; i < lightprobe_camera_buffers.size(); i++) { + RD::get_singleton()->free(lightprobe_camera_buffers[i]); } + lightprobe_camera_buffers.clear(); + RD::get_singleton()->free(lightprobe_process_frame); - if (RD::get_singleton()->uniform_set_is_valid(debug_probes_uniform_set)) { - RD::get_singleton()->free(debug_probes_uniform_set); - } - debug_probes_uniform_set = RID(); + RD::get_singleton()->free(occlusion_data[0]); + RD::get_singleton()->free(occlusion_data[1]); if (debug_probes_scene_data_ubo.is_valid()) { RD::get_singleton()->free(debug_probes_scene_data_ubo); @@ -1180,19 +1067,25 @@ GI::SDFGI::~SDFGI() { } } -void GI::SDFGI::update(RID p_env, const Vector3 &p_world_position) { - bounce_feedback = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_bounce_feedback(p_env); - energy = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_energy(p_env); - normal_bias = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_normal_bias(p_env); - probe_bias = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_probe_bias(p_env); - reads_sky = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_read_sky_light(p_env); +void GI::HDDAGI::update(RID p_env, const Vector3 &p_world_position) { + bounce_feedback = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_bounce_feedback(p_env); + energy = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_energy(p_env); + reads_sky = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_read_sky_light(p_env); + using_probe_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_probes(p_env); + using_reflection_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_reflection(p_env); + using_ambient_filter = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_filter_ambient(p_env); + reflection_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_reflection_bias(p_env); + normal_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_normal_bias(p_env); + probe_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_probe_bias(p_env); + occlusion_bias = RendererSceneRenderRD::get_singleton()->environment_get_hddagi_occlusion_bias(p_env); - int32_t drag_margin = (cascade_size / SDFGI::PROBE_DIVISOR) / 2; + int32_t drag_margin = REGION_CELLS / 2; - for (SDFGI::Cascade &cascade : cascades) { + int idx = 0; + for (HDDAGI::Cascade &cascade : cascades) { cascade.dirty_regions = Vector3i(); - Vector3 probe_half_size = Vector3(1, 1, 1) * cascade.cell_size * float(cascade_size / SDFGI::PROBE_DIVISOR) * 0.5; + Vector3 probe_half_size = Vector3(1, 1, 1) * cascade.cell_size * float(REGION_CELLS) * 0.5; probe_half_size = Vector3(0, 0, 0); Vector3 world_position = p_world_position; @@ -1214,113 +1107,157 @@ void GI::SDFGI::update(RID p_env, const Vector3 &p_world_position) { if (cascade.dirty_regions[j] == 0) { continue; // not dirty - } else if (uint32_t(ABS(cascade.dirty_regions[j])) >= cascade_size) { + } else if (uint32_t(ABS(cascade.dirty_regions[j])) >= uint32_t(cascade_size[j])) { //moved too much, just redraw everything (make all dirty) - cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + cascade.dirty_regions = HDDAGI::Cascade::DIRTY_ALL; break; } } - - if (cascade.dirty_regions != Vector3i() && cascade.dirty_regions != SDFGI::Cascade::DIRTY_ALL) { +#ifdef DIRTY_ALL_FRAMES + // DEBUG + cascade.dirty_regions = HDDAGI::Cascade::DIRTY_ALL; + break; +#endif + if (cascade.dirty_regions != Vector3i() && cascade.dirty_regions != HDDAGI::Cascade::DIRTY_ALL) { //see how much the total dirty volume represents from the total volume - uint32_t total_volume = cascade_size * cascade_size * cascade_size; + uint32_t total_volume = cascade_size.x * cascade_size.y * cascade_size.z; uint32_t safe_volume = 1; for (int j = 0; j < 3; j++) { - safe_volume *= cascade_size - ABS(cascade.dirty_regions[j]); + safe_volume *= cascade_size[j] - ABS(cascade.dirty_regions[j]); } uint32_t dirty_volume = total_volume - safe_volume; if (dirty_volume > (safe_volume / 2)) { //more than half the volume is dirty, make all dirty so its only rendered once - cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + cascade.dirty_regions = HDDAGI::Cascade::DIRTY_ALL; } } - } -} -void GI::SDFGI::update_light() { - RD::get_singleton()->draw_command_begin_label("SDFGI Update dynamic Light"); + if (cascade.dirty_regions != Vector3i()) { + uint32_t dirty_mask = 0; + for (int j = 0; j < 3; j++) { + if (cascade.dirty_regions[j] != 0) { + dirty_mask |= (1 << j); + } + } + // Notify all cascades equal or smaller than this that some motion happened. + // In an axis, which is used for light ray cache invalidation. - for (uint32_t i = 0; i < cascades.size(); i++) { - RD::get_singleton()->buffer_copy(cascades[i].solid_cell_dispatch_buffer_storage, cascades[i].solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); + for (int j = idx; j >= 0; j--) { + cascades[j].motion_accum |= dirty_mask; + } + } + + idx++; } + update_frame++; +} + +void GI::HDDAGI::update_light() { + RD::get_singleton()->draw_command_begin_label("HDDAGI Update dynamic Light"); + /* Update dynamic light */ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.direct_light_pipeline[SDFGIShader::DIRECT_LIGHT_MODE_DYNAMIC]); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.direct_light_pipeline[HDDAGIShader::DIRECT_LIGHT_MODE_DYNAMIC]); - SDFGIShader::DirectLightPushConstant push_constant; + HDDAGIShader::DirectLightPushConstant push_constant; - push_constant.grid_size[0] = cascade_size; - push_constant.grid_size[1] = cascade_size; - push_constant.grid_size[2] = cascade_size; + push_constant.grid_size[0] = cascade_size[0]; + push_constant.grid_size[1] = cascade_size[1]; + push_constant.grid_size[2] = cascade_size[2]; push_constant.max_cascades = cascades.size(); - push_constant.probe_axis_size = probe_axis_count; + push_constant.probe_axis_size[0] = cascade_size[0] / REGION_CELLS + 1; + push_constant.probe_axis_size[1] = cascade_size[1] / REGION_CELLS + 1; + push_constant.probe_axis_size[2] = cascade_size[2] / REGION_CELLS + 1; + push_constant.bounce_feedback = bounce_feedback; push_constant.y_mult = y_mult; push_constant.use_occlusion = uses_occlusion; + push_constant.probe_cell_size = REGION_CELLS; for (uint32_t i = 0; i < cascades.size(); i++) { - SDFGI::Cascade &cascade = cascades[i]; + HDDAGI::Cascade &cascade = cascades[i]; push_constant.light_count = cascade_dynamic_light_count[i]; push_constant.cascade = i; + push_constant.dirty_dynamic_update = cascades[i].dynamic_lights_dirty; - if (cascades[i].all_dynamic_lights_dirty || gi->sdfgi_frames_to_update_light == RS::ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME) { + cascades[i].dynamic_lights_dirty = false; + + if (gi->hddagi_frames_to_update_light == RS::ENV_HDDAGI_UPDATE_LIGHT_IN_1_FRAME) { push_constant.process_offset = 0; push_constant.process_increment = 1; } else { - static const uint32_t frames_to_update_table[RS::ENV_SDFGI_UPDATE_LIGHT_MAX] = { + static const uint32_t frames_to_update_table[RS::ENV_HDDAGI_UPDATE_LIGHT_MAX] = { 1, 2, 4, 8, 16 }; - uint32_t frames_to_update = frames_to_update_table[gi->sdfgi_frames_to_update_light]; + uint32_t frames_to_update = frames_to_update_table[gi->hddagi_frames_to_update_light]; push_constant.process_offset = RSG::rasterizer->get_frame_number() % frames_to_update; push_constant.process_increment = frames_to_update; } - cascades[i].all_dynamic_lights_dirty = false; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_dynamic_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::DirectLightPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer_call, 0); + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.direct_light_shader_version[HDDAGIShader::DIRECT_LIGHT_MODE_DYNAMIC], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 3, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 4, cascade.light_process_dispatch_buffer_copy), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 5, cascade.light_process_buffer), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 6, voxel_light_tex_data), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 7, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 8, cascade.light_position_bufer), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 9, lightprobe_diffuse_tex)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::DirectLightPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.light_process_dispatch_buffer, 0); } + RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } -void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { - RD::get_singleton()->draw_command_begin_label("SDFGI Update Probes"); +void GI::HDDAGI::update_probes(RID p_env, SkyRD::Sky *p_sky, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform) { + RD::get_singleton()->draw_command_begin_label("HDDAGI Update Probes"); - SDFGIShader::IntegratePushConstant push_constant; - push_constant.grid_size[1] = cascade_size; - push_constant.grid_size[2] = cascade_size; - push_constant.grid_size[0] = cascade_size; + HDDAGIShader::IntegratePushConstant push_constant; + push_constant.grid_size[0] = cascade_size[0]; + push_constant.grid_size[1] = cascade_size[1]; + push_constant.grid_size[2] = cascade_size[2]; push_constant.max_cascades = cascades.size(); - push_constant.probe_axis_size = probe_axis_count; - push_constant.history_index = render_pass % history_size; - push_constant.history_size = history_size; - static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 }; - push_constant.ray_count = ray_count[gi->sdfgi_ray_count]; + push_constant.probe_axis_size[0] = cascade_size[0] / REGION_CELLS + 1; + push_constant.probe_axis_size[1] = cascade_size[1] / REGION_CELLS + 1; + push_constant.probe_axis_size[2] = cascade_size[2] / REGION_CELLS + 1; + + static const uint32_t frames_to_update_table[RS::ENV_HDDAGI_INACTIVE_PROBE_MAX] = { + 1, 2, 4, 8 + }; + push_constant.inactive_update_frames = frames_to_update_table[gi->inactive_probe_frames]; + push_constant.global_frame = RSG::rasterizer->get_frame_number(); + push_constant.history_size = frames_to_converge; push_constant.ray_bias = probe_bias; - push_constant.image_size[0] = probe_axis_count * probe_axis_count; - push_constant.image_size[1] = probe_axis_count; push_constant.store_ambient_texture = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_enabled(p_env); - - RID sky_uniform_set = gi->sdfgi_shader.integrate_default_sky_uniform_set; - push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_DISABLED; + push_constant.sky_mode = HDDAGIShader::IntegratePushConstant::SKY_MODE_DISABLED; push_constant.y_mult = y_mult; + RID integrate_sky_uniform_set; + int32_t probe_divisor = REGION_CELLS; + if (reads_sky && p_env.is_valid()) { push_constant.sky_energy = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy_multiplier(p_env); if (RendererSceneRenderRD::get_singleton()->environment_get_background(p_env) == RS::ENV_BG_CLEAR_COLOR) { - push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_COLOR; + push_constant.sky_mode = HDDAGIShader::IntegratePushConstant::SKY_MODE_COLOR; Color c = RSG::texture_storage->get_default_clear_color().srgb_to_linear(); push_constant.sky_color[0] = c.r; push_constant.sky_color[1] = c.g; push_constant.sky_color[2] = c.b; } else if (RendererSceneRenderRD::get_singleton()->environment_get_background(p_env) == RS::ENV_BG_COLOR) { - push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_COLOR; + push_constant.sky_mode = HDDAGIShader::IntegratePushConstant::SKY_MODE_COLOR; Color c = RendererSceneRenderRD::get_singleton()->environment_get_bg_color(p_env); push_constant.sky_color[0] = c.r; push_constant.sky_color[1] = c.g; @@ -1328,93 +1265,160 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { } else if (RendererSceneRenderRD::get_singleton()->environment_get_background(p_env) == RS::ENV_BG_SKY) { if (p_sky && p_sky->radiance.is_valid()) { - if (integrate_sky_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(integrate_sky_uniform_set)) { - Vector uniforms; - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 0; - u.append_id(p_sky->radiance); - uniforms.push_back(u); - } + integrate_sky_uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.integrate.version_get_shader(gi->hddagi_shader.integrate_shader, 0), + 1, + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 0, p_sky->radiance), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 1, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED))); - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 1; - u.append_id(RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } + push_constant.sky_mode = HDDAGIShader::IntegratePushConstant::SKY_MODE_SKY; + } + } + } - integrate_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.integrate.version_get_shader(gi->sdfgi_shader.integrate_shader, 0), 1); + { + RD::get_singleton()->texture_clear(lightprobe_camera_visibility_map, Color(0, 0, 0, 0), 0, 1, 0, cascades.size()); + + // Setup buffers first (must be done outside compute list). + for (uint32_t i = 0; i < cascades.size(); i++) { + Vector3 cascade_pos = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size; + float cascade_to_cell = 1.0 / cascades[i].cell_size; + Transform3D local_xform = p_cam_transform; + local_xform.origin -= cascade_pos; + local_xform.scale(Vector3(1, y_mult, 1) * cascade_to_cell); + + for (uint32_t j = 0; j < p_view_count; j++) { + Vector planes = p_projections[j].get_projection_planes(local_xform); + HDDAGIShader::IntegrateCameraUBO camera_ubo; + for (int k = 0; k < planes.size(); k++) { + Plane plane = planes[k]; + camera_ubo.planes[k * 4 + 0] = plane.normal.x; + camera_ubo.planes[k * 4 + 1] = plane.normal.y; + camera_ubo.planes[k * 4 + 2] = plane.normal.z; + camera_ubo.planes[k * 4 + 3] = plane.d; + } + Vector3 endpoints[8]; + p_projections[j].get_endpoints(local_xform, endpoints); + for (int k = 0; k < 8; k++) { + Vector3 p = endpoints[k]; + camera_ubo.points[k * 4 + 0] = p.x; + camera_ubo.points[k * 4 + 1] = p.y; + camera_ubo.points[k * 4 + 2] = p.z; + camera_ubo.points[k * 4 + 3] = 1; } - sky_uniform_set = integrate_sky_uniform_set; - push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_SKY; + int buffer_index = j * cascades.size() + i; + RD::get_singleton()->buffer_update(lightprobe_camera_buffers[buffer_index], 0, sizeof(HDDAGIShader::IntegrateCameraUBO), &camera_ubo); + } + } + + // Do visibility testing (all cascades and views in parallel). + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.integrate_pipeline[HDDAGIShader::INTEGRATE_MODE_CAMERA_VISIBILITY]); + + for (uint32_t i = 0; i < cascades.size(); i++) { + push_constant.cascade = i; + push_constant.world_offset[0] = cascades[i].position.x / probe_divisor; + push_constant.world_offset[1] = cascades[i].position.y / probe_divisor; + push_constant.world_offset[2] = cascades[i].position.z / probe_divisor; + + for (uint32_t j = 0; j < p_view_count; j++) { + int buffer_index = j * cascades.size() + i; + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.integrate_shader_version[HDDAGIShader::INTEGRATE_MODE_CAMERA_VISIBILITY], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, lightprobe_camera_visibility_map), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 2, lightprobe_camera_buffers[buffer_index])); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::IntegratePushConstant)); + Vector3i dispatch_threads = cascade_size / REGION_CELLS; + RD::get_singleton()->compute_list_dispatch_threads(compute_list, dispatch_threads.x, dispatch_threads.y, dispatch_threads.z); } } + RD::get_singleton()->compute_list_end(); + } + + if (integrate_sky_uniform_set.is_null()) { + integrate_sky_uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.integrate.version_get_shader(gi->hddagi_shader.integrate_shader, 0), + 1, + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 0, RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE)), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 1, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED))); } render_pass++; + RID integrate_unifom_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.integrate.version_get_shader(gi->hddagi_shader.integrate_shader, 0), + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 3, voxel_light_tex), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 4, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 5, lightprobe_specular_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 6, lightprobe_diffuse_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 7, lightprobe_ambient_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 8, lightprobe_hit_cache_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 9, lightprobe_hit_cache_version_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 10, region_version_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 11, lightprobe_moving_average_history), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 12, lightprobe_moving_average), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 13, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 14, lightprobe_process_frame), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 15, lightprobe_geometry_proximity_map), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 16, lightprobe_camera_visibility_map)); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_PROCESS]); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.integrate_pipeline[HDDAGIShader::INTEGRATE_MODE_PROCESS]); + + Vector3i probe_axis_count = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); - int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR; for (uint32_t i = 0; i < cascades.size(); i++) { push_constant.cascade = i; + push_constant.motion_accum = cascades[i].motion_accum; + cascades[i].motion_accum = 0; //clear after use. + push_constant.world_offset[0] = cascades[i].position.x / probe_divisor; push_constant.world_offset[1] = cascades[i].position.y / probe_divisor; push_constant.world_offset[2] = cascades[i].position.z / probe_divisor; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[i].integrate_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sky_uniform_set, 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, integrate_unifom_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, integrate_sky_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::IntegratePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count.x * LIGHTPROBE_OCT_SIZE, probe_axis_count.y * probe_axis_count.z * LIGHTPROBE_OCT_SIZE, 1); } - RD::get_singleton()->compute_list_end(); - RD::get_singleton()->draw_command_end_label(); -} + if (using_probe_filter) { + RD::get_singleton()->compute_list_add_barrier(compute_list); -void GI::SDFGI::store_probes() { - RD::get_singleton()->draw_command_begin_label("SDFGI Store Probes"); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.integrate_pipeline[HDDAGIShader::INTEGRATE_MODE_FILTER]); - SDFGIShader::IntegratePushConstant push_constant; - push_constant.grid_size[1] = cascade_size; - push_constant.grid_size[2] = cascade_size; - push_constant.grid_size[0] = cascade_size; - push_constant.max_cascades = cascades.size(); - push_constant.probe_axis_size = probe_axis_count; - push_constant.history_index = render_pass % history_size; - push_constant.history_size = history_size; - static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 4, 8, 16, 32, 64, 96, 128 }; - push_constant.ray_count = ray_count[gi->sdfgi_ray_count]; - push_constant.ray_bias = probe_bias; - push_constant.image_size[0] = probe_axis_count * probe_axis_count; - push_constant.image_size[1] = probe_axis_count; - push_constant.store_ambient_texture = false; - - push_constant.sky_mode = 0; - push_constant.y_mult = y_mult; + integrate_unifom_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.integrate_shader_version[HDDAGIShader::INTEGRATE_MODE_FILTER], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, lightprobe_diffuse_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, lightprobe_diffuse_filter_data), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 3, lightprobe_neighbour_visibility_map), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, lightprobe_geometry_proximity_map), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 5, lightprobe_camera_visibility_map)); - // Then store values into the lightprobe texture. Separating these steps has a small performance hit, but it allows for multiple bounces - RENDER_TIMESTAMP("Average SDFGI Probes"); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, integrate_unifom_set, 0); - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_STORE]); + for (uint32_t i = 0; i < cascades.size(); i++) { + push_constant.cascade = i; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::IntegratePushConstant)); - //convert to octahedral to store - push_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE; - push_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE; + push_constant.world_offset[0] = cascades[i].position.x / probe_divisor; + push_constant.world_offset[1] = cascades[i].position.y / probe_divisor; + push_constant.world_offset[2] = cascades[i].position.z / probe_divisor; - for (uint32_t i = 0; i < cascades.size(); i++) { - push_constant.cascade = i; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[i].integrate_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::IntegratePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count.x * LIGHTPROBE_OCT_SIZE, probe_axis_count.y * probe_axis_count.z * LIGHTPROBE_OCT_SIZE, 1); + } } RD::get_singleton()->compute_list_end(); @@ -1422,17 +1426,40 @@ void GI::SDFGI::store_probes() { RD::get_singleton()->draw_command_end_label(); } -int GI::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const { +void GI::HDDAGI::store_probes() { +} + +int GI::HDDAGI::get_pending_region_count() const { + int dirty_count = 0; + for (const RendererRD::GI::HDDAGI::Cascade &c : cascades) { + if (c.dirty_regions == RendererRD::GI::HDDAGI::Cascade::DIRTY_ALL) { + dirty_count++; + } else { + for (int j = 0; j < 3; j++) { + if (c.dirty_regions[j] != 0) { + dirty_count++; + } + } + } + } + + return dirty_count; +} + +int GI::HDDAGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds, Vector3i &r_scroll, Vector3i &r_region_world) const { + // higher cascades need to be processed first int dirty_count = 0; for (uint32_t i = 0; i < cascades.size(); i++) { - const SDFGI::Cascade &c = cascades[i]; + const HDDAGI::Cascade &c = cascades[i]; - if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) { + if (c.dirty_regions == HDDAGI::Cascade::DIRTY_ALL) { if (dirty_count == p_region) { r_local_offset = Vector3i(); - r_local_size = Vector3i(1, 1, 1) * cascade_size; + r_local_size = cascade_size; + r_scroll = HDDAGI::Cascade::DIRTY_ALL; + r_region_world = c.position / REGION_CELLS; - r_bounds.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + c.position)) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); + r_bounds.position = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + c.position)) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); return i; } @@ -1442,29 +1469,34 @@ int GI::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, V if (c.dirty_regions[j] != 0) { if (dirty_count == p_region) { Vector3i from = Vector3i(0, 0, 0); - Vector3i to = Vector3i(1, 1, 1) * cascade_size; + Vector3i to = cascade_size; + + r_scroll = Vector3i(); + r_scroll[j] = c.dirty_regions[j]; if (c.dirty_regions[j] > 0) { //fill from the beginning - to[j] = c.dirty_regions[j]; + to[j] = c.dirty_regions[j] + 2; // 2 extra voxels needed to rebuild light properly } else { //fill from the end - from[j] = to[j] + c.dirty_regions[j]; + from[j] = to[j] + c.dirty_regions[j] - 2; // 2 extra voxels needed to rebuild light properly } - for (int k = 0; k < j; k++) { - // "chip" away previous regions to avoid re-voxelizing the same thing - if (c.dirty_regions[k] > 0) { - from[k] += c.dirty_regions[k]; - } else if (c.dirty_regions[k] < 0) { - to[k] += c.dirty_regions[k]; + r_local_offset = from; + r_local_size = to - from; + + Vector3i cascade_position = c.position; + + // Remove next axes positions so we don't voxelize the wrong region + for (int k = j + 1; k < 3; k++) { + if (c.dirty_regions[k] != 0) { + cascade_position[k] += c.dirty_regions[k]; } } - r_local_offset = from; - r_local_size = to - from; + r_region_world = cascade_position / REGION_CELLS; - r_bounds.position = Vector3(from + Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + c.position) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); + r_bounds.position = Vector3(from + Vector3i(1, 1, 1) * -Vector3(cascade_size >> 1) + cascade_position) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / y_mult, 1); return i; @@ -1478,136 +1510,58 @@ int GI::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, V return -1; } -void GI::SDFGI::update_cascades() { +void GI::HDDAGI::update_cascades() { //update cascades - SDFGI::Cascade::UBO cascade_data[SDFGI::MAX_CASCADES]; - int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR; + HDDAGI::Cascade::UBO cascade_data[HDDAGI::MAX_CASCADES]; for (uint32_t i = 0; i < cascades.size(); i++) { - Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size; + Vector3 pos = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size; cascade_data[i].offset[0] = pos.x; cascade_data[i].offset[1] = pos.y; cascade_data[i].offset[2] = pos.z; cascade_data[i].to_cell = 1.0 / cascades[i].cell_size; - cascade_data[i].probe_offset[0] = cascades[i].position.x / probe_divisor; - cascade_data[i].probe_offset[1] = cascades[i].position.y / probe_divisor; - cascade_data[i].probe_offset[2] = cascades[i].position.z / probe_divisor; + cascade_data[i].region_world_offset[0] = cascades[i].position.x / REGION_CELLS; + cascade_data[i].region_world_offset[1] = cascades[i].position.y / REGION_CELLS; + cascade_data[i].region_world_offset[2] = cascades[i].position.z / REGION_CELLS; cascade_data[i].pad = 0; } - RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data); + RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(HDDAGI::Cascade::UBO) * HDDAGI::MAX_CASCADES, cascade_data); } -void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector &p_texture_views) { +void GI::HDDAGI::debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector &p_texture_views) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); for (uint32_t v = 0; v < p_view_count; v++) { - if (!debug_uniform_set[v].is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_uniform_set[v])) { - Vector uniforms; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { - if (i < cascades.size()) { - u.append_id(cascades[i].sdf_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { - if (i < cascades.size()) { - u.append_id(cascades[i].light_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { - if (i < cascades.size()) { - u.append_id(cascades[i].light_aniso_0_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { - if (i < cascades.size()) { - u.append_id(cascades[i].light_aniso_1_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 5; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(occlusion_texture); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 8; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 9; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.append_id(cascades_ubo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 10; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.append_id(p_texture_views[v]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 11; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(lightprobe_texture); - uniforms.push_back(u); - } - debug_uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_shader_version, 0); - } + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.debug_shader_version, + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 3, voxel_light_tex), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 4, material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 5, material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 6, lightprobe_diffuse_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 7, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 8, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 9, p_texture_views[v]), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 10, voxel_light_neighbour_data)); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.debug_pipeline); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, debug_uniform_set[v], 0); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.debug_pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); - SDFGIShader::DebugPushConstant push_constant; - push_constant.grid_size[0] = cascade_size; - push_constant.grid_size[1] = cascade_size; - push_constant.grid_size[2] = cascade_size; + HDDAGIShader::DebugPushConstant push_constant; + push_constant.grid_size[0] = cascade_size[0]; + push_constant.grid_size[1] = cascade_size[1]; + push_constant.grid_size[2] = cascade_size[2]; push_constant.max_cascades = cascades.size(); - push_constant.screen_size[0] = p_width; - push_constant.screen_size[1] = p_height; + push_constant.screen_size = p_width; + push_constant.screen_size |= p_height << 16; + push_constant.esm_strength = 1.0; push_constant.y_mult = y_mult; push_constant.z_near = -p_projections[v].get_z_near(); @@ -1617,6 +1571,7 @@ void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projection push_constant.cam_basis[i][j] = p_transform.basis.rows[j][i]; } } + push_constant.cam_origin[0] = p_transform.origin[0]; push_constant.cam_origin[1] = p_transform.origin[1]; push_constant.cam_origin[2] = p_transform.origin[2]; @@ -1629,7 +1584,7 @@ void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projection } } - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::DebugPushConstant)); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(HDDAGIShader::DebugPushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_width, p_height, 1); RD::get_singleton()->compute_list_end(); @@ -1639,26 +1594,24 @@ void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projection copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2i(Point2i(), rtsize), true, false, false, false, RID(), p_view_count > 1); } -void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - +void GI::HDDAGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { // setup scene data { - SDFGIShader::DebugProbesSceneData scene_data; + HDDAGIShader::DebugProbesSceneData scene_data; if (debug_probes_scene_data_ubo.is_null()) { - debug_probes_scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGIShader::DebugProbesSceneData)); + debug_probes_scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(HDDAGIShader::DebugProbesSceneData)); } for (uint32_t v = 0; v < p_view_count; v++) { RendererRD::MaterialStorage::store_camera(p_camera_with_transforms[v], scene_data.projection[v]); } - RD::get_singleton()->buffer_update(debug_probes_scene_data_ubo, 0, sizeof(SDFGIShader::DebugProbesSceneData), &scene_data); + RD::get_singleton()->buffer_update(debug_probes_scene_data_ubo, 0, sizeof(HDDAGIShader::DebugProbesSceneData), &scene_data); } // setup push constant - SDFGIShader::DebugProbesPushConstant push_constant; + HDDAGIShader::DebugProbesPushConstant push_constant; //gen spheres from strips uint32_t band_points = 16; @@ -1667,198 +1620,174 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con push_constant.band_mask = band_points - 2; push_constant.section_arc = Math_TAU / float(push_constant.sections_in_band); push_constant.y_mult = y_mult; + push_constant.oct_size = LIGHTPROBE_OCT_SIZE; + Vector3i probe_axis_count = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); uint32_t total_points = push_constant.sections_in_band * band_points; - uint32_t total_probes = probe_axis_count * probe_axis_count * probe_axis_count; + uint32_t total_probes = probe_axis_count.x * probe_axis_count.y * probe_axis_count.z; - push_constant.grid_size[0] = cascade_size; - push_constant.grid_size[1] = cascade_size; - push_constant.grid_size[2] = cascade_size; + push_constant.grid_size[0] = cascade_size[0]; + push_constant.grid_size[1] = cascade_size[1]; + push_constant.grid_size[2] = cascade_size[2]; push_constant.cascade = 0; - push_constant.probe_axis_size = probe_axis_count; + push_constant.probe_axis_size[0] = probe_axis_count[0]; + push_constant.probe_axis_size[1] = probe_axis_count[1]; + push_constant.probe_axis_size[2] = probe_axis_count[2]; - if (!debug_probes_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_probes_uniform_set)) { - Vector uniforms; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.append_id(cascades_ubo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(lightprobe_texture); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.append_id(occlusion_texture); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 5; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.append_id(debug_probes_scene_data_ubo); - uniforms.push_back(u); - } + HDDAGIShader::ProbeDebugMode mode = p_view_count > 1 ? HDDAGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : HDDAGIShader::PROBE_DEBUG_PROBES; - debug_probes_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_probes.version_get_shader(gi->sdfgi_shader.debug_probes_shader, 0), 0); - } + RID debug_probes_uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.debug_probes_shader_version[mode], + 0, + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 1, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 2, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 3, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 4, debug_probes_scene_data_ubo), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 5, lightprobe_diffuse_tex) - SDFGIShader::ProbeDebugMode mode = p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : SDFGIShader::PROBE_DEBUG_PROBES; + ); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); - RD::get_singleton()->draw_command_begin_label("Debug SDFGI"); + RD::get_singleton()->draw_command_begin_label("Debug HDDAGI"); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->hddagi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, debug_probes_uniform_set, 0); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant)); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(HDDAGIShader::DebugProbesPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, false, total_probes, total_points); - if (gi->sdfgi_debug_probe_dir != Vector3()) { + if (gi->hddagi_debug_probe_dir != Vector3()) { uint32_t cascade = 0; - Vector3 offset = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[cascade].position)) * cascades[cascade].cell_size * Vector3(1.0, 1.0 / y_mult, 1.0); - Vector3 probe_size = cascades[cascade].cell_size * (cascade_size / SDFGI::PROBE_DIVISOR) * Vector3(1.0, 1.0 / y_mult, 1.0); - Vector3 ray_from = gi->sdfgi_debug_probe_pos; - Vector3 ray_to = gi->sdfgi_debug_probe_pos + gi->sdfgi_debug_probe_dir * cascades[cascade].cell_size * Math::sqrt(3.0) * cascade_size; + Vector3 offset = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cascades[cascade].position)) * cascades[cascade].cell_size * Vector3(1.0, 1.0 / y_mult, 1.0); + Vector3 probe_size = cascades[cascade].cell_size * REGION_CELLS * Vector3(1.0, 1.0 / y_mult, 1.0); + Vector3 ray_from = gi->hddagi_debug_probe_pos; + Vector3 ray_to = gi->hddagi_debug_probe_pos + gi->hddagi_debug_probe_dir * cascades[cascade].cell_size * Math::sqrt(3.0) * cascade_size[0]; float sphere_radius = 0.2; float closest_dist = 1e20; - gi->sdfgi_debug_probe_enabled = false; + gi->hddagi_debug_probe_enabled = false; - Vector3i probe_from = cascades[cascade].position / (cascade_size / SDFGI::PROBE_DIVISOR); - for (int i = 0; i < (SDFGI::PROBE_DIVISOR + 1); i++) { - for (int j = 0; j < (SDFGI::PROBE_DIVISOR + 1); j++) { - for (int k = 0; k < (SDFGI::PROBE_DIVISOR + 1); k++) { + Vector3i probe_from = cascades[cascade].position / REGION_CELLS; + for (int i = 0; i < (cascade_size[0] / REGION_CELLS + 1); i++) { + for (int j = 0; j < (cascade_size[1] / REGION_CELLS + 1); j++) { + for (int k = 0; k < (cascade_size[2] / REGION_CELLS + 1); k++) { Vector3 pos = offset + probe_size * Vector3(i, j, k); Vector3 res; if (Geometry3D::segment_intersects_sphere(ray_from, ray_to, pos, sphere_radius, &res)) { float d = ray_from.distance_to(res); if (d < closest_dist) { closest_dist = d; - gi->sdfgi_debug_probe_enabled = true; - gi->sdfgi_debug_probe_index = probe_from + Vector3i(i, j, k); + gi->hddagi_debug_probe_enabled = true; + gi->hddagi_debug_probe_index = probe_from + Vector3i(i, j, k); } } } } } - gi->sdfgi_debug_probe_dir = Vector3(); + print_line("Pos: ", gi->hddagi_debug_probe_pos); + print_line("Dir: ", gi->hddagi_debug_probe_dir); + print_line("Select: ", gi->hddagi_debug_probe_index); + +#if 0 + + Vector3i offset3 = (gi->hddagi_debug_probe_index & probe_axis_count); + Vector2i offset2(offset3.x, offset3.y + probe_axis_count.y * offset3.z); + offset2 *= (OCCLUSION_OCT_SIZE + 2); + + Vector data = RD::get_singleton()->texture_get_data(occlusion_process, 0); + Ref image = Image::create_from_data((OCCLUSION_OCT_SIZE + 2) * probe_axis_count.x, ((OCCLUSION_OCT_SIZE + 2) * probe_axis_count.y * probe_axis_count.z), false, Image::FORMAT_R8, data); + image->crop_from_point(offset2.x, offset2.y, OCCLUSION_OCT_SIZE + 2, OCCLUSION_OCT_SIZE + 2); + image->save_png("occlusion_probe.png"); + +#endif + + gi->hddagi_debug_probe_dir = Vector3(); } - if (gi->sdfgi_debug_probe_enabled) { + if (gi->hddagi_debug_probe_enabled) { uint32_t cascade = 0; - uint32_t probe_cells = (cascade_size / SDFGI::PROBE_DIVISOR); - Vector3i probe_from = cascades[cascade].position / probe_cells; - Vector3i ofs = gi->sdfgi_debug_probe_index - probe_from; + Vector3i probe_cells = (cascade_size / HDDAGI::REGION_CELLS); + Vector3i probe_from = cascades[cascade].position / REGION_CELLS; + Vector3i ofs = gi->hddagi_debug_probe_index - probe_from; + + bool probe_valid = true; if (ofs.x < 0 || ofs.y < 0 || ofs.z < 0) { - return; + probe_valid = false; } - if (ofs.x > SDFGI::PROBE_DIVISOR || ofs.y > SDFGI::PROBE_DIVISOR || ofs.z > SDFGI::PROBE_DIVISOR) { - return; + if (ofs.x > probe_cells.x || ofs.y > probe_cells.y || ofs.z > probe_cells.z) { + probe_valid = false; } - uint32_t mult = (SDFGI::PROBE_DIVISOR + 1); - uint32_t index = ofs.z * mult * mult + ofs.y * mult + ofs.x; + if (probe_valid) { + Vector3i mult = probe_cells + Vector3i(1, 1, 1); + uint32_t index = ofs.z * mult.x * mult.y + ofs.y * mult.x + ofs.x; + + push_constant.probe_debug_index = index; - push_constant.probe_debug_index = index; + uint32_t cell_count = HDDAGI::REGION_CELLS * 2 * HDDAGI::REGION_CELLS * 2 * HDDAGI::REGION_CELLS * 2; - uint32_t cell_count = probe_cells * 2 * probe_cells * 2 * probe_cells * 2; + debug_probes_uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.debug_probes_shader_version[p_view_count > 1 ? HDDAGIShader::PROBE_DEBUG_OCCLUSION_MULTIVIEW : HDDAGIShader::PROBE_DEBUG_OCCLUSION], + 0, + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 1, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 2, get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 3, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 4, debug_probes_scene_data_ubo), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 5, lightprobe_diffuse_tex) - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_VISIBILITY_MULTIVIEW : SDFGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, debug_probes_uniform_set, 0); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, false, cell_count, total_points); + ); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->hddagi_shader.debug_probes_pipeline[p_view_count > 1 ? HDDAGIShader::PROBE_DEBUG_OCCLUSION_MULTIVIEW : HDDAGIShader::PROBE_DEBUG_OCCLUSION].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, debug_probes_uniform_set, 0); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(HDDAGIShader::DebugProbesPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, false, cell_count, total_points); + } } - RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_list_end(); + RD::get_singleton()->draw_command_end_label(); } -void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data) { - if (p_render_data->sdfgi_update_data == nullptr) { +void GI::HDDAGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data) { + if (p_render_data->hddagi_update_data == nullptr) { return; } - RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - /* Update general SDFGI Buffer */ - - SDFGIData sdfgi_data; - - sdfgi_data.grid_size[0] = cascade_size; - sdfgi_data.grid_size[1] = cascade_size; - sdfgi_data.grid_size[2] = cascade_size; - - sdfgi_data.max_cascades = cascades.size(); - sdfgi_data.probe_axis_size = probe_axis_count; - sdfgi_data.cascade_probe_size[0] = sdfgi_data.probe_axis_size - 1; //float version for performance - sdfgi_data.cascade_probe_size[1] = sdfgi_data.probe_axis_size - 1; - sdfgi_data.cascade_probe_size[2] = sdfgi_data.probe_axis_size - 1; - - float csize = cascade_size; - sdfgi_data.probe_to_uvw = 1.0 / float(sdfgi_data.cascade_probe_size[0]); - sdfgi_data.use_occlusion = uses_occlusion; - //sdfgi_data.energy = energy; - - sdfgi_data.y_mult = y_mult; - - float cascade_voxel_size = (csize / sdfgi_data.cascade_probe_size[0]); - float occlusion_clamp = (cascade_voxel_size - 0.5) / cascade_voxel_size; - sdfgi_data.occlusion_clamp[0] = occlusion_clamp; - sdfgi_data.occlusion_clamp[1] = occlusion_clamp; - sdfgi_data.occlusion_clamp[2] = occlusion_clamp; - sdfgi_data.normal_bias = (normal_bias / csize) * sdfgi_data.cascade_probe_size[0]; - - //vec2 tex_pixel_size = 1.0 / vec2(ivec2( (OCT_SIZE+2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE+2) * params.probe_axis_size ) ); - //vec3 probe_uv_offset = (ivec3(OCT_SIZE+2,OCT_SIZE+2,(OCT_SIZE+2) * params.probe_axis_size)) * tex_pixel_size.xyx; + RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); + /* Update general HDDAGI Buffer */ - uint32_t oct_size = SDFGI::LIGHTPROBE_OCT_SIZE; + HDDAGIData hddagi_data; - sdfgi_data.lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size * sdfgi_data.probe_axis_size); - sdfgi_data.lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size); - sdfgi_data.lightprobe_tex_pixel_size[2] = 1.0; + hddagi_data.grid_size[0] = cascade_size[0]; + hddagi_data.grid_size[1] = cascade_size[1]; + hddagi_data.grid_size[2] = cascade_size[2]; - sdfgi_data.energy = energy; + hddagi_data.max_cascades = cascades.size(); - sdfgi_data.lightprobe_uv_offset[0] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[0]; - sdfgi_data.lightprobe_uv_offset[1] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[1]; - sdfgi_data.lightprobe_uv_offset[2] = float((oct_size + 2) * sdfgi_data.probe_axis_size) * sdfgi_data.lightprobe_tex_pixel_size[0]; + hddagi_data.probe_axis_size[0] = cascade_size[0] / REGION_CELLS + 1; + hddagi_data.probe_axis_size[1] = cascade_size[1] / REGION_CELLS + 1; + hddagi_data.probe_axis_size[2] = cascade_size[2] / REGION_CELLS + 1; - sdfgi_data.occlusion_renormalize[0] = 0.5; - sdfgi_data.occlusion_renormalize[1] = 1.0; - sdfgi_data.occlusion_renormalize[2] = 1.0 / float(sdfgi_data.max_cascades); + hddagi_data.y_mult = y_mult; + hddagi_data.normal_bias = normal_bias; + hddagi_data.reflection_bias = reflection_bias; + hddagi_data.esm_strength = 1.0; - int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR; + hddagi_data.energy = energy; - for (uint32_t i = 0; i < sdfgi_data.max_cascades; i++) { - SDFGIData::ProbeCascadeData &c = sdfgi_data.cascades[i]; - Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size; + for (int32_t i = 0; i < hddagi_data.max_cascades; i++) { + HDDAGIData::ProbeCascadeData &c = hddagi_data.cascades[i]; + Vector3 pos = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cascades[i].position)) * cascades[i].cell_size; Vector3 cam_origin = p_transform.origin; cam_origin.y *= y_mult; pos -= cam_origin; //make pos local to camera, to reduce numerical error c.position[0] = pos.x; c.position[1] = pos.y; c.position[2] = pos.z; - c.to_probe = 1.0 / (float(cascade_size) * cascades[i].cell_size / float(probe_axis_count - 1)); + c.to_probe = 1.0 / (float(cascade_size[0]) * cascades[i].cell_size / float(hddagi_data.probe_axis_size[0] - 1)); - Vector3i probe_ofs = cascades[i].position / probe_divisor; - c.probe_world_offset[0] = probe_ofs.x; - c.probe_world_offset[1] = probe_ofs.y; - c.probe_world_offset[2] = probe_ofs.z; + c.region_world_offset[0] = cascades[i].position.x / REGION_CELLS; + c.region_world_offset[1] = cascades[i].position.y / REGION_CELLS; + c.region_world_offset[2] = cascades[i].position.z / REGION_CELLS; c.to_cell = 1.0 / cascades[i].cell_size; c.exposure_normalization = 1.0; @@ -1868,21 +1797,21 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } } - RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data); + RD::get_singleton()->buffer_update(gi->hddagi_ubo, 0, sizeof(HDDAGIData), &hddagi_data); - /* Update dynamic lights in SDFGI cascades */ + /* Update dynamic lights in HDDAGI cascades */ for (uint32_t i = 0; i < cascades.size(); i++) { - SDFGI::Cascade &cascade = cascades[i]; + HDDAGI::Cascade &cascade = cascades[i]; - SDFGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS]; + HDDAGIShader::Light lights[HDDAGI::MAX_DYNAMIC_LIGHTS]; uint32_t idx = 0; - for (uint32_t j = 0; j < (uint32_t)p_render_data->sdfgi_update_data->directional_lights->size(); j++) { - if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + for (uint32_t j = 0; j < (uint32_t)p_render_data->hddagi_update_data->directional_lights->size(); j++) { + if (idx == HDDAGI::MAX_DYNAMIC_LIGHTS) { break; } - RID light_instance = p_render_data->sdfgi_update_data->directional_lights->get(j); + RID light_instance = p_render_data->hddagi_update_data->directional_lights->get(j); ERR_CONTINUE(!light_storage->owns_light_instance(light_instance)); RID light = light_storage->light_instance_get_base_light(light_instance); @@ -1919,23 +1848,23 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } AABB cascade_aabb; - cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cascade.position)) * cascade.cell_size; + cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cascade.position)) * cascade.cell_size; cascade_aabb.size = Vector3(1, 1, 1) * cascade_size * cascade.cell_size; - for (uint32_t j = 0; j < p_render_data->sdfgi_update_data->positional_light_count; j++) { - if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + for (uint32_t j = 0; j < p_render_data->hddagi_update_data->positional_light_count; j++) { + if (idx == HDDAGI::MAX_DYNAMIC_LIGHTS) { break; } - RID light_instance = p_render_data->sdfgi_update_data->positional_light_instances[j]; + RID light_instance = p_render_data->hddagi_update_data->positional_light_instances[j]; ERR_CONTINUE(!light_storage->owns_light_instance(light_instance)); RID light = light_storage->light_instance_get_base_light(light_instance); AABB light_aabb = light_storage->light_instance_get_base_aabb(light_instance); Transform3D light_transform = light_storage->light_instance_get_base_transform(light_instance); - uint32_t max_sdfgi_cascade = RSG::light_storage->light_get_max_sdfgi_cascade(light); - if (i > max_sdfgi_cascade) { + uint32_t max_hddagi_cascade = RSG::light_storage->light_get_max_hddagi_cascade(light); + if (i > max_hddagi_cascade) { continue; } @@ -1990,401 +1919,40 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } if (idx > 0) { - RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDFGIShader::Light), lights); + RD::get_singleton()->buffer_update(cascade.light_position_bufer, 0, idx * sizeof(HDDAGIShader::Light), lights); } cascade_dynamic_light_count[i] = idx; } } -void GI::SDFGI::render_region(Ref p_render_buffers, int p_region, const PagedArray &p_instances, float p_exposure_normalization) { - //print_line("rendering region " + itos(p_region)); - ERR_FAIL_COND(p_render_buffers.is_null()); // we wouldn't be here if this failed but... - AABB bounds; - Vector3i from; - Vector3i size; - - int cascade_prev = get_pending_region_data(p_region - 1, from, size, bounds); - int cascade_next = get_pending_region_data(p_region + 1, from, size, bounds); - int cascade = get_pending_region_data(p_region, from, size, bounds); - ERR_FAIL_COND(cascade < 0); - - if (cascade_prev != cascade) { - //initialize render - RD::get_singleton()->texture_clear(render_albedo, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(render_emission, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(render_emission_aniso, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(render_geom_facing, Color(0, 0, 0, 0), 0, 1, 0, 1); - } - - //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(cascades[cascade].cell_size)); - RendererSceneRenderRD::get_singleton()->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing, p_exposure_normalization); - - if (cascade_next != cascade) { - RD::get_singleton()->draw_command_begin_label("SDFGI Pre-Process Cascade"); - - RENDER_TIMESTAMP("> SDFGI Update SDF"); - //done rendering! must update SDF - //clear dispatch indirect data - - SDFGIShader::PreprocessPushConstant push_constant; - memset(&push_constant, 0, sizeof(SDFGIShader::PreprocessPushConstant)); - - RENDER_TIMESTAMP("SDFGI Scroll SDF"); - - //scroll - if (cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { - //for scroll - Vector3i dirty = cascades[cascade].dirty_regions; - push_constant.scroll[0] = dirty.x; - push_constant.scroll[1] = dirty.y; - push_constant.scroll[2] = dirty.z; - } else { - //for no scroll - push_constant.scroll[0] = 0; - push_constant.scroll[1] = 0; - push_constant.scroll[2] = 0; - } - - cascades[cascade].all_dynamic_lights_dirty = true; - cascades[cascade].baked_exposure_normalization = p_exposure_normalization; - - push_constant.grid_size = cascade_size; - push_constant.cascade = cascade; - - if (cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { - RD::get_singleton()->buffer_copy(cascades[cascade].solid_cell_dispatch_buffer_storage, cascades[cascade].solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - - //must pre scroll existing data because not all is dirty - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_SCROLL]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].scroll_uniform_set, 0); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascades[cascade].solid_cell_dispatch_buffer_call, 0); - // no barrier do all together - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_SCROLL_OCCLUSION]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].scroll_occlusion_uniform_set, 0); - - Vector3i dirty = cascades[cascade].dirty_regions; - Vector3i groups; - groups.x = cascade_size - ABS(dirty.x); - groups.y = cascade_size - ABS(dirty.y); - groups.z = cascade_size - ABS(dirty.z); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, groups.x, groups.y, groups.z); - - //no barrier, continue together - - { - //scroll probes and their history also - - SDFGIShader::IntegratePushConstant ipush_constant; - ipush_constant.grid_size[1] = cascade_size; - ipush_constant.grid_size[2] = cascade_size; - ipush_constant.grid_size[0] = cascade_size; - ipush_constant.max_cascades = cascades.size(); - ipush_constant.probe_axis_size = probe_axis_count; - ipush_constant.history_index = 0; - ipush_constant.history_size = history_size; - ipush_constant.ray_count = 0; - ipush_constant.ray_bias = 0; - ipush_constant.sky_mode = 0; - ipush_constant.sky_energy = 0; - ipush_constant.sky_color[0] = 0; - ipush_constant.sky_color[1] = 0; - ipush_constant.sky_color[2] = 0; - ipush_constant.y_mult = y_mult; - ipush_constant.store_ambient_texture = false; - - ipush_constant.image_size[0] = probe_axis_count * probe_axis_count; - ipush_constant.image_size[1] = probe_axis_count; - - int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR; - ipush_constant.cascade = cascade; - ipush_constant.world_offset[0] = cascades[cascade].position.x / probe_divisor; - ipush_constant.world_offset[1] = cascades[cascade].position.y / probe_divisor; - ipush_constant.world_offset[2] = cascades[cascade].position.z / probe_divisor; - - ipush_constant.scroll[0] = dirty.x / probe_divisor; - ipush_constant.scroll[1] = dirty.y / probe_divisor; - ipush_constant.scroll[2] = dirty.z / probe_divisor; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_SCROLL]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDFGIShader::IntegratePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1); - - RD::get_singleton()->compute_list_add_barrier(compute_list); - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_SCROLL_STORE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDFGIShader::IntegratePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1); - - RD::get_singleton()->compute_list_add_barrier(compute_list); - - if (bounce_feedback > 0.0) { - //multibounce requires this to be stored so direct light can read from it - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_STORE]); - - //convert to octahedral to store - ipush_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE; - ipush_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE; - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].integrate_uniform_set, 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi->sdfgi_shader.integrate_default_sky_uniform_set, 1); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDFGIShader::IntegratePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1); - } - } - - //ok finally barrier - RD::get_singleton()->compute_list_end(); - } - - //clear dispatch indirect data - uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 }; - RD::get_singleton()->buffer_update(cascades[cascade].solid_cell_dispatch_buffer_storage, 0, sizeof(uint32_t) * 4, dispatch_indirct_data); - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - - bool half_size = true; //much faster, very little difference - static const int optimized_jf_group_size = 8; - - if (half_size) { - push_constant.grid_size >>= 1; - - uint32_t cascade_half_size = cascade_size >> 1; - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_initialize_half_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - - //must start with regular jumpflood - - push_constant.half_size = true; - { - RENDER_TIMESTAMP("SDFGI Jump Flood (Half-Size)"); - - uint32_t s = cascade_half_size; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD]); - - int jf_us = 0; - //start with regular jump flood for very coarse reads, as this is impossible to optimize - while (s > 1) { - s /= 2; - push_constant.step_size = s; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_half_uniform_set[jf_us], 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - jf_us = jf_us == 0 ? 1 : 0; - - if (cascade_half_size / (s / 2) >= optimized_jf_group_size) { - break; - } - } - - RENDER_TIMESTAMP("SDFGI Jump Flood Optimized (Half-Size)"); - - //continue with optimized jump flood for smaller reads - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); - while (s > 1) { - s /= 2; - push_constant.step_size = s; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_half_uniform_set[jf_us], 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - jf_us = jf_us == 0 ? 1 : 0; - } - } - - // restore grid size for last passes - push_constant.grid_size = cascade_size; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_upscale_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - - //run one pass of fullsize jumpflood to fix up half size artifacts - - push_constant.half_size = false; - push_constant.step_size = 1; - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[upscale_jfa_uniform_set_index], 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - - } else { - //full size jumpflood - RENDER_TIMESTAMP("SDFGI Jump Flood (Full-Size)"); - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdf_initialize_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - - RD::get_singleton()->compute_list_add_barrier(compute_list); - - push_constant.half_size = false; - { - uint32_t s = cascade_size; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD]); - - int jf_us = 0; - //start with regular jump flood for very coarse reads, as this is impossible to optimize - while (s > 1) { - s /= 2; - push_constant.step_size = s; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[jf_us], 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - jf_us = jf_us == 0 ? 1 : 0; - - if (cascade_size / (s / 2) >= optimized_jf_group_size) { - break; - } - } - - RENDER_TIMESTAMP("SDFGI Jump Flood Optimized (Full-Size)"); - - //continue with optimized jump flood for smaller reads - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); - while (s > 1) { - s /= 2; - push_constant.step_size = s; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, jump_flood_uniform_set[jf_us], 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - RD::get_singleton()->compute_list_add_barrier(compute_list); - jf_us = jf_us == 0 ? 1 : 0; - } - } - } - - RENDER_TIMESTAMP("SDFGI Occlusion"); - - // occlusion - { - uint32_t probe_size = cascade_size / SDFGI::PROBE_DIVISOR; - Vector3i probe_global_pos = cascades[cascade].position / probe_size; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_OCCLUSION]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, occlusion_uniform_set, 0); - for (int i = 0; i < 8; i++) { - //dispatch all at once for performance - Vector3i offset(i & 1, (i >> 1) & 1, (i >> 2) & 1); - - if ((probe_global_pos.x & 1) != 0) { - offset.x = (offset.x + 1) & 1; - } - if ((probe_global_pos.y & 1) != 0) { - offset.y = (offset.y + 1) & 1; - } - if ((probe_global_pos.z & 1) != 0) { - offset.z = (offset.z + 1) & 1; - } - push_constant.probe_offset[0] = offset.x; - push_constant.probe_offset[1] = offset.y; - push_constant.probe_offset[2] = offset.z; - push_constant.occlusion_index = i; - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - - Vector3i groups = Vector3i(probe_size + 1, probe_size + 1, probe_size + 1) - offset; //if offset, it's one less probe per axis to compute - RD::get_singleton()->compute_list_dispatch(compute_list, groups.x, groups.y, groups.z); - } - RD::get_singleton()->compute_list_add_barrier(compute_list); - } - - RENDER_TIMESTAMP("SDFGI Store"); - - // store - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_STORE]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].sdf_store_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); - - RD::get_singleton()->compute_list_end(); - - //clear these textures, as they will have previous garbage on next draw - RD::get_singleton()->texture_clear(cascades[cascade].light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(cascades[cascade].light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); - RD::get_singleton()->texture_clear(cascades[cascade].light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); - -#if 0 - Vector data = RD::get_singleton()->texture_get_data(cascades[cascade].sdf, 0); - Ref img; - img.instantiate(); - for (uint32_t i = 0; i < cascade_size; i++) { - Vector subarr = data.slice(128 * 128 * i, 128 * 128 * (i + 1)); - img->set_data(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr); - img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png"); - } - - //finalize render and update sdf -#endif - -#if 0 - Vector data = RD::get_singleton()->texture_get_data(render_albedo, 0); - Ref img; - img.instantiate(); - for (uint32_t i = 0; i < cascade_size; i++) { - Vector subarr = data.slice(128 * 128 * i * 2, 128 * 128 * (i + 1) * 2); - img->createcascade_size, cascade_size, false, Image::FORMAT_RGB565, subarr); - img->convert(Image::FORMAT_RGBA8); - img->save_png("res://cascade_" + itos(cascade) + "_" + itos(i) + ".png"); - } - - //finalize render and update sdf -#endif - - RENDER_TIMESTAMP("< SDFGI Update SDF"); - RD::get_singleton()->draw_command_end_label(); - } -} - -void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray *p_positional_light_cull_result) { +void GI::HDDAGI::render_static_lights(RenderDataRD *p_render_data, Ref p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray *p_positional_light_cull_result) { ERR_FAIL_COND(p_render_buffers.is_null()); // we wouldn't be here if this failed but... RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - RD::get_singleton()->draw_command_begin_label("SDFGI Render Static Lights"); + RD::get_singleton()->draw_command_begin_label("HDDAGI Render Static Lights"); update_cascades(); - SDFGIShader::Light lights[SDFGI::MAX_STATIC_LIGHTS]; - uint32_t light_count[SDFGI::MAX_STATIC_LIGHTS]; + HDDAGIShader::Light lights[HDDAGI::MAX_STATIC_LIGHTS]; + uint32_t light_count[HDDAGI::MAX_STATIC_LIGHTS]; for (uint32_t i = 0; i < p_cascade_count; i++) { ERR_CONTINUE(p_cascade_indices[i] >= cascades.size()); - SDFGI::Cascade &cc = cascades[p_cascade_indices[i]]; + HDDAGI::Cascade &cc = cascades[p_cascade_indices[i]]; { //fill light buffer AABB cascade_aabb; - cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(cascade_size >> 1) + cc.position)) * cc.cell_size; + cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -(cascade_size >> 1) + cc.position)) * cc.cell_size; cascade_aabb.size = Vector3(1, 1, 1) * cascade_size * cc.cell_size; int idx = 0; for (uint32_t j = 0; j < (uint32_t)p_positional_light_cull_result[i].size(); j++) { - if (idx == SDFGI::MAX_STATIC_LIGHTS) { + if (idx == HDDAGI::MAX_STATIC_LIGHTS) { break; } @@ -2395,8 +1963,8 @@ void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Reflight_instance_get_base_aabb(light_instance); Transform3D light_transform = light_storage->light_instance_get_base_transform(light_instance); - uint32_t max_sdfgi_cascade = RSG::light_storage->light_get_max_sdfgi_cascade(light); - if (p_cascade_indices[i] > max_sdfgi_cascade) { + uint32_t max_hddagi_cascade = RSG::light_storage->light_get_max_hddagi_cascade(light); + if (p_cascade_indices[i] > max_hddagi_cascade) { continue; } @@ -2453,37 +2021,33 @@ void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref 0) { - RD::get_singleton()->buffer_update(cc.lights_buffer, 0, idx * sizeof(SDFGIShader::Light), lights); + RD::get_singleton()->buffer_update(cc.light_position_bufer, 0, idx * sizeof(HDDAGIShader::Light), lights); } light_count[i] = idx; } } - for (uint32_t i = 0; i < p_cascade_count; i++) { - ERR_CONTINUE(p_cascade_indices[i] >= cascades.size()); - - SDFGI::Cascade &cc = cascades[p_cascade_indices[i]]; - if (light_count[i] > 0) { - RD::get_singleton()->buffer_copy(cc.solid_cell_dispatch_buffer_storage, cc.solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); - } - } - /* Static Lights */ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.direct_light_pipeline[SDFGIShader::DIRECT_LIGHT_MODE_STATIC]); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->hddagi_shader.direct_light_pipeline[HDDAGIShader::DIRECT_LIGHT_MODE_STATIC]); + + HDDAGIShader::DirectLightPushConstant dl_push_constant; - SDFGIShader::DirectLightPushConstant dl_push_constant; + Vector3i probe_axis_count = cascade_size / REGION_CELLS + Vector3i(1, 1, 1); - dl_push_constant.grid_size[0] = cascade_size; - dl_push_constant.grid_size[1] = cascade_size; - dl_push_constant.grid_size[2] = cascade_size; + dl_push_constant.grid_size[0] = cascade_size[0]; + dl_push_constant.grid_size[1] = cascade_size[1]; + dl_push_constant.grid_size[2] = cascade_size[2]; dl_push_constant.max_cascades = cascades.size(); - dl_push_constant.probe_axis_size = probe_axis_count; + dl_push_constant.probe_axis_size[0] = probe_axis_count[0]; + dl_push_constant.probe_axis_size[1] = probe_axis_count[1]; + dl_push_constant.probe_axis_size[2] = probe_axis_count[2]; dl_push_constant.bounce_feedback = 0.0; // this is static light, do not multibounce yet dl_push_constant.y_mult = y_mult; dl_push_constant.use_occlusion = uses_occlusion; + dl_push_constant.probe_cell_size = REGION_CELLS; //all must be processed dl_push_constant.process_offset = 0; @@ -2492,15 +2056,27 @@ void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref= cascades.size()); - SDFGI::Cascade &cc = cascades[p_cascade_indices[i]]; + HDDAGI::Cascade &cc = cascades[p_cascade_indices[i]]; dl_push_constant.light_count = light_count[i]; dl_push_constant.cascade = p_cascade_indices[i]; if (dl_push_constant.light_count > 0) { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cc.sdf_direct_light_static_uniform_set, 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(SDFGIShader::DirectLightPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer_call, 0); + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + gi->hddagi_shader.direct_light_shader_version[HDDAGIShader::DIRECT_LIGHT_MODE_STATIC], + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 3, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 4, cc.light_process_dispatch_buffer_copy), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 5, cc.light_process_buffer), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 6, voxel_light_tex_data), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 7, cascades_ubo), + RD::Uniform(RD::UNIFORM_TYPE_STORAGE_BUFFER, 8, cc.light_position_bufer)); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(HDDAGIShader::DirectLightPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.light_process_dispatch_buffer, 0); } } @@ -3351,9 +2927,8 @@ void GI::VoxelGIInstance::debug(RD::DrawListID p_draw_list, RID p_framebuffer, c GI::GI() { singleton = this; - sdfgi_ray_count = RS::EnvironmentSDFGIRayCount(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count")), 0, int32_t(RS::ENV_SDFGI_RAY_COUNT_MAX - 1))); - sdfgi_frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge")), 0, int32_t(RS::ENV_SDFGI_CONVERGE_MAX - 1))); - sdfgi_frames_to_update_light = RS::EnvironmentSDFGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_update_lights")), 0, int32_t(RS::ENV_SDFGI_UPDATE_LIGHT_MAX - 1))); + hddagi_frames_to_converge = RS::EnvironmentHDDAGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/hddagi/frames_to_converge")), 0, int32_t(RS::ENV_HDDAGI_CONVERGE_MAX - 1))); + hddagi_frames_to_update_light = RS::EnvironmentHDDAGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/hddagi/frames_to_update_lights")), 0, int32_t(RS::ENV_HDDAGI_UPDATE_LIGHT_MAX - 1))); } GI::~GI() { @@ -3361,9 +2936,6 @@ GI::~GI() { } void GI::init(SkyRD *p_sky) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - /* GI */ { @@ -3421,87 +2993,67 @@ void GI::init(SkyRD *p_sky) { { Vector preprocess_modes; - preprocess_modes.push_back("\n#define MODE_SCROLL\n"); - preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n"); - preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n"); - preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n"); - preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n"); - preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n"); - preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n"); + preprocess_modes.push_back("\n#define MODE_REGION_STORE\n"); + preprocess_modes.push_back("\n#define MODE_LIGHT_STORE\n"); + preprocess_modes.push_back("\n#define MODE_LIGHT_SCROLL\n"); preprocess_modes.push_back("\n#define MODE_OCCLUSION\n"); - preprocess_modes.push_back("\n#define MODE_STORE\n"); - String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n"; - sdfgi_shader.preprocess.initialize(preprocess_modes, defines); - sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create(); - for (int i = 0; i < SDFGIShader::PRE_PROCESS_MAX; i++) { - sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i)); + preprocess_modes.push_back("\n#define MODE_OCCLUSION_STORE\n"); + preprocess_modes.push_back("\n#define MODE_LIGHTPROBE_SCROLL\n"); + preprocess_modes.push_back("\n#define MODE_LIGHTPROBE_NEIGHBOURS\n"); + preprocess_modes.push_back("\n#define MODE_LIGHTPROBE_GEOMETRY_PROXIMITY\n"); + preprocess_modes.push_back("\n#define MODE_LIGHTPROBE_UPDATE_FRAMES\n"); + + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE_HALF " + itos(HDDAGI::OCCLUSION_OCT_SIZE / 2) + "\n#define OCCLUSION_SUBPIXELS " + itos(HDDAGI::OCCLUSION_SUBPIXELS) + "\n"; + + hddagi_shader.preprocess.initialize(preprocess_modes, defines); + hddagi_shader.preprocess_shader = hddagi_shader.preprocess.version_create(); + for (int i = 0; i < HDDAGIShader::PRE_PROCESS_MAX; i++) { + hddagi_shader.preprocess_shader_version[i] = hddagi_shader.preprocess.version_get_shader(hddagi_shader.preprocess_shader, i); + hddagi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(hddagi_shader.preprocess_shader_version[i]); } } { //calculate tables - String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n"; Vector direct_light_modes; direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n"); direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n"); - sdfgi_shader.direct_light.initialize(direct_light_modes, defines); - sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create(); - for (int i = 0; i < SDFGIShader::DIRECT_LIGHT_MODE_MAX; i++) { - sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i)); + hddagi_shader.direct_light.initialize(direct_light_modes, defines); + hddagi_shader.direct_light_shader = hddagi_shader.direct_light.version_create(); + for (int i = 0; i < HDDAGIShader::DIRECT_LIGHT_MODE_MAX; i++) { + hddagi_shader.direct_light_shader_version[i] = hddagi_shader.direct_light.version_get_shader(hddagi_shader.direct_light_shader, i); + hddagi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(hddagi_shader.direct_light_shader_version[i]); } } { //calculate tables - String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; - defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n"; + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n"; + + defines += "\n#define SH_SIZE " + itos(HDDAGI::SH_SIZE) + "\n"; if (p_sky->sky_use_cubemap_array) { defines += "\n#define USE_CUBEMAP_ARRAY\n"; } Vector integrate_modes; integrate_modes.push_back("\n#define MODE_PROCESS\n"); - integrate_modes.push_back("\n#define MODE_STORE\n"); - integrate_modes.push_back("\n#define MODE_SCROLL\n"); - integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n"); - sdfgi_shader.integrate.initialize(integrate_modes, defines); - sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create(); - - for (int i = 0; i < SDFGIShader::INTEGRATE_MODE_MAX; i++) { - sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i)); - } + integrate_modes.push_back("\n#define MODE_FILTER\n"); + integrate_modes.push_back("\n#define MODE_CAMERA_VISIBILITY\n"); + hddagi_shader.integrate.initialize(integrate_modes, defines); + hddagi_shader.integrate_shader = hddagi_shader.integrate.version_create(); - { - Vector uniforms; - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 0; - if (p_sky->sky_use_cubemap_array) { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_WHITE)); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE)); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 1; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - - sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1); + for (int i = 0; i < HDDAGIShader::INTEGRATE_MODE_MAX; i++) { + hddagi_shader.integrate_shader_version[i] = hddagi_shader.integrate.version_get_shader(hddagi_shader.integrate_shader, i); + hddagi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(hddagi_shader.integrate_shader_version[i]); } } //GK { //calculate tables - String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n"; if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { defines += "\n#define USE_VRS\n"; } @@ -3512,8 +3064,10 @@ void GI::init(SkyRD *p_sky) { Vector gi_modes; gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_VOXEL_GI - gi_modes.push_back("\n#define USE_SDFGI\n"); // MODE_SDFGI - gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_COMBINED + gi_modes.push_back("\n#define USE_HDDAGI\n"); // MODE_HDDAGI + gi_modes.push_back("\n#define USE_HDDAGI\n\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_COMBINED + gi_modes.push_back("\n#define USE_HDDAGI\n\n#define USE_AMBIENT_BLEND\n"); // MODE_HDDAGI + gi_modes.push_back("\n#define USE_HDDAGI\n\n#define USE_VOXEL_GI_INSTANCES\n\n#define USE_AMBIENT_BLEND\n"); // MODE_COMBINED shader.initialize(gi_modes, defines); shader_version = shader.version_create(); @@ -3547,31 +3101,67 @@ void GI::init(SkyRD *p_sky) { } } - sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGIData)); + hddagi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(HDDAGIData)); + } + { + String defines; + //calculate tables + if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { + defines += "\n#define USE_VRS\n"; + } + Vector filter_modes; + filter_modes.push_back("\n#define MODE_BILATERAL_FILTER\n"); + filter_modes.push_back("\n#define MODE_BILATERAL_FILTER\n#define HALF_SIZE\n"); + filter_shader.initialize(filter_modes, defines); + filter_shader_version = filter_shader.version_create(); + + Vector specialization_constants; + + { + RD::PipelineSpecializationConstant sc; + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sc.constant_id = 0; // FILTER_SHADER_SPECIALIZATION_HALF_RES + sc.bool_value = false; + specialization_constants.push_back(sc); + + sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; + sc.constant_id = 1; // FILTER_SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX + sc.bool_value = false; + specialization_constants.push_back(sc); + } + + for (int v = 0; v < FILTER_SHADER_SPECIALIZATION_VARIATIONS; v++) { + specialization_constants.ptrw()[0].bool_value = (v & FILTER_SHADER_SPECIALIZATION_HALF_RES) ? true : false; + specialization_constants.ptrw()[1].bool_value = (v & FILTER_SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX) ? true : false; + for (int i = 0; i < FILTER_MODE_MAX; i++) { + filter_pipelines[v][i] = RD::get_singleton()->compute_pipeline_create(filter_shader.version_get_shader(filter_shader_version, i), specialization_constants); + } + } } + { - String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n"; Vector debug_modes; debug_modes.push_back(""); - sdfgi_shader.debug.initialize(debug_modes, defines); - sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create(); - sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0); - sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version); + hddagi_shader.debug.initialize(debug_modes, defines); + hddagi_shader.debug_shader = hddagi_shader.debug.version_create(); + hddagi_shader.debug_shader_version = hddagi_shader.debug.version_get_shader(hddagi_shader.debug_shader, 0); + hddagi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(hddagi_shader.debug_shader_version); } { - String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + String defines = "\n#define LIGHTPROBE_OCT_SIZE " + itos(HDDAGI::LIGHTPROBE_OCT_SIZE) + "\n#define OCCLUSION_OCT_SIZE " + itos(HDDAGI::OCCLUSION_OCT_SIZE) + "\n#define OCCLUSION_SUBPIXELS " + itos(HDDAGI::OCCLUSION_SUBPIXELS) + "\n"; Vector versions; versions.push_back("\n#define MODE_PROBES\n"); versions.push_back("\n#define MODE_PROBES\n#define USE_MULTIVIEW\n"); - versions.push_back("\n#define MODE_VISIBILITY\n"); - versions.push_back("\n#define MODE_VISIBILITY\n#define USE_MULTIVIEW\n"); + versions.push_back("\n#define MODE_OCCLUSION\n"); + versions.push_back("\n#define MODE_OCCLUSION\n#define USE_MULTIVIEW\n"); - sdfgi_shader.debug_probes.initialize(versions, defines); + hddagi_shader.debug_probes.initialize(versions, defines); // TODO disable multiview versions if turned off - sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create(); + hddagi_shader.debug_probes_shader = hddagi_shader.debug_probes.version_create(); { RD::PipelineRasterizationState rs; @@ -3580,11 +3170,14 @@ void GI::init(SkyRD *p_sky) { ds.enable_depth_test = true; ds.enable_depth_write = true; ds.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL; - for (int i = 0; i < SDFGIShader::PROBE_DEBUG_MAX; i++) { + RD::PipelineColorBlendState cb = RD::PipelineColorBlendState::create_disabled(); + RD::RenderPrimitive rp = RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS; + + for (int i = 0; i < HDDAGIShader::PROBE_DEBUG_MAX; i++) { // TODO check if version is enabled - RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i); - sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + hddagi_shader.debug_probes_shader_version[i] = hddagi_shader.debug_probes.version_get_shader(hddagi_shader.debug_probes_shader, i); + hddagi_shader.debug_probes_pipeline[i].setup(hddagi_shader.debug_probes_shader_version[i], rp, rs, RD::PipelineMultisampleState(), ds, cb, 0); } } } @@ -3599,8 +3192,8 @@ void GI::free() { if (voxel_gi_lights_uniform.is_valid()) { RD::get_singleton()->free(voxel_gi_lights_uniform); } - if (sdfgi_ubo.is_valid()) { - RD::get_singleton()->free(sdfgi_ubo); + if (hddagi_ubo.is_valid()) { + RD::get_singleton()->free(hddagi_ubo); } if (voxel_gi_debug_shader_version.is_valid()) { @@ -3612,20 +3205,20 @@ void GI::free() { if (shader_version.is_valid()) { shader.version_free(shader_version); } - if (sdfgi_shader.debug_probes_shader.is_valid()) { - sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader); + if (hddagi_shader.debug_probes_shader.is_valid()) { + hddagi_shader.debug_probes.version_free(hddagi_shader.debug_probes_shader); } - if (sdfgi_shader.debug_shader.is_valid()) { - sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader); + if (hddagi_shader.debug_shader.is_valid()) { + hddagi_shader.debug.version_free(hddagi_shader.debug_shader); } - if (sdfgi_shader.direct_light_shader.is_valid()) { - sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader); + if (hddagi_shader.direct_light_shader.is_valid()) { + hddagi_shader.direct_light.version_free(hddagi_shader.direct_light_shader); } - if (sdfgi_shader.integrate_shader.is_valid()) { - sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader); + if (hddagi_shader.integrate_shader.is_valid()) { + hddagi_shader.integrate.version_free(hddagi_shader.integrate_shader); } - if (sdfgi_shader.preprocess_shader.is_valid()) { - sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader); + if (hddagi_shader.preprocess_shader.is_valid()) { + hddagi_shader.preprocess.version_free(hddagi_shader.preprocess_shader); } if (voxel_gi_lights) { @@ -3633,13 +3226,13 @@ void GI::free() { } } -Ref GI::create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) { - Ref sdfgi; - sdfgi.instantiate(); +Ref GI::create_hddagi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) { + Ref hddagi; + hddagi.instantiate(); - sdfgi->create(p_env, p_world_position, p_requested_history_size, this); + hddagi->create(p_env, p_world_position, p_requested_history_size, this); - return sdfgi; + return hddagi; } void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref p_render_buffers, const Transform3D &p_transform, const PagedArray &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used) { @@ -3656,8 +3249,6 @@ void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Refget_voxel_gi_buffer(); VoxelGIData voxel_gi_data[MAX_VOXEL_GI_INSTANCES]; - bool voxel_gi_instances_changed = false; - Transform3D to_camera; to_camera.origin = p_transform.origin; //only translation, make local @@ -3717,26 +3308,10 @@ void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Refvoxel_gi_textures[i]) { - voxel_gi_instances_changed = true; rbgi->voxel_gi_textures[i] = texture; } } - if (voxel_gi_instances_changed) { - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - if (RD::get_singleton()->uniform_set_is_valid(rbgi->uniform_set[v])) { - RD::get_singleton()->free(rbgi->uniform_set[v]); - } - rbgi->uniform_set[v] = RID(); - } - - if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { - // VoxelGI instances have changed, so we need to update volumetric fog. - Ref fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); - fog->sync_gi_dependent_sets_validity(true); - } - } - if (p_voxel_gi_instances.size() > 0) { RD::get_singleton()->draw_command_begin_label("VoxelGIs Setup"); @@ -3754,13 +3329,6 @@ RID GI::RenderBuffersGI::get_voxel_gi_buffer() { } void GI::RenderBuffersGI::free_data() { - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - if (RD::get_singleton()->uniform_set_is_valid(uniform_set[v])) { - RD::get_singleton()->free(uniform_set[v]); - } - uniform_set[v] = RID(); - } - if (scene_data_ubo.is_valid()) { RD::get_singleton()->free(scene_data_ubo); scene_data_ubo = RID(); @@ -3774,7 +3342,6 @@ void GI::RenderBuffersGI::free_data() { void GI::process_gi(Ref p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray &p_voxel_gi_instances) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); ERR_FAIL_COND_MSG(p_view_count > 2, "Maximum of 2 views supported for Processing GI."); @@ -3793,15 +3360,43 @@ void GI::process_gi(Ref p_render_buffers, const RID *p_nor if (!p_render_buffers->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) { Size2i size = internal_size; - uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; if (half_resolution) { size.x >>= 1; size.y >>= 1; } - p_render_buffers->create_texture(RB_SCOPE_GI, RB_TEX_AMBIENT, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, usage_bits, RD::TEXTURE_SAMPLES_1, size); - p_render_buffers->create_texture(RB_SCOPE_GI, RB_TEX_REFLECTION, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, usage_bits, RD::TEXTURE_SAMPLES_1, size); + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_UINT; + tf.width = size.x; + tf.height = size.y; + tf.depth = 1; + tf.array_layers = 1; + tf.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + tf.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + RD::TextureFormat tf_blend; + tf_blend.format = RD::DATA_FORMAT_R8G8_UNORM; + + tf_blend.width = size.x; + tf_blend.height = size.y; + tf_blend.depth = 1; + tf_blend.array_layers = 1; + tf_blend.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + p_render_buffers->create_texture_from_format(RB_SCOPE_GI, RB_TEX_AMBIENT_U32, tf); + p_render_buffers->create_texture_from_format(RB_SCOPE_GI, RB_TEX_REFLECTION_U32, tf); + p_render_buffers->create_texture_from_format(RB_SCOPE_GI, RB_TEX_AMBIENT_REFLECTION_BLEND, tf_blend); + + p_render_buffers->create_texture_from_format(RB_SCOPE_GI, RB_TEX_REFLECTION_U32_FILTERED, tf); + p_render_buffers->create_texture_from_format(RB_SCOPE_GI, RB_TEX_AMBIENT_REFLECTION_BLEND_FILTERED, tf_blend); + + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; + p_render_buffers->create_texture_view(RB_SCOPE_GI, RB_TEX_AMBIENT_U32, RB_TEX_AMBIENT, tv); + p_render_buffers->create_texture_view(RB_SCOPE_GI, RB_TEX_REFLECTION_U32, RB_TEX_REFLECTION, tv); + p_render_buffers->create_texture_view(RB_SCOPE_GI, RB_TEX_REFLECTION_U32_FILTERED, RB_TEX_REFLECTION_FILTERED, tv); rbgi->using_half_size_gi = half_resolution; } @@ -3860,12 +3455,12 @@ void GI::process_gi(Ref p_render_buffers, const RID *p_nor push_constant.proj_info[2] = (1.0f - p_projections[0].columns[0][2]) / p_projections[0].columns[0][0]; push_constant.proj_info[3] = (1.0f + p_projections[0].columns[1][2]) / p_projections[0].columns[1][1]; - bool use_sdfgi = p_render_buffers->has_custom_data(RB_SCOPE_SDFGI); + bool use_hddagi = p_render_buffers->has_custom_data(RB_SCOPE_HDDAGI); bool use_voxel_gi_instances = push_constant.max_voxel_gi_instances > 0; - Ref sdfgi; - if (use_sdfgi) { - sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); + Ref hddagi; + if (use_hddagi) { + hddagi = p_render_buffers->get_custom_data(RB_SCOPE_HDDAGI); } uint32_t pipeline_specialization = 0; @@ -3880,184 +3475,63 @@ void GI::process_gi(Ref p_render_buffers, const RID *p_nor pipeline_specialization |= SHADER_SPECIALIZATION_USE_VRS; } - Mode mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_COMBINED : (use_sdfgi ? MODE_SDFGI : MODE_VOXEL_GI); + Mode mode; + + if (use_hddagi) { + if (use_voxel_gi_instances) { + mode = hddagi->using_ambient_filter ? MODE_COMBINED_BLEND_AMBIENT : MODE_COMBINED; + } else { + mode = hddagi->using_ambient_filter ? MODE_HDDAGI_BLEND_AMBIENT : MODE_HDDAGI; + } + push_constant.occlusion_bias = hddagi->occlusion_bias; + } else { + mode = MODE_VOXEL_GI; + push_constant.occlusion_bias = 0; + } for (uint32_t v = 0; v < p_view_count; v++) { push_constant.view_index = v; // setup our uniform set - if (rbgi->uniform_set[v].is_null() || !RD::get_singleton()->uniform_set_is_valid(rbgi->uniform_set[v])) { - Vector uniforms; - { - RD::Uniform u; - u.binding = 1; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (use_sdfgi && j < sdfgi->cascades.size()) { - u.append_id(sdfgi->cascades[j].sdf_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 2; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (use_sdfgi && j < sdfgi->cascades.size()) { - u.append_id(sdfgi->cascades[j].light_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (use_sdfgi && j < sdfgi->cascades.size()) { - u.append_id(sdfgi->cascades[j].light_aniso_0_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (use_sdfgi && j < sdfgi->cascades.size()) { - u.append_id(sdfgi->cascades[j].light_aniso_1_tex); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 5; - if (use_sdfgi) { - u.append_id(sdfgi->occlusion_texture); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 6; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - u.binding = 7; - u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 9; - u.append_id(p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_AMBIENT, v, 0)); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 10; - u.append_id(p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_REFLECTION, v, 0)); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 11; - if (use_sdfgi) { - u.append_id(sdfgi->lightprobe_texture); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE)); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 12; - u.append_id(p_render_buffers->get_depth_texture(v)); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 13; - u.append_id(p_normal_roughness_slices[v]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 14; - RID buffer = p_voxel_gi_buffer.is_valid() ? p_voxel_gi_buffer : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); - u.append_id(buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 15; - u.append_id(sdfgi_ubo); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 16; - u.append_id(rbgi->get_voxel_gi_buffer()); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 17; - for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) { - u.append_id(rbgi->voxel_gi_textures[i]); - } - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 18; - u.append_id(rbgi->scene_data_ubo); - uniforms.push_back(u); - } - if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 19; - RID buffer = has_vrs_texture ? p_render_buffers->get_texture_slice(RB_SCOPE_VRS, RB_TEXTURE, v, 0) : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_VRS); - u.append_id(buffer); - uniforms.push_back(u); - } - - rbgi->uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0); - } + RD::Uniform vgiu; + vgiu.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + vgiu.binding = 17; + for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) { + vgiu.append_id(rbgi->voxel_gi_textures[i]); + } + + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + shader.version_get_shader(shader_version, 0), + 0, + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 1, hddagi->voxel_bits_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 2, hddagi->voxel_region_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 3, hddagi->voxel_light_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 4, hddagi->lightprobe_specular_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 5, hddagi->using_probe_filter ? hddagi->lightprobe_diffuse_filter_tex : hddagi->lightprobe_diffuse_tex), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 6, hddagi->get_lightprobe_occlusion_textures()), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 7, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 8, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 9, hddagi->voxel_disocclusion_tex), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 10, hddagi->voxel_light_neighbour_data), + + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 12, p_render_buffers->get_depth_texture(v)), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 13, p_normal_roughness_slices[v]), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 14, p_voxel_gi_buffer.is_valid() ? p_voxel_gi_buffer : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK)), + + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 15, hddagi_ubo), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 16, rbgi->get_voxel_gi_buffer()), + vgiu, // 17 + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 18, rbgi->scene_data_ubo), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 19, has_vrs_texture ? p_render_buffers->get_texture_slice(RB_SCOPE_VRS, RB_TEXTURE, v, 0) : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_VRS)), + + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 20, p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_AMBIENT_U32, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 21, p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_REFLECTION_U32, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 22, p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_AMBIENT_REFLECTION_BLEND, v, 0)) + + ); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[pipeline_specialization][mode]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rbgi->uniform_set[v], 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); if (rbgi->using_half_size_gi) { @@ -4067,6 +3541,57 @@ void GI::process_gi(Ref p_render_buffers, const RID *p_nor } } + if (hddagi->using_reflection_filter) { + uint32_t filter_pipeline_specialization = 0; + if (rbgi->using_half_size_gi) { + filter_pipeline_specialization |= FILTER_SHADER_SPECIALIZATION_HALF_RES; + } + if (p_view_count > 1) { + filter_pipeline_specialization |= FILTER_SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX; + } + + FilterPushConstant filter_push_constant; + filter_push_constant.orthogonal = push_constant.orthogonal; + filter_push_constant.z_near = push_constant.z_near; + filter_push_constant.z_far = push_constant.z_far; + filter_push_constant.proj_info[0] = push_constant.proj_info[0]; + filter_push_constant.proj_info[1] = push_constant.proj_info[1]; + filter_push_constant.proj_info[2] = push_constant.proj_info[2]; + filter_push_constant.proj_info[3] = push_constant.proj_info[3]; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, filter_pipelines[filter_pipeline_specialization][rbgi->using_half_size_gi ? FILTER_MODE_BILATERAL_HALF_SIZE : FILTER_MODE_BILATERAL]); + + for (int i = 0; i < 2; i++) { + RD::get_singleton()->compute_list_add_barrier(compute_list); + + filter_push_constant.filter_dir[1] = i & 1; + filter_push_constant.filter_dir[0] = (i + 1) & 1; + + for (uint32_t v = 0; v < p_view_count; v++) { + RID uniform_set = UniformSetCacheRD::get_singleton()->get_cache( + filter_shader.version_get_shader(filter_shader_version, 0), + 0, + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 0, p_render_buffers->get_depth_texture(v)), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 1, p_normal_roughness_slices[v]), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 2, p_render_buffers->get_texture_slice(RB_SCOPE_GI, i == 0 ? RB_TEX_REFLECTION : RB_TEX_REFLECTION_FILTERED, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_TEXTURE, 3, p_render_buffers->get_texture_slice(RB_SCOPE_GI, i == 0 ? RB_TEX_AMBIENT_REFLECTION_BLEND : RB_TEX_AMBIENT_REFLECTION_BLEND_FILTERED, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 4, p_render_buffers->get_texture_slice(RB_SCOPE_GI, i == 0 ? RB_TEX_REFLECTION_U32_FILTERED : RB_TEX_REFLECTION_U32, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_IMAGE, 5, p_render_buffers->get_texture_slice(RB_SCOPE_GI, i == 0 ? RB_TEX_AMBIENT_REFLECTION_BLEND_FILTERED : RB_TEX_AMBIENT_REFLECTION_BLEND, v, 0)), + RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, 6, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)), + RD::Uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 7, rbgi->scene_data_ubo)); + + filter_push_constant.view_index = v; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &filter_push_constant, sizeof(FilterPushConstant)); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + if (rbgi->using_half_size_gi) { + RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.x >> 1, internal_size.y >> 1, 1); + } else { + RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.x, internal_size.y, 1); + } + } + } + } RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } diff --git a/servers/rendering/renderer_rd/environment/gi.h b/servers/rendering/renderer_rd/environment/gi.h index 6169d386b53e..929d1b76aeee 100644 --- a/servers/rendering/renderer_rd/environment/gi.h +++ b/servers/rendering/renderer_rd/environment/gi.h @@ -37,11 +37,12 @@ #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_rd/environment/sky.h" #include "servers/rendering/renderer_rd/shaders/environment/gi.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_debug.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_debug_probes.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_direct_light.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_filter.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_integrate.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/environment/hddagi_preprocess.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl.gen.h" #include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h" @@ -50,10 +51,19 @@ #include "servers/rendering/storage/utilities.h" #define RB_SCOPE_GI SNAME("rbgi") -#define RB_SCOPE_SDFGI SNAME("sdfgi") +#define RB_SCOPE_HDDAGI SNAME("hddagi") #define RB_TEX_AMBIENT SNAME("ambient") #define RB_TEX_REFLECTION SNAME("reflection") +#define RB_TEX_AMBIENT_REFLECTION_BLEND SNAME("ambient_reflection_blend") + +#define RB_TEX_AMBIENT_U32 SNAME("ambient_u32") +#define RB_TEX_REFLECTION_U32 SNAME("reflection_u32") + +#define RB_TEX_REFLECTION_FILTERED SNAME("reflection_filtered") +#define RB_TEX_AMBIENT_REFLECTION_BLEND_FILTERED SNAME("ambient_reflection_blend_filtered") + +#define RB_TEX_REFLECTION_U32_FILTERED SNAME("reflection_u32_filtered") // Forward declare RenderDataRD and RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound class RenderDataRD; @@ -258,46 +268,57 @@ class GI : public RendererGI { PipelineCacheRD voxel_gi_debug_shader_version_pipelines[VOXEL_GI_DEBUG_MAX]; RID voxel_gi_debug_uniform_set; - /* SDFGI */ - - struct SDFGIShader { - enum SDFGIPreprocessShaderVersion { - PRE_PROCESS_SCROLL, - PRE_PROCESS_SCROLL_OCCLUSION, - PRE_PROCESS_JUMP_FLOOD_INITIALIZE, - PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF, - PRE_PROCESS_JUMP_FLOOD, - PRE_PROCESS_JUMP_FLOOD_OPTIMIZED, - PRE_PROCESS_JUMP_FLOOD_UPSCALE, + /* HDDAGI */ + + struct HDDAGIShader { + enum HDDAGIPreprocessShaderVersion { + PRE_PROCESS_REGION_STORE, + PRE_PROCESS_LIGHT_STORE, + PRE_PROCESS_LIGHT_SCROLL, PRE_PROCESS_OCCLUSION, - PRE_PROCESS_STORE, + PRE_PROCESS_OCCLUSION_STORE, + PRE_PROCESS_LIGHTPROBE_SCROLL, + PRE_PROCESS_LIGHTPROBE_NEIGHBOURS, + PRE_PROCESS_LIGHTPROBE_GEOMETRY_PROXIMITY, + PRE_PROCESS_LIGHTPROBE_UPDATE_FRAMES, PRE_PROCESS_MAX }; struct PreprocessPushConstant { + int32_t grid_size[3]; + uint32_t region_version; + int32_t scroll[3]; - int32_t grid_size; + uint32_t cascade_count; - int32_t probe_offset[3]; - int32_t step_size; + int32_t offset[3]; + uint32_t probe_update_frames; - int32_t half_size; - uint32_t occlusion_index; - int32_t cascade; - uint32_t pad; + int32_t limit[3]; + uint32_t cascade; + + int32_t region_world_pos[3]; + int32_t maximum_light_cells; + + int32_t probe_axis_size[3]; + uint32_t ray_hit_cache_frames; + + uint32_t upper_region_world_pos[3]; + int occlusion_offset; }; - SdfgiPreprocessShaderRD preprocess; + HddagiPreprocessShaderRD preprocess; RID preprocess_shader; + RID preprocess_shader_version[PRE_PROCESS_MAX]; RID preprocess_pipeline[PRE_PROCESS_MAX]; struct DebugPushConstant { float grid_size[3]; uint32_t max_cascades; - int32_t screen_size[2]; + uint32_t screen_size; + float esm_strength; float y_mult; - float z_near; float inv_projection[3][4]; @@ -305,7 +326,7 @@ class GI : public RendererGI { float cam_origin[3]; }; - SdfgiDebugShaderRD debug; + HddagiDebugShaderRD debug; RID debug_shader; RID debug_shader_version; RID debug_pipeline; @@ -313,8 +334,8 @@ class GI : public RendererGI { enum ProbeDebugMode { PROBE_DEBUG_PROBES, PROBE_DEBUG_PROBES_MULTIVIEW, - PROBE_DEBUG_VISIBILITY, - PROBE_DEBUG_VISIBILITY_MULTIVIEW, + PROBE_DEBUG_OCCLUSION, + PROBE_DEBUG_OCCLUSION_MULTIVIEW, PROBE_DEBUG_MAX }; @@ -331,16 +352,18 @@ class GI : public RendererGI { float grid_size[3]; uint32_t cascade; - uint32_t pad; + int32_t oct_size; float y_mult; int32_t probe_debug_index; - int32_t probe_axis_size; + uint32_t pad; + + int32_t probe_axis_size[3]; + uint32_t pad2; }; - SdfgiDebugProbesShaderRD debug_probes; + HddagiDebugProbesShaderRD debug_probes; RID debug_probes_shader; - RID debug_probes_shader_version; - + RID debug_probes_shader_version[PROBE_DEBUG_MAX]; PipelineCacheRD debug_probes_pipeline[PROBE_DEBUG_MAX]; struct Light { @@ -360,7 +383,7 @@ class GI : public RendererGI { }; struct DirectLightPushConstant { - float grid_size[3]; + int32_t grid_size[3]; uint32_t max_cascades; uint32_t cascade; @@ -368,10 +391,13 @@ class GI : public RendererGI { uint32_t process_offset; uint32_t process_increment; - int32_t probe_axis_size; float bounce_feedback; float y_mult; uint32_t use_occlusion; + uint32_t probe_cell_size; + + int32_t probe_axis_size[3]; + uint32_t dirty_dynamic_update; }; enum { @@ -379,17 +405,18 @@ class GI : public RendererGI { DIRECT_LIGHT_MODE_DYNAMIC, DIRECT_LIGHT_MODE_MAX }; - SdfgiDirectLightShaderRD direct_light; + HddagiDirectLightShaderRD direct_light; RID direct_light_shader; + RID direct_light_shader_version[DIRECT_LIGHT_MODE_MAX]; RID direct_light_pipeline[DIRECT_LIGHT_MODE_MAX]; enum { INTEGRATE_MODE_PROCESS, - INTEGRATE_MODE_STORE, - INTEGRATE_MODE_SCROLL, - INTEGRATE_MODE_SCROLL_STORE, + INTEGRATE_MODE_FILTER, + INTEGRATE_MODE_CAMERA_VISIBILITY, INTEGRATE_MODE_MAX }; + struct IntegratePushConstant { enum { SKY_MODE_DISABLED, @@ -397,18 +424,14 @@ class GI : public RendererGI { SKY_MODE_SKY, }; - float grid_size[3]; + int32_t grid_size[3]; uint32_t max_cascades; - uint32_t probe_axis_size; + float ray_bias; uint32_t cascade; - uint32_t history_index; + int32_t inactive_update_frames; uint32_t history_size; - uint32_t ray_count; - float ray_bias; - int32_t image_size[2]; - int32_t world_offset[3]; uint32_t sky_mode; @@ -418,17 +441,25 @@ class GI : public RendererGI { float sky_color[3]; float y_mult; + uint32_t probe_axis_size[3]; uint32_t store_ambient_texture; - uint32_t pad[3]; + + uint32_t pad[2]; + int32_t global_frame; + uint32_t motion_accum; // Motion that happened since last update (bit 0 in X, bit 1 in Y, bit 2 in Z). + }; + + struct IntegrateCameraUBO { + float planes[6 * 4]; + float points[8 * 4]; }; - SdfgiIntegrateShaderRD integrate; + HddagiIntegrateShaderRD integrate; RID integrate_shader; + RID integrate_shader_version[INTEGRATE_MODE_MAX]; RID integrate_pipeline[INTEGRATE_MODE_MAX]; - RID integrate_default_sky_uniform_set; - - } sdfgi_shader; + } hddagi_shader; public: static GI *get_singleton() { return singleton; } @@ -456,7 +487,6 @@ class GI : public RendererGI { /* GI buffers */ bool using_half_size_gi = false; - RID uniform_set[RendererSceneRender::MAX_RENDER_VIEWS]; RID scene_data_ubo; RID get_voxel_gi_buffer(); @@ -541,20 +571,22 @@ class GI : public RendererGI { RS::VoxelGIQuality voxel_gi_quality = RS::VOXEL_GI_QUALITY_LOW; - /* SDFGI */ + /* HDDAGI */ - class SDFGI : public RenderBufferCustomDataRD { - GDCLASS(SDFGI, RenderBufferCustomDataRD) + class HDDAGI : public RenderBufferCustomDataRD { + GDCLASS(HDDAGI, RenderBufferCustomDataRD) public: enum { MAX_CASCADES = 8, CASCADE_SIZE = 128, - PROBE_DIVISOR = 16, - ANISOTROPY_SIZE = 6, + REGION_CELLS = 8, MAX_DYNAMIC_LIGHTS = 128, MAX_STATIC_LIGHTS = 1024, - LIGHTPROBE_OCT_SIZE = 6, + LIGHTPROBE_OCT_SIZE = 5, + LIGHTPROBE_HISTORY_FRAMES = 2, + OCCLUSION_OCT_SIZE = 14, + OCCLUSION_SUBPIXELS = 4, SH_SIZE = 16 }; @@ -562,101 +594,99 @@ class GI : public RendererGI { struct UBO { float offset[3]; float to_cell; - int32_t probe_offset[3]; + int32_t region_world_offset[3]; uint32_t pad; float pad2[4]; }; //cascade blocks are full-size for volume (128^3), half size for albedo/emission - RID sdf_tex; - RID light_tex; - RID light_aniso_0_tex; - RID light_aniso_1_tex; - - RID light_data; - RID light_aniso_0_data; - RID light_aniso_1_data; - - struct SolidCell { // this struct is unused, but remains as reference for size - uint32_t position; - uint32_t albedo; - uint32_t static_light; - uint32_t static_light_aniso; - }; - - // Buffers for indirect compute dispatch. - RID solid_cell_dispatch_buffer_storage; - RID solid_cell_dispatch_buffer_call; - RID solid_cell_buffer; - - RID lightprobe_history_tex; - RID lightprobe_average_tex; - float cell_size; Vector3i position; static const Vector3i DIRTY_ALL; Vector3i dirty_regions; //(0,0,0 is not dirty, negative is refresh from the end, DIRTY_ALL is refresh all. - RID sdf_store_uniform_set; - RID sdf_direct_light_static_uniform_set; - RID sdf_direct_light_dynamic_uniform_set; - RID scroll_uniform_set; - RID scroll_occlusion_uniform_set; - RID integrate_uniform_set; - RID lights_buffer; + RID light_process_buffer; + RID light_process_dispatch_buffer; + RID light_process_dispatch_buffer_copy; - float baked_exposure_normalization = 1.0; + RID light_position_bufer; + + bool static_lights_dirty = true; + bool dynamic_lights_dirty = true; + + struct LightProcessCell { // this struct is unused, but remains as reference for size + uint32_t position; + uint32_t albedo; + uint32_t emission; + uint32_t normal; + }; + + uint32_t motion_accum = 0; + uint16_t latest_version = 0; - bool all_dynamic_lights_dirty = true; + float baked_exposure_normalization = 1.0; }; + Vector3i cascade_size; + // access to our containers GI *gi = nullptr; - // used for rendering (voxelization) - RID render_albedo; + RID render_albedo; //x6, anisotropic + RID render_aniso_normals; RID render_emission; RID render_emission_aniso; - RID render_occlusion[8]; - RID render_geom_facing; - RID render_sdf[2]; - RID render_sdf_half[2]; - - // used for ping pong processing in cascades - RID sdf_initialize_uniform_set; - RID sdf_initialize_half_uniform_set; - RID jump_flood_uniform_set[2]; - RID jump_flood_half_uniform_set[2]; - RID sdf_upscale_uniform_set; - int upscale_jfa_uniform_set_index; - RID occlusion_uniform_set; - - uint32_t cascade_size = 128; + RID voxel_bits_tex; + RID voxel_region_tex; + RID voxel_disocclusion_tex; + RID voxel_light_tex; + RID voxel_light_tex_data; + RID voxel_light_neighbour_data; + RID region_version_data; + + RID light_process_buffer_render; + RID light_process_dispatch_buffer_render; + + RID lightprobe_specular_tex; + RID lightprobe_specular_data; + RID lightprobe_diffuse_data; + RID lightprobe_diffuse_tex; + RID lightprobe_ambient_data; + RID lightprobe_ambient_tex; + RID lightprobe_diffuse_filter_data; + RID lightprobe_diffuse_filter_tex; + RID lightprobe_hit_cache_data; + RID lightprobe_hit_cache_version_data; + RID lightprobe_moving_average; + RID lightprobe_moving_average_history; + RID lightprobe_neighbour_visibility_map; + RID lightprobe_geometry_proximity_map; + RID lightprobe_camera_visibility_map; + RID lightprobe_process_frame; // 28 bits is frame, upper 4 bits is frames remaining to do full updates (for having updated light when scrolling). + + Vector lightprobe_camera_buffers; + + RID occlusion_data[2]; + RID occlusion_tex[2]; LocalVector cascades; - RID lightprobe_texture; - RID lightprobe_data; - RID occlusion_texture; - RID occlusion_data; - RID ambient_texture; //integrates with volumetric fog - - RID lightprobe_history_scroll; //used for scrolling lightprobes - RID lightprobe_average_scroll; //used for scrolling lightprobes - - uint32_t history_size = 0; float solid_cell_ratio = 0; uint32_t solid_cell_count = 0; + uint32_t sampling_cache_buffer_cascade_size = 0; + + uint32_t update_frame = 0; + uint32_t frames_to_converge = 6; + + bool using_probe_filter = true; + bool using_reflection_filter = true; + bool using_ambient_filter = true; int num_cascades = 6; float min_cell_size = 0; - uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints - RID debug_uniform_set[RendererSceneRender::MAX_RENDER_VIEWS]; - RID debug_probes_scene_data_ubo; - RID debug_probes_uniform_set; RID cascades_ubo; bool uses_occlusion = false; @@ -664,29 +694,50 @@ class GI : public RendererGI { bool reads_sky = true; float energy = 1.0; float normal_bias = 1.1; + float reflection_bias = 2.0; float probe_bias = 1.1; - RS::EnvironmentSDFGIYScale y_scale_mode = RS::ENV_SDFGI_Y_SCALE_75_PERCENT; + float occlusion_bias = 0.1; + RS::EnvironmentHDDAGICascadeFormat cascade_format = RS::ENV_HDDAGI_CASCADE_FORMAT_16x8x16; float y_mult = 1.0; uint32_t version = 0; uint32_t render_pass = 0; - int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically - RID integrate_sky_uniform_set; + int32_t cascade_dynamic_light_count[HDDAGI::MAX_CASCADES]; //used dynamically + + RID debug_probes_scene_data_ubo; virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{}; virtual void free_data() override; - ~SDFGI(); + ~HDDAGI(); void create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi); void update(RID p_env, const Vector3 &p_world_position); void update_light(); - void update_probes(RID p_env, RendererRD::SkyRD::Sky *p_sky); + void update_probes(RID p_env, RendererRD::SkyRD::Sky *p_sky, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform); void store_probes(); - int get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const; + int get_pending_region_count() const; + int get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds, Vector3i &r_scroll, Vector3i &r_region_world) const; void update_cascades(); + RID get_lightprobe_diffuse_texture() { + if (using_probe_filter) { + return lightprobe_diffuse_filter_tex; + } else { + return lightprobe_diffuse_tex; + } + } + + RID get_lightprobe_specular_texture() { + return lightprobe_specular_tex; + } + + Vector get_lightprobe_occlusion_textures() { + Vector ret = { occlusion_tex[0], occlusion_tex[1] }; + return ret; + } + void debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector &p_texture_views); void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms); @@ -695,57 +746,52 @@ class GI : public RendererGI { void render_static_lights(RenderDataRD *p_render_data, Ref p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray *p_positional_light_cull_result); }; - RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16; - RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_30_FRAMES; - RS::EnvironmentSDFGIFramesToUpdateLight sdfgi_frames_to_update_light = RS::ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES; + RS::EnvironmentHDDAGIFramesToConverge hddagi_frames_to_converge = RS::ENV_HDDAGI_CONVERGE_IN_12_FRAMES; + RS::EnvironmentHDDAGIFramesToUpdateLight hddagi_frames_to_update_light = RS::ENV_HDDAGI_UPDATE_LIGHT_IN_4_FRAMES; + RS::EnvironmentHDDAGIInactiveProbeFrames inactive_probe_frames = RS::ENV_HDDAGI_INACTIVE_PROBE_4_FRAMES; - float sdfgi_solid_cell_ratio = 0.25; - Vector3 sdfgi_debug_probe_pos; - Vector3 sdfgi_debug_probe_dir; - bool sdfgi_debug_probe_enabled = false; - Vector3i sdfgi_debug_probe_index; - uint32_t sdfgi_current_version = 0; + float hddagi_solid_cell_ratio = 0.5; + Vector3 hddagi_debug_probe_pos; + Vector3 hddagi_debug_probe_dir; + bool hddagi_debug_probe_enabled = false; + Vector3i hddagi_debug_probe_index; + uint32_t hddagi_current_version = 0; - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; } + int hddagi_get_lightprobe_octahedron_size() const { return HDDAGI::LIGHTPROBE_OCT_SIZE; } + int hddagi_get_occlusion_octahedron_size() const { return HDDAGI::OCCLUSION_OCT_SIZE; } - virtual void sdfgi_reset() override; + virtual void hddagi_reset() override; - struct SDFGIData { - float grid_size[3]; - uint32_t max_cascades; + struct HDDAGIData { + int32_t grid_size[3]; + int32_t max_cascades; - uint32_t use_occlusion; - int32_t probe_axis_size; - float probe_to_uvw; float normal_bias; - - float lightprobe_tex_pixel_size[3]; float energy; - - float lightprobe_uv_offset[3]; float y_mult; + float reflection_bias; - float occlusion_clamp[3]; - uint32_t pad3; - - float occlusion_renormalize[3]; - uint32_t pad4; + int32_t probe_axis_size[3]; + float esm_strength; - float cascade_probe_size[3]; - uint32_t pad5; + uint32_t pad3[4]; struct ProbeCascadeData { float position[3]; //offset of (0,0,0) in world coordinates - float to_probe; // 1/bounds * grid_size - int32_t probe_world_offset[3]; + float to_probe; + + int32_t region_world_offset[3]; float to_cell; // 1/bounds * grid_size - float pad[3]; + + uint32_t pad[3]; float exposure_normalization; + + uint32_t pad2[4]; }; - ProbeCascadeData cascades[SDFGI::MAX_CASCADES]; + ProbeCascadeData cascades[HDDAGI::MAX_CASCADES]; }; struct VoxelGIData { @@ -783,16 +829,18 @@ class GI : public RendererGI { float z_near; float z_far; - float pad2; - float pad3; + uint32_t pad; + float occlusion_bias; }; - RID sdfgi_ubo; + RID hddagi_ubo; enum Mode { MODE_VOXEL_GI, - MODE_SDFGI, + MODE_HDDAGI, MODE_COMBINED, + MODE_HDDAGI_BLEND_AMBIENT, + MODE_COMBINED_BLEND_AMBIENT, MODE_MAX }; @@ -810,13 +858,40 @@ class GI : public RendererGI { RID shader_version; RID pipelines[SHADER_SPECIALIZATION_VARIATIONS][MODE_MAX]; + enum FilterMode { + FILTER_MODE_BILATERAL, + FILTER_MODE_BILATERAL_HALF_SIZE, + FILTER_MODE_MAX + }; + enum FilterShaderSpecializations { + FILTER_SHADER_SPECIALIZATION_HALF_RES = 1 << 0, + FILTER_SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX = 1 << 1, + FILTER_SHADER_SPECIALIZATION_VARIATIONS = 4 + }; + + struct FilterPushConstant { + uint32_t orthogonal; + float z_near; + float z_far; + uint32_t view_index; + + float proj_info[4]; + + int32_t filter_dir[2]; + uint32_t pad[2]; + }; + + HddagiFilterShaderRD filter_shader; + RID filter_shader_version; + RID filter_pipelines[FILTER_SHADER_SPECIALIZATION_VARIATIONS][MODE_MAX]; + GI(); ~GI(); void init(RendererRD::SkyRD *p_sky); void free(); - Ref create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size); + Ref create_hddagi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size); void setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref p_render_buffers, const Transform3D &p_transform, const PagedArray &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used); void process_gi(Ref p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray &p_voxel_gi_instances); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 453b21a6418a..55ed9b152481 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -131,8 +131,8 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::free_data() { fsr2_context = nullptr; } - if (!render_sdfgi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_sdfgi_uniform_set)) { - RD::get_singleton()->free(render_sdfgi_uniform_set); + if (!render_hddagi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_hddagi_uniform_set)) { + RD::get_singleton()->free(render_hddagi_uniform_set); } } @@ -616,12 +616,20 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat scene_state.ubo.cluster_width = cluster_screen_width; } - scene_state.ubo.gi_upscale_for_msaa = false; + scene_state.ubo.gi_upscale = false; scene_state.ubo.volumetric_fog_enabled = false; if (rd.is_valid()) { - if (rd->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { - scene_state.ubo.gi_upscale_for_msaa = true; + if (rd->has_custom_data(RB_SCOPE_GI)) { + Ref rbgi = rd->get_custom_data(RB_SCOPE_GI); + if (rd->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED || rbgi->using_half_size_gi) { + scene_state.ubo.gi_upscale = true; + if (rbgi->using_half_size_gi) { + scene_state.ubo.gi_upscale_shift = 1; + } else { + scene_state.ubo.gi_upscale_shift = 0; + } + } } if (rd->has_custom_data(RB_SCOPE_FOG)) { @@ -802,7 +810,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } -void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi, bool p_using_opaque_gi, bool p_using_motion_pass, bool p_append) { +void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_hddagi, bool p_using_opaque_gi, bool p_using_motion_pass, bool p_append) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); uint64_t frame = RSG::rasterizer->get_frame_number(); @@ -959,8 +967,8 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con flags |= INSTANCE_DATA_FLAG_USE_VOXEL_GI; uses_gi = true; } else { - if (p_using_sdfgi && inst->can_sdfgi) { - flags |= INSTANCE_DATA_FLAG_USE_SDFGI; + if (p_using_hddagi && inst->can_hddagi) { + flags |= INSTANCE_DATA_FLAG_USE_HDDAGI; uses_gi = true; } inst->gi_offset_cache = 0xFFFFFFFF; @@ -1149,10 +1157,10 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, } } -/* SDFGI */ +/* HDDAGI */ -void RenderForwardClustered::_update_sdfgi(RenderDataRD *p_render_data) { - if (p_render_data->sdfgi_update_data == nullptr) { +void RenderForwardClustered::_update_hddagi(RenderDataRD *p_render_data) { + if (p_render_data->hddagi_update_data == nullptr) { return; } @@ -1161,19 +1169,19 @@ void RenderForwardClustered::_update_sdfgi(RenderDataRD *p_render_data) { rb = p_render_data->render_buffers; } - if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { - RENDER_TIMESTAMP("Render SDFGI"); - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + RENDER_TIMESTAMP("Render HDDAGI"); + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); float exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); } - for (int i = 0; i < p_render_data->render_sdfgi_region_count; i++) { - sdfgi->render_region(rb, p_render_data->render_sdfgi_regions[i].region, p_render_data->render_sdfgi_regions[i].instances, exposure_normalization); + for (int i = 0; i < p_render_data->render_hddagi_region_count; i++) { + hddagi->render_region(rb, p_render_data->render_hddagi_regions[i].region, p_render_data->render_hddagi_regions[i].instances, exposure_normalization); } - if (p_render_data->sdfgi_update_data->update_static) { - sdfgi->render_static_lights(p_render_data, rb, p_render_data->sdfgi_update_data->static_cascade_count, p_render_data->sdfgi_update_data->static_cascade_indices, p_render_data->sdfgi_update_data->static_positional_lights); + if (p_render_data->hddagi_update_data->update_static) { + hddagi->render_static_lights(p_render_data, rb, p_render_data->hddagi_update_data->static_cascade_count, p_render_data->hddagi_update_data->static_cascade_indices, p_render_data->hddagi_update_data->static_positional_lights); } } } @@ -1219,9 +1227,9 @@ void RenderForwardClustered::_update_volumetric_fog(Ref p_ ERR_FAIL_COND(!p_render_buffers->has_custom_data(RB_SCOPE_GI)); Ref rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI); - Ref sdfgi; - if (p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { - sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); + Ref hddagi; + if (p_render_buffers->has_custom_data(RB_SCOPE_HDDAGI)) { + hddagi = p_render_buffers->get_custom_data(RB_SCOPE_HDDAGI); } Size2i size = p_render_buffers->get_internal_size(); @@ -1273,7 +1281,7 @@ void RenderForwardClustered::_update_volumetric_fog(Ref p_ settings.vfog = fog; settings.cluster_builder = rb_data->cluster_builder; settings.rbgi = rbgi; - settings.sdfgi = sdfgi; + settings.hddagi = hddagi; settings.env = p_environment; settings.sky = &sky; settings.gi = &gi; @@ -1404,9 +1412,9 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } - if (rb.is_valid() && p_use_gi && rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - sdfgi->store_probes(); + if (rb.is_valid() && p_use_gi && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + hddagi->store_probes(); } Size2i viewport_size = Size2i(1, 1); @@ -1620,8 +1628,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool ce_needs_normal_roughness = _compositor_effects_has_flag(p_render_data, RS::COMPOSITOR_EFFECT_FLAG_NEEDS_ROUGHNESS); bool ce_needs_separate_specular = _compositor_effects_has_flag(p_render_data, RS::COMPOSITOR_EFFECT_FLAG_NEEDS_SEPARATE_SPECULAR); - // sdfgi first - _update_sdfgi(p_render_data); + // hddagi first + _update_hddagi(p_render_data); // assign render indices to voxel_gi_instances for (uint32_t i = 0; i < (uint32_t)p_render_data->voxel_gi_instances->size(); i++) { @@ -1641,12 +1649,12 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co p_render_data->voxel_gi_count = 0; - if (rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - if (sdfgi.is_valid()) { - sdfgi->update_cascades(); - sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data); - sdfgi->update_light(); + if (rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + if (hddagi.is_valid()) { + hddagi->update_cascades(); + hddagi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data); + hddagi->update_light(); } } @@ -1701,7 +1709,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co Vector depth_pass_clear; bool using_separate_specular = false; bool using_ssr = false; - bool using_sdfgi = false; + bool using_hddagi = false; bool using_voxelgi = false; bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_ssil = !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); @@ -1735,8 +1743,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } if (p_render_data->environment.is_valid()) { - if (environment_get_sdfgi_enabled(p_render_data->environment) && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { - using_sdfgi = true; + if (environment_get_hddagi_enabled(p_render_data->environment) && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { + using_hddagi = true; } if (environment_get_ssr_enabled(p_render_data->environment)) { using_separate_specular = true; @@ -1767,7 +1775,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co // May have changed due to the above (light buffer enlarged, as an example). _update_render_base_uniform_set(); - _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi, using_motion_pass); + _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_hddagi, using_hddagi || using_voxelgi, using_motion_pass); render_list[RENDER_LIST_OPAQUE].sort_by_key(); render_list[RENDER_LIST_MOTION].sort_by_key(); render_list[RENDER_LIST_OPAQUE_NO_DEPTH_PREPASS].sort_by_key(); @@ -1786,7 +1794,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI; } else if (p_render_data->environment.is_valid()) { if (using_ssr || - using_sdfgi || + using_hddagi || environment_get_ssao_enabled(p_render_data->environment) || using_ssil || ce_needs_normal_roughness || @@ -1941,7 +1949,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool ce_pre_transparent_resolved_depth = use_msaa && _compositor_effects_has_flag(p_render_data, RS::COMPOSITOR_EFFECT_FLAG_ACCESS_RESOLVED_DEPTH, RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT); bool debug_voxelgis = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_ALBEDO || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION; - bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; + bool debug_hddagi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_HDDAGI_PROBES; bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid(); uint32_t spec_constant_base_flags = 0; @@ -1954,7 +1962,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); if (depth_pre_pass) { //depth pre pass - bool needs_pre_resolve = _needs_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); + bool needs_pre_resolve = _needs_post_prepass_render(p_render_data, using_hddagi || using_voxelgi); + if (needs_pre_resolve) { RENDER_TIMESTAMP("GI + Render Depth Pre-Pass (Parallel)"); } else { @@ -1965,15 +1974,16 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pass_clear, 0.0); RD::get_singleton()->draw_list_end(); //start compute processes here, so they run at the same time as depth pre-pass - _post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); + _post_prepass_render(p_render_data, using_hddagi || using_voxelgi); } RD::get_singleton()->draw_command_begin_label("Render Depth Pre-Pass"); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID(), samplers); - bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth; + bool finish_depth = using_ssao || using_ssil || using_hddagi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); + _render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector() : depth_pass_clear); RD::get_singleton()->draw_command_end_label(); @@ -2015,7 +2025,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co normal_roughness_views[v] = rb_data->get_normal_roughness(v); } } - _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID()); + _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_hddagi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID()); RENDER_TIMESTAMP("Render Opaque Pass"); @@ -2118,14 +2128,15 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_list_end(); } - if (debug_sdfgi_probes) { + if (debug_hddagi_probes) { Projection dc; dc.set_depth_correction(true); Projection cms[RendererSceneRender::MAX_RENDER_VIEWS]; for (uint32_t v = 0; v < p_render_data->scene_data->view_count; v++) { cms[v] = (dc * p_render_data->scene_data->view_projection[v]) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); } - _debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms); + + _debug_hddagi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms); } if (draw_sky || draw_sky_fog_only) { @@ -2362,18 +2373,18 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (rb_data.is_valid()) { _render_buffers_debug_draw(p_render_data); - if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_HDDAGI && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); Vector view_rids; - // SDFGI renders at internal resolution, need to check if our debug correctly supports outputting upscaled. + // HDDAGI renders at internal resolution, need to check if our debug correctly supports outputting upscaled. Size2i size = rb->get_internal_size(); RID source_texture = rb->get_internal_texture(); for (uint32_t v = 0; v < rb->get_view_count(); v++) { view_rids.push_back(rb->get_internal_texture(v)); } - sdfgi->debug_draw(p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform, size.x, size.y, rb->get_render_target(), source_texture, view_rids); + hddagi->debug_draw(p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->cam_transform, size.x, size.y, rb->get_render_target(), source_texture, view_rids); } } } @@ -2868,10 +2879,10 @@ void RenderForwardClustered::_render_uv2(const PagedArraydraw_command_end_label(); } -void RenderForwardClustered::_render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { - RENDER_TIMESTAMP("Render SDFGI"); +void RenderForwardClustered::_render_hddagi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_normal_bits_texture, float p_exposure_normalization) { + RENDER_TIMESTAMP("Render HDDAGI"); - RD::get_singleton()->draw_command_begin_label("Render SDFGI Voxel"); + RD::get_singleton()->draw_command_begin_label("Render HDDAGI Voxel"); RenderSceneDataRD scene_data; @@ -2932,12 +2943,12 @@ void RenderForwardClustered::_render_sdfgi(Ref p_render_bu scene_data.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), Color()); - RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); + RID rp_uniform_set = _setup_hddagi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_normal_bits_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); - HashMap::Iterator E = sdfgi_framebuffer_size_cache.find(fb_size); + HashMap::Iterator E = hddagi_framebuffer_size_cache.find(fb_size); if (!E) { RID fb = RD::get_singleton()->framebuffer_create_empty(fb_size); - E = sdfgi_framebuffer_size_cache.insert(fb_size, fb); + E = hddagi_framebuffer_size_cache.insert(fb_size, fb); } RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false); @@ -3053,7 +3064,7 @@ void RenderForwardClustered::_update_render_base_uniform_set() { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 13; - u.append_id(sdfgi_get_ubo()); + u.append_id(hddagi_get_ubo()); uniforms.push_back(u); } @@ -3347,10 +3358,18 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 30; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT_REFLECTION_BLEND) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT_REFLECTION_BLEND) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); + u.append_id(texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 31; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; - if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - t = sdfgi->lightprobe_texture; + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + t = hddagi->get_lightprobe_specular_texture(); } if (t.is_null()) { t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); @@ -3360,22 +3379,41 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 31; + u.binding = 32; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; - if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - t = sdfgi->occlusion_texture; + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + t = hddagi->get_lightprobe_diffuse_texture(); } if (t.is_null()) { - t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); + t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); } u.append_id(t); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 32; + u.binding = 33; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_HDDAGI)) { + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + Vector t = hddagi->get_lightprobe_occlusion_textures(); + u.append_id(t[0]); + u.append_id(t[1]); + } else { + RID r = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + u.append_id(r); + u.append_id(r); + } + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 34; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; RID voxel_gi; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_GI)) { @@ -3387,7 +3425,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 33; + u.binding = 35; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID vfog; if (rb_data.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) { @@ -3404,7 +3442,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 34; + u.binding = 36; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssil = rb.is_valid() && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSIL, RB_FINAL) : RID(); RID texture = ssil.is_valid() ? ssil : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3415,7 +3453,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_rd, RENDER_PASS_UNIFORM_SET, uniforms); } -RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers) { +RID RenderForwardClustered::_setup_hddagi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_normal_bits_texture, const RendererRD::MaterialStorage::Samplers &p_samplers) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); Vector uniforms; @@ -3583,8 +3621,6 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te uniforms.append_array(p_samplers.get_uniforms(12)); - // actual sdfgi stuff - { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; @@ -3610,11 +3646,12 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 27; - u.append_id(p_geom_facing_texture); + u.append_id(p_normal_bits_texture); + uniforms.push_back(u); } - return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_sdfgi_rd, RENDER_PASS_UNIFORM_SET, uniforms); + return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_hddagi_rd, RENDER_PASS_UNIFORM_SET, uniforms); } RID RenderForwardClustered::_render_buffers_get_normal_texture(Ref p_render_buffers) { @@ -3658,103 +3695,94 @@ void RenderForwardClustered::sub_surface_scattering_set_scale(float p_scale, flo RenderForwardClustered *RenderForwardClustered::singleton = nullptr; -void RenderForwardClustered::sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) { +void RenderForwardClustered::hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) { Ref rb = p_render_buffers; ERR_FAIL_COND(rb.is_null()); - Ref sdfgi; - if (rb->has_custom_data(RB_SCOPE_SDFGI)) { - sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + Ref hddagi; + if (rb->has_custom_data(RB_SCOPE_HDDAGI)) { + hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); } - bool needs_sdfgi = p_environment.is_valid() && environment_get_sdfgi_enabled(p_environment); - bool needs_reset = sdfgi.is_valid() ? sdfgi->version != gi.sdfgi_current_version : false; + bool needs_hddagi = p_environment.is_valid() && environment_get_hddagi_enabled(p_environment); + bool needs_reset = hddagi.is_valid() ? hddagi->version != gi.hddagi_current_version : false; - if (!needs_sdfgi || needs_reset) { - if (sdfgi.is_valid()) { + if (!needs_hddagi || needs_reset) { + if (hddagi.is_valid()) { // delete it - sdfgi.unref(); - rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); + hddagi.unref(); + rb->set_custom_data(RB_SCOPE_HDDAGI, hddagi); } - if (!needs_sdfgi) { + if (!needs_hddagi) { return; } } - // Ensure advanced shaders are available if SDFGI is used. - // Call here as this is the first entry point for SDFGI. + // Ensure advanced shaders are available if HDDAGI is used. + // Call here as this is the first entry point for HDDAGI. scene_shader.enable_advanced_shader_group(); - static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; - uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge]; + static const uint32_t history_frames_to_converge[RS::ENV_HDDAGI_CONVERGE_MAX] = { 6, 12, 18, 24, 32 }; + uint32_t requested_history_size = history_frames_to_converge[gi.hddagi_frames_to_converge]; - if (sdfgi.is_valid() && (sdfgi->num_cascades != environment_get_sdfgi_cascades(p_environment) || sdfgi->min_cell_size != environment_get_sdfgi_min_cell_size(p_environment) || requested_history_size != sdfgi->history_size || sdfgi->uses_occlusion != environment_get_sdfgi_use_occlusion(p_environment) || sdfgi->y_scale_mode != environment_get_sdfgi_y_scale(p_environment))) { + if (hddagi.is_valid() && (hddagi->num_cascades != environment_get_hddagi_cascades(p_environment) || hddagi->min_cell_size != environment_get_hddagi_min_cell_size(p_environment) || hddagi->cascade_format != environment_get_hddagi_cascade_format(p_environment) || hddagi->frames_to_converge != requested_history_size)) { //configuration changed, erase - sdfgi.unref(); - rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); + hddagi.unref(); + rb->set_custom_data(RB_SCOPE_HDDAGI, hddagi); } - if (sdfgi.is_null()) { + if (hddagi.is_null()) { // re-create - sdfgi = gi.create_sdfgi(p_environment, p_world_position, requested_history_size); - rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); + hddagi = gi.create_hddagi(p_environment, p_world_position, requested_history_size); + rb->set_custom_data(RB_SCOPE_HDDAGI, hddagi); } else { //check for updates - sdfgi->update(p_environment, p_world_position); + hddagi->update(p_environment, p_world_position); } } -int RenderForwardClustered::sdfgi_get_pending_region_count(const Ref &p_render_buffers) const { +int RenderForwardClustered::hddagi_get_pending_region_count(const Ref &p_render_buffers) const { Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), 0); - if (!rb->has_custom_data(RB_SCOPE_SDFGI)) { + if (!rb->has_custom_data(RB_SCOPE_HDDAGI)) { return 0; } - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - - int dirty_count = 0; - for (const RendererRD::GI::SDFGI::Cascade &c : sdfgi->cascades) { - if (c.dirty_regions == RendererRD::GI::SDFGI::Cascade::DIRTY_ALL) { - dirty_count++; - } else { - for (int j = 0; j < 3; j++) { - if (c.dirty_regions[j] != 0) { - dirty_count++; - } - } - } - } + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); - return dirty_count; + return hddagi->get_pending_region_count(); } -AABB RenderForwardClustered::sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const { +AABB RenderForwardClustered::hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; + Vector3i scroll; + Vector3i region_ofs; Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), AABB()); - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - ERR_FAIL_COND_V(sdfgi.is_null(), AABB()); + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + ERR_FAIL_COND_V(hddagi.is_null(), AABB()); - int c = sdfgi->get_pending_region_data(p_region, from, size, bounds); + int c = hddagi->get_pending_region_data(p_region, from, size, bounds, scroll, region_ofs); ERR_FAIL_COND_V(c == -1, AABB()); return bounds; } -uint32_t RenderForwardClustered::sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const { +uint32_t RenderForwardClustered::hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; + Vector3i scroll; + Vector3i region_ofs; Ref rb = p_render_buffers; ERR_FAIL_COND_V(rb.is_null(), -1); - Ref sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - ERR_FAIL_COND_V(sdfgi.is_null(), -1); + Ref hddagi = rb->get_custom_data(RB_SCOPE_HDDAGI); + ERR_FAIL_COND_V(hddagi.is_null(), -1); - return sdfgi->get_pending_region_data(p_region, from, size, bounds); + return hddagi->get_pending_region_data(p_region, from, size, bounds, scroll, region_ofs); } void RenderForwardClustered::GeometryInstanceForwardClustered::_mark_dirty() { @@ -3886,7 +3914,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet sdcache->sort.material_id_hi = p_material_id >> 16; sdcache->sort.shader_id = p_shader_id; sdcache->sort.geometry_id = p_mesh.get_local_index(); //only meshes can repeat anyway - sdcache->sort.uses_forward_gi = ginstance->can_sdfgi; + sdcache->sort.uses_forward_gi = ginstance->can_hddagi; sdcache->sort.priority = p_material->priority; sdcache->sort.uses_projector = ginstance->using_projectors; sdcache->sort.uses_softshadow = ginstance->using_softshadows; @@ -4101,11 +4129,11 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p } ginstance->store_transform_cache = store_transform; - ginstance->can_sdfgi = false; + ginstance->can_hddagi = false; if (!RendererRD::LightStorage::get_singleton()->lightmap_instance_is_valid(ginstance->lightmap_instance)) { if (ginstance->voxel_gi_instances[0].is_null() && (ginstance->data->use_baked_light || ginstance->data->use_dynamic_gi)) { - ginstance->can_sdfgi = true; + ginstance->can_hddagi = true; } } @@ -4306,7 +4334,8 @@ RenderForwardClustered::RenderForwardClustered() { print_line_rich("[b]Forward+: Using debanding"); defines += "\n#define USE_DEBANDING \n"; } - defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n"; + defines += "\n#define LIGHTPROBE_OCT_SIZE " + itos(gi.hddagi_get_lightprobe_octahedron_size()) + "\n"; + defines += "\n#define OCCLUSION_OCT_SIZE " + itos(gi.hddagi_get_occlusion_octahedron_size()) + "\n"; defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; { @@ -4433,8 +4462,8 @@ RenderForwardClustered::~RenderForwardClustered() { memdelete_arr(scene_state.lightmap_captures); } - while (sdfgi_framebuffer_size_cache.begin()) { - RD::get_singleton()->free(sdfgi_framebuffer_size_cache.begin()->value); - sdfgi_framebuffer_size_cache.remove(sdfgi_framebuffer_size_cache.begin()); + while (hddagi_framebuffer_size_cache.begin()) { + RD::get_singleton()->free(hddagi_framebuffer_size_cache.begin()->value); + hddagi_framebuffer_size_cache.remove(hddagi_framebuffer_size_cache.begin()); } } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index dc37dd553f28..6631111fe534 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -76,7 +76,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { }; enum { - SDFGI_MAX_CASCADES = 8, + HDDAGI_MAX_CASCADES = 8, MAX_VOXEL_GI_INSTANCESS = 8, MAX_LIGHTMAPS = 8, MAX_VOXEL_GI_INSTANCESS_PER_INSTANCE = 2, @@ -123,7 +123,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { DEPTH_FB_ROUGHNESS_VOXELGI }; - RID render_sdfgi_uniform_set; + RID render_hddagi_uniform_set; void ensure_specular(); bool has_specular() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); } @@ -164,7 +164,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint64_t lightmap_texture_array_version = 0xFFFFFFFF; void _update_render_base_uniform_set(); - RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers); + RID _setup_hddagi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_normal_bits_texture, const RendererRD::MaterialStorage::Samplers &p_samplers); RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0); struct BestFitNormal { @@ -249,7 +249,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, - INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6, + INSTANCE_DATA_FLAG_USE_HDDAGI = 1 << 6, INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7, INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8, INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9, @@ -281,10 +281,10 @@ class RenderForwardClustered : public RendererSceneRenderRD { float sdf_to_bounds[16]; int32_t sdf_offset[3]; - uint32_t pad2; + uint32_t gi_upscale_shift; int32_t sdf_size[3]; - uint32_t gi_upscale_for_msaa; + uint32_t gi_upscale; uint32_t volumetric_fog_enabled; float volumetric_fog_inv_length; @@ -384,9 +384,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); - void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi = false, bool p_using_opaque_gi = false, bool p_using_motion_pass = false, bool p_append = false); + void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_hddagi = false, bool p_using_opaque_gi = false, bool p_using_motion_pass = false, bool p_append = false); - HashMap sdfgi_framebuffer_size_cache; + HashMap hddagi_framebuffer_size_cache; struct GeometryInstanceData; class GeometryInstanceForwardClustered; @@ -468,7 +468,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { RID transforms_uniform_set; uint32_t instance_count = 0; uint32_t trail_steps = 1; - bool can_sdfgi = false; + bool can_hddagi = false; bool using_projectors = false; bool using_softshadows = false; @@ -586,8 +586,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { ClusterBuilderSharedDataRD cluster_builder_shared; ClusterBuilderRD *current_cluster_builder = nullptr; - /* SDFGI */ - void _update_sdfgi(RenderDataRD *p_render_data); + /* HDDAGI */ + void _update_hddagi(RenderDataRD *p_render_data); /* Volumetric fog */ RID shadow_sampler; @@ -633,7 +633,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; + virtual void _render_hddagi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_normal_bits_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) override; public: @@ -649,13 +649,13 @@ class RenderForwardClustered : public RendererSceneRenderRD { virtual void base_uniforms_changed() override; - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - virtual void sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override; - virtual int sdfgi_get_pending_region_count(const Ref &p_render_buffers) const override; - virtual AABB sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override; - virtual uint32_t sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override; - RID sdfgi_get_ubo() const { return gi.sdfgi_ubo; } + virtual void hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override; + virtual int hddagi_get_pending_region_count(const Ref &p_render_buffers) const override; + virtual AABB hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override; + virtual uint32_t hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override; + RID hddagi_get_ubo() const { return gi.hddagi_ubo; } /* GEOMETRY INSTANCE */ diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index e3c2fef93e86..1ff4bd592bc9 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -877,7 +877,7 @@ void fragment() { MaterialData *md = static_cast(material_storage->material_get_data(default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D)); default_shader_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS); - default_shader_sdfgi_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF); + default_shader_hddagi_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF); default_material_shader_ptr = md->shader_data; default_material_uniform_set = md->uniform_set; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index ebe7af666c5f..1e9cf90d2c4e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -269,7 +269,7 @@ class SceneShaderForwardClustered { RID debug_shadow_splits_material_shader; RID debug_shadow_splits_material; RID default_shader_rd; - RID default_shader_sdfgi_rd; + RID default_shader_hddagi_rd; RID default_vec4_xform_buffer; RID default_vec4_xform_uniform_set; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 83c7ec417f09..71ee405135b5 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1515,8 +1515,8 @@ void RenderForwardMobile::_render_uv2(const PagedArray RD::get_singleton()->draw_command_end_label(); } -void RenderForwardMobile::_render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { - // we don't do SDFGI in low end.. +void RenderForwardMobile::_render_hddagi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_normal_bits_texture, float p_exposure_normalization) { + // we don't do HDDAGI in low end.. } void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) { @@ -2467,7 +2467,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI sdcache->sort.material_id_hi = p_material_id >> 16; sdcache->sort.shader_id = p_shader_id; sdcache->sort.geometry_id = p_mesh.get_local_index(); - // sdcache->sort.uses_forward_gi = ginstance->can_sdfgi; + // sdcache->sort.uses_forward_gi = ginstance->can_hddagi; sdcache->sort.priority = p_material->priority; uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); @@ -2801,7 +2801,7 @@ RenderForwardMobile::RenderForwardMobile() { print_line_rich("[b]Mobile: Using debanding"); defines += "\n#define USE_DEBANDING \n"; } - // defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n"; + // defines += "\n#define HDDAGI_OCT_SIZE " + itos(gi.hddagi_get_lightprobe_octahedron_size()) + "\n"; defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 34260bd701fb..55614b138ed1 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -381,7 +381,7 @@ class RenderForwardMobile : public RendererSceneRenderRD { INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3, INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, - INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6, + INSTANCE_DATA_FLAG_USE_HDDAGI = 1 << 6, INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7, INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8, INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9, @@ -510,7 +510,7 @@ class RenderForwardMobile : public RendererSceneRenderRD { virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; + virtual void _render_hddagi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_normal_bits_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) override; /* Forward ID */ @@ -546,12 +546,12 @@ class RenderForwardMobile : public RendererSceneRenderRD { virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth) override; - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - virtual void sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - virtual int sdfgi_get_pending_region_count(const Ref &p_render_buffers) const override { return 0; } - virtual AABB sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { return AABB(); } - virtual uint32_t sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { return 0; } + virtual void hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + virtual int hddagi_get_pending_region_count(const Ref &p_render_buffers) const override { return 0; } + virtual AABB hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const override { return AABB(); } + virtual uint32_t hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const override { return 0; } /* GEOMETRY INSTANCE */ diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 9ec4c36e9b7c..2131fedbffa6 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -87,15 +87,15 @@ void RendererSceneRenderRD::environment_set_volumetric_fog_filter_active(bool p_ volumetric_fog_filter_active = p_enable; } -void RendererSceneRenderRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { - gi.sdfgi_ray_count = p_ray_count; +void RendererSceneRenderRD::environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) { + gi.hddagi_frames_to_converge = p_frames; } - -void RendererSceneRenderRD::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) { - gi.sdfgi_frames_to_converge = p_frames; +void RendererSceneRenderRD::environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) { + gi.hddagi_frames_to_update_light = p_update; } -void RendererSceneRenderRD::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) { - gi.sdfgi_frames_to_update_light = p_update; + +void RendererSceneRenderRD::environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) { + gi.inactive_probe_frames = p_frames; } Ref RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) { @@ -220,16 +220,17 @@ void RendererSceneRenderRD::voxel_gi_update(RID p_probe, bool p_update_light_ins gi.voxel_gi_update(p_probe, p_update_light_instances, p_light_instances, p_dynamic_objects); } -void RendererSceneRenderRD::_debug_sdfgi_probes(Ref p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { +void RendererSceneRenderRD::_debug_hddagi_probes(Ref p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { ERR_FAIL_COND(p_render_buffers.is_null()); - if (!p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + if (!p_render_buffers->has_custom_data(RB_SCOPE_HDDAGI)) { return; //nothing to debug } - Ref sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); + Ref hddagi = p_render_buffers->get_custom_data(RB_SCOPE_HDDAGI); + + hddagi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms); - sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms); } //////////////////////////////// @@ -1091,7 +1092,7 @@ void RendererSceneRenderRD::_update_vrs(Ref p_render_buffe bool RendererSceneRenderRD::_needs_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi) { if (p_render_data->render_buffers.is_valid()) { - if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_HDDAGI)) { return true; } } @@ -1100,16 +1101,17 @@ bool RendererSceneRenderRD::_needs_post_prepass_render(RenderDataRD *p_render_da void RendererSceneRenderRD::_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi) { if (p_render_data->render_buffers.is_valid() && p_use_gi) { - if (!p_render_data->render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + if (!p_render_data->render_buffers->has_custom_data(RB_SCOPE_HDDAGI)) { return; } - Ref sdfgi = p_render_data->render_buffers->get_custom_data(RB_SCOPE_SDFGI); - sdfgi->update_probes(p_render_data->environment, sky.sky_owner.get_or_null(environment_get_sky(p_render_data->environment))); + Ref hddagi = p_render_data->render_buffers->get_custom_data(RB_SCOPE_HDDAGI); + hddagi->update_probes(p_render_data->environment, sky.sky_owner.get_or_null(environment_get_sky(p_render_data->environment)), p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform); } } -void RendererSceneRenderRD::render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RenderingMethod::RenderInfo *r_render_info) { +void RendererSceneRenderRD::render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data, RenderingMethod::RenderInfo *r_render_info) { + RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); @@ -1200,9 +1202,9 @@ void RendererSceneRenderRD::render_scene(const Ref &p_render render_data.render_shadows = p_render_shadows; render_data.render_shadow_count = p_render_shadow_count; - render_data.render_sdfgi_regions = p_render_sdfgi_regions; - render_data.render_sdfgi_region_count = p_render_sdfgi_region_count; - render_data.sdfgi_update_data = p_sdfgi_update_data; + render_data.render_hddagi_regions = p_render_hddagi_regions; + render_data.render_hddagi_region_count = p_render_hddagi_region_count; + render_data.hddagi_update_data = p_hddagi_update_data; render_data.render_info = r_render_info; @@ -1409,9 +1411,9 @@ TypedArray RendererSceneRenderRD::bake_render_uv2(RID p_base, const Typed return ret; } -void RendererSceneRenderRD::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { - gi.sdfgi_debug_probe_pos = p_position; - gi.sdfgi_debug_probe_dir = p_dir; +void RendererSceneRenderRD::hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { + gi.hddagi_debug_probe_pos = p_position; + gi.hddagi_debug_probe_dir = p_dir; } RendererSceneRenderRD *RendererSceneRenderRD::singleton = nullptr; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 066d995bf266..7c65e9909283 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -89,10 +89,10 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; - virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0; + virtual void _render_hddagi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_normal_bits_texture, float p_exposure_normalization) = 0; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) = 0; - void _debug_sdfgi_probes(Ref p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms); + void _debug_hddagi_probes(Ref p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms); virtual RID _render_buffers_get_normal_texture(Ref p_render_buffers) = 0; virtual RID _render_buffers_get_velocity_texture(Ref p_render_buffers) = 0; @@ -195,9 +195,10 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override; virtual void environment_set_volumetric_fog_filter_active(bool p_enable) override; - virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; - virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; - virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override; + virtual void environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) override; + virtual void environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) override; + + virtual void environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) override; virtual Ref environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; @@ -241,7 +242,7 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void base_uniforms_changed() = 0; - virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; + virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; @@ -328,7 +329,7 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void set_time(double p_time, double p_step) override; - virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; + virtual void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; virtual bool is_vrs_supported() const; virtual bool is_dynamic_gi_supported() const; diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index 480172f9dcae..8b65b5bdc801 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -8,7 +8,12 @@ #extension GL_EXT_samplerless_texture_functions : enable #endif -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +#define square(m) ((m) * (m)) + +#define GROUP_SIZE 8 +#define DITHER_SIZE 2 + +layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in; #define M_PI 3.141592 @@ -18,64 +23,58 @@ layout(constant_id = 0) const bool sc_half_res = false; layout(constant_id = 1) const bool sc_use_full_projection_matrix = false; layout(constant_id = 2) const bool sc_use_vrs = false; -#define SDFGI_MAX_CASCADES 8 +#define HDDAGI_MAX_CASCADES 8 + +//set 0 for HDDAGI and render buffers + +layout(rg32ui, set = 0, binding = 1) uniform restrict readonly uimage3D voxel_cascades; +layout(r8ui, set = 0, binding = 2) uniform restrict readonly uimage3D voxel_region_cascades; +layout(set = 0, binding = 3) uniform texture3D light_cascades; -//set 0 for SDFGI and render buffers +layout(set = 0, binding = 4) uniform texture2DArray lightprobe_specular; +layout(set = 0, binding = 5) uniform texture2DArray lightprobe_diffuse; +layout(set = 0, binding = 6) uniform texture3D occlusion[2]; -layout(set = 0, binding = 1) uniform texture3D sdf_cascades[SDFGI_MAX_CASCADES]; -layout(set = 0, binding = 2) uniform texture3D light_cascades[SDFGI_MAX_CASCADES]; -layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[SDFGI_MAX_CASCADES]; -layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[SDFGI_MAX_CASCADES]; -layout(set = 0, binding = 5) uniform texture3D occlusion_texture; +layout(set = 0, binding = 7) uniform sampler linear_sampler; +layout(set = 0, binding = 8) uniform sampler linear_sampler_with_mipmaps; -layout(set = 0, binding = 6) uniform sampler linear_sampler; -layout(set = 0, binding = 7) uniform sampler linear_sampler_with_mipmaps; +layout(r8ui, set = 0, binding = 9) uniform restrict readonly uimage3D voxel_disocclusion; +layout(r32ui, set = 0, binding = 10) uniform restrict readonly uimage3D voxel_neighbours; struct ProbeCascadeData { vec3 position; float to_probe; - ivec3 probe_world_offset; + + ivec3 region_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; float exposure_normalization; -}; - -layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer; -layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D reflection_buffer; -layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture; + uvec4 pad2; +}; layout(set = 0, binding = 12) uniform texture2D depth_buffer; layout(set = 0, binding = 13) uniform texture2D normal_roughness_buffer; layout(set = 0, binding = 14) uniform utexture2D voxel_gi_buffer; -layout(set = 0, binding = 15, std140) uniform SDFGI { - vec3 grid_size; - uint max_cascades; +layout(set = 0, binding = 15, std140) uniform HDDAGI { + ivec3 grid_size; + int max_cascades; - bool use_occlusion; - int probe_axis_size; - float probe_to_uvw; float normal_bias; - - vec3 lightprobe_tex_pixel_size; float energy; - - vec3 lightprobe_uv_offset; float y_mult; + float reflection_bias; - vec3 occlusion_clamp; - uint pad3; - - vec3 occlusion_renormalize; - uint pad4; + ivec3 probe_axis_size; + float esm_strength; - vec3 cascade_probe_size; - uint pad5; + uvec4 pad3; - ProbeCascadeData cascades[SDFGI_MAX_CASCADES]; + ProbeCascadeData cascades[HDDAGI_MAX_CASCADES]; } -sdfgi; +hddagi; #define MAX_VOXEL_GI_INSTANCES 8 @@ -115,6 +114,9 @@ scene_data; #ifdef USE_VRS layout(r8ui, set = 0, binding = 19) uniform restrict readonly uimage2D vrs_buffer; #endif +layout(r32ui, set = 0, binding = 20) uniform restrict writeonly uimage2D ambient_buffer; +layout(r32ui, set = 0, binding = 21) uniform restrict writeonly uimage2D reflection_buffer; +layout(rg8, set = 0, binding = 22) uniform restrict writeonly image2D blend_buffer; layout(push_constant, std430) uniform Params { uint max_voxel_gi_instances; @@ -126,11 +128,15 @@ layout(push_constant, std430) uniform Params { float z_near; float z_far; - float pad2; - float pad3; + uint pad; + float occlusion_bias; } params; +shared vec3 group_positions[GROUP_SIZE * GROUP_SIZE]; +shared vec4 group_reflections[GROUP_SIZE * GROUP_SIZE]; +shared vec3 group_normals[GROUP_SIZE * GROUP_SIZE]; + vec2 octahedron_wrap(vec2 v) { vec2 signVal; signVal.x = v.x >= 0.0 ? 1.0 : -1.0; @@ -158,6 +164,21 @@ vec4 blend_color(vec4 src, vec4 dst) { return res; } +uint rgbe_encode(vec3 rgb) { + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); + + rgb = clamp(rgb, 0, rgbe_max); + + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); + + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); + + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + return e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); +} + vec3 reconstruct_position(ivec2 screen_pos) { if (sc_use_full_projection_matrix) { vec4 pos; @@ -189,105 +210,459 @@ vec3 reconstruct_position(ivec2 screen_pos) { } } -void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) { - cascade_pos += cam_normal * sdfgi.normal_bias; +vec4 fetch_normal_and_roughness(ivec2 pos) { + vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0); + normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0); + return normal_roughness; +} - vec3 base_pos = floor(cascade_pos); - //cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal; - ivec3 probe_base_pos = ivec3(base_pos); +#define PROBE_CELLS 8 +#define OCC_DISTANCE_MAX 16.0 +#define REGION_SIZE 8 - vec4 diffuse_accum = vec4(0.0); - vec3 specular_accum; +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} - ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade)); - tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size; - tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1); +ivec2 probe_to_tex(ivec3 local_probe, int p_cascade) { + ivec3 cell = modi(hddagi.cascades[p_cascade].region_world_offset + local_probe, hddagi.probe_axis_size); + return cell.xy + ivec2(0, cell.z * int(hddagi.probe_axis_size.y)); +} - vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size; +#define ROUGHNESS_TO_REFLECTION_TRESHOOLD 0.25 - vec3 specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size; +bool bayer_dither(float value) { +#if DITHER_SIZE == 3 - specular_accum = vec3(0.0); + uvec2 dt = gl_GlobalInvocationID.xy % 3; + uint index = dt.x + dt.y * 3; - vec4 light_accum = vec4(0.0); - float weight_accum = 0.0; + float table[9] = float[9](0.0 / 9.0, 7.0 / 9.0, 3.0 / 9.0, + 6.0 / 9.0, 5.0 / 9.0, 2.0 / 9.0, + 4.0 / 9.0, 1.0 / 9.0, 8.0 / 9.0); - for (uint j = 0; j < 8; j++) { - ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); - ivec3 probe_posi = probe_base_pos; - probe_posi += offset; + return value > table[index]; +#else - // Compute weight + uvec2 dt = gl_GlobalInvocationID.xy & 0x1; + uint index = dt.x + dt.y * 2; - vec3 probe_pos = vec3(probe_posi); - vec3 probe_to_pos = cascade_pos - probe_pos; - vec3 probe_dir = normalize(-probe_to_pos); + const float table[4] = float[](0.0, 0.5, 0.75, 0.25); - vec3 trilinear = vec3(1.0) - abs(probe_to_pos); - float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir)); + return value > table[index]; + +#endif + +#if 0 + uvec2 dt = gl_GlobalInvocationID.xy & 0x3; + uint index = dt.x + dt.y * 4; + + const float table[16] = float[](0.0625, 0.5625, 0.1875, 0.6875, 0.8125, 0.3125, 0.9375, 0.4375, 0.25, 0.75, 0.125, 0.625, 1.0, 0.5, 0.875, 0.375); + + return value > table[index]; +#endif +} + +uint hash(uint x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +float dist_to_box(vec3 min_bound, vec3 max_bound, bvec3 step, vec3 pos, vec3 inv_dir) { + vec3 box = (mix(min_bound, max_bound, step) - pos) * inv_dir; + return min(box.x, min(box.y, box.z)); +} + +float point_to_ray_distance(vec3 point, vec3 ray_origin, vec3 ray_direction) { + // Normalize the ray direction + vec3 dir_normalized = normalize(ray_direction); + + // Compute the vector from the ray origin to the point + vec3 vec_to_point = point - ray_origin; + + // Project the vector to point onto the ray direction + float t = dot(vec_to_point, dir_normalized); + + // Calculate the projection point on the ray + vec3 projection = ray_origin + t * dir_normalized; + + // Return the distance between the point and its projection on the ray + return length(point - projection); +} + +//find arbitrary tangent and bitangent, then build a matrix +mat3 create_basis_from_normal(vec3 normal) { + vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); + vec3 tangent = normalize(cross(v0, normal)); + vec3 bitangent = normalize(cross(tangent, normal)); + return mat3(tangent, bitangent, normal); +} + +vec3 vogel_hemisphere(int i, int n, float f) { + float goldenAngle = 137.5077640500378546463487 * float(i); + float theta = radians(goldenAngle); + + float phi = acos(1.0 - 2.0 * (float(i) + f) / float(n)); + + float x = sin(phi) * cos(theta); + float y = sin(phi) * sin(theta); + float z = cos(phi); + + return vec3(x, y, z); +} + +bool trace_ray_hdda(vec3 ray_pos, vec3 ray_dir, int p_cascade, out ivec3 r_cell, out ivec3 r_side, out int r_cascade) { + const int LEVEL_CASCADE = -1; + const int LEVEL_REGION = 0; + const int LEVEL_BLOCK = 1; + const int LEVEL_VOXEL = 2; + const int MAX_LEVEL = 3; + + //#define HQ_RAY + +#ifdef HQ_RAY + const int fp_bits = 16; +#else + const int fp_bits = 10; +#endif + const int fp_block_bits = fp_bits + 2; + const int fp_region_bits = fp_block_bits + 1; + const int fp_cascade_bits = fp_region_bits + 4; - // Compute lightprobe occlusion + bvec3 limit_dir = greaterThan(ray_dir, vec3(0.0)); + ivec3 step = mix(ivec3(0), ivec3(1), limit_dir); + ivec3 ray_sign = ivec3(sign(ray_dir)); - if (sdfgi.use_occlusion) { - ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); - vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); + ivec3 ray_dir_fp = ivec3(ray_dir * float(1 << fp_bits)); - vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw; - occ_pos.z += float(cascade); - if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures - occ_pos.x += 1.0; +#ifdef HQ_RAY + const float limit = 1.0 / 65535.0; +#else + const float limit = 1.0 / 127.0; +#endif + bvec3 ray_zero = lessThan(abs(ray_dir), vec3(limit)); + ivec3 inv_ray_dir_fp = ivec3(float(1 << fp_bits) / ray_dir); + + const ivec3 level_masks[MAX_LEVEL] = ivec3[]( + ivec3(1 << fp_region_bits) - ivec3(1), + ivec3(1 << fp_block_bits) - ivec3(1), + ivec3(1 << fp_bits) - ivec3(1)); + + ivec3 region_offset_mask = (ivec3(hddagi.grid_size) / REGION_SIZE) - ivec3(1); + + ivec3 limits[MAX_LEVEL]; + + limits[LEVEL_REGION] = ((ivec3(hddagi.grid_size) << fp_bits) - ivec3(1)) * step; // Region limit does not change, so initialize now. + ivec3 scroll_limit; + + // Initialize to cascade + int level = LEVEL_CASCADE; + int cascade = p_cascade - 1; + + ivec3 cascade_base; + ivec3 region_base; + uvec2 block; + bool hit = false; + ivec3 pos; + + while (true) { + // This loop is written so there is only one single main iteration. + // This ensures that different compute threads working on different + // levels can still run together without blocking each other. + + if (level == LEVEL_VOXEL) { + // The first level should be (in a worst case scenario) the most used + // so it needs to appear first. The rest of the levels go from more to least used order. + + ivec3 block_local = (pos & level_masks[LEVEL_BLOCK]) >> fp_bits; + uint block_index = uint(block_local.z * 16 + block_local.y * 4 + block_local.x); + if (block_index < 32) { + // Low 32 bits. + if (bool(block.x & uint(1 << block_index))) { + hit = true; + break; + } + } else { + // High 32 bits. + block_index -= 32; + if (bool(block.y & uint(1 << block_index))) { + hit = true; + break; + } + } + } else if (level == LEVEL_BLOCK) { + ivec3 block_local = (pos & level_masks[LEVEL_REGION]) >> fp_block_bits; + block = imageLoad(voxel_cascades, region_base + block_local).rg; + if (block != uvec2(0)) { + // Have voxels inside + level = LEVEL_VOXEL; + limits[LEVEL_VOXEL] = pos - (pos & level_masks[LEVEL_BLOCK]) + step * (level_masks[LEVEL_BLOCK] + ivec3(1)); + continue; } + } else if (level == LEVEL_REGION) { + ivec3 region = pos >> fp_region_bits; + region = (hddagi.cascades[cascade].region_world_offset + region) & region_offset_mask; // Scroll to world + region += cascade_base; + bool region_used = imageLoad(voxel_region_cascades, region).r > 0; + + if (region_used) { + // The region has contents. + region_base = (region << 1); + level = LEVEL_BLOCK; + limits[LEVEL_BLOCK] = pos - (pos & level_masks[LEVEL_REGION]) + step * (level_masks[LEVEL_REGION] + ivec3(1)); + continue; + } + } else if (level == LEVEL_CASCADE) { + // Return to global + if (cascade >= p_cascade) { + ray_pos = vec3(pos) / float(1 << fp_bits); + ray_pos /= hddagi.cascades[cascade].to_cell; + ray_pos += hddagi.cascades[cascade].position; + } + + cascade++; + if (cascade == hddagi.max_cascades) { + break; + } + + ray_pos -= hddagi.cascades[cascade].position; + ray_pos *= hddagi.cascades[cascade].to_cell; + pos = ivec3(ray_pos * float(1 << fp_bits)); + if (any(lessThan(pos, ivec3(0))) || any(greaterThanEqual(pos, ivec3(hddagi.grid_size) << fp_bits))) { + // Outside this cascade, go to next. + continue; + } + + cascade_base = ivec3(0, int(hddagi.grid_size.y / REGION_SIZE) * cascade, 0); + level = LEVEL_REGION; - occ_pos *= sdfgi.occlusion_renormalize; - float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask); + // Put a limit so the jump to the next level is not so strong - weight *= max(occlusion, 0.01); + { + vec3 box = (vec3(hddagi.grid_size * step) - ray_pos) / ray_dir; + vec3 axis = vec3(1, 0, 0); + float m = box.x; + if (box.y < m) { + m = box.y; + axis = vec3(0, 1, 0); + } + + if (box.z < m) { + axis = vec3(0, 0, 1); + } + + vec3 half_size = vec3(hddagi.grid_size) / 2.0; + vec3 inner_pos = -hddagi.cascades[cascade].position * hddagi.cascades[cascade].to_cell - half_size; + + float inner_dir = dot(axis, inner_pos); + float blend = abs(inner_dir) / float(REGION_SIZE * 0.5); + + scroll_limit = limits[LEVEL_REGION]; + if (bayer_dither(blend)) { + scroll_limit += (ivec3(axis * sign(inner_dir)) * REGION_SIZE) << fp_bits; + } + } + + continue; } - // Compute lightprobe texture position + // Fixed point, multi-level DDA. - vec3 diffuse; - vec3 pos_uvw = diffuse_posf; - pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; - pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; - diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb; + ivec3 mask = level_masks[level]; + ivec3 box = mask * step; + ivec3 pos_diff = box - (pos & mask); +#ifdef HQ_RAY + ivec3 mul_res = mul64(pos_diff, inv_ray_dir_fp, fp_bits); +#else + ivec3 mul_res = (pos_diff * inv_ray_dir_fp) >> fp_bits; +#endif + ivec3 tv = mix(mul_res, ivec3(0x7FFFFFFF), ray_zero); + int t = min(tv.x, min(tv.y, tv.z)); - diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); + // The general idea here is that we _always_ need to increment to the closest next cell + // (this is a DDA after all), so adv_box forces this increment for the minimum axis. - { - vec3 specular = vec3(0.0); - vec3 pos_uvw = specular_posf; - pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; - pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; - if (roughness < 0.99) { - specular = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb; + ivec3 adv_box = pos_diff + ray_sign; +#ifdef HQ_RAY + ivec3 adv_t = mul64(ray_dir_fp, ivec3(t), fp_bits); +#else + ivec3 adv_t = (ray_dir_fp * t) >> fp_bits; +#endif + pos += mix(adv_t, adv_box, equal(ivec3(t), tv)); + + { // Test against scroll limit. + bvec3 limit = lessThan(pos, scroll_limit); + bvec3 eq = equal(limit, limit_dir); + if (!all(eq)) { + // Hit scroll limit, clamp limit and go to next cascade. + level = LEVEL_CASCADE; + pos = mix(scroll_limit, pos, eq); + continue; + } + } + + while (true) { + bvec3 limit = lessThan(pos, limits[level]); + bool inside = all(equal(limit, limit_dir)); + if (inside) { + break; } - if (roughness > 0.2) { - specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25); + level -= 1; + if (level == LEVEL_CASCADE) { + break; } + } + } - specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; + if (hit) { + ivec3 mask = level_masks[LEVEL_VOXEL]; + ivec3 box = mask * (step ^ ivec3(1)); + ivec3 pos_diff = box - (pos & mask); +#ifdef HQ_RAY + ivec3 mul_res = mul64(pos_diff, -inv_ray_dir_fp, fp_bits); +#else + ivec3 mul_res = (pos_diff * -inv_ray_dir_fp); +#endif + + ivec3 tv = mix(mul_res, ivec3(0x7FFFFFFF), ray_zero); + + int m; + if (tv.x < tv.y) { + r_side = ivec3(1, 0, 0); + m = tv.x; + } else { + r_side = ivec3(0, 1, 0); + m = tv.y; + } + if (tv.z < m) { + r_side = ivec3(0, 0, 1); } + + r_side *= -ray_sign; + + r_cell = pos >> fp_bits; + + r_cascade = cascade; } - if (diffuse_accum.a > 0.0) { - diffuse_accum.rgb /= diffuse_accum.a; + return hit; +} + +void sdfvoxel_gi_process(int cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, bool dynamic_object, out vec3 diffuse_light, out vec3 specular_light) { + vec3 posf = cascade_pos; + + if (!dynamic_object) { + posf += cam_normal * hddagi.normal_bias; } - diffuse_light = diffuse_accum.rgb; + ivec3 posi = ivec3(posf); + ivec3 base_probe = posi / PROBE_CELLS; + + vec3 diffuse_accum = vec3(0.0); + vec3 specular_accum = vec3(0.0); + float weight_accum = 0.0; + + ivec3 occ_pos = posi; // faster and numerically safer to do this computation as ints + vec3 pos_fract = posf - vec3(posi); + occ_pos = (occ_pos + hddagi.cascades[cascade].region_world_offset * PROBE_CELLS) & (hddagi.grid_size - 1); + occ_pos.y += (hddagi.grid_size.y + 2) * cascade; + occ_pos += ivec3(1); + ivec3 occ_total_size = hddagi.grid_size + ivec3(2); + occ_total_size.y *= hddagi.max_cascades; + vec3 occ_posf = (vec3(occ_pos) + pos_fract) / vec3(occ_total_size); + + vec4 occ_0 = texture(sampler3D(occlusion[0], linear_sampler), occ_posf); + vec4 occ_1 = texture(sampler3D(occlusion[1], linear_sampler), occ_posf); + + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); + + vec4 accum_light = vec4(0.0); + + vec2 light_probe_tex_to_uv = 1.0 / vec2((LIGHTPROBE_OCT_SIZE + 2) * hddagi.probe_axis_size.x, (LIGHTPROBE_OCT_SIZE + 2) * hddagi.probe_axis_size.y * hddagi.probe_axis_size.z); + vec2 light_uv = octahedron_encode(vec3(cam_normal)) * float(LIGHTPROBE_OCT_SIZE); + vec2 light_uv_spec = octahedron_encode(vec3(cam_specular_normal)) * float(LIGHTPROBE_OCT_SIZE); + + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + + vec3 probe_pos = vec3(probe * PROBE_CELLS); + + vec3 probe_to_pos = posf - probe_pos; + vec3 n = normalize(probe_to_pos); + float d = length(probe_to_pos); + + float weight = 1.0; + //weight *= pow(max(0.0001, (dot(-n, cam_normal) + 1.0) * 0.5),2.0) + 0.2; + if (!dynamic_object) { + weight *= max(0.005, (dot(-n, cam_normal))); + } + + ivec3 probe_occ = (hddagi.cascades[cascade].region_world_offset + probe) & ivec3(1); + + uint weight_index = 0; + if (probe_occ.x != 0) { + weight_index |= 1; + } + if (probe_occ.y != 0) { + weight_index |= 2; + } + if (probe_occ.z != 0) { + weight_index |= 4; + } + + weight *= max(params.occlusion_bias, occ_weights[weight_index]); + + vec3 trilinear = vec3(1.0) - abs(probe_to_pos / float(PROBE_CELLS)); + + weight *= trilinear.x * trilinear.y * trilinear.z; + + ivec2 tex_pos = probe_to_tex(probe, cascade); + vec2 base_tex_uv = vec2(ivec2(tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1))); + vec2 tex_uv = base_tex_uv + light_uv; + tex_uv *= light_probe_tex_to_uv; + + vec3 probe_light = texture(sampler2DArray(lightprobe_diffuse, linear_sampler), vec3(tex_uv, float(cascade))).rgb; + + diffuse_accum += probe_light * weight; + + tex_uv = base_tex_uv + light_uv_spec; + tex_uv *= light_probe_tex_to_uv; + + vec3 probe_ref_light; + if (roughness < 0.99 && roughness > 0.00) { + probe_ref_light = texture(sampler2DArray(lightprobe_specular, linear_sampler), vec3(tex_uv, float(cascade))).rgb; + } else { + probe_ref_light = vec3(0.0); + } + + vec3 probe_ref_full_light; + if (roughness > ROUGHNESS_TO_REFLECTION_TRESHOOLD) { + probe_ref_full_light = texture(sampler2DArray(lightprobe_diffuse, linear_sampler), vec3(tex_uv, float(cascade))).rgb; + } else { + probe_ref_full_light = vec3(0.0); + } - if (diffuse_accum.a > 0.0) { - specular_accum /= diffuse_accum.a; + probe_ref_light = mix(probe_ref_light, probe_ref_full_light, smoothstep(ROUGHNESS_TO_REFLECTION_TRESHOOLD, 1.0, roughness)); + + specular_accum += probe_ref_light * weight; + + weight_accum += weight; } - specular_light = specular_accum; + diffuse_light = diffuse_accum / weight_accum; + specular_light = specular_accum / weight_accum; } -void sdfgi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, out vec4 ambient_light, out vec4 reflection_light) { +void hddagi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, bool dynamic_object, out vec4 ambient_light, out vec4 reflection_light) { //make vertex orientation the world one, but still align to camera - vertex.y *= sdfgi.y_mult; - normal.y *= sdfgi.y_mult; - reflection.y *= sdfgi.y_mult; + vertex.y *= hddagi.y_mult; + normal.y *= hddagi.y_mult; + reflection.y *= hddagi.y_mult; //renormalize normal = normalize(normal); @@ -306,175 +681,245 @@ void sdfgi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, o // helper constants, compute once - uint cascade = 0xFFFFFFFF; + int cascade = 0x7FFFFFFF; vec3 cascade_pos; vec3 cascade_normal; + float cell_size; - for (uint i = 0; i < sdfgi.max_cascades; i++) { - cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe; + for (int i = 0; i < hddagi.max_cascades; i++) { + cascade_pos = (cam_pos - hddagi.cascades[i].position) * hddagi.cascades[i].to_cell; - if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) { + if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, vec3(hddagi.grid_size)))) { continue; //skip cascade } cascade = i; + cell_size = 1.0 / hddagi.cascades[i].to_cell; + break; } - if (cascade < SDFGI_MAX_CASCADES) { + if (cascade < HDDAGI_MAX_CASCADES) { ambient_light = vec4(0, 0, 0, 1); reflection_light = vec4(0, 0, 0, 1); - float blend; vec3 diffuse, specular; - sdfvoxel_gi_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse, specular); - + float blend; { //process blend - float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5; - float blend_to = blend_from + 2.0; + vec3 blend_from = ((vec3(hddagi.probe_axis_size) - 1) / 2.0); - vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe; + vec3 inner_pos = cam_pos * hddagi.cascades[cascade].to_probe; - float len = length(inner_pos); + vec3 inner_dist = blend_from - abs(inner_pos); - inner_pos = abs(normalize(inner_pos)); - len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + float min_d = min(inner_dist.x, min(inner_dist.y, inner_dist.z)); - if (len >= blend_from) { - blend = smoothstep(blend_from, blend_to, len); - } else { + blend = clamp(1.0 - smoothstep(0.5, 2.5, min_d), 0, 1); + + if (cascade < hddagi.max_cascades - 1) { + cell_size = mix(cell_size, 1.0 / hddagi.cascades[cascade + 1].to_cell, blend); + } + +#ifndef USE_AMBIENT_BLEND + if (cascade < hddagi.max_cascades - 1) { + if (bayer_dither(blend)) { + cascade++; + cascade_pos = (cam_pos - hddagi.cascades[cascade].position) * hddagi.cascades[cascade].to_cell; + } blend = 0.0; } +#endif } - if (blend > 0.0) { - //blend - if (cascade == sdfgi.max_cascades - 1) { - ambient_light.a = 1.0 - blend; - reflection_light.a = 1.0 - blend; + sdfvoxel_gi_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, dynamic_object, diffuse, specular); - } else { +#ifdef USE_AMBIENT_BLEND + + if (blend > 0.0) { + if (cascade < hddagi.max_cascades - 1) { + vec3 blend_cascade_pos = (cam_pos - hddagi.cascades[cascade + 1].position) * hddagi.cascades[cascade + 1].to_cell; vec3 diffuse2, specular2; - cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe; - sdfvoxel_gi_process(cascade + 1, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse2, specular2); + sdfvoxel_gi_process(cascade + 1, blend_cascade_pos, cam_pos, cam_normal, reflection, roughness, dynamic_object, diffuse2, specular2); diffuse = mix(diffuse, diffuse2, blend); specular = mix(specular, specular2, blend); - } - } - ambient_light.rgb = diffuse; - - if (roughness < 0.2) { - vec3 pos_to_uvw = 1.0 / sdfgi.grid_size; - vec4 light_accum = vec4(0.0); + if (bayer_dither(blend)) { + // Apply dither for roughness here. + cascade++; + cascade_pos = (cam_pos - hddagi.cascades[cascade].position) * hddagi.cascades[cascade].to_cell; + } - float blend_size = (sdfgi.grid_size.x / float(sdfgi.probe_axis_size - 1)) * 0.5; + blend = 0.0; + } + } - float radius_sizes[SDFGI_MAX_CASCADES]; - cascade = 0xFFFF; +#endif - float base_distance = length(cam_pos); - for (uint i = 0; i < sdfgi.max_cascades; i++) { - radius_sizes[i] = (1.0 / sdfgi.cascades[i].to_cell) * (sdfgi.grid_size.x * 0.5 - blend_size); - if (cascade == 0xFFFF && base_distance < radius_sizes[i]) { - cascade = i; - } - } + ambient_light.rgb = diffuse; + ambient_light.a = 1.0 - blend; - cascade = min(cascade, sdfgi.max_cascades - 1); + if (roughness < ROUGHNESS_TO_REFLECTION_TRESHOOLD) { + ivec3 hit_cell; + ivec3 hit_face; + int hit_cascade; + vec4 light = vec4(0); - float max_distance = radius_sizes[sdfgi.max_cascades - 1]; vec3 ray_pos = cam_pos; vec3 ray_dir = reflection; - { - float prev_radius = cascade > 0 ? radius_sizes[cascade - 1] : 0.0; - float base_blend = (base_distance - prev_radius) / (radius_sizes[cascade] - prev_radius); - float bias = (1.0 + base_blend) * 1.1; - vec3 abs_ray_dir = abs(ray_dir); - //ray_pos += ray_dir * (bias / sdfgi.cascades[cascade].to_cell); //bias to avoid self occlusion - ray_pos += (ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) + cam_normal * 1.4) * bias / sdfgi.cascades[cascade].to_cell; - } - float softness = 0.2 + min(1.0, roughness * 5.0) * 4.0; //approximation to roughness so it does not seem like a hard fade - uint i = 0; - bool found = false; - while (true) { - if (length(ray_pos) >= max_distance || light_accum.a > 0.99) { - break; - } - if (!found && i >= cascade && length(ray_pos) < radius_sizes[i]) { - uint next_i = min(i + 1, sdfgi.max_cascades - 1); - cascade = max(i, cascade); //never go down - - vec3 pos = ray_pos - sdfgi.cascades[i].position; - pos *= sdfgi.cascades[i].to_cell * pos_to_uvw; + vec3 start_cell = (ray_pos - hddagi.cascades[cascade].position) * hddagi.cascades[cascade].to_cell; - float fdistance = textureLod(sampler3D(sdf_cascades[i], linear_sampler), pos, 0.0).r * 255.0 - 1.1; + { // Bias ray - vec4 hit_light = vec4(0.0); - if (fdistance < softness) { - hit_light.rgb = textureLod(sampler3D(light_cascades[i], linear_sampler), pos, 0.0).rgb; - hit_light.rgb *= 0.5; //approximation given value read is actually meant for anisotropy - hit_light.a = clamp(1.0 - (fdistance / softness), 0.0, 1.0); - hit_light.rgb *= hit_light.a; - } - - fdistance /= sdfgi.cascades[i].to_cell; + vec3 abs_cam_normal = abs(cam_normal); + vec3 ray_bias = cam_normal * 1.0 / max(abs_cam_normal.x, max(abs_cam_normal.y, abs_cam_normal.z)); - if (i < (sdfgi.max_cascades - 1)) { - pos = ray_pos - sdfgi.cascades[next_i].position; - pos *= sdfgi.cascades[next_i].to_cell * pos_to_uvw; + start_cell += ray_bias * hddagi.reflection_bias; // large bias to pass through the reflector cell. + ray_pos = start_cell / hddagi.cascades[cascade].to_cell + hddagi.cascades[cascade].position; + } - float fdistance2 = textureLod(sampler3D(sdf_cascades[next_i], linear_sampler), pos, 0.0).r * 255.0 - 1.1; + mat3 normal_mat = create_basis_from_normal(ray_dir); + vec3 n = vogel_hemisphere(int((gl_GlobalInvocationID.x % DITHER_SIZE) + (gl_GlobalInvocationID.y % DITHER_SIZE) * DITHER_SIZE), DITHER_SIZE * DITHER_SIZE, 0.0); + n = normalize(mix(vec3(0, 0, -1), n, roughness * roughness)); + n.z = -n.z; + n = normal_mat * n; + + if (trace_ray_hdda(ray_pos, n, cascade, hit_cell, hit_face, hit_cascade)) { + bool valid = true; + bool disoccluded = false; + if (hit_cascade == cascade) { + if (ivec3(start_cell) == hit_cell) { + // self hit the start cell, ouch, load the disocclusion + + ivec3 read_cell = (hit_cell + (hddagi.cascades[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(hddagi.grid_size) - 1); + uint disocc = imageLoad(voxel_disocclusion, read_cell + ivec3(0, (hddagi.grid_size.y * hit_cascade), 0)).r; + + if (disocc == 0) { + // Can happen.. guess. + vec3 abs_normal = abs(cam_normal); + // Find closest normal to cam normal. + int ni = 0; + float m = abs_normal.x; + if (abs_normal.y > m) { + m = abs_normal.y; + ni = 1; + } + if (abs_normal.z > m) { + ni = 2; + } + + vec3 local = fract(start_cell) - 0.5; // create local cell. + + const vec3 axes[5] = vec3[](vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1), vec3(1, 0, 0), vec3(0, 1, 0)); + // Find the closest axis to push. + vec3 ax_a = axes[ni + 1]; + vec3 ax_b = axes[ni + 2]; + + vec3 advance; + if (abs(dot(ax_a, local)) > abs(dot(ax_b, local))) { + advance = ax_a * sign(local); + } else { + advance = ax_b * sign(local); + } + + start_cell += advance; + hit_cell += ivec3(advance); + + read_cell = (hit_cell + (hddagi.cascades[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(hddagi.grid_size) - 1); + disocc = imageLoad(voxel_disocclusion, read_cell).r; + } - vec4 hit_light2 = vec4(0.0); - if (fdistance2 < softness) { - hit_light2.rgb = textureLod(sampler3D(light_cascades[next_i], linear_sampler), pos, 0.0).rgb; - hit_light2.rgb *= 0.5; //approximation given value read is actually meant for anisotropy - hit_light2.a = clamp(1.0 - (fdistance2 / softness), 0.0, 1.0); - hit_light2.rgb *= hit_light2.a; + // find best disocclusion direction. + + vec3 local = fract(start_cell) - 0.5; // create local cell. + + const vec3 aniso_dir[6] = vec3[]( + vec3(-1, 0, 0), + vec3(1, 0, 0), + vec3(0, -1, 0), + vec3(0, 1, 0), + vec3(0, 0, -1), + vec3(0, 0, 1)); + + int best_axis = 0; + float best_d = -20; + for (int i = 0; i < 6; i++) { + if (bool(disocc & (1 << i))) { + float d = dot(local, aniso_dir[i]); + if (d > best_d) { + best_axis = i; + best_d = d; + } + } } - float prev_radius = i == 0 ? 0.0 : radius_sizes[max(0, i - 1)]; - float blend = clamp((length(ray_pos) - prev_radius) / (radius_sizes[i] - prev_radius), 0.0, 1.0); + hit_face = ivec3(aniso_dir[best_axis]); - fdistance2 /= sdfgi.cascades[next_i].to_cell; + /* + if (disocc == 0) { + light.rgb = vec3(1,0,0); + } else { + light.rgb = vec3(0,1,0); + } + light.rgb = aniso_dir[best_axis] * 0.5 + 0.5; + light.a = 1; + valid=false;*/ + disoccluded = true; + } + } + if (valid) { + hit_cell += hit_face; + ivec3 read_cell = (hit_cell + (hddagi.cascades[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(hddagi.grid_size) - 1); + light.rgb = texelFetch(sampler3D(light_cascades, linear_sampler), read_cell + ivec3(0, (hddagi.grid_size.y * hit_cascade), 0), 0).rgb; + light.a = 1; + + if (!disoccluded) { + // filter using the neighbours! + uint neighbour_bits = imageLoad(voxel_neighbours, read_cell + ivec3(0, (hddagi.grid_size.y * hit_cascade), 0)).r; + vec3 cascade_ofs = hddagi.cascades[hit_cascade].position; + float to_cell = hddagi.cascades[hit_cascade].to_cell; + float cascade_cell_size = 1.0 / to_cell; + + const ivec3 facing_directions[26] = ivec3[](ivec3(-1, 0, 0), ivec3(1, 0, 0), ivec3(0, -1, 0), ivec3(0, 1, 0), ivec3(0, 0, -1), ivec3(0, 0, 1), ivec3(-1, -1, -1), ivec3(-1, -1, 0), ivec3(-1, -1, 1), ivec3(-1, 0, -1), ivec3(-1, 0, 1), ivec3(-1, 1, -1), ivec3(-1, 1, 0), ivec3(-1, 1, 1), ivec3(0, -1, -1), ivec3(0, -1, 1), ivec3(0, 1, -1), ivec3(0, 1, 1), ivec3(1, -1, -1), ivec3(1, -1, 0), ivec3(1, -1, 1), ivec3(1, 0, -1), ivec3(1, 0, 1), ivec3(1, 1, -1), ivec3(1, 1, 0), ivec3(1, 1, 1)); + vec3 light_cell_pos = (vec3(hit_cell) + 0.5) * cascade_cell_size + cascade_ofs; + vec4 light_accum = vec4(light.rgb, 1.0) * max(0.0, 1.0 - point_to_ray_distance(light_cell_pos, ray_pos, ray_dir) * to_cell); + while (neighbour_bits != 0) { + uint msb = findLSB(neighbour_bits); + vec3 rel = vec3(facing_directions[msb]); + vec3 neighbour_pos = light_cell_pos + rel * cascade_cell_size; + float w = max(0.0, 1.0 - point_to_ray_distance(neighbour_pos, ray_pos, ray_dir) * to_cell); + if (w > 0.0) { + ivec3 neighbour_cell = hit_cell + facing_directions[msb]; + read_cell = (neighbour_cell + (hddagi.cascades[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(hddagi.grid_size) - 1); + vec3 neighbour_light = texelFetch(sampler3D(light_cascades, linear_sampler), read_cell + ivec3(0, (hddagi.grid_size.y * hit_cascade), 0), 0).rgb; + light_accum += vec4(neighbour_light, 1.0) * w; + } + + neighbour_bits &= ~(1 << msb); + } - hit_light = mix(hit_light, hit_light2, blend); - fdistance = mix(fdistance, fdistance2, blend); + if (light_accum.a > 0.0) { + light.rgb = light_accum.rgb / light_accum.a; + } } - light_accum += hit_light; - ray_pos += ray_dir * fdistance; - found = true; - } - i++; - if (i == sdfgi.max_cascades) { - i = 0; - found = false; + } else { + //light = vec4(0,0,0,1); } } - vec3 light = light_accum.rgb / max(light_accum.a, 0.00001); - float alpha = min(1.0, light_accum.a); - - float b = min(1.0, roughness * 5.0); - - float sa = 1.0 - b; - - reflection_light.a = alpha * sa + b; - if (reflection_light.a == 0) { - specular = vec3(0.0); - } else { - specular = (light * alpha * sa + specular * b) / reflection_light.a; - } + reflection_light = mix(light, vec4(specular, 1.0 - blend), smoothstep(0, ROUGHNESS_TO_REFLECTION_TRESHOOLD, roughness)); + } else { + reflection_light.rgb = specular; + reflection_light.a = 1.0 - blend; } - reflection_light.rgb = specular; + //ambient_light.rgb = cam_bias * 0.5 + 0.5; + //reflection_light.rgb = vec3(0.0); - ambient_light.rgb *= sdfgi.energy; - reflection_light.rgb *= sdfgi.energy; + ambient_light.rgb *= hddagi.energy; + reflection_light.rgb *= hddagi.energy; } else { ambient_light = vec4(0); reflection_light = vec4(0); @@ -604,77 +1049,56 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 out_blend += blend; } -vec4 fetch_normal_and_roughness(ivec2 pos) { - vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0); - normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0); - return normal_roughness; -} - -void process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 reflection_light) { - vec4 normal_roughness = fetch_normal_and_roughness(pos); - - vec3 normal = normal_roughness.xyz; - - if (normal.length() > 0.5) { - //valid normal, can do GI - float roughness = normal_roughness.w; - bool dynamic_object = roughness > 0.5; - if (dynamic_object) { - roughness = 1.0 - roughness; - } - roughness /= (127.0 / 255.0); - vec3 view = -normalize(mat3(scene_data.cam_transform) * (vertex - scene_data.eye_offset[gl_GlobalInvocationID.z].xyz)); - vertex = mat3(scene_data.cam_transform) * vertex; - normal = normalize(mat3(scene_data.cam_transform) * normal); - vec3 reflection = normalize(reflect(-view, normal)); - -#ifdef USE_SDFGI - sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light); +void process_gi(ivec2 pos, vec3 view_vertex, vec3 view_normal, float roughness, bool dynamic_object, inout vec4 ambient_light, inout vec4 reflection_light) { + //valid normal, can do GI + vec3 view = -normalize(mat3(scene_data.cam_transform) * (view_vertex - scene_data.eye_offset[gl_GlobalInvocationID.z].xyz)); + vec3 vertex = mat3(scene_data.cam_transform) * view_vertex; + vec3 normal = normalize(mat3(scene_data.cam_transform) * view_normal); + vec3 reflection = normalize(reflect(-view, normal)); +#ifdef USE_HDDAGI + hddagi_process(vertex, normal, reflection, roughness, dynamic_object, ambient_light, reflection_light); #endif #ifdef USE_VOXEL_GI_INSTANCES - { + { #ifdef SAMPLE_VOXEL_GI_NEAREST - uvec2 voxel_gi_tex = texelFetch(voxel_gi_buffer, pos, 0).rg; + uvec2 voxel_gi_tex = texelFetch(voxel_gi_buffer, pos, 0).rg; #else - uvec2 voxel_gi_tex = texelFetch(usampler2D(voxel_gi_buffer, linear_sampler), pos, 0).rg; + uvec2 voxel_gi_tex = texelFetch(usampler2D(voxel_gi_buffer, linear_sampler), pos, 0).rg; #endif - roughness *= roughness; - //find arbitrary tangent and bitangent, then build a matrix - vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); - vec3 tangent = normalize(cross(v0, normal)); - vec3 bitangent = normalize(cross(tangent, normal)); - mat3 normal_mat = mat3(tangent, bitangent, normal); - - vec4 amb_accum = vec4(0.0); - vec4 spec_accum = vec4(0.0); - float blend_accum = 0.0; - - for (uint i = 0; i < params.max_voxel_gi_instances; i++) { - if (any(equal(uvec2(i), voxel_gi_tex))) { - voxel_gi_compute(i, vertex, normal, reflection, normal_mat, roughness, spec_accum, amb_accum, blend_accum); - } - } - if (blend_accum > 0.0) { - amb_accum /= blend_accum; - spec_accum /= blend_accum; + roughness *= roughness; + mat3 normal_mat = create_basis_from_normal(normal); + + vec4 amb_accum = vec4(0.0); + vec4 spec_accum = vec4(0.0); + float blend_accum = 0.0; + + for (uint i = 0; i < params.max_voxel_gi_instances; i++) { + if (any(equal(uvec2(i), voxel_gi_tex))) { + voxel_gi_compute(i, vertex, normal, reflection, normal_mat, roughness, spec_accum, amb_accum, blend_accum); } + } + if (blend_accum > 0.0) { + amb_accum /= blend_accum; + spec_accum /= blend_accum; + } -#ifdef USE_SDFGI - reflection_light = blend_color(spec_accum, reflection_light); - ambient_light = blend_color(amb_accum, ambient_light); +#ifdef USE_HDDAGI + reflection_light = blend_color(spec_accum, reflection_light); + ambient_light = blend_color(amb_accum, ambient_light); #else - reflection_light = spec_accum; - ambient_light = amb_accum; -#endif - } + reflection_light = spec_accum; + ambient_light = amb_accum; #endif } +#endif } void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); +#if defined(USE_HDDAGI) || defined(USE_VOXEL_GI_INSTANCES) + uint vrs_x, vrs_y; #ifdef USE_VRS if (sc_use_vrs) { @@ -713,76 +1137,178 @@ void main() { vec4 ambient_light = vec4(0.0); vec4 reflection_light = vec4(0.0); - vec3 vertex = reconstruct_position(pos); + vec3 vertex; + vec3 normal; + float roughness; + + bool found_vertex = false; + + vertex = reconstruct_position(pos); + vec4 normal_roughness = fetch_normal_and_roughness(pos); + found_vertex = length(normal_roughness.xyz) > 0.5; + normal = normal_roughness.xyz; + roughness = normal_roughness.w; + bool dynamic_object = roughness > 0.5; + if (dynamic_object) { + roughness = 1.0 - roughness; + } + roughness /= (127.0 / 255.0); vertex.y = -vertex.y; - process_gi(pos, vertex, ambient_light, reflection_light); + if (found_vertex) { + process_gi(pos, vertex, normal, roughness, dynamic_object, ambient_light, reflection_light); + } + +#ifdef USE_HDDAGI + + // If using reflections, blend the 4 adjacent pixels to get rid of dither + uint group_pos = gl_LocalInvocationID.y * GROUP_SIZE + gl_LocalInvocationID.x; + group_positions[group_pos] = vertex; + group_normals[group_pos] = normal; + group_reflections[group_pos] = reflection_light; + + memoryBarrierShared(); + barrier(); + + if (roughness < ROUGHNESS_TO_REFLECTION_TRESHOOLD) { + uvec2 local_group_pos_base = gl_LocalInvocationID.xy - (gl_LocalInvocationID.xy % DITHER_SIZE); + uint local_group_pos = local_group_pos_base.y * GROUP_SIZE + local_group_pos_base.x; + + vec3 positions[DITHER_SIZE * DITHER_SIZE]; + vec3 normals[DITHER_SIZE * DITHER_SIZE]; + + vec4 average = vec4(0.0); + for (int i = 0; i < DITHER_SIZE; i++) { + for (int j = 0; j < DITHER_SIZE; j++) { + uint src_pos = local_group_pos + i * GROUP_SIZE + j; + normals[i * DITHER_SIZE + j] = group_normals[src_pos]; + positions[i * DITHER_SIZE + j] = group_positions[src_pos]; + average += group_reflections[src_pos]; + } + } + + average /= 4.0; + + const int subgroup_count = (DITHER_SIZE - 1) * (DITHER_SIZE - 1); + uvec4 subgroups[subgroup_count] = uvec4[]( +#if DITHER_SIZE == 2 + uvec4(0, 1, 2, 3) +#elif DITHER_SIZE == 3 + uvec4(0, 1, 3, 4), uvec4(1, 2, 4, 5), uvec4(3, 4, 6, 7), uvec4(4, 5, 7, 8) +#endif + ); + + const float same_plane_threshold = 0.9659258262890683; // 15 degrees tolerance + + float weight = 1.0; + for (int i = 0; i < subgroup_count; i++) { + uvec4 sg = subgroups[i]; + // Weight positions in plane. + vec3 p[4] = vec3[](positions[sg.x], positions[sg.y], positions[sg.z], positions[sg.w]); + vec3 n1 = normalize(cross(p[0] - p[2], p[0] - p[1])); + vec3 n2 = normalize(cross(p[2] - p[3], p[2] - p[1])); + weight *= max(0.0, smoothstep(same_plane_threshold, 1, dot(n1, n2))); + + // Weight normal difference. + vec3 n[4] = vec3[](normals[sg.x], normals[sg.y], normals[sg.z], normals[sg.w]); + weight *= max(0.0, smoothstep(same_plane_threshold, 1, length((n[0] + n[1] + n[2] + n[3]) / 4.0))); + } + + reflection_light = mix(reflection_light, average, weight); + } +#endif if (sc_half_res) { pos >>= 1; } - imageStore(ambient_buffer, pos, ambient_light); - imageStore(reflection_buffer, pos, reflection_light); + uint ambient_rgbe = rgbe_encode(ambient_light.rgb); + uint reflection_rgbe = rgbe_encode(reflection_light.rgb); + uint blend = uint(clamp(reflection_light.a * 0xF, 0, 0xF)) | (uint(clamp(ambient_light.a * 0xF, 0, 0xF)) << 4); + + imageStore(ambient_buffer, pos, uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos, uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos, vec4(ambient_light.a, reflection_light.a, 0, 0)); #ifdef USE_VRS if (sc_use_vrs) { if (vrs_x > 1) { - imageStore(ambient_buffer, pos + ivec2(1, 0), ambient_light); - imageStore(reflection_buffer, pos + ivec2(1, 0), reflection_light); + imageStore(ambient_buffer, pos + ivec2(1, 0), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(1, 0), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(1, 0), uvec4(blend)); } if (vrs_x > 2) { - imageStore(ambient_buffer, pos + ivec2(2, 0), ambient_light); - imageStore(reflection_buffer, pos + ivec2(2, 0), reflection_light); + imageStore(ambient_buffer, pos + ivec2(2, 0), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(2, 0), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(2, 0), uvec4(blend)); - imageStore(ambient_buffer, pos + ivec2(3, 0), ambient_light); - imageStore(reflection_buffer, pos + ivec2(3, 0), reflection_light); + imageStore(ambient_buffer, pos + ivec2(3, 0), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(3, 0), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(3, 0), uvec4(blend)); } if (vrs_y > 1) { - imageStore(ambient_buffer, pos + ivec2(0, 1), ambient_light); - imageStore(reflection_buffer, pos + ivec2(0, 1), reflection_light); + imageStore(ambient_buffer, pos + ivec2(0, 1), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(0, 1), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(0, 1), uvec4(blend)); } if (vrs_y > 1 && vrs_x > 1) { - imageStore(ambient_buffer, pos + ivec2(1, 1), ambient_light); - imageStore(reflection_buffer, pos + ivec2(1, 1), reflection_light); + imageStore(ambient_buffer, pos + ivec2(1, 1), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(1, 1), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(1, 1), uvec4(blend)); } if (vrs_y > 1 && vrs_x > 2) { - imageStore(ambient_buffer, pos + ivec2(2, 1), ambient_light); - imageStore(reflection_buffer, pos + ivec2(2, 1), reflection_light); + imageStore(ambient_buffer, pos + ivec2(2, 1), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(2, 1), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(2, 1), uvec4(blend)); - imageStore(ambient_buffer, pos + ivec2(3, 1), ambient_light); - imageStore(reflection_buffer, pos + ivec2(3, 1), reflection_light); + imageStore(ambient_buffer, pos + ivec2(3, 1), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(3, 1), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(3, 1), uvec4(blend)); } if (vrs_y > 2) { - imageStore(ambient_buffer, pos + ivec2(0, 2), ambient_light); - imageStore(reflection_buffer, pos + ivec2(0, 2), reflection_light); - imageStore(ambient_buffer, pos + ivec2(0, 3), ambient_light); - imageStore(reflection_buffer, pos + ivec2(0, 3), reflection_light); + imageStore(ambient_buffer, pos + ivec2(0, 2), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(0, 2), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(0, 2), uvec4(blend)); + + imageStore(ambient_buffer, pos + ivec2(0, 3), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(0, 3), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(0, 3), uvec4(blend)); } if (vrs_y > 2 && vrs_x > 1) { - imageStore(ambient_buffer, pos + ivec2(1, 2), ambient_light); - imageStore(reflection_buffer, pos + ivec2(1, 2), reflection_light); - imageStore(ambient_buffer, pos + ivec2(1, 3), ambient_light); - imageStore(reflection_buffer, pos + ivec2(1, 3), reflection_light); + imageStore(ambient_buffer, pos + ivec2(1, 2), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(1, 2), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(1, 2), uvec4(blend)); + + imageStore(ambient_buffer, pos + ivec2(1, 3), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(1, 3), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(1, 3), uvec4(blend)); } if (vrs_y > 2 && vrs_x > 2) { - imageStore(ambient_buffer, pos + ivec2(2, 2), ambient_light); - imageStore(reflection_buffer, pos + ivec2(2, 2), reflection_light); - imageStore(ambient_buffer, pos + ivec2(2, 3), ambient_light); - imageStore(reflection_buffer, pos + ivec2(2, 3), reflection_light); + imageStore(ambient_buffer, pos + ivec2(2, 2), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(2, 2), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(2, 2), uvec4(blend)); + + imageStore(ambient_buffer, pos + ivec2(2, 3), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(2, 3), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(2, 3), uvec4(blend)); - imageStore(ambient_buffer, pos + ivec2(3, 2), ambient_light); - imageStore(reflection_buffer, pos + ivec2(3, 2), reflection_light); - imageStore(ambient_buffer, pos + ivec2(3, 3), ambient_light); - imageStore(reflection_buffer, pos + ivec2(3, 3), reflection_light); + imageStore(ambient_buffer, pos + ivec2(3, 2), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(3, 2), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(3, 2), uvec4(blend)); + + imageStore(ambient_buffer, pos + ivec2(3, 3), uvec4(ambient_rgbe)); + imageStore(reflection_buffer, pos + ivec2(3, 3), uvec4(reflection_rgbe)); + imageStore(blend_buffer, pos + ivec2(3, 3), uvec4(blend)); } } #endif + +#endif } diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_debug.glsl new file mode 100644 index 000000000000..6c0f3f5dcac4 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_debug.glsl @@ -0,0 +1,462 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#define MAX_CASCADES 8 +#define REGION_SIZE 8 + +#define square(m) ((m) * (m)) + +layout(rg32ui, set = 0, binding = 1) uniform restrict readonly uimage3D voxel_cascades; +layout(r8ui, set = 0, binding = 2) uniform restrict readonly uimage3D voxel_region_cascades; + +layout(set = 0, binding = 3) uniform texture3D light_cascades; + +layout(set = 0, binding = 4) uniform sampler linear_sampler; +layout(set = 0, binding = 5) uniform sampler nearest_sampler; + +layout(set = 0, binding = 6) uniform texture2DArray light_probes; +layout(set = 0, binding = 7) uniform texture3D occlusion[2]; + +layout(r32ui, set = 0, binding = 10) uniform restrict readonly uimage3D voxel_neighbours; + +struct CascadeData { + vec3 offset; //offset of (0,0,0) in world coordinates + float to_cell; // 1/bounds * grid_size + ivec3 region_world_offset; + uint pad; + vec4 pad2; +}; + +layout(set = 0, binding = 8, std140) restrict readonly uniform Cascades { + CascadeData data[MAX_CASCADES]; +} +cascades; + +layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D screen_buffer; + +layout(push_constant, std430) uniform Params { + vec3 grid_size; + uint max_cascades; + + uint screen_size; + float esm_strength; + float y_mult; + float z_near; + + mat3x4 inv_projection; + // We pack these more tightly than mat3 and vec3, which will require some reconstruction trickery. + float cam_basis[3][3]; + float cam_origin[3]; +} +params; + +vec3 linear_to_srgb(vec3 color) { + //if going to srgb, clamp from 0 to 1. + color = clamp(color, vec3(0.0), vec3(1.0)); + const vec3 a = vec3(0.055f); + return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); +} + +ivec3 mul64(ivec3 a, ivec3 b, int fp_shift) { + ivec3 lsb, msb; + imulExtended(a, b, msb, lsb); + lsb = ivec3(uvec3(lsb) >> fp_shift); + lsb |= msb << (32 - fp_shift); + return lsb; +} + +bool trace_ray_hdda(vec3 ray_pos, vec3 ray_dir, int p_cascade, out ivec3 r_cell, out ivec3 r_side, out int r_cascade) { + const int LEVEL_CASCADE = -1; + const int LEVEL_REGION = 0; + const int LEVEL_BLOCK = 1; + const int LEVEL_VOXEL = 2; + const int MAX_LEVEL = 3; + + //#define HQ_RAY + +#ifdef HQ_RAY + const int fp_bits = 16; +#else + const int fp_bits = 10; +#endif + const int fp_block_bits = fp_bits + 2; + const int fp_region_bits = fp_block_bits + 1; + const int fp_cascade_bits = fp_region_bits + 4; + + bvec3 limit_dir = greaterThan(ray_dir, vec3(0.0)); + ivec3 step = mix(ivec3(0), ivec3(1), limit_dir); + ivec3 ray_sign = ivec3(sign(ray_dir)); + + ivec3 ray_dir_fp = ivec3(ray_dir * float(1 << fp_bits)); + +#ifdef HQ_RAY + const float limit = 1.0 / 65535.0; +#else + const float limit = 1.0 / 127.0; +#endif + bvec3 ray_zero = lessThan(abs(ray_dir), vec3(limit)); + ivec3 inv_ray_dir_fp = ivec3(float(1 << fp_bits) / ray_dir); + + const ivec3 level_masks[MAX_LEVEL] = ivec3[]( + ivec3(1 << fp_region_bits) - ivec3(1), + ivec3(1 << fp_block_bits) - ivec3(1), + ivec3(1 << fp_bits) - ivec3(1)); + + ivec3 region_offset_mask = (ivec3(params.grid_size) / REGION_SIZE) - ivec3(1); + + ivec3 limits[MAX_LEVEL]; + + limits[LEVEL_REGION] = ((ivec3(params.grid_size) << fp_bits) - ivec3(1)) * step; // Region limit does not change, so initialize now. + + // Initialize to cascade + int level = LEVEL_CASCADE; + int cascade = p_cascade - 1; + + ivec3 cascade_base; + ivec3 region_base; + uvec2 block; + bool hit = false; + + ivec3 pos; + + while (true) { + // This loop is written so there is only one single main iteration. + // This ensures that different compute threads working on different + // levels can still run together without blocking each other. + + if (level == LEVEL_VOXEL) { + // The first level should be (in a worst case scenario) the most used + // so it needs to appear first. The rest of the levels go from more to least used order. + + ivec3 block_local = (pos & level_masks[LEVEL_BLOCK]) >> fp_bits; + uint block_index = uint(block_local.z * 16 + block_local.y * 4 + block_local.x); + if (block_index < 32) { + // Low 32 bits. + if (bool(block.x & uint(1 << block_index))) { + hit = true; + break; + } + } else { + // High 32 bits. + block_index -= 32; + if (bool(block.y & uint(1 << block_index))) { + hit = true; + break; + } + } + } else if (level == LEVEL_BLOCK) { + ivec3 block_local = (pos & level_masks[LEVEL_REGION]) >> fp_block_bits; + block = imageLoad(voxel_cascades, region_base + block_local).rg; + if (block != uvec2(0)) { + // Have voxels inside + level = LEVEL_VOXEL; + limits[LEVEL_VOXEL] = pos - (pos & level_masks[LEVEL_BLOCK]) + step * (level_masks[LEVEL_BLOCK] + ivec3(1)); + continue; + } + } else if (level == LEVEL_REGION) { + ivec3 region = pos >> fp_region_bits; + region = (cascades.data[cascade].region_world_offset + region) & region_offset_mask; // Scroll to world + region += cascade_base; + bool region_used = imageLoad(voxel_region_cascades, region).r > 0; + + if (region_used) { + // The region has contents. + region_base = (region << 1); + level = LEVEL_BLOCK; + limits[LEVEL_BLOCK] = pos - (pos & level_masks[LEVEL_REGION]) + step * (level_masks[LEVEL_REGION] + ivec3(1)); + continue; + } + } else if (level == LEVEL_CASCADE) { + // Return to global + if (cascade >= p_cascade) { + ray_pos = vec3(pos) / float(1 << fp_bits); + ray_pos /= cascades.data[cascade].to_cell; + ray_pos += cascades.data[cascade].offset; + } + + cascade++; + if (cascade == params.max_cascades) { + break; + } + + ray_pos -= cascades.data[cascade].offset; + ray_pos *= cascades.data[cascade].to_cell; + pos = ivec3(ray_pos * float(1 << fp_bits)); + if (any(lessThan(pos, ivec3(0))) || any(greaterThanEqual(pos, ivec3(params.grid_size) << fp_bits))) { + // Outside this cascade, go to next. + continue; + } + + cascade_base = ivec3(0, int(params.grid_size.y / REGION_SIZE) * cascade, 0); + level = LEVEL_REGION; + continue; + } + + // Fixed point, multi-level DDA. + + ivec3 mask = level_masks[level]; + ivec3 box = mask * step; + ivec3 pos_diff = box - (pos & mask); +#ifdef HQ_RAY + ivec3 mul_res = mul64(pos_diff, inv_ray_dir_fp, fp_bits); +#else + ivec3 mul_res = (pos_diff * inv_ray_dir_fp) >> fp_bits; +#endif + ivec3 tv = mix(mul_res, ivec3(0x7FFFFFFF), ray_zero); + int t = min(tv.x, min(tv.y, tv.z)); + + // The general idea here is that we _always_ need to increment to the closest next cell + // (this is a DDA after all), so adv_box forces this increment for the minimum axis. + + ivec3 adv_box = pos_diff + ray_sign; +#ifdef HQ_RAY + ivec3 adv_t = mul64(ray_dir_fp, ivec3(t), fp_bits); +#else + ivec3 adv_t = (ray_dir_fp * t) >> fp_bits; +#endif + pos += mix(adv_t, adv_box, equal(ivec3(t), tv)); + + while (true) { + bvec3 limit = lessThan(pos, limits[level]); + bool inside = all(equal(limit, limit_dir)); + if (inside) { + break; + } + level -= 1; + if (level == LEVEL_CASCADE) { + break; + } + } + } + + if (hit) { + ivec3 mask = level_masks[LEVEL_VOXEL]; + ivec3 box = mask * (step ^ ivec3(1)); + ivec3 pos_diff = box - (pos & mask); +#ifdef HQ_RAY + ivec3 mul_res = mul64(pos_diff, -inv_ray_dir_fp, fp_bits); +#else + ivec3 mul_res = (pos_diff * -inv_ray_dir_fp); +#endif + + ivec3 tv = mix(mul_res, ivec3(0x7FFFFFFF), ray_zero); + + int m; + if (tv.x < tv.y) { + r_side = ivec3(1, 0, 0); + m = tv.x; + } else { + r_side = ivec3(0, 1, 0); + m = tv.y; + } + if (tv.z < m) { + r_side = ivec3(0, 0, 1); + } + + r_side *= -ray_sign; + + r_cell = pos >> fp_bits; + + r_cascade = cascade; + } + + return hit; +} + +#define PROBE_CELLS 8 + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +ivec2 probe_to_tex(ivec3 local_probe, int p_cascade) { + ivec3 probe_axis_size = ivec3(params.grid_size) / PROBE_CELLS + ivec3(1); + ivec3 cell = modi(cascades.data[p_cascade].region_world_offset + local_probe, probe_axis_size); + return cell.xy + ivec2(0, cell.z * int(probe_axis_size.y)); +} + +vec2 octahedron_wrap(vec2 v) { + vec2 signVal; + signVal.x = v.x >= 0.0 ? 1.0 : -1.0; + signVal.y = v.y >= 0.0 ? 1.0 : -1.0; + return (1.0 - abs(v.yx)) * signVal; +} + +vec2 octahedron_encode(vec3 n) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); + n.xy = n.xy * 0.5 + 0.5; + return n.xy; +} + +float point_to_ray_distance(vec3 point, vec3 ray_origin, vec3 ray_direction) { + // Normalize the ray direction + vec3 dir_normalized = normalize(ray_direction); + + // Compute the vector from the ray origin to the point + vec3 vec_to_point = point - ray_origin; + + // Project the vector to point onto the ray direction + float t = dot(vec_to_point, dir_normalized); + + // Calculate the projection point on the ray + vec3 projection = ray_origin + t * dir_normalized; + + // Return the distance between the point and its projection on the ray + return length(point - projection); +} + +#define OCC_DISTANCE_MAX 16.0 + +void main() { + // Pixel being shaded + ivec2 screen_size = ivec2(params.screen_size & 0xFFFF, params.screen_size >> 16); + + ivec2 screen_pos = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(screen_pos, screen_size))) { //too large, do nothing + return; + } + + vec3 ray_pos; + vec3 ray_dir; + vec3 cam_dir; + { + ray_pos = vec3(params.cam_origin[0], params.cam_origin[1], params.cam_origin[2]); + + ray_dir.xy = ((vec2(screen_pos) / vec2(screen_size)) * 2.0 - 1.0); + ray_dir.z = params.z_near; + + ray_dir = (vec4(ray_dir, 1.0) * mat4(params.inv_projection)).xyz; + + mat3 cam_basis; + { + vec3 c0 = vec3(params.cam_basis[0][0], params.cam_basis[0][1], params.cam_basis[0][2]); + vec3 c1 = vec3(params.cam_basis[1][0], params.cam_basis[1][1], params.cam_basis[1][2]); + vec3 c2 = vec3(params.cam_basis[2][0], params.cam_basis[2][1], params.cam_basis[2][2]); + cam_basis = mat3(c0, c1, c2); + } + ray_dir = normalize(cam_basis * ray_dir); + cam_dir = vec3(params.cam_basis[2][0], params.cam_basis[2][1], params.cam_basis[2][2]); + } + + ray_pos.y *= params.y_mult; + ray_dir.y *= params.y_mult; + ray_dir = normalize(ray_dir); + + vec3 light = vec3(0.0); + ivec3 hit_cell; + vec3 hit_uvw; + int hit_cascade = 0; + + ivec3 hit_face; + + if (trace_ray_hdda(ray_pos, ray_dir, 0, hit_cell, hit_face, hit_cascade)) { + hit_cell += hit_face; + ivec3 read_cell = (hit_cell + (cascades.data[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(params.grid_size) - 1); + light = texelFetch(sampler3D(light_cascades, linear_sampler), read_cell + ivec3(0, (params.grid_size.y * hit_cascade), 0), 0).rgb; + //light = vec3(abs(hit_face)); + + if (true) { + // Check the neighbours! + uint neighbour_bits = imageLoad(voxel_neighbours, read_cell + ivec3(0, (params.grid_size.y * hit_cascade), 0)).r; + vec3 cascade_ofs = cascades.data[hit_cascade].offset; + float to_cell = cascades.data[hit_cascade].to_cell; + float cascade_cell_size = 1.0 / to_cell; + + const ivec3 facing_directions[26] = ivec3[](ivec3(-1, 0, 0), ivec3(1, 0, 0), ivec3(0, -1, 0), ivec3(0, 1, 0), ivec3(0, 0, -1), ivec3(0, 0, 1), ivec3(-1, -1, -1), ivec3(-1, -1, 0), ivec3(-1, -1, 1), ivec3(-1, 0, -1), ivec3(-1, 0, 1), ivec3(-1, 1, -1), ivec3(-1, 1, 0), ivec3(-1, 1, 1), ivec3(0, -1, -1), ivec3(0, -1, 1), ivec3(0, 1, -1), ivec3(0, 1, 1), ivec3(1, -1, -1), ivec3(1, -1, 0), ivec3(1, -1, 1), ivec3(1, 0, -1), ivec3(1, 0, 1), ivec3(1, 1, -1), ivec3(1, 1, 0), ivec3(1, 1, 1)); + vec3 light_cell_pos = (vec3(hit_cell) + 0.5) * cascade_cell_size + cascade_ofs; + vec4 light_accum = vec4(light, 1.0) * max(0.0, 1.0 - point_to_ray_distance(light_cell_pos, ray_pos, ray_dir) * to_cell); + while (neighbour_bits != 0) { + uint msb = findLSB(neighbour_bits); + vec3 rel = vec3(facing_directions[msb]); + vec3 neighbour_pos = light_cell_pos + rel * cascade_cell_size; + float w = max(0.0, 1.0 - point_to_ray_distance(neighbour_pos, ray_pos, ray_dir) * to_cell); + if (w > 0.0) { + ivec3 neighbour_cell = hit_cell + facing_directions[msb]; + read_cell = (neighbour_cell + (cascades.data[hit_cascade].region_world_offset * REGION_SIZE)) & (ivec3(params.grid_size) - 1); + vec3 neighbour_light = texelFetch(sampler3D(light_cascades, linear_sampler), read_cell + ivec3(0, (params.grid_size.y * hit_cascade), 0), 0).rgb; + light_accum += vec4(neighbour_light, 1.0) * w; + } + + neighbour_bits &= ~(1 << msb); + } + + light = light_accum.rgb / light_accum.a; + } + + if (false) { + // compute occlusion + + int cascade = hit_cascade; + hit_cell.y %= int(params.grid_size.y); + ivec3 pos = hit_cell; + ivec3 base_probe = pos / PROBE_CELLS; + + ivec3 occ_pos = read_cell + ivec3(1); + occ_pos.y += (int(params.grid_size.y) + 2) * hit_cascade; + vec4 occ_0 = texelFetch(sampler3D(occlusion[0], linear_sampler), occ_pos, 0); + vec4 occ_1 = texelFetch(sampler3D(occlusion[1], linear_sampler), occ_pos, 0); + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); + + vec3 posf = vec3(pos) + 0.5; // Actual point in the center of the box. + + ivec3 probe_axis_size = ivec3(params.grid_size) / PROBE_CELLS + ivec3(1); + + vec4 accum_light = vec4(0.0); + + vec2 light_probe_tex_to_uv = 1.0 / vec2((LIGHTPROBE_OCT_SIZE + 2) * probe_axis_size.x, (LIGHTPROBE_OCT_SIZE + 2) * probe_axis_size.y * probe_axis_size.z); + vec2 light_uv = octahedron_encode(vec3(hit_face)) * float(LIGHTPROBE_OCT_SIZE); + + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + + vec3 probe_pos = vec3(probe * PROBE_CELLS); + + vec3 probe_to_pos = posf - probe_pos; + + ivec3 probe_occ = (probe + cascades.data[hit_cascade].region_world_offset) & ivec3(1); + uint weight_index = 0; + if (probe_occ.x != 0) { + weight_index |= 1; + } + if (probe_occ.y != 0) { + weight_index |= 2; + } + if (probe_occ.z != 0) { + weight_index |= 4; + } + + float weight = occ_weights[weight_index]; + + weight = max(0.000001, weight); // make sure not zero (only trilinear can be zero) + + vec3 trilinear = vec3(1.0) - abs(probe_to_pos / float(PROBE_CELLS)); + + weight *= trilinear.x * trilinear.y * trilinear.z; + + ivec2 tex_pos = probe_to_tex(probe, cascade); + vec2 tex_uv = vec2(ivec2(tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1))) + light_uv; + tex_uv *= light_probe_tex_to_uv; + + vec3 probe_light = texture(sampler2DArray(light_probes, linear_sampler), vec3(tex_uv, float(cascade))).rgb; + + accum_light += vec4(probe_light, 1.0) * weight; + } + + light += accum_light.rgb / accum_light.a; + } + + //light = abs(hit_normal);//texelFetch(sampler3D(light_cascades, linear_sampler), hit_cell + normal_ofs,0).rgb; + } + + imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0)); +} diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_debug_probes.glsl new file mode 100644 index 000000000000..07b46723702a --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_debug_probes.glsl @@ -0,0 +1,317 @@ +#[vertex] + +#version 450 + +#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) +#extension GL_EXT_multiview : enable +#endif + +#ifdef USE_MULTIVIEW +#ifdef has_VK_KHR_multiview +#define ViewIndex gl_ViewIndex +#else // has_VK_KHR_multiview +// !BAS! This needs to become an input once we implement our fallback! +#define ViewIndex 0 +#endif // has_VK_KHR_multiview +#else // USE_MULTIVIEW +// Set to zero, not supported in non stereo +#define ViewIndex 0 +#endif //USE_MULTIVIEW + +#VERSION_DEFINES + +#define MAX_CASCADES 8 +#define MAX_VIEWS 2 + +layout(push_constant, std430) uniform Params { + uint band_power; + uint sections_in_band; + uint band_mask; + float section_arc; + + vec3 grid_size; + uint cascade; + + int oct_size; + float y_mult; + uint probe_debug_index; + uint pad; + + ivec3 probe_axis_size; + uint pad2; +} +params; + +// https://in4k.untergrund.net/html_articles/hugi_27_-_coding_corner_polaris_sphere_tessellation_101.htm + +vec3 get_sphere_vertex(uint p_vertex_id) { + float x_angle = float(p_vertex_id & 1u) + (p_vertex_id >> params.band_power); + + float y_angle = float((p_vertex_id & params.band_mask) >> 1) + ((p_vertex_id >> params.band_power) * params.sections_in_band); + + x_angle *= params.section_arc * 0.5f; // remember - 180AA x rot not 360 + y_angle *= -params.section_arc; + + vec3 point = vec3(sin(x_angle) * sin(y_angle), cos(x_angle), sin(x_angle) * cos(y_angle)); + + return point; +} + +layout(location = 0) out vec3 normal_interp; +layout(location = 1) out flat uint probe_index; +layout(location = 2) out vec4 color_interp; + +struct CascadeData { + vec3 offset; //offset of (0,0,0) in world coordinates + float to_cell; // 1/bounds * grid_size + ivec3 region_world_offset; + uint pad; + vec4 pad2; +}; + +layout(set = 0, binding = 1, std140) uniform Cascades { + CascadeData data[MAX_CASCADES]; +} +cascades; + +layout(set = 0, binding = 2) uniform texture3D occlusion_texture[2]; + +layout(set = 0, binding = 3) uniform sampler linear_sampler; + +layout(set = 0, binding = 4, std140) uniform SceneData { + mat4 projection[MAX_VIEWS]; +} +scene_data; + +vec2 octahedron_wrap(vec2 v) { + vec2 signVal; + signVal.x = v.x >= 0.0 ? 1.0 : -1.0; + signVal.y = v.y >= 0.0 ? 1.0 : -1.0; + return (1.0 - abs(v.yx)) * signVal; +} + +vec2 octahedron_encode(vec3 n) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); + n.xy = n.xy * 0.5 + 0.5; + return n.xy; +} + +vec3 octahedron_decode(vec2 f) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + f = f * 2.0 - 1.0; + vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y)); + float t = clamp(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return normalize(n); +} + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +#define OCC_DISTANCE_MAX 16.0 + +void main() { +#if defined(MODE_OCCLUSION) + probe_index = params.probe_debug_index; + + int probe_voxels = int(params.grid_size.x) / int(params.probe_axis_size.x - 1); + + ivec3 probe_cell; + probe_cell.x = int(probe_index) % params.probe_axis_size.x; + probe_cell.y = (int(probe_index) / params.probe_axis_size.x) % params.probe_axis_size.y; + probe_cell.z = int(probe_index) / (params.probe_axis_size.x * params.probe_axis_size.y); + + vec3 vertex = get_sphere_vertex(gl_VertexIndex) * 0.01; + + int occluder_index = int(gl_InstanceIndex); + + int diameter = probe_voxels * 2; + ivec3 occluder_pos; + occluder_pos.x = int(occluder_index % diameter); + occluder_pos.y = int((occluder_index / diameter) % diameter); + occluder_pos.z = int(occluder_index / (diameter * diameter)); + + float cell_size = 1.0 / cascades.data[params.cascade].to_cell; + vec3 probe_cell_size = (params.grid_size / vec3(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell; + + occluder_pos -= ivec3(diameter / 2); + vec3 occluder_offset = (vec3(occluder_pos) + 0.5) * cell_size; + + vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size + occluder_offset) / vec3(1.0, params.y_mult, 1.0); + + ivec3 global_probe_pos = probe_cell + cascades.data[params.cascade].region_world_offset; + + ivec3 tex_pos = (global_probe_pos * probe_voxels + occluder_pos) & (ivec3(params.grid_size) - 1); + tex_pos += ivec3(1.0); // margin + + uint occlusion_layer = 0; + if ((global_probe_pos.x & 1) != 0) { + occlusion_layer |= 1; + } + if ((global_probe_pos.y & 1) != 0) { + occlusion_layer |= 2; + } + if ((global_probe_pos.z & 1) != 0) { + occlusion_layer |= 4; + } + + const vec4 layer_axis[4] = vec4[]( + vec4(1, 0, 0, 0), + vec4(0, 1, 0, 0), + vec4(0, 0, 1, 0), + vec4(0, 0, 0, 1)); + + float visibility; + if (occlusion_layer < 4) { + visibility = dot(texelFetch(sampler3D(occlusion_texture[0], linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]); + } else { + visibility = dot(texelFetch(sampler3D(occlusion_texture[1], linear_sampler), tex_pos, 0), layer_axis[occlusion_layer - 4]); + } + + gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0); + + color_interp = mix(vec4(1, 0, 0, 1), vec4(0.0, 1, 0.0, 1), visibility); +#else + + probe_index = gl_InstanceIndex; + + vec3 probe_cell_size = (params.grid_size / vec3(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell; + + ivec3 probe_cell; + probe_cell.x = int(probe_index) % params.probe_axis_size.x; + probe_cell.y = (int(probe_index) / params.probe_axis_size.x) % params.probe_axis_size.y; + probe_cell.z = int(probe_index) / (params.probe_axis_size.x * params.probe_axis_size.y); + + normal_interp = get_sphere_vertex(gl_VertexIndex); + vec3 vertex = normal_interp; + vertex *= 0.2; + + vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0); + + gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0); +#endif +} + +#[fragment] + +#version 450 + +#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) +#extension GL_EXT_multiview : enable +#endif + +#ifdef USE_MULTIVIEW +#ifdef has_VK_KHR_multiview +#define ViewIndex gl_ViewIndex +#else // has_VK_KHR_multiview +// !BAS! This needs to become an input once we implement our fallback! +#define ViewIndex 0 +#endif // has_VK_KHR_multiview +#else // USE_MULTIVIEW +// Set to zero, not supported in non stereo +#define ViewIndex 0 +#endif //USE_MULTIVIEW + +#VERSION_DEFINES + +#define MAX_VIEWS 2 +#define MAX_CASCADES 8 + +layout(location = 0) out vec4 frag_color; + +#if !defined(MODE_OCCLUSION_FILTER) && !defined(MODE_OCCLUSION_LINES) +layout(set = 0, binding = 3) uniform sampler linear_sampler; +layout(set = 0, binding = 5) uniform texture2DArray lightprobe_texture; + +layout(push_constant, std430) uniform Params { + uint band_power; + uint sections_in_band; + uint band_mask; + float section_arc; + + vec3 grid_size; + uint cascade; + + int oct_size; + float y_mult; + uint probe_debug_index; + uint pad; + + ivec3 probe_axis_size; + uint pad2; +} +params; +#endif + +layout(location = 0) in vec3 normal_interp; +layout(location = 1) in flat uint probe_index; +layout(location = 2) in vec4 color_interp; + +vec2 octahedron_wrap(vec2 v) { + vec2 signVal; + signVal.x = v.x >= 0.0 ? 1.0 : -1.0; + signVal.y = v.y >= 0.0 ? 1.0 : -1.0; + return (1.0 - abs(v.yx)) * signVal; +} + +vec2 octahedron_encode(vec3 n) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); + n.xy = n.xy * 0.5 + 0.5; + return n.xy; +} + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +struct CascadeData { + vec3 offset; //offset of (0,0,0) in world coordinates + float to_cell; // 1/bounds * grid_size + ivec3 region_world_offset; + uint pad; + vec4 pad2; +}; + +layout(set = 0, binding = 1, std140) uniform Cascades { + CascadeData data[MAX_CASCADES]; +} +cascades; + +void main() { +#if defined(MODE_OCCLUSION) + frag_color = color_interp; +#else + int oct_size = params.oct_size; + int oct_margin = 1; + + ivec3 probe_cell; + probe_cell.x = int(probe_index) % params.probe_axis_size.x; + probe_cell.y = (int(probe_index) / params.probe_axis_size.x) % params.probe_axis_size.y; + probe_cell.z = int(probe_index) / (params.probe_axis_size.x * params.probe_axis_size.y); + probe_cell += cascades.data[params.cascade].region_world_offset; + + probe_cell = modi(probe_cell, ivec3(params.probe_axis_size)); + + vec3 tex_posf = vec3(ivec3((probe_cell.xy + ivec2(0, probe_cell.z * int(params.probe_axis_size.y))) * (oct_size + oct_margin * 2) + ivec2(oct_margin), params.cascade)); + + //vec3 tex_pos_ofs = vec3(octahedron_encode(normal_interp) * float(oct_size), 0.0); + tex_posf += vec3(octahedron_encode(normalize(normal_interp)) * float(oct_size), 0.0); + tex_posf.xy /= vec2(ivec2(params.probe_axis_size.x * (oct_size + 2 * oct_margin), params.probe_axis_size.z * params.probe_axis_size.y * (oct_size + 2 * oct_margin))); + + vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), tex_posf, 0.0); + + frag_color = indirect_light; +#endif +} diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_direct_light.glsl new file mode 100644 index 000000000000..5f2fd4cbd532 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_direct_light.glsl @@ -0,0 +1,518 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +#define MAX_CASCADES 8 + +layout(rg32ui, set = 0, binding = 1) uniform restrict readonly uimage3D voxel_cascades; +layout(r8ui, set = 0, binding = 2) uniform restrict readonly uimage3D voxel_region_cascades; + +layout(set = 0, binding = 3) uniform sampler linear_sampler; + +layout(set = 0, binding = 4, std430) restrict readonly buffer DispatchData { + uint x; + uint y; + uint z; + uint total_count; +} +dispatch_data; + +struct ProcessVoxel { + uint position; // xyz 10 bit packed - then 2 extra bits for dynamic and static pending + uint albedo_normal; // 0 - 16, 17 - 31 normal in octahedral format + uint emission; // RGBE emission + uint occlusion; // cached 4 bits occlusion for each 8 neighbouring probes +}; + +#define PROCESS_STATIC_PENDING_BIT 0x80000000 +#define PROCESS_DYNAMIC_PENDING_BIT 0x40000000 + +// Can always write, because it needs to set off dirty bits +layout(set = 0, binding = 5, std430) restrict buffer ProcessVoxels { + ProcessVoxel data[]; +} +process_voxels; + +layout(r32ui, set = 0, binding = 6) uniform restrict writeonly uimage3D dst_light; + +struct CascadeData { + vec3 offset; //offset of (0,0,0) in world coordinates + float to_cell; // 1/bounds * grid_size + ivec3 region_world_offset; + uint pad; + vec4 pad2; +}; + +layout(set = 0, binding = 7, std140) uniform Cascades { + CascadeData data[MAX_CASCADES]; +} +cascades; + +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 + +struct Light { + vec3 color; + float energy; + + vec3 direction; + bool has_shadow; + + vec3 position; + float attenuation; + + uint type; + float cos_spot_angle; + float inv_spot_attenuation; + float radius; +}; + +layout(set = 0, binding = 8, std140) buffer restrict readonly Lights { + Light data[]; +} +lights; + +#ifndef MODE_PROCESS_STATIC +layout(set = 0, binding = 9) uniform texture2DArray lightprobe_texture; +#endif + +layout(push_constant, std430) uniform Params { + ivec3 grid_size; + int max_cascades; + + int cascade; + uint light_count; + uint process_offset; + uint process_increment; + + float bounce_feedback; + float y_mult; + bool use_occlusion; + int probe_cell_size; + + ivec3 probe_axis_size; + bool dirty_dynamic_update; +} +params; + +vec2 octahedron_wrap(vec2 v) { + vec2 signVal; + signVal.x = v.x >= 0.0 ? 1.0 : -1.0; + signVal.y = v.y >= 0.0 ? 1.0 : -1.0; + return (1.0 - abs(v.yx)) * signVal; +} + +vec2 octahedron_encode(vec3 n) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); + n.xy = n.xy * 0.5 + 0.5; + return n.xy; +} + +vec3 octahedron_decode(vec2 f) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + f = f * 2.0 - 1.0; + vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y)); + float t = clamp(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return normalize(n); +} + +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} + +#define REGION_SIZE 8 + +bool trace_ray_hdda(vec3 ray_pos, vec3 ray_dir, float p_distance, int p_cascade) { + const int LEVEL_CASCADE = -1; + const int LEVEL_REGION = 0; + const int LEVEL_BLOCK = 1; + const int LEVEL_VOXEL = 2; + const int MAX_LEVEL = 3; + + const int fp_bits = 10; + const int fp_block_bits = fp_bits + 2; + const int fp_region_bits = fp_block_bits + 1; + const int fp_cascade_bits = fp_region_bits + 4; + + bvec3 limit_dir = greaterThan(ray_dir, vec3(0.0)); + ivec3 step = mix(ivec3(0), ivec3(1), limit_dir); + ivec3 ray_sign = ivec3(sign(ray_dir)); + + ivec3 ray_dir_fp = ivec3(ray_dir * float(1 << fp_bits)); + + bvec3 ray_zero = lessThan(abs(ray_dir), vec3(1.0 / 127.0)); + ivec3 inv_ray_dir_fp = ivec3(float(1 << fp_bits) / ray_dir); + + const ivec3 level_masks[MAX_LEVEL] = ivec3[]( + ivec3(1 << fp_region_bits) - ivec3(1), + ivec3(1 << fp_block_bits) - ivec3(1), + ivec3(1 << fp_bits) - ivec3(1)); + + ivec3 region_offset_mask = (params.grid_size / REGION_SIZE) - ivec3(1); + + ivec3 limits[MAX_LEVEL]; + + limits[LEVEL_REGION] = ((params.grid_size << fp_bits) - ivec3(1)) * step; // Region limit does not change, so initialize now. + + // Initialize to cascade + int level = LEVEL_CASCADE; + int cascade = p_cascade - 1; + + ivec3 cascade_base; + ivec3 region_base; + uvec2 block; + bool hit = false; + + ivec3 pos; + float distance = p_distance; + ivec3 distance_limit; + bool distance_limit_valid; + + while (true) { + // This loop is written so there is only one single main iteration. + // This ensures that different compute threads working on different + // levels can still run together without blocking each other. + + if (level == LEVEL_VOXEL) { + // The first level should be (in a worst case scenario) the most used + // so it needs to appear first. The rest of the levels go from more to least used order. + + ivec3 block_local = (pos & level_masks[LEVEL_BLOCK]) >> fp_bits; + uint block_index = uint(block_local.z * 16 + block_local.y * 4 + block_local.x); + if (block_index < 32) { + // Low 32 bits. + if (bool(block.x & uint(1 << block_index))) { + hit = true; + break; + } + } else { + // High 32 bits. + block_index -= 32; + if (bool(block.y & uint(1 << block_index))) { + hit = true; + break; + } + } + } else if (level == LEVEL_BLOCK) { + ivec3 block_local = (pos & level_masks[LEVEL_REGION]) >> fp_block_bits; + block = imageLoad(voxel_cascades, region_base + block_local).rg; + if (block != uvec2(0)) { + // Have voxels inside + level = LEVEL_VOXEL; + limits[LEVEL_VOXEL] = pos - (pos & level_masks[LEVEL_BLOCK]) + step * (level_masks[LEVEL_BLOCK] + ivec3(1)); + continue; + } + } else if (level == LEVEL_REGION) { + ivec3 region = pos >> fp_region_bits; + region = (cascades.data[cascade].region_world_offset + region) & region_offset_mask; // Scroll to world + region += cascade_base; + bool region_used = imageLoad(voxel_region_cascades, region).r > 0; + + if (region_used) { + // The region has contents. + region_base = (region << 1); + level = LEVEL_BLOCK; + limits[LEVEL_BLOCK] = pos - (pos & level_masks[LEVEL_REGION]) + step * (level_masks[LEVEL_REGION] + ivec3(1)); + continue; + } + } else if (level == LEVEL_CASCADE) { + // Return to global + if (cascade >= p_cascade) { + ray_pos = vec3(pos) / float(1 << fp_bits); + ray_pos /= cascades.data[cascade].to_cell; + ray_pos += cascades.data[cascade].offset; + distance /= cascades.data[cascade].to_cell; + } + + cascade++; + if (cascade == params.max_cascades) { + break; + } + + ray_pos -= cascades.data[cascade].offset; + ray_pos *= cascades.data[cascade].to_cell; + + pos = ivec3(ray_pos * float(1 << fp_bits)); + if (any(lessThan(pos, ivec3(0))) || any(greaterThanEqual(pos, params.grid_size << fp_bits))) { + // Outside this cascade, go to next. + continue; + } + + distance *= cascades.data[cascade].to_cell; + + vec3 box = (vec3(params.grid_size * step) - ray_pos) / ray_dir; + float advance_to_bounds = min(box.x, min(box.y, box.z)); + + if (distance < advance_to_bounds) { + // Can hit the distance in this cascade? + distance_limit = pos + ray_sign * ivec3(distance * (1 << fp_bits)); + distance_limit_valid = true; + } else { + // We can't so subtract the advance to the end of the cascade. + distance -= advance_to_bounds; + distance_limit = ray_sign * 0xFFF << fp_bits; // Unreachable limit + distance_limit_valid = false; + } + + cascade_base = ivec3(0, int(params.grid_size.y / REGION_SIZE) * cascade, 0); + level = LEVEL_REGION; + continue; + } + + // Fixed point, multi-level DDA. + + ivec3 mask = level_masks[level]; + ivec3 box = mask * step; + ivec3 pos_diff = box - (pos & mask); + ivec3 tv = mix((pos_diff * inv_ray_dir_fp), ivec3(0x7FFFFFFF), ray_zero) >> fp_bits; + int t = min(tv.x, min(tv.y, tv.z)); + + // The general idea here is that we _always_ need to increment to the closest next cell + // (this is a DDA after all), so adv_box forces this increment for the minimum axis. + + ivec3 adv_box = pos_diff + ray_sign; + ivec3 adv_t = (ray_dir_fp * t) >> fp_bits; + + pos += mix(adv_t, adv_box, equal(ivec3(t), tv)); + + if (distance_limit_valid) { // Test against distance limit. + bvec3 limit = lessThan(pos, distance_limit); + bvec3 eq = equal(limit, limit_dir); + if (!all(eq)) { + break; // Reached limit, break. + } + } + + while (true) { + bvec3 limit = lessThan(pos, limits[level]); + bool inside = all(equal(limit, limit_dir)); + if (inside) { + break; + } + level -= 1; + if (level == LEVEL_CASCADE) { + break; + } + } + } + + return hit; +} + +uint rgbe_encode(vec3 rgb) { + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); + + rgb = clamp(rgb, 0, rgbe_max); + + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); + + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); + + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + return e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); +} + +vec3 rgbe_decode(uint p_rgbe) { + vec4 rgbef = vec4((uvec4(p_rgbe) >> uvec4(0, 9, 18, 27)) & uvec4(0x1FF, 0x1FF, 0x1FF, 0x1F)); + return rgbef.rgb * pow(2.0, rgbef.a - 15.0 - 9.0); +} + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +ivec2 probe_to_tex(ivec3 local_probe) { + ivec3 cell = modi(cascades.data[params.cascade].region_world_offset + local_probe, params.probe_axis_size); + return cell.xy + ivec2(0, cell.z * int(params.probe_axis_size.y)); +} + +void main() { + uint voxel_index = uint(gl_GlobalInvocationID.x); + + if (voxel_index >= dispatch_data.total_count) { + return; + } + +#ifdef MODE_PROCESS_STATIC + // Discard if not marked for static update + if (!bool(process_voxels.data[voxel_index].position & PROCESS_STATIC_PENDING_BIT)) { + return; + } +#else + + //used for skipping voxels every N frames + if (params.process_increment > 1) { + if (((voxel_index + params.process_offset) % params.process_increment) != 0) { + if (params.dirty_dynamic_update && bool(process_voxels.data[voxel_index].position & PROCESS_DYNAMIC_PENDING_BIT)) { + //saved because it still needs dynamic update + } else { + return; + } + } + } + + if (params.dirty_dynamic_update) { + process_voxels.data[voxel_index].position &= ~uint(PROCESS_DYNAMIC_PENDING_BIT); + } +#endif + + // Decode ProcessVoxel + + ivec3 positioni = ivec3((uvec3(process_voxels.data[voxel_index].position) >> uvec3(0, 7, 14)) & uvec3(0x7F)); + + vec3 position = vec3(positioni) + vec3(0.5); + position /= cascades.data[params.cascade].to_cell; + position += cascades.data[params.cascade].offset; + + uint voxel_albedo = process_voxels.data[voxel_index].albedo_normal; + + vec3 albedo = vec3((uvec3(process_voxels.data[voxel_index].albedo_normal) >> uvec3(0, 5, 11)) & uvec3(0x1F, 0x3F, 0x1F)) / vec3(0x1F, 0x3F, 0x1F); + vec2 normal_oct = vec2((uvec2(process_voxels.data[voxel_index].albedo_normal) >> uvec2(16, 24)) & uvec2(0xFF, 0xFF)) / vec2(0xFF, 0xFF); + vec3 normal = octahedron_decode(normal_oct); + vec3 emission = rgbe_decode(process_voxels.data[voxel_index].emission); + uint occlusionu = process_voxels.data[voxel_index].occlusion; + + vec3 light_accum = vec3(0.0); + + // Add indirect light first, in order to save computation resources +#ifndef MODE_PROCESS_STATIC + + if (params.bounce_feedback > 0.001) { + vec3 feedback = albedo * params.bounce_feedback; + ivec3 base_probe = positioni / params.probe_cell_size; + vec2 probe_tex_to_uv = 1.0 / vec2((LIGHTPROBE_OCT_SIZE + 2) * params.probe_axis_size.x, (LIGHTPROBE_OCT_SIZE + 2) * params.probe_axis_size.y * params.probe_axis_size.z); + + for (int i = 0; i < 8; i++) { + float weight = float((occlusionu >> (i * 4)) & 0xF) / float(0xF); //precached occlusion + if (weight == 0.0) { + // Do not waste time. + continue; + } + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + ivec2 tex_pos = probe_to_tex(probe); + vec2 tex_uv = vec2(ivec2(tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1))) + normal_oct * float(LIGHTPROBE_OCT_SIZE); + tex_uv *= probe_tex_to_uv; + vec3 light = texture(sampler2DArray(lightprobe_texture, linear_sampler), vec3(tex_uv, float(params.cascade))).rgb; + light_accum += light * weight; + } + + light_accum *= feedback; + } +#endif + + // Raytrace light + + for (uint i = 0; i < params.light_count; i++) { + float attenuation = 1.0; + vec3 direction; + float light_distance = 1e20; + + switch (lights.data[i].type) { + case LIGHT_TYPE_DIRECTIONAL: { + direction = -lights.data[i].direction; + attenuation *= max(0.0, dot(normal, direction)); + } break; + case LIGHT_TYPE_OMNI: { + vec3 rel_vec = lights.data[i].position - position; + direction = normalize(rel_vec); + light_distance = length(rel_vec); + rel_vec.y /= params.y_mult; + attenuation = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, lights.data[i].attenuation); + attenuation *= max(0.0, dot(normal, direction)); + + } break; + case LIGHT_TYPE_SPOT: { + vec3 rel_vec = lights.data[i].position - position; + direction = normalize(rel_vec); + + light_distance = length(rel_vec); + rel_vec.y /= params.y_mult; + attenuation = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, lights.data[i].attenuation); + attenuation *= max(0.0, dot(normal, direction)); + + float cos_spot_angle = lights.data[i].cos_spot_angle; + float cos_angle = dot(-direction, lights.data[i].direction); + + if (cos_angle < cos_spot_angle) { + continue; + } + + float scos = max(cos_angle, cos_spot_angle); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle)); + attenuation *= 1.0 - pow(spot_rim, lights.data[i].inv_spot_attenuation); + } break; + } + + if (attenuation < 0.001) { + continue; + } + + bool hit = false; + + vec3 ray_pos = position; + vec3 ray_dir = direction; + vec3 inv_dir = 1.0 / ray_dir; + + /* No bias needed for voxels + float cell_size = 1.0 / cascades.data[params.cascade].to_cell; + ray_pos += sign(direction) * cell_size * 0.48; // go almost to the box edge but remain inside + ray_pos += ray_dir * 0.4 * cell_size; //apply a small bias from there + */ + + if (lights.data[i].has_shadow) { + hit = trace_ray_hdda(ray_pos, ray_dir, light_distance, params.cascade); + } + + if (!hit) { + light_accum += albedo * lights.data[i].color.rgb * lights.data[i].energy * attenuation; + } + } + + light_accum += emission; + +#if 0 + vec3 an = normal; + if (an.x < 0) { + an.x = an.x * -0.25; + } + if (an.y < 0) { + an.y = an.y * -0.25; + } + if (an.z < 0) { + an.z = an.z * -0.25; + } + light_accum = an; +#endif + +#ifdef MODE_PROCESS_STATIC + // Add to self, since its static. + process_voxels.data[voxel_index].emission = rgbe_encode(light_accum.rgb); + process_voxels.data[voxel_index].position &= ~uint(PROCESS_STATIC_PENDING_BIT); // Clear process static bit. +#else + + // Store to light texture + positioni = (positioni + cascades.data[params.cascade].region_world_offset * REGION_SIZE) & (params.grid_size - 1); + positioni.y += int(params.cascade) * params.grid_size.y; + imageStore(dst_light, positioni, uvec4(rgbe_encode(light_accum.rgb))); + +#endif +} diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_filter.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_filter.glsl new file mode 100644 index 000000000000..28ff785691a8 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_filter.glsl @@ -0,0 +1,164 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +#define WG_SIZE 8 + +layout(local_size_x = WG_SIZE, local_size_y = WG_SIZE, local_size_z = 1) in; + +layout(set = 0, binding = 0) uniform texture2D depth_buffer; +layout(set = 0, binding = 1) uniform texture2D normal_roughness_buffer; +layout(set = 0, binding = 2) uniform texture2D specular_buffer; +layout(set = 0, binding = 3) uniform texture2D blend_buffer; +layout(r32ui, set = 0, binding = 4) uniform restrict writeonly uimage2D dst_specular_buffer; +layout(rg8, set = 0, binding = 5) uniform restrict writeonly image2D dst_blend_buffer; +layout(set = 0, binding = 6) uniform sampler linear_sampler; + +layout(constant_id = 1) const bool sc_use_full_projection_matrix = false; +layout(constant_id = 0) const bool sc_half_res = false; + +#ifdef HALF_SIZE +#define RADIUS 6 +#else +#define RADIUS 12 +#endif + +#define ROUGHNESS_TO_REFLECTION_TRESHOOLD 0.3 + +layout(set = 0, binding = 7, std140) uniform SceneData { + mat4x4 inv_projection[2]; + mat4x4 cam_transform; + vec4 eye_offset[2]; + + ivec2 screen_size; + float pad1; + float pad2; +} +scene_data; + +layout(push_constant, std430) uniform Params { + bool orthogonal; + float z_near; + float z_far; + uint view_index; + + vec4 proj_info; + + ivec2 filter_dir; + uvec2 pad; +} +params; + +vec3 reconstruct_position(ivec2 screen_pos) { + if (sc_use_full_projection_matrix) { + vec4 pos; + pos.xy = (2.0 * vec2(screen_pos) / vec2(scene_data.screen_size)) - 1.0; + pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r * 2.0 - 1.0; + pos.w = 1.0; + + pos = scene_data.inv_projection[params.view_index] * pos; + + return pos.xyz / pos.w; + } else { + vec3 pos; + pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r; + + pos.z = pos.z * 2.0 - 1.0; + if (params.orthogonal) { + pos.z = ((pos.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + } else { + pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - pos.z * (params.z_far - params.z_near)); + } + pos.z = -pos.z; + + pos.xy = vec2(screen_pos) * params.proj_info.xy + params.proj_info.zw; + if (!params.orthogonal) { + pos.xy *= pos.z; + } + + return pos; + } +} + +vec4 fetch_normal_and_roughness(ivec2 pos) { + vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0); + if (normal_roughness.xyz != vec3(0)) { + normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0); + bool dynamic_object = normal_roughness.a > 0.5; + if (dynamic_object) { + normal_roughness.a = 1.0 - normal_roughness.a; + } + normal_roughness.a /= (127.0 / 255.0); + } + return normal_roughness; +} + +uint rgbe_encode(vec3 rgb) { + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); + + rgb = clamp(rgb, 0, rgbe_max); + + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); + + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); + + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + return e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); +} + +void main() { + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + + ivec2 depth_pos = pos; + if (sc_half_res) { + depth_pos <<= 1; + } + + vec4 normal_roughness = fetch_normal_and_roughness(depth_pos); + + if (normal_roughness.rgb == vec3(0.0) || normal_roughness.a >= ROUGHNESS_TO_REFLECTION_TRESHOOLD) { + return; // No need to do anything. + } + + float depth = reconstruct_position(depth_pos).z; + + vec4 specular_accum = vec4(0); + float total_weight = 0; + float diffuse_blend = texelFetch(sampler2D(blend_buffer, linear_sampler), pos, 0).r; + + for (int i = -RADIUS; i <= RADIUS; i++) { + ivec2 read_pos = pos + params.filter_dir * i; + vec4 specular; + specular.rgb = texelFetch(sampler2D(specular_buffer, linear_sampler), read_pos, 0).rgb; + specular.a = texelFetch(sampler2D(blend_buffer, linear_sampler), read_pos, 0).g; + if (sc_half_res) { + read_pos <<= 1; + } + float d = reconstruct_position(read_pos).z; + vec4 nr = fetch_normal_and_roughness(read_pos); + + float weight = exp(-abs(depth - d)); + float dp = max(0.0, dot(nr.rgb, normal_roughness.rgb)); + dp = pow(dp, 4.0); // The more curvature, the less filter. + weight *= dp; + weight *= max(0.0, 1.0 - abs(nr.a - normal_roughness.a) * 4.0); + + if (weight > 0.0) { + specular_accum += specular * weight; + total_weight += weight; + } + } + + if (total_weight > 0.0) { + specular_accum /= total_weight; + } + + //imageStore(dst_specular_buffer,pos,uvec4(rgbe_encode(specular_accum.rgb))); + imageStore(dst_specular_buffer, pos, uvec4(rgbe_encode(specular_accum.rgb))); + uint blend = uint(clamp(specular_accum.a * 0xF, 0, 0xF)) | (uint(clamp(diffuse_blend * 0xF, 0, 0xF)) << 4); + imageStore(dst_blend_buffer, pos, vec4(diffuse_blend, specular_accum.a, 0, 0)); +} diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_integrate.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_integrate.glsl new file mode 100644 index 000000000000..352b2607f87c --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_integrate.glsl @@ -0,0 +1,917 @@ +´´#[compute] + +#version 450 + +#VERSION_DEFINES + +#ifndef MOLTENVK_USED +#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) + +#extension GL_KHR_shader_subgroup_ballot : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable + +#define USE_SUBGROUPS +#endif +#endif // MOLTENVK_USED + +#define REGION_SIZE 8 + +#define CACHE_IS_VALID 0x80000000 +#define CACHE_IS_HIT 0x40000000 + +#define MAX_CASCADES 8 + +#ifdef MODE_PROCESS + + layout(local_size_x = LIGHTPROBE_OCT_SIZE, local_size_y = LIGHTPROBE_OCT_SIZE, local_size_z = 1) in; + +#define TRACE_SUBPIXEL + +layout(rg32ui, set = 0, binding = 1) uniform restrict readonly uimage3D voxel_cascades; +layout(r8ui, set = 0, binding = 2) uniform restrict readonly uimage3D voxel_region_cascades; + +layout(set = 0, binding = 3) uniform texture3D light_cascades; +layout(set = 0, binding = 4) uniform sampler linear_sampler; +layout(r32ui, set = 0, binding = 5) uniform restrict uimage2DArray lightprobe_texture_data; +layout(r32ui, set = 0, binding = 6) uniform restrict writeonly uimage2DArray lightprobe_diffuse_data; +layout(r32ui, set = 0, binding = 7) uniform restrict writeonly uimage2DArray lightprobe_ambient_data; +layout(r32ui, set = 0, binding = 8) uniform restrict uimage2DArray ray_hit_cache; +layout(r16ui, set = 0, binding = 9) uniform restrict uimage2DArray ray_hit_cache_version; +layout(r16ui, set = 0, binding = 10) uniform restrict uimage3D region_versions; +layout(r32ui, set = 0, binding = 11) uniform restrict uimage2DArray lightprobe_moving_average_history; +layout(r32ui, set = 0, binding = 12) uniform restrict uimage2DArray lightprobe_moving_average; +layout(r32ui, set = 0, binding = 14) uniform restrict uimage2DArray lightprobe_update_frames; +layout(r8, set = 0, binding = 15) uniform restrict readonly image2DArray lightprobe_geometry_proximity; +layout(r8, set = 0, binding = 16) uniform restrict readonly image2DArray lightprobe_camera_visibility; + +#ifdef USE_CUBEMAP_ARRAY +layout(set = 1, binding = 0) uniform textureCubeArray sky_irradiance; +#else +layout(set = 1, binding = 0) uniform textureCube sky_irradiance; +#endif +layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps; + +#define SKY_MODE_DISABLED 0 +#define SKY_MODE_COLOR 1 +#define SKY_MODE_SKY 2 + +struct CascadeData { + vec3 offset; //offset of (0,0,0) in world coordinates + float to_cell; // 1/bounds * grid_size + ivec3 region_world_offset; + uint pad; + vec4 pad2; +}; + +layout(set = 0, binding = 13, std140) uniform Cascades { + CascadeData data[MAX_CASCADES]; +} +cascades; + +// MODE_PROCESS +#endif + +#ifdef MODE_FILTER + +layout(local_size_x = LIGHTPROBE_OCT_SIZE, local_size_y = LIGHTPROBE_OCT_SIZE, local_size_z = 1) in; + +layout(r32ui, set = 0, binding = 1) uniform restrict readonly uimage2DArray lightprobe_src_diffuse_data; +layout(r32ui, set = 0, binding = 2) uniform restrict writeonly uimage2DArray lightprobe_dst_diffuse_data; +layout(r8ui, set = 0, binding = 3) uniform restrict readonly uimage2DArray lightprobe_neighbours; +layout(r8, set = 0, binding = 4) uniform restrict readonly image2DArray lightprobe_geometry_proximity; +layout(r8, set = 0, binding = 5) uniform restrict readonly image2DArray lightprobe_camera_visibility; + +#endif + +#ifdef MODE_CAMERA_VISIBILITY + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +layout(r8, set = 0, binding = 1) uniform restrict writeonly image2DArray lightprobe_camera_visibility; + +#define MAX_CAMERA_PLANES 6 +#define MAX_CAMERA_POINTS 8 + +layout(set = 0, binding = 2, std140) uniform CameraPlanes { + vec4 planes[MAX_CAMERA_PLANES]; + vec4 points[MAX_CAMERA_POINTS]; +} +camera; + +#endif + +layout(push_constant, std430) uniform Params { + ivec3 grid_size; + int max_cascades; + + float ray_bias; + int cascade; + int inactive_update_frames; + int history_size; + + ivec3 world_offset; + uint sky_mode; + + ivec3 scroll; + float sky_energy; + + vec3 sky_color; + float y_mult; + + ivec3 probe_axis_size; + bool store_ambient_texture; + + uvec2 pad; + int global_frame; + uint motion_accum; // Motion that happened since last update (bit 0 in X, bit 1 in Y, bit 2 in Z). +} +params; + +uvec3 hash3(uvec3 x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +uint hash(uint x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +float hashf3(vec3 co) { + return fract(sin(dot(co, vec3(12.9898, 78.233, 137.13451))) * 43758.5453); +} + +vec3 octahedron_decode(vec2 f) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + f = f * 2.0 - 1.0; + vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y)); + float t = clamp(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return normalize(n); +} + +uint rgbe_encode(vec3 rgb) { + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); + + rgb = clamp(rgb, 0, rgbe_max); + + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); + + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); + + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + return e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); +} + +vec3 rgbe_decode(uint p_rgbe) { + vec4 rgbef = vec4((uvec4(p_rgbe) >> uvec4(0, 9, 18, 27)) & uvec4(0x1FF, 0x1FF, 0x1FF, 0x1F)); + return rgbef.rgb * pow(2.0, rgbef.a - 15.0 - 9.0); +} + +#define FP_BITS 14 +#define FP_MAX ((1 << 22) - 1) + +uvec3 rgbe_decode_fp(uint p_rgbe, int p_fp_bits) { + uvec4 rgbe = (uvec4(p_rgbe) >> uvec4(0, 9, 18, 27)) & + uvec4(0x1FF, 0x1FF, 0x1FF, 0x1F); + int shift = int(rgbe.a) - 15 - 9 + p_fp_bits; + if (shift >= 0) { + rgbe.rgb <<= uint(shift); + } else { + rgbe.rgb >>= uint(-shift); + } + return rgbe.rgb; +} + +#ifdef MODE_PROCESS + +bool trace_ray_hdda(vec3 ray_pos, vec3 ray_dir, int p_cascade, out ivec3 r_cell, out ivec3 r_side, out int r_cascade) { + const int LEVEL_CASCADE = -1; + const int LEVEL_REGION = 0; + const int LEVEL_BLOCK = 1; + const int LEVEL_VOXEL = 2; + const int MAX_LEVEL = 3; + + const int fp_bits = 8; + const int fp_block_bits = fp_bits + 2; + const int fp_region_bits = fp_block_bits + 1; + const int fp_cascade_bits = fp_region_bits + 4; + + bvec3 limit_dir = greaterThan(ray_dir, vec3(0.0)); + ivec3 step = mix(ivec3(0), ivec3(1), limit_dir); + ivec3 ray_sign = ivec3(sign(ray_dir)); + + ivec3 ray_dir_fp = ivec3(ray_dir * float(1 << fp_bits)); + + bvec3 ray_zero = lessThan(abs(ray_dir), vec3(1.0 / 127.0)); + ivec3 inv_ray_dir_fp = ivec3(float(1 << fp_bits) / ray_dir); + + const ivec3 level_masks[MAX_LEVEL] = ivec3[]( + ivec3(1 << fp_region_bits) - ivec3(1), + ivec3(1 << fp_block_bits) - ivec3(1), + ivec3(1 << fp_bits) - ivec3(1)); + + ivec3 region_offset_mask = (params.grid_size / REGION_SIZE) - ivec3(1); + + ivec3 limits[MAX_LEVEL]; + + limits[LEVEL_REGION] = ((params.grid_size << fp_bits) - ivec3(1)) * step; // Region limit does not change, so initialize now. + + // Initialize to cascade + int level = LEVEL_CASCADE; + int cascade = p_cascade - 1; + + ivec3 cascade_base; + ivec3 region_base; + uvec2 block; + bool hit = false; + + ivec3 pos; + + while (true) { + // This loop is written so there is only one single main iteration. + // This ensures that different compute threads working on different + // levels can still run together without blocking each other. + + if (level == LEVEL_VOXEL) { + // The first level should be (in a worst case scenario) the most used + // so it needs to appear first. The rest of the levels go from more to least used order. + + ivec3 block_local = (pos & level_masks[LEVEL_BLOCK]) >> fp_bits; + uint block_index = uint(block_local.z * 16 + block_local.y * 4 + block_local.x); + if (block_index < 32) { + // Low 32 bits. + if (bool(block.x & uint(1 << block_index))) { + hit = true; + break; + } + } else { + // High 32 bits. + block_index -= 32; + if (bool(block.y & uint(1 << block_index))) { + hit = true; + break; + } + } + } else if (level == LEVEL_BLOCK) { + ivec3 block_local = (pos & level_masks[LEVEL_REGION]) >> fp_block_bits; + block = imageLoad(voxel_cascades, region_base + block_local).rg; + if (block != uvec2(0)) { + // Have voxels inside + level = LEVEL_VOXEL; + limits[LEVEL_VOXEL] = pos - (pos & level_masks[LEVEL_BLOCK]) + step * (level_masks[LEVEL_BLOCK] + ivec3(1)); + continue; + } + } else if (level == LEVEL_REGION) { + ivec3 region = pos >> fp_region_bits; + region = (cascades.data[cascade].region_world_offset + region) & region_offset_mask; // Scroll to world + region += cascade_base; + bool region_used = imageLoad(voxel_region_cascades, region).r > 0; + + if (region_used) { + // The region has contents. + region_base = (region << 1); + level = LEVEL_BLOCK; + limits[LEVEL_BLOCK] = pos - (pos & level_masks[LEVEL_REGION]) + step * (level_masks[LEVEL_REGION] + ivec3(1)); + continue; + } + } else if (level == LEVEL_CASCADE) { + // Return to global + if (cascade >= p_cascade) { + ray_pos = vec3(pos) / float(1 << fp_bits); + ray_pos /= cascades.data[cascade].to_cell; + ray_pos += cascades.data[cascade].offset; + } + + cascade++; + if (cascade == params.max_cascades) { + break; + } + + ray_pos -= cascades.data[cascade].offset; + ray_pos *= cascades.data[cascade].to_cell; + pos = ivec3(ray_pos * float(1 << fp_bits)); + if (any(lessThan(pos, ivec3(0))) || any(greaterThanEqual(pos, params.grid_size << fp_bits))) { + // Outside this cascade, go to next. + continue; + } + + cascade_base = ivec3(0, params.grid_size.y / REGION_SIZE * cascade, 0); + level = LEVEL_REGION; + continue; + } + + // Fixed point, multi-level DDA. + + ivec3 mask = level_masks[level]; + ivec3 box = mask * step; + ivec3 pos_diff = box - (pos & mask); + ivec3 tv = mix((pos_diff * inv_ray_dir_fp), ivec3(0x7FFFFFFF), ray_zero) >> fp_bits; + int t = min(tv.x, min(tv.y, tv.z)); + + // The general idea here is that we _always_ need to increment to the closest next cell + // (this is a DDA after all), so adv_box forces this increment for the minimum axis. + + ivec3 adv_box = pos_diff + ray_sign; + ivec3 adv_t = (ray_dir_fp * t) >> fp_bits; + + pos += mix(adv_t, adv_box, equal(ivec3(t), tv)); + + while (true) { + bvec3 limit = lessThan(pos, limits[level]); + bool inside = all(equal(limit, limit_dir)); + if (inside) { + break; + } + level -= 1; + if (level == LEVEL_CASCADE) { + break; + } + } + } + + if (hit) { + ivec3 mask = level_masks[LEVEL_VOXEL]; + ivec3 box = mask * (step ^ ivec3(1)); + ivec3 pos_diff = box - (pos & mask); + ivec3 tv = mix((pos_diff * -inv_ray_dir_fp), ivec3(0x7FFFFFFF), ray_zero); + + int m; + if (tv.x < tv.y) { + r_side = ivec3(1, 0, 0); + m = tv.x; + } else { + r_side = ivec3(0, 1, 0); + m = tv.y; + } + if (tv.z < m) { + r_side = ivec3(0, 0, 1); + } + + r_side *= -ray_sign; + + r_cell = pos >> fp_bits; + + r_cascade = cascade; + } + + return hit; +} + +#if LIGHTPROBE_OCT_SIZE == 4 +const uint neighbour_max_weights = 8; +const uint neighbour_weights[128] = uint[](15544, 73563, 135085, 206971, 270171, 528301, 796795, 988221, 8569, 82130, 144347, 200892, 272100, 336249, 397500, 0, 4284, 78811, 147666, 205177, 331964, 401785, 468708, 0, 10363, 69549, 139099, 212152, 466779, 724909, 791613, 993403, 8569, 75492, 278738, 336249, 537563, 594108, 790716, 0, 73563, 135085, 270171, 343224, 403579, 528301, 600187, 660541, 69549, 139099, 338043, 408760, 466779, 595005, 665723, 724909, 141028, 205177, 401785, 475346, 659644, 734171, 987324, 0, 4284, 275419, 331964, 540882, 598393, 795001, 861924, 0, 266157, 338043, 398397, 532315, 605368, 665723, 859995, 921517, 332861, 403579, 462765, 600187, 670904, 728923, 855981, 925531, 200892, 397500, 472027, 663929, 737490, 927460, 991609, 0, 10363, 201789, 266157, 532315, 801976, 859995, 921517, 993403, 534244, 598393, 659644, 795001, 868562, 930779, 987324, 0, 594108, 663929, 730852, 790716, 865243, 934098, 991609, 0, 5181, 206971, 462765, 728923, 796795, 855981, 925531, 998584); +const uint wrap_neighbours[(LIGHTPROBE_OCT_SIZE + 2) * (LIGHTPROBE_OCT_SIZE + 2)] = uint[](196611, 3, 2, 1, 0, 196608, 196608, 0, 1, 2, 3, 196611, 131072, 65536, 65537, 65538, 65539, 131075, 65536, 131072, 131073, 131074, 131075, 65539, 0, 196608, 196609, 196610, 196611, 3, 3, 196611, 196610, 196609, 196608, 0); +#endif + +#if LIGHTPROBE_OCT_SIZE == 5 +const uint neighbour_max_weights = 15; +const uint neighbour_weights[375] = uint[](11139, 72624, 131886, 201671, 271258, 334768, 394335, 590836, 656174, 988103, 1319834, 1377268, 1579952, 0, 0, 6839, 76283, 139717, 205401, 267029, 334519, 400776, 461448, 527528, 590801, 657717, 984017, 1311697, 0, 0, 778, 74103, 141723, 205175, 262922, 330016, 400965, 466633, 532037, 592160, 655986, 723045, 789015, 854117, 918130, 4885, 74329, 139717, 207355, 268983, 328657, 396456, 461448, 531848, 596663, 919861, 1246161, 1573841, 0, 0, 9114, 70599, 131886, 203696, 273283, 328692, 525407, 596912, 918318, 1250247, 1317808, 1508340, 1581978, 0, 0, 6839, 72375, 133429, 197585, 263121, 338427, 400776, 664005, 723592, 991833, 1051816, 1315605, 1377233, 0, 0, 1026, 72722, 138504, 199687, 334866, 403430, 465362, 525422, 662792, 727506, 789836, 986119, 1049710, 0, 0, 68049, 138485, 199121, 399699, 468771, 530771, 657381, 727832, 794768, 858904, 919525, 1117965, 0, 0, 0, 68615, 138504, 203794, 263170, 394350, 465362, 534502, 597010, 789836, 858578, 924936, 1180782, 1248263, 0, 0, 977, 66513, 133429, 203447, 268983, 531848, 600571, 854664, 926149, 1182888, 1253977, 1508305, 1577749, 0, 0, 778, 67872, 131698, 336247, 400965, 460901, 666011, 728777, 789015, 991607, 1056325, 1116261, 1311498, 1378592, 1442418, 133093, 330193, 399699, 465688, 662773, 730915, 794768, 855821, 985553, 1055059, 1121048, 1443813, 0, 0, 0, 133468, 396510, 466974, 527582, 657756, 729118, 796314, 860190, 919900, 1051870, 1122334, 1182942, 1444188, 0, 0, 133093, 465688, 530771, 592337, 724749, 794768, 861987, 924917, 1121048, 1186131, 1247697, 1443813, 0, 0, 0, 131698, 198944, 262922, 460901, 532037, 598391, 789015, 859849, 928155, 1116261, 1187397, 1253751, 1442418, 1509664, 1573642, 4885, 66513, 336473, 396456, 664005, 723592, 993787, 1056136, 1317559, 1383095, 1444149, 1508305, 1573841, 0, 0, 330759, 394350, 662792, 727506, 789836, 990226, 1058790, 1120722, 1180782, 1311746, 1383442, 1449224, 1510407, 0, 0, 462605, 657381, 727832, 794768, 858904, 919525, 1055059, 1124131, 1186131, 1378769, 1449205, 1509841, 0, 0, 0, 525422, 592903, 789836, 858578, 924936, 1049710, 1120722, 1189862, 1252370, 1379335, 1449224, 1514514, 1573890, 0, 0, 197585, 267029, 527528, 598617, 854664, 926149, 1187208, 1255931, 1311697, 1377233, 1444149, 1514167, 1579703, 0, 0, 9114, 66548, 269232, 332743, 656174, 990128, 1049695, 1246196, 1321859, 1383344, 1442606, 1512391, 1581978, 0, 0, 977, 328657, 657717, 989879, 1056136, 1116808, 1182888, 1246161, 1317559, 1387003, 1450437, 1516121, 1577749, 0, 0, 655986, 723045, 789015, 854117, 918130, 985376, 1056325, 1121993, 1187397, 1247520, 1311498, 1384823, 1452443, 1515895, 1573642, 263121, 590801, 919861, 984017, 1051816, 1116808, 1187208, 1252023, 1315605, 1385049, 1450437, 1518075, 1579703, 0, 0, 7088, 197620, 271258, 594887, 918318, 984052, 1180767, 1252272, 1319834, 1381319, 1442606, 1514416, 1584003, 0, 0); +const uint wrap_neighbours[(LIGHTPROBE_OCT_SIZE + 2) * (LIGHTPROBE_OCT_SIZE + 2)] = uint[](262148, 4, 3, 2, 1, 0, 262144, 262144, 0, 1, 2, 3, 4, 262148, 196608, 65536, 65537, 65538, 65539, 65540, 196612, 131072, 131072, 131073, 131074, 131075, 131076, 131076, 65536, 196608, 196609, 196610, 196611, 196612, 65540, 0, 262144, 262145, 262146, 262147, 262148, 4, 4, 262148, 262147, 262146, 262145, 262144, 0); +#endif + +#if LIGHTPROBE_OCT_SIZE == 6 +const uint neighbour_max_weights = 18; +const uint neighbour_weights[648] = uint[](7409, 71136, 133126, 197977, 266811, 334266, 398816, 461221, 723696, 788486, 1181017, 1577531, 1902410, 1972666, 2034416, 2230090, 2299522, 0, 5357, 72623, 137015, 201808, 268219, 332144, 398278, 464109, 527325, 591849, 658038, 722921, 789403, 852980, 1180390, 1574889, 1968758, 2295545, 2062, 71772, 138508, 203472, 267601, 329055, 396334, 464939, 530524, 595281, 659485, 721675, 789292, 855086, 919566, 984415, 1049355, 0, 1375, 70993, 137936, 204044, 268380, 329742, 393995, 462877, 529745, 596060, 661547, 724014, 852747, 918879, 985102, 1051694, 1116972, 0, 4464, 71611, 136272, 202551, 269231, 333037, 395241, 461430, 526313, 592861, 660717, 725958, 1049588, 1117083, 1508070, 1902569, 1967865, 2296438, 6586, 70203, 132441, 198662, 267744, 335089, 396016, 657829, 726496, 1116166, 1508697, 1574730, 1905211, 1971842, 2033482, 2231024, 2300346, 0, 5357, 70598, 134043, 197350, 264169, 330358, 400303, 464109, 525300, 792375, 855005, 1184848, 1247209, 1578939, 1641078, 1970544, 2033641, 2295545, 2433, 71055, 137147, 200658, 264903, 398735, 466053, 529807, 592583, 792507, 857487, 919937, 1183698, 1247943, 1575623, 0, 0, 0, 68573, 137015, 201808, 264169, 394228, 464109, 531375, 595899, 658038, 789403, 857030, 922861, 987504, 1050601, 1180390, 1247209, 1313398, 1378041, 67561, 136272, 202551, 265181, 461430, 530363, 596911, 660717, 721908, 853993, 921968, 988397, 1053638, 1117083, 1312505, 1378934, 1443817, 1508070, 68295, 135122, 202683, 267663, 330113, 527047, 595343, 662661, 726415, 985473, 1054095, 1120187, 1444551, 1511378, 1903303, 0, 0, 0, 2678, 67561, 131814, 199579, 267206, 333037, 590836, 660717, 727983, 1051613, 1120055, 1443817, 1512528, 1837686, 1906619, 1967865, 2230249, 2298224, 2062, 68654, 133932, 399452, 464939, 527406, 793868, 858204, 919566, 1186512, 1250641, 1312095, 1578321, 1642525, 1704715, 1967455, 2032395, 0, 66548, 134043, 197350, 396253, 464109, 529350, 591849, 792375, 859055, 922861, 985718, 1184848, 1251259, 1315184, 1378041, 1574889, 1641078, 1705961, 133126, 197977, 461221, 529888, 594491, 788486, 857568, 924913, 989626, 1051376, 1181017, 1249851, 1317306, 1382018, 1443658, 1706736, 1771338, 0, 132441, 198662, 528955, 595424, 657829, 854768, 924090, 990449, 1054176, 1116166, 1247050, 1316482, 1382842, 1446459, 1508697, 1705802, 1772272, 0, 131814, 199579, 263156, 526313, 594886, 660717, 723933, 920182, 988397, 1055663, 1120055, 1312505, 1380720, 1447867, 1512528, 1771497, 1837686, 1902569, 199468, 265262, 329742, 592942, 661547, 727132, 985102, 1054812, 1121548, 1377631, 1447249, 1514192, 1770251, 1839133, 1906001, 2229003, 2295135, 0, 1375, 66315, 398673, 462877, 525067, 793296, 857425, 918879, 1187084, 1251420, 1312782, 1579100, 1644587, 1707054, 1968142, 2034734, 2100012, 0, 395241, 461430, 526313, 791632, 858043, 921968, 984825, 1185591, 1252271, 1316077, 1378934, 1575901, 1643757, 1708998, 1771497, 2032628, 2100123, 2163430, 527088, 591690, 787801, 856635, 924090, 988802, 1050442, 1181702, 1250784, 1318129, 1382842, 1444592, 1640869, 1709536, 1774139, 2099206, 2164057, 0, 526154, 592624, 853834, 923266, 989626, 1053243, 1115481, 1247984, 1317306, 1383665, 1447392, 1509382, 1708603, 1775072, 1837477, 2098521, 2164742, 0, 591849, 658038, 722921, 919289, 987504, 1054651, 1119312, 1313398, 1381613, 1448879, 1513271, 1705961, 1774534, 1840365, 1903581, 2097894, 2165659, 2229236, 262923, 329055, 590603, 659485, 726353, 984415, 1054033, 1120976, 1378318, 1448028, 1514764, 1772590, 1841195, 1906780, 2165548, 2231342, 2295822, 0, 4464, 67561, 329465, 399291, 461430, 791632, 853993, 1185591, 1248221, 1579951, 1643757, 1704948, 1971437, 2036678, 2100123, 2163430, 2230249, 2296438, 395975, 790482, 854727, 1185723, 1250703, 1313153, 1578383, 1645701, 1709455, 1772231, 1968513, 2037135, 2103227, 2166738, 2230983, 0, 0, 0, 787174, 853993, 920182, 984825, 1182619, 1250246, 1316077, 1380720, 1443817, 1573876, 1643757, 1711023, 1775547, 1837686, 2034653, 2103095, 2167888, 2230249, 919289, 985718, 1050601, 1114854, 1247209, 1315184, 1381613, 1446854, 1510299, 1641078, 1710011, 1776559, 1840365, 1901556, 2033641, 2102352, 2168631, 2231261, 723655, 1051335, 1118162, 1378689, 1447311, 1513403, 1706695, 1774991, 1842309, 1906063, 2034375, 2101202, 2168763, 2233743, 2296193, 0, 0, 0, 1785, 264169, 332144, 658038, 726971, 1050601, 1119312, 1444829, 1513271, 1770484, 1840365, 1907631, 1968758, 2033641, 2097894, 2165659, 2233286, 2299117, 6586, 68336, 264010, 333442, 397883, 722762, 787801, 1181702, 1578464, 1640869, 1903344, 1973489, 2037216, 2099206, 2164057, 2232891, 2300346, 0, 2678, 329465, 395241, 787174, 1182619, 1246196, 1577926, 1643757, 1706973, 1771497, 1837686, 1902569, 1971437, 2038703, 2103095, 2167888, 2234299, 2298224, 1182508, 1248302, 1312782, 1377631, 1442571, 1575982, 1644587, 1710172, 1774929, 1839133, 1901323, 1968142, 2037852, 2104588, 2169552, 2233681, 2295135, 0, 1245963, 1312095, 1378318, 1444910, 1510188, 1573643, 1642525, 1709393, 1775708, 1841195, 1903662, 1967455, 2037073, 2104016, 2170124, 2234460, 2295822, 0, 1785, 330358, 722921, 1114854, 1442804, 1510299, 1574889, 1641078, 1705961, 1772509, 1840365, 1905606, 1970544, 2037691, 2102352, 2168631, 2235311, 2299117, 5762, 67402, 264944, 334266, 395082, 725563, 1115481, 1509382, 1575664, 1837477, 1906144, 1972666, 2036283, 2098521, 2164742, 2233824, 2301169, 0); +const uint wrap_neighbours[(LIGHTPROBE_OCT_SIZE + 2) * (LIGHTPROBE_OCT_SIZE + 2)] = uint[](327685, 5, 4, 3, 2, 1, 0, 327680, 327680, 0, 1, 2, 3, 4, 5, 327685, 262144, 65536, 65537, 65538, 65539, 65540, 65541, 262149, 196608, 131072, 131073, 131074, 131075, 131076, 131077, 196613, 131072, 196608, 196609, 196610, 196611, 196612, 196613, 131077, 65536, 262144, 262145, 262146, 262147, 262148, 262149, 65541, 0, 327680, 327681, 327682, 327683, 327684, 327685, 5, 5, 327685, 327684, 327683, 327682, 327681, 327680, 0); +#endif + +shared uvec3 neighbours_accum[LIGHTPROBE_OCT_SIZE * LIGHTPROBE_OCT_SIZE]; +shared vec3 neighbours[LIGHTPROBE_OCT_SIZE * LIGHTPROBE_OCT_SIZE]; +shared uvec3 ambient_accum; +shared int probe_history_index; + +// MODE_PROCESS +#endif +/* +#if LIGHTPROBE_OCT_SIZE == 4 +const vec3 oct_directions[16]=vec3[](vec3( (-0.408248, -0.408248, -0.816497)), vec3( (-0.316228, -0.948683, 0)), vec3( (0.316228, -0.948683, 0)), vec3( (0.408248, -0.408248, -0.816497)), vec3( (-0.948683, -0.316228, 0)), vec3( (-0.408248, -0.408248, 0.816497)), vec3( (0.408248, -0.408248, 0.816497)), vec3( (0.948683, -0.316228, 0)), vec3( (-0.948683, 0.316228, 0)), vec3( (-0.408248, 0.408248, 0.816497)), vec3( (0.408248, 0.408248, 0.816497)), vec3( (0.948683, 0.316228, 0)), vec3( (-0.408248, 0.408248, -0.816497)), vec3( (-0.316228, 0.948683, 0)), vec3( (0.316228, 0.948683, 0)), vec3( (0.408248, 0.408248, -0.816497))); +#endif + +#if LIGHTPROBE_OCT_SIZE == 5 +const vec3 oct_directions[25]=vec3[](vec3( (-0.301511, -0.301511, -0.904534)), vec3( (-0.301511, -0.904534, -0.301511)), vec3( (0, -0.970142, 0.242536)), vec3( (0.301511, -0.904534, -0.301511)), vec3( (0.301511, -0.301511, -0.904534)), vec3( (-0.904534, -0.301511, -0.301511)), vec3( (-0.666667, -0.666667, 0.333333)), vec3( (0, -0.5547, 0.83205)), vec3( (0.666667, -0.666667, 0.333333)), vec3( (0.904534, -0.301511, -0.301511)), vec3( (-0.970142, 0, 0.242536)), vec3( (-0.5547, 0, 0.83205)), vec3( (0, 0, 1)), vec3( (0.5547, 0, 0.83205)), vec3( (0.970142, 0, 0.242536)), vec3( (-0.904534, 0.301511, -0.301511)), vec3( (-0.666667, 0.666667, 0.333333)), vec3( (0, 0.5547, 0.83205)), vec3( (0.666667, 0.666667, 0.333333)), vec3( (0.904534, 0.301511, -0.301511)), vec3( (-0.301511, 0.301511, -0.904534)), vec3( (-0.301511, 0.904534, -0.301511)), vec3( (0, 0.970142, 0.242536)), vec3( (0.301511, 0.904534, -0.301511)), vec3( (0.301511, 0.301511, -0.904534))); +#endif + +shared uvec3 neighbours[LIGHTPROBE_OCT_SIZE*LIGHTPROBE_OCT_SIZE]; +*/ + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +#define FRAME_MASK 0x0FFFFFFF +#define FORCE_UPDATE_MASK 0xF0000000 +#define FORCE_UPDATE_SHIFT 28 + +void main() { +#ifdef MODE_PROCESS + + ivec2 pos = ivec2(gl_WorkGroupID.xy); + ivec2 local_pos = ivec2(gl_LocalInvocationID.xy); + uint probe_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * LIGHTPROBE_OCT_SIZE; + + // clear + neighbours_accum[probe_index] = uvec3(0); + + ivec3 probe_cell; + probe_cell.x = pos.x; + probe_cell.y = pos.y % params.probe_axis_size.y; + probe_cell.z = pos.y / params.probe_axis_size.y; + + ivec3 probe_world_pos = params.world_offset + probe_cell; + + ivec3 probe_scroll_pos = modi(probe_world_pos, params.probe_axis_size); + ivec3 probe_texture_pos = ivec3((probe_scroll_pos.xy + ivec2(0, probe_scroll_pos.z * params.probe_axis_size.y)), params.cascade); + + if (probe_index == 0) { + // Determine whether it should process the probe. + + bool process = false; + + // Fetch frame. + uint frame = imageLoad(lightprobe_update_frames, probe_texture_pos).r; + uint forced_update = frame >> FORCE_UPDATE_SHIFT; + if (forced_update > 0) { + // Check whether it must force the update + process = true; + forced_update--; + frame = (frame & FRAME_MASK) | (forced_update << FORCE_UPDATE_SHIFT); + } + + bool geom_proximity = imageLoad(lightprobe_geometry_proximity, probe_texture_pos).r > 0.5; + if (geom_proximity) { + bool camera_visible = imageLoad(lightprobe_camera_visibility, probe_texture_pos).r > 0.5; + process = camera_visible; + } + + if (!process) { + int frame_offset = 0; + if ((probe_world_pos.x & 1) != 0) { + frame_offset |= 1; + } + if ((probe_world_pos.y & 1) != 0) { + frame_offset |= 2; + } + if ((probe_world_pos.z & 1) != 0) { + frame_offset |= 4; + } + + if (((params.global_frame + frame_offset) % params.inactive_update_frames) == 0) { + // Process every params.inactive_update_frames. + process = true; + } + } + + if (process) { + uint local_frame = frame & FRAME_MASK; + probe_history_index = int(local_frame) % params.history_size; + frame = ((local_frame + 1) & FRAME_MASK) | (frame & FORCE_UPDATE_MASK); + // Store it back. + imageStore(lightprobe_update_frames, probe_texture_pos, uvec4(frame)); + + ambient_accum = uvec3(0); + } else { + probe_history_index = -1; // No processing. + } + } + + memoryBarrierShared(); + barrier(); + + if (probe_history_index < 0) { + return; + } + + float probe_cell_size = float(params.grid_size.x) / float(params.probe_axis_size.x - 1) / cascades.data[params.cascade].to_cell; + + vec3 ray_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size; + + // Ensure a unique hash that includes the probe world position, the local octahedron pixel, and the history frame index + uvec3 h3 = hash3(uvec3((uvec3(probe_world_pos) * LIGHTPROBE_OCT_SIZE * LIGHTPROBE_OCT_SIZE + uvec3(probe_index)) * uvec3(params.history_size) + uvec3(probe_history_index))); + uint h = (h3.x ^ h3.y) ^ h3.z; + vec2 sample_ofs = vec2(ivec2(h >> 16, h & 0xFFFF)) / vec2(0xFFFF); + vec3 ray_dir = octahedron_decode((vec2(local_pos) + sample_ofs) / vec2(LIGHTPROBE_OCT_SIZE)); + + ray_dir.y *= params.y_mult; + ray_dir = normalize(ray_dir); + + // Apply bias (by a cell) + float bias = params.ray_bias; + vec3 abs_ray_dir = abs(ray_dir); + ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell; + + ivec3 cache_texture_pos = ivec3(probe_texture_pos.xy * LIGHTPROBE_OCT_SIZE + local_pos, probe_texture_pos.z * params.history_size + probe_history_index); + uint cache_entry = imageLoad(ray_hit_cache, cache_texture_pos).r; + + bool hit; + ivec3 hit_cell; + int hit_cascade; + + bool cache_valid = bool(cache_entry & CACHE_IS_VALID); + + vec3 cache_invalidated_debug = vec3(0.0); + + if (cache_valid) { + // Make sure the cache is really valid + hit = bool(cache_entry & CACHE_IS_HIT); + uvec4 uhit = (uvec4(cache_entry) >> uvec4(0, 8, 16, 24)) & uvec4(0xFF, 0xFF, 0xFF, 0x7); + hit_cell = ivec3(uhit.xyz); + hit_cascade = int(uhit.w); + uint axis = (cache_entry >> 27) & 0x3; + if (bool((1 << axis) & params.motion_accum)) { + // There was motion in this axis, cache is no longer valid. + cache_valid = false; + cache_invalidated_debug = vec3(0, 0, 4.0); + } else if (hit) { + // Check if the region pointed to is still valid. + uint version = imageLoad(ray_hit_cache_version, cache_texture_pos).r; + uint region_version = imageLoad(region_versions, (hit_cell / REGION_SIZE) + ivec3(0, hit_cascade * (params.grid_size.y / REGION_SIZE), 0)).r; + + if (region_version != version) { + cache_valid = false; + cache_invalidated_debug = (hit_cascade == params.cascade) ? vec3(0.0, 4.00, 0.0) : vec3(4.0, 0, 0.0); + } + } + } + + if (!cache_valid) { + ivec3 hit_face; + hit = trace_ray_hdda(ray_pos, ray_dir, params.cascade, hit_cell, hit_face, hit_cascade); + if (hit) { + hit_cell += hit_face; + + ivec3 reg_cell_offset = cascades.data[hit_cascade].region_world_offset * REGION_SIZE; + hit_cell = (hit_cell + reg_cell_offset) & (params.grid_size - 1); // Read from wrapped world coordinates + } + } + + vec3 light; + + if (hit) { + ivec3 spos = hit_cell; + spos.y += hit_cascade * params.grid_size.y; + light = texelFetch(sampler3D(light_cascades, linear_sampler), spos, 0).rgb; + } else if (params.sky_mode == SKY_MODE_SKY) { +#ifdef USE_CUBEMAP_ARRAY + light = textureLod(samplerCubeArray(sky_irradiance, linear_sampler_mipmaps), vec4(ray_dir, 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates. +#else + light = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates. +#endif + light *= params.sky_energy; + } else if (params.sky_mode == SKY_MODE_COLOR) { + light = params.sky_color; + light *= params.sky_energy; + } else { + light = vec3(0); + } + + memoryBarrierShared(); + barrier(); + + // Plot the light to the octahedron using bilinear filtering +#ifdef TRACE_SUBPIXEL + sample_ofs = sample_ofs * 2.0 - 1.0; + ivec2 bilinear_base = ivec2(1) + local_pos - mix(ivec2(0), ivec2(1), lessThan(sample_ofs, vec2(0))); + vec2 blend = mix(sample_ofs, 1.0 + sample_ofs, lessThan(sample_ofs, vec2(0))); + for (int i = 0; i < 2; i++) { + float i_w = i == 0 ? 1.0 - blend.y : blend.y; + for (int j = 0; j < 2; j++) { + float j_w = j == 0 ? 1.0 - blend.x : blend.x; + uint wrap_neighbour = wrap_neighbours[(bilinear_base.y + i) * (LIGHTPROBE_OCT_SIZE + 2) + (bilinear_base.x + j)]; + ivec2 write_to = ivec2(wrap_neighbour & 0xFFFF, wrap_neighbour >> 16); + int write_offset = write_to.y * LIGHTPROBE_OCT_SIZE + write_to.x; + float write_weight = i_w * j_w; + + uvec3 lightu = uvec3(clamp((light * write_weight) * float(1 << FP_BITS), 0, float(FP_MAX))); + atomicAdd(neighbours_accum[write_offset].r, lightu.r); + atomicAdd(neighbours_accum[write_offset].g, lightu.g); + atomicAdd(neighbours_accum[write_offset].b, lightu.b); + } + } +#else + + neighbours[probe_index] = light; +#endif + + if (!cache_valid) { + cache_entry = CACHE_IS_VALID; + if (hit) { + // Determine the side of the cascade box this ray exited through, this is important for invalidation purposes. + + vec3 unit_pos = ray_pos - cascades.data[params.cascade].offset; + unit_pos *= cascades.data[params.cascade].to_cell; + + vec3 t0 = -unit_pos / ray_dir; + vec3 t1 = (vec3(params.grid_size) - unit_pos) / ray_dir; + vec3 tmax = max(t0, t1); + + uint axis; + float m; + if (tmax.x < tmax.y) { + axis = 0; + m = tmax.x; + } else { + axis = 1; + m = tmax.y; + } + if (tmax.z < m) { + axis = 2; + } + + uvec3 ucell = (uvec3(hit_cell) & uvec3(0xFF)) << uvec3(0, 8, 16); + cache_entry |= CACHE_IS_HIT | ucell.x | ucell.y | ucell.z | (uint(min(7, hit_cascade)) << 24) | (axis << 27); + + uint region_version = imageLoad(region_versions, (hit_cell >> REGION_SIZE) + ivec3(0, hit_cascade * (params.grid_size.y / REGION_SIZE), 0)).r; + + imageStore(ray_hit_cache_version, cache_texture_pos, uvec4(region_version)); + } + + imageStore(ray_hit_cache, cache_texture_pos, uvec4(cache_entry)); + } + + groupMemoryBarrier(); + barrier(); + + // convert back to float and do moving average + + { +#ifdef TRACE_SUBPIXEL + light = vec3(neighbours_accum[probe_index]) / float(1 << FP_BITS); +#else + light = neighbours[probe_index]; +#endif + + // Encode to RGBE to store in accumulator + + uint light_rgbe = rgbe_encode(light); + + ivec3 ma_pos = ivec3(cache_texture_pos.xy * ivec2(3, 1), params.cascade); + + uvec3 moving_average = uvec3( + imageLoad(lightprobe_moving_average, ma_pos + ivec3(0, 0, 0)).r, + imageLoad(lightprobe_moving_average, ma_pos + ivec3(1, 0, 0)).r, + imageLoad(lightprobe_moving_average, ma_pos + ivec3(2, 0, 0)).r); + + ivec3 history_pos = ivec3(probe_texture_pos.xy * 4 + ivec2(probe_index % 4, probe_index / 4), probe_texture_pos.z * params.history_size + probe_history_index); + + uvec3 prev_val = rgbe_decode_fp(imageLoad(lightprobe_moving_average_history, cache_texture_pos).r, FP_BITS); + + moving_average -= prev_val; + uvec3 new_val = rgbe_decode_fp(light_rgbe, FP_BITS); // Round trip to ensure integer consistency + moving_average += new_val; + + imageStore(lightprobe_moving_average_history, cache_texture_pos, uvec4(light_rgbe)); + + imageStore(lightprobe_moving_average, ma_pos + ivec3(0, 0, 0), uvec4(moving_average.r)); + imageStore(lightprobe_moving_average, ma_pos + ivec3(1, 0, 0), uvec4(moving_average.g)); + imageStore(lightprobe_moving_average, ma_pos + ivec3(2, 0, 0), uvec4(moving_average.b)); + + moving_average /= params.history_size; + + if (params.store_ambient_texture) { + atomicAdd(ambient_accum.r, moving_average.r); + atomicAdd(ambient_accum.g, moving_average.g); + atomicAdd(ambient_accum.b, moving_average.b); + } + + light = vec3(moving_average) / float(1 << FP_BITS); + neighbours[probe_index] = light; + } + + groupMemoryBarrier(); + barrier(); + + // Compute specular, diffuse, ambient + + vec3 diffuse_light = vec3(0); + vec3 specular_light = light; + + for (uint i = 0; i < neighbour_max_weights; i++) { + uint n = neighbour_weights[probe_index * neighbour_max_weights + i]; + uint index = n >> 16; + float weight = float(n & 0xFFFF) / float(0xFFFF); + diffuse_light += neighbours[index] * weight; + } + + ivec3 store_texture_pos = ivec3(probe_texture_pos.xy * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1), probe_texture_pos.z); + ivec3 probe_read_pos = store_texture_pos + ivec3(local_pos, 0); + + //if (cache_invalidated_debug!=vec3(0.0)) { + // diffuse_light = cache_invalidated_debug; + //} + + // Store in octahedral map + + ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2)); + copy_to[0] = probe_read_pos; + + if (local_pos == ivec2(0, 0)) { + copy_to[1] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - 1, -1, 0); + copy_to[2] = store_texture_pos + ivec3(-1, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, 0)) { + copy_to[1] = store_texture_pos + ivec3(0, -1, 0); + copy_to[2] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = store_texture_pos + ivec3(-1, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(0, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = store_texture_pos + ivec3(-1, 0, 0); + copy_to[2] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE, 0); + copy_to[3] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, -1, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = store_texture_pos + ivec3(0, LIGHTPROBE_OCT_SIZE, 0); + copy_to[2] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, 0, 0); + copy_to[3] = store_texture_pos + ivec3(-1, -1, 0); + } else if (local_pos.y == 0) { + copy_to[1] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0); + } else if (local_pos.x == 0) { + copy_to[1] = store_texture_pos + ivec3(local_pos.x - 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } else if (local_pos.y == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = store_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0); + } else if (local_pos.x == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = store_texture_pos + ivec3(local_pos.x + 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } + + uint light_rgbe = rgbe_encode(specular_light); + uint diffuse_rgbe = rgbe_encode(diffuse_light); + + for (int i = 0; i < 4; i++) { + if (copy_to[i] == ivec3(-2, -2, -2)) { + continue; + } + imageStore(lightprobe_texture_data, copy_to[i], uvec4(light_rgbe)); + imageStore(lightprobe_diffuse_data, copy_to[i], uvec4(diffuse_rgbe)); + // also to diffuse + } + + if (params.store_ambient_texture && probe_index == 0) { + vec3 ambient_light = vec3(ambient_accum) / float(1 << FP_BITS); + imageStore(lightprobe_ambient_data, ivec3(probe_texture_pos.xy, params.cascade), uvec4(rgbe_encode(ambient_light))); + } + +#endif + +#ifdef MODE_FILTER + + ivec2 pos = ivec2(gl_WorkGroupID.xy); + ivec2 local_pos = ivec2(gl_LocalInvocationID.xy); + + ivec3 probe_cell; + probe_cell.x = pos.x; + probe_cell.y = pos.y % params.probe_axis_size.y; + probe_cell.z = pos.y / params.probe_axis_size.y; + + ivec3 probe_world_pos = params.world_offset + probe_cell; + + ivec3 probe_scroll_pos = modi(probe_world_pos, params.probe_axis_size); + ivec3 probe_base_pos = ivec3((probe_scroll_pos.xy + ivec2(0, probe_scroll_pos.z * params.probe_axis_size.y)), params.cascade); + + bool geom_proximity = imageLoad(lightprobe_geometry_proximity, probe_base_pos).r > 0.5; + bool cam_visibility = imageLoad(lightprobe_camera_visibility, probe_base_pos).r > 0.5; + + ivec3 probe_texture_pos = ivec3(probe_base_pos.xy * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1), probe_base_pos.z); + ivec3 probe_read_pos = probe_texture_pos + ivec3(local_pos, 0); + + vec4 light; + light.rgb = rgbe_decode(imageLoad(lightprobe_src_diffuse_data, probe_read_pos).r); + light.a = 1.0; + + if (geom_proximity && cam_visibility) { + // Only filter if there is geom proximity and probe is visible by camera. + + const vec3 aniso_dir[6] = vec3[]( + vec3(-1, 0, 0), + vec3(1, 0, 0), + vec3(0, -1, 0), + vec3(0, 1, 0), + vec3(0, 0, -1), + vec3(0, 0, 1)); + + uint neighbour_visibility = imageLoad(lightprobe_neighbours, probe_base_pos).r; + + for (int i = 0; i < 6; i++) { + float visibility = ((neighbour_visibility >> (i * 4)) & 0xF) / float(0xF); + if (visibility == 0.0) { + continue; // un-neighboured + } + + ivec3 neighbour_probe = probe_cell + ivec3(aniso_dir[i]); + if (any(lessThan(neighbour_probe, ivec3(0))) || any(greaterThanEqual(neighbour_probe, params.probe_axis_size))) { + continue; // Outside range. + } + + ivec3 probe_world_pos2 = params.world_offset + neighbour_probe; + ivec3 probe_scroll_pos2 = modi(probe_world_pos2, params.probe_axis_size); + ivec3 probe_base_pos2 = ivec3((probe_scroll_pos2.xy + ivec2(0, probe_scroll_pos2.z * params.probe_axis_size.y)), params.cascade); + + ivec3 probe_texture_pos2 = ivec3(probe_base_pos2.xy * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1), probe_base_pos2.z); + ivec3 probe_read_pos2 = probe_texture_pos2 + ivec3(local_pos, 0); + + vec4 light2; + light2.rgb = rgbe_decode(imageLoad(lightprobe_src_diffuse_data, probe_read_pos2).r); + light2.a = 1.0; + + light += light2 * 0.7 * visibility; + } + light.rgb /= light.a; + } + + ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2)); + copy_to[0] = probe_read_pos; + + if (local_pos == ivec2(0, 0)) { + copy_to[1] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - 1, -1, 0); + copy_to[2] = probe_texture_pos + ivec3(-1, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, 0)) { + copy_to[1] = probe_texture_pos + ivec3(0, -1, 0); + copy_to[2] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = probe_texture_pos + ivec3(-1, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(0, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = probe_texture_pos + ivec3(-1, 0, 0); + copy_to[2] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE, 0); + copy_to[3] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, -1, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = probe_texture_pos + ivec3(0, LIGHTPROBE_OCT_SIZE, 0); + copy_to[2] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE, 0, 0); + copy_to[3] = probe_texture_pos + ivec3(-1, -1, 0); + } else if (local_pos.y == 0) { + copy_to[1] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0); + } else if (local_pos.x == 0) { + copy_to[1] = probe_texture_pos + ivec3(local_pos.x - 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } else if (local_pos.y == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = probe_texture_pos + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0); + } else if (local_pos.x == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = probe_texture_pos + ivec3(local_pos.x + 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } + + uint light_rgbe = rgbe_encode(light.rgb); + + for (int i = 0; i < 4; i++) { + if (copy_to[i] == ivec3(-2, -2, -2)) { + continue; + } + imageStore(lightprobe_dst_diffuse_data, copy_to[i], uvec4(light_rgbe)); + } + +#endif + +#ifdef MODE_CAMERA_VISIBILITY + + ivec3 region = ivec3(gl_GlobalInvocationID.xyz); + + if (any(greaterThanEqual(region, params.grid_size / REGION_SIZE))) { + return; + } + + vec3 half_extents = vec3(REGION_SIZE * 0.5); + vec3 ofs = vec3(region * REGION_SIZE) + half_extents; + + bool intersects = true; + + // Test planes + for (int i = 0; i < MAX_CAMERA_PLANES; i++) { + vec4 plane = camera.planes[i]; + vec3 point = ofs + mix(+half_extents, -half_extents, greaterThan(plane.xyz, vec3(0.0))); + if (dot(plane.xyz, point) > plane.w) { + return; // Does not intersect. + } + } + + // Test points. Most cases the above test is fast and discards entirely, but this one is needed if passes. + + ivec3 bad_points_pos = ivec3(0); + ivec3 bad_points_neg = ivec3(0); + vec3 test_min = ofs - half_extents; + vec3 test_max = ofs + half_extents; + for (int i = 0; i < MAX_CAMERA_POINTS; i++) { + vec3 point = camera.points[i].xyz; + bad_points_neg += mix(ivec3(0), ivec3(1), lessThan(point, test_min)); + bad_points_pos += mix(ivec3(0), ivec3(1), greaterThan(point, test_max)); + } + + if (any(equal(bad_points_pos, ivec3(MAX_CAMERA_POINTS))) || any(equal(bad_points_neg, ivec3(MAX_CAMERA_POINTS)))) { + return; // Does not intersect. + } + + // Mark it + + for (int i = 0; i < 8; i++) { + ivec3 probe = params.world_offset + region + ((ivec3(i) >> ivec3(1, 2, 4)) & ivec3(1)); + ivec3 probe_scroll_pos = modi(probe, params.probe_axis_size); + ivec3 probe_pos = ivec3((probe_scroll_pos.xy + ivec2(0, probe_scroll_pos.z * params.probe_axis_size.y)), params.cascade); + + imageStore(lightprobe_camera_visibility, probe_pos, vec4(1.0)); + } + +#endif +} diff --git a/servers/rendering/renderer_rd/shaders/environment/hddagi_preprocess.glsl b/servers/rendering/renderer_rd/shaders/environment/hddagi_preprocess.glsl new file mode 100644 index 000000000000..e247a26661be --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/environment/hddagi_preprocess.glsl @@ -0,0 +1,1481 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +#define REGION_SIZE 8 + +#define square(m) ((m) * (m)) + +#ifdef MODE_REGION_STORE + +layout(local_size_x = REGION_SIZE, local_size_y = REGION_SIZE, local_size_z = REGION_SIZE) in; + +layout(r32ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_normal_bits; +layout(rg32ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_voxels; +layout(r8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D dst_region_bits; +layout(r16ui, set = 0, binding = 4) uniform restrict writeonly uimage3D region_version; + +shared uint solid_bit_count; +shared uint region_bits[((REGION_SIZE / 4) * (REGION_SIZE / 4) * (REGION_SIZE / 4)) * 2]; + +#endif + +#ifdef MODE_LIGHT_STORE + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_albedo; +layout(r32ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_emission; +layout(r32ui, set = 0, binding = 3) uniform restrict readonly uimage3D src_emission_aniso; +layout(r32ui, set = 0, binding = 4) uniform restrict readonly uimage3D src_normal_bits; + +layout(set = 0, binding = 8) uniform texture3D occlusion[2]; +layout(set = 0, binding = 9) uniform sampler linear_sampler; + +layout(r8ui, set = 0, binding = 10) uniform restrict writeonly uimage3D dst_disocclusion; + +layout(r32ui, set = 0, binding = 11) uniform restrict writeonly uimage3D voxel_neighbours; +layout(r32ui, set = 0, binding = 12) uniform restrict uimage3D light_tex; + +shared uint normal_facings[6 * 6 * 6]; + +uint get_normal_facing(ivec3 p_pos) { + p_pos += ivec3(1); + return normal_facings[p_pos.z * 6 * 6 + p_pos.y * 6 + p_pos.x]; +} + +void set_normal_facing(ivec3 p_pos, uint p_facing) { + p_pos += ivec3(1); + normal_facings[p_pos.z * 6 * 6 + p_pos.y * 6 + p_pos.x] = p_facing; +} + +#endif + +#if defined(MODE_LIGHT_STORE) || defined(MODE_LIGHT_SCROLL) + +struct ProcessVoxel { + uint position; // xyz 10 bit packed - then 2 extra bits for dynamic and static pending + uint albedo_normal; // 0 - 16, 17 - 31 normal in octahedral format + uint emission; // RGBE emission + uint occlusion; // cached 4 bits occlusion for each 8 neighbouring probes +}; + +#endif + +#if defined(MODE_LIGHT_STORE) || defined(MODE_LIGHT_SCROLL) + +#define PROCESS_STATIC_PENDING_BIT 0x80000000 +#define PROCESS_DYNAMIC_PENDING_BIT 0x40000000 + +layout(set = 0, binding = 5, std430) restrict buffer DispatchData { + uint x; + uint y; + uint z; + uint total_count; +} +dispatch_data; + +layout(set = 0, binding = 6, std430) restrict buffer writeonly ProcessVoxels { + ProcessVoxel data[]; +} +dst_process_voxels; + +shared uint store_position_count; +shared uint store_from_index; + +#endif + +#if defined(MODE_LIGHT_SCROLL) + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(r32ui, set = 0, binding = 1) uniform restrict uimage3D light_tex; + +layout(set = 0, binding = 7, std430) restrict buffer SrcDispatchData { + uint x; + uint y; + uint z; + uint total_count; +} +src_dispatch_data; + +layout(set = 0, binding = 8, std430) restrict buffer readonly SrcProcessVoxels { + ProcessVoxel data[]; +} +src_process_voxels; + +#endif + +// Some occlusion defines + +#ifdef HIGH_DENSITY_PROBES +#define PROBE_CELLS 4 +#else +#define PROBE_CELLS 8 +#endif + +#define OCC_DISTANCE_MAX 16.0 + +#ifdef MODE_OCCLUSION + +#define REGION_THREADS (REGION_SIZE * REGION_SIZE) + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(r32ui, set = 0, binding = 3) uniform restrict readonly uimage3D src_normal_bits; +layout(r16ui, set = 0, binding = 4) uniform restrict writeonly uimage3D dst_occlusion; + +shared float occlusion[REGION_SIZE * REGION_SIZE * REGION_SIZE * 4]; // 4096 +shared uint bit_normals[REGION_SIZE * REGION_SIZE * REGION_SIZE]; // 2048 +shared uint solid_cell_list[REGION_SIZE * REGION_SIZE * REGION_SIZE]; // 2048 +shared uint solid_cell_count; + +void set_occlusion(ivec3 p_pos, int p_layer, float p_occlusion) { + int occ_pos = p_pos.z * (REGION_SIZE * REGION_SIZE) + p_pos.y * REGION_SIZE + p_pos.x; + occlusion[occ_pos * 4 + p_layer] = p_occlusion; +} + +float get_occlusion(ivec3 p_pos, int p_layer) { + int occ_pos = p_pos.z * (REGION_SIZE * REGION_SIZE) + p_pos.y * REGION_SIZE + p_pos.x; + return occlusion[occ_pos * 4 + p_layer]; +} + +void set_bit_normal(ivec3 p_pos, uint p_normal) { + int occ_pos = p_pos.z * (REGION_SIZE * REGION_SIZE) + p_pos.y * REGION_SIZE + p_pos.x; + bit_normals[occ_pos] = p_normal; +} + +uint get_bit_normal(ivec3 p_pos) { + int occ_pos = p_pos.z * (REGION_SIZE * REGION_SIZE) + p_pos.y * REGION_SIZE + p_pos.x; + return bit_normals[occ_pos]; +} + +ivec3 offset_to_pos(int p_offset) { + return ivec3(p_offset % REGION_SIZE, (p_offset / REGION_SIZE) % REGION_SIZE, p_offset / (REGION_SIZE * REGION_SIZE)); +} + +const uvec2 group_size_offset[11] = uvec2[](uvec2(1, 0), uvec2(3, 1), uvec2(6, 4), uvec2(10, 10), uvec2(15, 20), uvec2(21, 35), uvec2(28, 56), uvec2(36, 84), uvec2(42, 120), uvec2(46, 162), uvec2(48, 208)); +const uint group_pos[256] = uint[](0, + 65536, 256, 1, + 131072, 65792, 512, 65537, 257, 2, + 196608, 131328, 66048, 768, 131073, 65793, 513, 65538, 258, 3, + 262144, 196864, 131584, 66304, 1024, 196609, 131329, 66049, 769, 131074, 65794, 514, 65539, 259, 4, + 327680, 262400, 197120, 131840, 66560, 1280, 262145, 196865, 131585, 66305, 1025, 196610, 131330, 66050, 770, 131075, 65795, 515, 65540, 260, 5, + 393216, 327936, 262656, 197376, 132096, 66816, 1536, 327681, 262401, 197121, 131841, 66561, 1281, 262146, 196866, 131586, 66306, 1026, 196611, 131331, 66051, 771, 131076, 65796, 516, 65541, 261, 6, + 458752, 393472, 328192, 262912, 197632, 132352, 67072, 1792, 393217, 327937, 262657, 197377, 132097, 66817, 1537, 327682, 262402, 197122, 131842, 66562, 1282, 262147, 196867, 131587, 66307, 1027, 196612, 131332, 66052, 772, 131077, 65797, 517, 65542, 262, 7, + 459008, 393728, 328448, 263168, 197888, 132608, 67328, 458753, 393473, 328193, 262913, 197633, 132353, 67073, 1793, 393218, 327938, 262658, 197378, 132098, 66818, 1538, 327683, 262403, 197123, 131843, 66563, 1283, 262148, 196868, 131588, 66308, 1028, 196613, 131333, 66053, 773, 131078, 65798, 518, 65543, 263, + 459264, 393984, 328704, 263424, 198144, 132864, 459009, 393729, 328449, 263169, 197889, 132609, 67329, 458754, 393474, 328194, 262914, 197634, 132354, 67074, 1794, 393219, 327939, 262659, 197379, 132099, 66819, 1539, 327684, 262404, 197124, 131844, 66564, 1284, 262149, 196869, 131589, 66309, 1029, 196614, 131334, 66054, 774, 131079, 65799, 519, + 459520, 394240, 328960, 263680, 198400, 459265, 393985, 328705, 263425, 198145, 132865, 459010, 393730, 328450, 263170, 197890, 132610, 67330, 458755, 393475, 328195, 262915, 197635, 132355, 67075, 1795, 393220, 327940, 262660, 197380, 132100, 66820, 1540, 327685, 262405, 197125, 131845, 66565, 1285, 262150, 196870, 131590, 66310, 1030, 196615, 131335, 66055, 775); +#endif + +#ifdef MODE_LIGHTPROBE_NEIGHBOURS + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +layout(set = 0, binding = 1) uniform texture3D occlusion[2]; +layout(set = 0, binding = 2) uniform sampler linear_sampler; +layout(r32ui, set = 0, binding = 3) uniform restrict writeonly uimage2DArray neighbour_probe_visibility; + +#endif + +#ifdef MODE_LIGHTPROBE_UPDATE_FRAMES + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +layout(r32ui, set = 0, binding = 1) uniform restrict uimage2DArray lightprobe_frames; + +#endif + +#ifdef MODE_LIGHTPROBE_GEOMETRY_PROXIMITY + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +layout(r8ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_region_bits; +layout(r8, set = 0, binding = 2) uniform restrict writeonly image2DArray dst_probe_geometry_proximity; + +#endif + +#ifdef MODE_LIGHTPROBE_SCROLL + +layout(local_size_x = LIGHTPROBE_OCT_SIZE, local_size_y = LIGHTPROBE_OCT_SIZE, local_size_z = 1) in; + +layout(r32ui, set = 0, binding = 1) uniform restrict uimage2DArray lightprobe_specular_data; +layout(r32ui, set = 0, binding = 2) uniform restrict uimage2DArray lightprobe_diffuse_data; +layout(r32ui, set = 0, binding = 3) uniform restrict uimage2DArray lightprobe_ambient_data; +layout(r32ui, set = 0, binding = 4) uniform restrict uimage2DArray ray_hit_cache; +layout(r32ui, set = 0, binding = 5) uniform restrict uimage2DArray lightprobe_moving_average_history; +layout(r32ui, set = 0, binding = 6) uniform restrict uimage2DArray lightprobe_moving_average; + +layout(set = 0, binding = 7) uniform texture3D occlusion[2]; +layout(set = 0, binding = 8) uniform sampler linear_sampler; + +shared float occlusion_blend[8]; + +#endif + +layout(push_constant, std430) uniform Params { + ivec3 grid_size; + uint region_version; + + ivec3 scroll; + int cascade_count; + + ivec3 offset; + int probe_update_frames; + + ivec3 limit; + int cascade; + + ivec3 region_world_pos; + int maximum_light_cells; + + ivec3 probe_axis_size; + int ray_hit_cache_frames; + + ivec3 upper_region_world_pos; + int occlusion_offset; +} +params; + +vec2 octahedron_wrap(vec2 v) { + vec2 signVal; + signVal.x = v.x >= 0.0 ? 1.0 : -1.0; + signVal.y = v.y >= 0.0 ? 1.0 : -1.0; + return (1.0 - abs(v.yx)) * signVal; +} + +vec2 octahedron_encode(vec3 n) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); + n.xy = n.xy * 0.5 + 0.5; + return n.xy; +} + +vec3 octahedron_decode(vec2 f) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + f = f * 2.0 - 1.0; + vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y)); + float t = clamp(-n.z, 0.0, 1.0); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return normalize(n); +} + +uint rgbe_encode(vec3 rgb) { + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); + + rgb = clamp(rgb, 0, rgbe_max); + + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); + + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); + + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + return e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); +} + +vec3 rgbe_decode(uint p_rgbe) { + vec4 rgbef = vec4((uvec4(p_rgbe) >> uvec4(0, 9, 18, 27)) & uvec4(0x1FF, 0x1FF, 0x1FF, 0x1F)); + return rgbef.rgb * pow(2.0, rgbef.a - 15.0 - 9.0); +} + +#define FP_BITS 14 +#define FP_MAX ((1 << 22) - 1) + +uvec3 rgbe_decode_fp(uint p_rgbe, int p_bits) { + uvec4 rgbe = (uvec4(p_rgbe) >> uvec4(0, 9, 18, 27)) & uvec4(0x1FF, 0x1FF, 0x1FF, 0x1F); + int shift = int(rgbe.a) - 15 - 9 + p_bits; + if (shift >= 0) { + rgbe.rgb <<= uint(shift); + } else { + rgbe.rgb >>= uint(-shift); + } + return rgbe.rgb; +} + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +ivec2 probe_to_tex(ivec3 local_probe) { + ivec3 cell = modi(params.region_world_pos + local_probe, ivec3(params.probe_axis_size)); + return cell.xy + ivec2(0, cell.z * int(params.probe_axis_size.y)); +} + +ivec2 probe_to_texp(ivec3 local_probe) { + ivec3 cell = modi(params.upper_region_world_pos + local_probe, ivec3(params.probe_axis_size)); + return cell.xy + ivec2(0, cell.z * int(params.probe_axis_size.y)); +} + +void main() { +#ifdef MODE_REGION_STORE + + ivec3 region_mask = (params.grid_size / REGION_SIZE) - 1; + + ivec3 src_pos = ivec3(gl_GlobalInvocationID.xyz) + params.offset; + ivec3 region_pos = ivec3(gl_LocalInvocationID.xyz); + ivec3 subregion = region_pos / 4; + uint subregion_ofs = subregion.z * 4 + subregion.y * 2 + subregion.x; + ivec3 subregion_pos = region_pos % 4; + uint subregion_bit = subregion_pos.z * 16 + subregion_pos.y * 4 + subregion_pos.x; + + if (region_pos == ivec3(0)) { + solid_bit_count = 0; + } + + if (subregion_pos == ivec3(0)) { + region_bits[subregion_ofs * 2 + 0] = 0; + region_bits[subregion_ofs * 2 + 1] = 0; + } + + groupMemoryBarrier(); + barrier(); + + uint p = imageLoad(src_normal_bits, src_pos).r; + + if (p != 0) { + atomicAdd(solid_bit_count, 1); + if (subregion_bit < 32) { + atomicOr(region_bits[subregion_ofs * 2 + 0], 1 << subregion_bit); + } else { + atomicOr(region_bits[subregion_ofs * 2 + 1], 1 << (subregion_bit - 32)); + } + } + + groupMemoryBarrier(); + barrier(); + + ivec3 dst_region = ((src_pos / REGION_SIZE) + params.region_world_pos) & region_mask; + + if (subregion_pos == ivec3(0)) { + ivec3 dst_pos = (dst_region * REGION_SIZE + region_pos) / 4; + uvec2 bits = uvec2(region_bits[subregion_ofs * 2 + 0], region_bits[subregion_ofs * 2 + 1]); + dst_pos.y += params.cascade * (params.grid_size.y / 4); + imageStore(dst_voxels, dst_pos, uvec4(bits, uvec2(0))); + } + + if (region_pos == ivec3(0)) { + dst_region.y += params.cascade * (params.grid_size.y / REGION_SIZE); + imageStore(dst_region_bits, dst_region, uvec4(solid_bit_count > 0 ? 1 : 0)); + + imageStore(region_version, dst_region, uvec4(params.region_version)); + + // Store region version + } + +#endif + +#ifdef MODE_LIGHT_SCROLL + + int src_index = int(gl_GlobalInvocationID).x; + int local = int(gl_LocalInvocationID).x; + + if (src_index >= src_dispatch_data.total_count) { + // Do not process. + return; + } + + if (src_index >= params.maximum_light_cells) { + return; + } + + if (local == 0) { + store_position_count = 0; // Base one stores as zero, the others wait + if (src_index == 0) { + // This lone thread clears y and z. + dispatch_data.y = 1; + dispatch_data.z = 1; + } + } + + groupMemoryBarrier(); + barrier(); + + ivec3 src_pos = (ivec3(src_process_voxels.data[src_index].position) >> ivec3(0, 7, 14)) & ivec3(0x7F); + bool inside_area = all(greaterThanEqual(src_pos, params.offset)) && all(lessThan(src_pos, params.limit)); + + if (!inside_area) { + ivec3 light_pos = src_pos + params.scroll; + light_pos = (light_pos + (params.region_world_pos * REGION_SIZE)) & (params.grid_size - 1); + light_pos.y += params.grid_size.y * params.cascade; + + // As this will be a new area, clear the new region from the old values. + imageStore(light_tex, light_pos, uvec4(0)); + } + uint index; + + if (inside_area) { + index = atomicAdd(store_position_count, 1); + } + + groupMemoryBarrier(); + barrier(); + + // global increment only once per group, to reduce pressure + + if (!inside_area || store_position_count == 0) { + return; + } + + if (index == 0) { + store_from_index = atomicAdd(dispatch_data.total_count, store_position_count); + uint group_count = (store_from_index + store_position_count - 1) / 64 + 1; + atomicMax(dispatch_data.x, group_count); + } + + groupMemoryBarrier(); + barrier(); + + index += store_from_index; + + ivec3 dst_pos = src_pos + params.scroll; + + uint src_pending_bits = src_process_voxels.data[src_index].position & ~uint((1 << 21) - 1); + + dst_process_voxels.data[index].position = uint(dst_pos.x | (dst_pos.y << 7) | (dst_pos.z << 14)) | src_pending_bits; + dst_process_voxels.data[index].albedo_normal = src_process_voxels.data[src_index].albedo_normal; + dst_process_voxels.data[index].emission = src_process_voxels.data[src_index].emission; + dst_process_voxels.data[index].occlusion = src_process_voxels.data[src_index].occlusion; + +#endif + +#ifdef MODE_LIGHT_STORE + + ivec3 local = ivec3(gl_LocalInvocationID.xyz); + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz) + params.offset; + + { // Fill the local normal facing pool. + ivec3 load_from = local * 6 / 4; + ivec3 load_to = (local + ivec3(1)) * 6 / 4; + ivec3 base_pos = params.offset + ivec3(gl_WorkGroupID.xyz) * 4; + + for (int i = load_from.x; i < load_to.x; i++) { + for (int j = load_from.y; j < load_to.y; j++) { + for (int k = load_from.z; k < load_to.z; k++) { + ivec3 i_ofs = ivec3(i, j, k) - ivec3(1); + ivec3 load_pos = base_pos + i_ofs; + uint solid = 0; + if (all(greaterThanEqual(load_pos, ivec3(0))) && all(lessThan(load_pos, params.grid_size))) { + solid = imageLoad(src_normal_bits, load_pos).r; + } + set_normal_facing(i_ofs, solid); + } + } + } + } + + if (any(greaterThanEqual(pos, params.limit))) { + // Storing is not a multiple of the workgroup, so invalid threads can happen. + return; + } + + groupMemoryBarrier(); + barrier(); + + uint solid = get_normal_facing(local); + + if (local == ivec3(0)) { + store_position_count = 0; // Base one stores as zero, the others wait + if (pos == params.offset) { + // This lone thread clears y and z. + dispatch_data.y = 1; + dispatch_data.z = 1; + } + } + + vec4 albedo_accum = vec4(0.0); + vec4 emission_accum = vec4(0.0); + vec3 normal_accum = vec3(0.0); + uint occlusionu = 0; + + //opposite to aniso dir + const ivec3 offsets[6] = ivec3[]( + ivec3(1, 0, 0), + ivec3(-1, 0, 0), + ivec3(0, 1, 0), + ivec3(0, -1, 0), + ivec3(0, 0, 1), + ivec3(0, 0, -1)); + + const vec3 aniso_dir[6] = vec3[]( + vec3(-1, 0, 0), + vec3(1, 0, 0), + vec3(0, -1, 0), + vec3(0, 1, 0), + vec3(0, 0, -1), + vec3(0, 0, 1)); + + // aniso dir in bitform + const uint aniso_mask[6] = uint[]( + (1 << 0), + (1 << 1), + (1 << 2), + (1 << 3), + (1 << 4), + (1 << 5)); + + const uint aniso_offset_mask[6] = uint[]( + (1 << 1), + (1 << 0), + (1 << 3), + (1 << 2), + (1 << 5), + (1 << 4)); + + bool voxels_found = false; + uint disocclusion = 0; + + const int facing_direction_count = 26; + const vec3 facing_directions[26] = vec3[](vec3(-1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(0.0, 0.0, 1.0), vec3(-0.5773502691896258, -0.5773502691896258, -0.5773502691896258), vec3(-0.7071067811865475, -0.7071067811865475, 0.0), vec3(-0.5773502691896258, -0.5773502691896258, 0.5773502691896258), vec3(-0.7071067811865475, 0.0, -0.7071067811865475), vec3(-0.7071067811865475, 0.0, 0.7071067811865475), vec3(-0.5773502691896258, 0.5773502691896258, -0.5773502691896258), vec3(-0.7071067811865475, 0.7071067811865475, 0.0), vec3(-0.5773502691896258, 0.5773502691896258, 0.5773502691896258), vec3(0.0, -0.7071067811865475, -0.7071067811865475), vec3(0.0, -0.7071067811865475, 0.7071067811865475), vec3(0.0, 0.7071067811865475, -0.7071067811865475), vec3(0.0, 0.7071067811865475, 0.7071067811865475), vec3(0.5773502691896258, -0.5773502691896258, -0.5773502691896258), vec3(0.7071067811865475, -0.7071067811865475, 0.0), vec3(0.5773502691896258, -0.5773502691896258, 0.5773502691896258), vec3(0.7071067811865475, 0.0, -0.7071067811865475), vec3(0.7071067811865475, 0.0, 0.7071067811865475), vec3(0.5773502691896258, 0.5773502691896258, -0.5773502691896258), vec3(0.7071067811865475, 0.7071067811865475, 0.0), vec3(0.5773502691896258, 0.5773502691896258, 0.5773502691896258)); + + bool use_for_filter = false; + + for (int i = 0; i < 6; i++) { + uint n = get_normal_facing(local + offsets[i]); + if (n == 0) { + disocclusion |= aniso_offset_mask[i]; + } else if (solid == 0) { + use_for_filter = true; + } + + if (solid != 0 || !bool(n & aniso_mask[i])) { + // Not solid, continue. + continue; + } + + voxels_found = true; + + for (int j = 0; j < facing_direction_count; j++) { + if (bool(n & uint((1 << (j + 6))))) { + normal_accum += facing_directions[j]; + } + } + + ivec3 ofs = pos + offsets[i]; + //normal_accum += aniso_dir[i]; + + ivec3 albedo_ofs = ofs >> 1; + albedo_ofs.z *= 6; + albedo_ofs.z += i; + + uint a = imageLoad(src_albedo, albedo_ofs).r; + albedo_accum += vec4(vec3((ivec3(a) >> ivec3(0, 5, 11)) & ivec3(0x1f, 0x3f, 0x1f)) / vec3(31.0, 63.0, 31.0), 1.0); + + uint rgbe = imageLoad(src_emission, ofs >> 1).r; + + vec3 emission = rgbe_decode(rgbe); + + uint rgbe_aniso = imageLoad(src_emission_aniso, ofs >> 1).r; + float strength = ((rgbe_aniso >> (i * 5)) & 0x1F) / float(0x1F); + emission_accum += vec4(emission * strength, 1.0); + } + + ivec3 base_dst_pos = (pos + params.region_world_pos * REGION_SIZE) & (params.grid_size - 1); + ivec3 dst_pos = base_dst_pos + params.grid_size.y * params.cascade; + imageStore(dst_disocclusion, dst_pos, uvec4(disocclusion)); + + if (solid != 0) { + return; // No further use for this. + } + + if (use_for_filter) { + uint neighbour_voxels = 0; + + for (int i = 0; i < facing_direction_count; i++) { + ivec3 neighbour = ivec3(sign(facing_directions[i])); + ivec3 neighbour_pos = local + neighbour; + uint n = get_normal_facing(neighbour_pos); + if (n == 0) { + continue; // Nothing here + } + + for (int j = 0; j < 6; j++) { + //if (!bool(n&(1< 1) { + // must make sure we are not occluded towards this + ivec3 test_dirs[3] = ivec3[](ivec3(nn_rel.x, 0, 0), ivec3(0, nn_rel.y, 0), ivec3(0, 0, nn_rel.z)); + int occlusions = 0; + for (int k = 0; k < 3; k++) { + if (test_dirs[k] == ivec3(0)) { + continue; // Direction not used + } + + q = get_normal_facing(local + test_dirs[k]); + if (q != 0) { + occlusions++; + } + } + + if (occlusions >= 2) { + continue; // Occluded from here, ignore. May be unoccluded from another neighbour. + } + } + + const uint reverse_map[27] = uint[](6, 14, 18, 9, 4, 21, 11, 16, 23, 7, 2, 19, 0, 0, 1, 12, 3, 24, 8, 15, 20, 10, 5, 22, 13, 17, 25); + ivec3 abs_pos = nn_rel + ivec3(1); + // All good, this is a valid neighbour! + neighbour_voxels |= 1 << reverse_map[abs_pos.z * 3 * 3 + abs_pos.y * 3 + abs_pos.x]; + } + } + + ivec3 store_pos = (pos + params.region_world_pos * REGION_SIZE) & (params.grid_size - ivec3(1)); + store_pos.y += params.grid_size.y * params.cascade; + imageStore(voxel_neighbours, store_pos, uvec4(neighbour_voxels)); + if (!voxels_found) { + // Light voxels won't be stored here, but still ensure this is black to avoid light leaking from outside. + imageStore(light_tex, store_pos, uvec4(0)); + } + } + + groupMemoryBarrier(); + barrier(); + + uint index; + + if (voxels_found) { + index = atomicAdd(store_position_count, 1); + } + + groupMemoryBarrier(); + barrier(); + + if (!voxels_found || store_position_count == 0) { + return; + } + + // global increment only once per group, to reduce pressure + + if (index == 0) { + store_from_index = atomicAdd(dispatch_data.total_count, store_position_count); + uint group_count = (store_from_index + store_position_count - 1) / 64 + 1; + atomicMax(dispatch_data.x, group_count); + } + + groupMemoryBarrier(); + barrier(); + + { + // compute occlusion + + ivec3 base_probe = params.region_world_pos + pos / PROBE_CELLS; + + ivec3 occ_pos = base_dst_pos + ivec3(1); + occ_pos.y += (params.grid_size.y + 2) * params.cascade; + + vec4 occ_0 = texelFetch(sampler3D(occlusion[0], linear_sampler), occ_pos, 0); + vec4 occ_1 = texelFetch(sampler3D(occlusion[1], linear_sampler), occ_pos, 0); + + if (bool(base_probe.x & 1)) { + occ_0.xyzw = occ_0.yxwz; + occ_1.xyzw = occ_1.yxwz; + } + + if (bool(base_probe.y & 1)) { + occ_0.xyzw = occ_0.zwxy; + occ_1.xyzw = occ_1.zwxy; + } + + if (bool(base_probe.z & 1)) { + vec4 tmp = occ_0; + occ_0 = occ_1; + occ_1 = tmp; + } + + float total_weight = dot(occ_0, vec4(1)) + dot(occ_1, vec4(1)); + if (total_weight > 0.0) { + occ_0 /= total_weight; + occ_1 /= total_weight; + } + + float weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); + + for (int i = 0; i < 8; i++) { + float w; + if (total_weight > 0.0) { + w = weights[i]; + w *= 15.0; + } else { + w = 0; + } + occlusionu |= uint(clamp(w, 0.0, 15.0)) << (i * 4); + } + } + + index += store_from_index; + + if (index >= params.maximum_light_cells) { + return; + } + + normal_accum = normalize(normal_accum); + albedo_accum.rgb /= albedo_accum.a; + emission_accum.rgb /= emission_accum.a; + + dst_process_voxels.data[index].position = uint(pos.x | (pos.y << 7) | (pos.z << 14)) | PROCESS_STATIC_PENDING_BIT | PROCESS_DYNAMIC_PENDING_BIT; + + uint albedo_norm = 0; + albedo_norm |= clamp(uint(albedo_accum.r * 31.0), 0, 31) << 0; + albedo_norm |= clamp(uint(albedo_accum.g * 63.0), 0, 63) << 5; + albedo_norm |= clamp(uint(albedo_accum.b * 31.0), 0, 31) << 11; + + vec2 octa_normal = octahedron_encode(normal_accum); + uvec2 octa_unormal = clamp(uvec2(octa_normal * 255), uvec2(0), uvec2(255)); + albedo_norm |= (octa_unormal.x << 16) | (octa_unormal.y << 24); + + dst_process_voxels.data[index].albedo_normal = albedo_norm; + dst_process_voxels.data[index].emission = rgbe_encode(emission_accum.rgb); + + dst_process_voxels.data[index].occlusion = occlusionu; + + // Compute probe neighbours + +#endif + +#ifdef MODE_LIGHTPROBE_NEIGHBOURS + + ivec3 probe_from = params.offset / REGION_SIZE; + ivec3 probe_to = params.limit / REGION_SIZE; + + ivec3 probe = ivec3(gl_GlobalInvocationID.xyz) + probe_from; + + if (any(greaterThan(probe, probe_to))) { + return; + } + + uint neighbour_visibility = 0; + ivec3 occ_bits = (params.region_world_pos + probe) & ivec3(1); + uint occlusion_layer = 0; + if (occ_bits.x != 0) { + occlusion_layer |= 1; + } + if (occ_bits.y != 0) { + occlusion_layer |= 2; + } + if (occ_bits.z != 0) { + occlusion_layer |= 4; + } + + ivec3 occ_tex_size = params.grid_size + ivec3(2); + occ_tex_size.y *= params.cascade_count; + for (int i = 0; i < 6; i++) { + const vec3 aniso_dir[6] = vec3[]( + vec3(-1, 0, 0), + vec3(1, 0, 0), + vec3(0, -1, 0), + vec3(0, 1, 0), + vec3(0, 0, -1), + vec3(0, 0, 1)); + + ivec3 dir = ivec3(aniso_dir[i]); + ivec3 probe_next = probe + dir; + if (any(lessThan(probe_next, ivec3(0))) || any(greaterThanEqual(probe_next, params.probe_axis_size))) { + continue; // Outside range + } + vec3 test_pos = vec3(probe * REGION_SIZE) + aniso_dir[i] * (REGION_SIZE - 1); + + ivec3 occ_pos = ivec3(test_pos); + occ_pos = (occ_pos + params.region_world_pos * REGION_SIZE) & (params.grid_size - ivec3(1)); + occ_pos += ivec3(1); + occ_pos.y += (int(params.grid_size.y) + 2) * params.cascade; + vec3 occ_posf = vec3(occ_pos) + fract(test_pos); + occ_posf /= vec3(occ_tex_size); + vec4 occ_0 = texture(sampler3D(occlusion[0], linear_sampler), occ_posf); + vec4 occ_1 = texture(sampler3D(occlusion[1], linear_sampler), occ_posf); + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); + + float visibility = occ_weights[occlusion_layer]; + + neighbour_visibility |= uint(clamp(visibility * 0xF, 0, 0xF)) << (i * 4); + } + + ivec2 probe_tex_pos = probe_to_tex(probe); + imageStore(neighbour_probe_visibility, ivec3(probe_tex_pos, params.cascade), uvec4(neighbour_visibility)); + +#endif + +#ifdef MODE_LIGHTPROBE_GEOMETRY_PROXIMITY + + ivec3 probe = ivec3(gl_GlobalInvocationID.xyz); + + if (any(greaterThanEqual(probe, params.probe_axis_size))) { + return; + } + + ivec3 region_mask = (params.grid_size / REGION_SIZE) - 1; + + bool found_geometry = false; + for (int i = 0; i < 8; i++) { + // Check all 8 regions around + ivec3 offset = ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)) * ivec3(2) - ivec3(1); + ivec3 region = probe + offset; + if (any(lessThan(region, ivec3(0)))) { + continue; + } + if (any(greaterThanEqual(region, params.probe_axis_size - ivec3(1)))) { + continue; + } + + region = (region + params.region_world_pos) & region_mask; + + region.y += params.cascade * (params.grid_size.y / REGION_SIZE); + if (imageLoad(src_region_bits, region).r > 0) { + found_geometry = true; + break; + } + } + + ivec2 tex_pos = probe_to_tex(probe); + imageStore(dst_probe_geometry_proximity, ivec3(tex_pos, params.cascade), vec4(found_geometry ? 1.0 : 0.0)); + +#endif + +#ifdef MODE_LIGHTPROBE_UPDATE_FRAMES + + ivec3 probe_from = params.offset / REGION_SIZE; + ivec3 probe_to = params.limit / REGION_SIZE; + + ivec3 probe = ivec3(gl_GlobalInvocationID.xyz) + probe_from; + + if (any(greaterThan(probe, probe_to))) { + return; + } + + ivec3 tex_pos = ivec3(probe_to_tex(probe), params.cascade); + + uint frame = imageLoad(lightprobe_frames, tex_pos).r; + frame &= 0x0FFFFFFF; // Clear update frames counter + frame |= uint(params.probe_update_frames) << 28; // Reset frames counter. + + imageStore(lightprobe_frames, tex_pos, uvec4(frame)); + +#endif + +#ifdef MODE_OCCLUSION + + // when x+y+z = step you do what you have to do. + // read from x-1, y-1, z-1 + // If the main base voxel is occluded, we should try to see if it passes through? + + int invocation_idx = int(gl_LocalInvocationID.x); + ivec3 workgroup = ivec3(gl_WorkGroupID.xyz); + + if (invocation_idx == 0) { + solid_cell_count = 0; + } + + groupMemoryBarrier(); + barrier(); + + //ivec3 local_pos = ivec3(invocation_idx % PROBE_CELLS, (invocation_idx % (PROBE_CELLS * PROBE_CELLS)) / PROBE_CELLS, invocation_idx / (PROBE_CELLS * PROBE_CELLS)); + int cells_load_total = PROBE_CELLS * PROBE_CELLS * PROBE_CELLS; + int group_total = 64; + + { + int load_from = invocation_idx * cells_load_total / group_total; + int load_to = (invocation_idx + 1) * cells_load_total / group_total; + + for (int i = load_from; i < load_to; i++) { + ivec3 load_pos; + + load_pos.z = i / (PROBE_CELLS * PROBE_CELLS); + load_pos.y = (i / PROBE_CELLS) % PROBE_CELLS; + load_pos.x = i % PROBE_CELLS; + + ivec3 tex_load_pos = load_pos + ivec3(workgroup.xyz * PROBE_CELLS) + params.offset; + uint n = imageLoad(src_normal_bits, tex_load_pos).r; + set_bit_normal(load_pos, n); + + if (n != 0) { + uint index = atomicAdd(solid_cell_count, 1); + solid_cell_list[index] = uint(load_pos.x | (load_pos.y << 7) | (load_pos.z << 14)); + } + } + } + + groupMemoryBarrier(); + barrier(); + + //process occlusion + +#define OCC_STEPS (PROBE_CELLS * 3 - 2) +#define OCC_HALF_STEPS (OCC_STEPS / 2) + + int probe_offset = params.occlusion_offset; + + ivec3 base_world_region = params.region_world_pos + (params.offset / REGION_SIZE) + workgroup; + + for (int step = 0; step < OCC_STEPS; step++) { + bool shrink = step >= OCC_HALF_STEPS; + int occ_step = shrink ? OCC_HALF_STEPS - (step - OCC_HALF_STEPS) - 1 : step; + + if (invocation_idx < group_size_offset[occ_step].x) { + uint pv = group_pos[group_size_offset[occ_step].y + invocation_idx]; + + // Position in the occlusion volume. + // This is an optimized version of the algorithm. + // The idea is that you only process when x+y+z == step. + // As such, this is what proc_abs represents (unpacks). + + ivec3 proc_abs = (ivec3(int(pv)) >> ivec3(0, 8, 16)) & ivec3(0xFF); + + if (shrink) { + proc_abs = ivec3(PROBE_CELLS) - proc_abs - ivec3(1); + } + + for (int i = 0; i < 4; i++) { + // Bits to indicate where the probe starts and the direction it spreads to. + ivec3 bits = (((ivec3(i + probe_offset) >> ivec3(0, 1, 2)) + base_world_region) & ivec3(1, 1, 1)); + ivec3 proc_sign = bits * 2 - 1; + + // Offset depends on the direction we go (probe we scan). + ivec3 offset = ivec3(PROBE_CELLS - 1) * (ivec3(1) - bits) + proc_abs * proc_sign; + + float occ; + + uint facing = get_bit_normal(offset); + + if (facing != 0) { //solid + occ = 0.0; + } else if (step == 0) { + occ = 1.0; + } else { + ivec3 read_dir = -proc_sign; + + float avg = 0.0; + occ = 0.0; + + ivec3 read_x = offset + ivec3(read_dir.x, 0, 0); + ivec3 read_y = offset + ivec3(0, read_dir.y, 0); + ivec3 read_z = offset + ivec3(0, 0, read_dir.z); + + if (all(greaterThanEqual(read_x, ivec3(0))) && all(lessThan(read_x, ivec3(PROBE_CELLS)))) { + uint facing_x = get_bit_normal(read_x); + if (facing_x == 0) { + occ += get_occlusion(read_x, i); + } + avg += 1.0; + } + + if (all(greaterThanEqual(read_y, ivec3(0))) && all(lessThan(read_y, ivec3(PROBE_CELLS)))) { + uint facing_y = get_bit_normal(read_y); + if (facing_y == 0) { + occ += get_occlusion(read_y, i); + } + avg += 1.0; + } + + if (all(greaterThanEqual(read_z, ivec3(0))) && all(lessThan(read_z, ivec3(PROBE_CELLS)))) { + uint facing_z = get_bit_normal(read_z); + if (facing_z == 0) { + occ += get_occlusion(read_z, i); + } + avg += 1.0; + } + + if (avg > 0.0) { + occ /= avg; + } + } + + set_occlusion(offset, i, occ); + } + } + + groupMemoryBarrier(); + barrier(); + } + + //bias solid voxels away + + int cell_from = invocation_idx * int(solid_cell_count) / group_total; + int cell_to = (invocation_idx + 1) * int(solid_cell_count) / group_total; + + for (int cell_i = cell_from; cell_i < cell_to; cell_i++) { + ivec3 offset = (ivec3(solid_cell_list[cell_i]) >> ivec3(0, 7, 14)) & (0x7F); + + uint facing = get_bit_normal(offset); + + for (int i = 0; i < 4; i++) { + ivec3 bits = (((ivec3(i + probe_offset) >> ivec3(0, 1, 2)) + base_world_region) & ivec3(1, 1, 1)); + ivec3 proc_sign = bits * 2 - 1; + ivec3 read_dir = -proc_sign; + + //only work on solids + + float avg = 0.0; + float occ = 0.0; + + ivec3 read_dir_x = ivec3(read_dir.x, 0, 0); + ivec3 read_dir_y = ivec3(0, read_dir.y, 0); + ivec3 read_dir_z = ivec3(0, 0, read_dir.z); + //solid + + uvec3 facing_neg = (uvec3(facing) >> uvec3(0, 2, 4)) & uvec3(1, 1, 1); + uvec3 facing_pos = (uvec3(facing) >> uvec3(1, 3, 5)) & uvec3(1, 1, 1); + + bvec3 read_valid = bvec3(mix(facing_neg, facing_pos, greaterThan(read_dir, ivec3(0)))); + + read_valid = mix(read_valid, bvec3(false), lessThan(offset + read_dir, ivec3(0))); + read_valid = mix(read_valid, bvec3(false), greaterThanEqual(offset + read_dir, ivec3(PROBE_CELLS))); + + //sides + if (read_valid.x) { + ivec3 read_offset = offset + read_dir_x; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + if (read_valid.y) { + ivec3 read_offset = offset + read_dir_y; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + if (read_valid.z) { + ivec3 read_offset = offset + read_dir_z; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + //adjacents + + if (all(read_valid.yz)) { + ivec3 read_offset = offset + read_dir_y + read_dir_z; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + if (all(read_valid.xz)) { + ivec3 read_offset = offset + read_dir_x + read_dir_z; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + if (all(read_valid.xy)) { + ivec3 read_offset = offset + read_dir_x + read_dir_y; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + //diagonal + + if (all(read_valid)) { + ivec3 read_offset = offset + read_dir; + uint f = get_bit_normal(read_offset); + if (f == 0) { + occ += get_occlusion(read_offset, i); + avg += 1.0; + } + } + + if (avg > 0.0) { + occ /= avg; + } + + set_occlusion(offset, i, occ); + } + } + + groupMemoryBarrier(); + barrier(); + + // Darken backfaces + cell_from = invocation_idx * cells_load_total / group_total; + cell_to = (invocation_idx + 1) * cells_load_total / group_total; + + for (int cell_i = cell_from; cell_i < cell_to; cell_i++) { + ivec3 offset; + offset.z = cell_i / (PROBE_CELLS * PROBE_CELLS); + offset.y = (cell_i / PROBE_CELLS) % PROBE_CELLS; + offset.x = cell_i % PROBE_CELLS; + + uint facing = get_bit_normal(offset); + + if (facing != 0) { + continue; + } + + for (int i = 0; i < 4; i++) { + ivec3 bits = (((ivec3(i + probe_offset) >> ivec3(0, 1, 2)) + base_world_region) & ivec3(1, 1, 1)); + ivec3 proc_sign = bits * 2 - 1; + ivec3 read_dir = -proc_sign; + + ivec3 read_dir_x = ivec3(read_dir.x, 0, 0); + ivec3 read_dir_y = ivec3(0, read_dir.y, 0); + ivec3 read_dir_z = ivec3(0, 0, read_dir.z); + + //solid, positive axis is odd bits, negative even. + uvec3 read_mask = mix(uvec3(2, 8, 32), uvec3(1, 4, 16), greaterThan(read_dir, ivec3(0))); //match positive with negative normals + + bvec3 read_valid = bvec3(true); + read_valid = mix(read_valid, bvec3(false), lessThan(offset + read_dir, ivec3(0))); + read_valid = mix(read_valid, bvec3(false), greaterThanEqual(offset + read_dir, ivec3(PROBE_CELLS))); + + float visible = 0.0; + float occlude_total = 0.0; + + if (read_valid.x) { + uint x_mask = get_bit_normal(offset + read_dir_x); + if (x_mask != 0) { + occlude_total += 1.0; + if (bool(x_mask & read_mask.x)) { + visible += 1.0; + } + } + } + + if (read_valid.y) { + uint y_mask = get_bit_normal(offset + read_dir_y); + if (y_mask != 0) { + occlude_total += 1.0; + if (bool(y_mask & read_mask.y)) { + visible += 1.0; + } + } + } + + if (read_valid.z) { + uint z_mask = get_bit_normal(offset + read_dir_z); + if (z_mask != 0) { + occlude_total += 1.0; + if (bool(z_mask & read_mask.z)) { + visible += 1.0; + } + } + } + + if (occlude_total > 0.0) { + float occ = get_occlusion(offset, i); + occ *= visible / occlude_total; + set_occlusion(offset, i, occ); + } + } + } + + // Store in occlusion texture + + groupMemoryBarrier(); + barrier(); + + cell_from = invocation_idx * cells_load_total / group_total; + cell_to = (invocation_idx + 1) * cells_load_total / group_total; + + for (int cell_i = cell_from; cell_i < cell_to; cell_i++) { + ivec3 cell_pos; + cell_pos.z = cell_i / (PROBE_CELLS * PROBE_CELLS); + cell_pos.y = (cell_i / PROBE_CELLS) % PROBE_CELLS; + cell_pos.x = cell_i % PROBE_CELLS; + + ivec3 cascade_pos = cell_pos + params.offset + workgroup * REGION_SIZE; // pos in texture + cascade_pos = (cascade_pos + params.region_world_pos * REGION_SIZE) & (params.grid_size - ivec3(1)); + cascade_pos += ivec3(1); // Margin + + ivec3 store_pos = cascade_pos; + + store_pos.y += (params.grid_size.y + 2) * params.cascade; + + uint enc_occlusion = 0; // 16 bits RGBA4444 + + for (int i = 0; i < 4; i++) { + float occ = get_occlusion(cell_pos, i); + enc_occlusion |= uint(clamp(occ * 0xF, 0, 0xF)) << (i * 4); + } + + imageStore(dst_occlusion, store_pos, uvec4(enc_occlusion)); + + // Store wrap faces + if (cascade_pos.x == 1) { + imageStore(dst_occlusion, store_pos + ivec3(params.grid_size.x, 0, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.y == 1) { + imageStore(dst_occlusion, store_pos + ivec3(0, params.grid_size.y, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.z == 1) { + imageStore(dst_occlusion, store_pos + ivec3(0, 0, params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.x == params.grid_size.x) { + imageStore(dst_occlusion, store_pos - ivec3(params.grid_size.x, 0, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.y == params.grid_size.y) { + imageStore(dst_occlusion, store_pos - ivec3(0, params.grid_size.y, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.z == params.grid_size.z) { + imageStore(dst_occlusion, store_pos - ivec3(0, 0, params.grid_size.z), uvec4(enc_occlusion)); + } + + // Store wrap edges + if (cascade_pos.xy == ivec2(1)) { + imageStore(dst_occlusion, store_pos + ivec3(params.grid_size.xy, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.xz == ivec2(1)) { + imageStore(dst_occlusion, store_pos + ivec3(params.grid_size.x, 0, params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.yz == ivec2(1)) { + imageStore(dst_occlusion, store_pos + ivec3(0, params.grid_size.yz), uvec4(enc_occlusion)); + } + + if (cascade_pos.xy == params.grid_size.xy) { + imageStore(dst_occlusion, store_pos - ivec3(params.grid_size.xy, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.xz == params.grid_size.xz) { + imageStore(dst_occlusion, store_pos - ivec3(params.grid_size.x, 0, params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.yz == params.grid_size.yz) { + imageStore(dst_occlusion, store_pos - ivec3(0, params.grid_size.yz), uvec4(enc_occlusion)); + } + + if (cascade_pos.xy == ivec2(1, params.grid_size.y)) { + imageStore(dst_occlusion, store_pos + ivec3(params.grid_size.x, -params.grid_size.y, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.xy == ivec2(params.grid_size.x, 1)) { + imageStore(dst_occlusion, store_pos + ivec3(-params.grid_size.x, params.grid_size.y, 0), uvec4(enc_occlusion)); + } + + if (cascade_pos.xz == ivec2(1, params.grid_size.z)) { + imageStore(dst_occlusion, store_pos + ivec3(params.grid_size.x, 0, -params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.xz == ivec2(params.grid_size.x, 1)) { + imageStore(dst_occlusion, store_pos + ivec3(-params.grid_size.x, 0, params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.yz == ivec2(1, params.grid_size.z)) { + imageStore(dst_occlusion, store_pos + ivec3(0, params.grid_size.y, -params.grid_size.z), uvec4(enc_occlusion)); + } + + if (cascade_pos.yz == ivec2(params.grid_size.y, 1)) { + imageStore(dst_occlusion, store_pos + ivec3(0, -params.grid_size.y, params.grid_size.z), uvec4(enc_occlusion)); + } + + // Store wrap vertices + for (int i = 0; i < 8; i++) { + bvec3 bits = bvec3((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + ivec3 test_pos = mix(ivec3(1), params.grid_size, bits); + ivec3 store_offset = mix(+params.grid_size, -params.grid_size, bits); + if (cascade_pos == test_pos) { + imageStore(dst_occlusion, store_pos + store_offset, uvec4(enc_occlusion)); + } + } + } + +#endif + +#ifdef MODE_LIGHTPROBE_SCROLL + + ivec2 local_pos = ivec2(gl_LocalInvocationID).xy; + + vec3 specular_light = vec3(0.0); + vec3 diffuse_light = vec3(0.0); + vec3 ambient_light = vec3(0.0); + + int upper_cascade = params.cascade + 1; + + if (upper_cascade < params.cascade_count) { + vec3 posi = params.offset + ivec3(gl_WorkGroupID.xyz) * PROBE_CELLS; + // Convert cell to world + posi += params.region_world_pos * REGION_SIZE - params.grid_size / 2; + posi -= (params.upper_region_world_pos * REGION_SIZE - params.grid_size / 2) * 2; + + vec3 posf = vec3(posi) / 2.0; + + ivec3 base_probe = ivec3(posf / PROBE_CELLS); + + if (local_pos == ivec2(0)) { + ivec3 occ_pos = ivec3(posf); + occ_pos = (occ_pos + params.upper_region_world_pos * REGION_SIZE) & (params.grid_size - ivec3(1)); + occ_pos += ivec3(1); + occ_pos.y += (int(params.grid_size.y) + 2) * upper_cascade; + vec3 occ_posf = vec3(occ_pos) + fract(posf); + vec4 occ_0 = texture(sampler3D(occlusion[0], linear_sampler), occ_posf); + vec4 occ_1 = texture(sampler3D(occlusion[1], linear_sampler), occ_posf); + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); + float occlusion_total = 0.0; + + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + + vec3 probe_pos = vec3(probe * PROBE_CELLS); + + vec3 probe_to_pos = posf - probe_pos; + + ivec3 probe_occ = (probe + params.upper_region_world_pos) & ivec3(1); + uint weight_index = 0; + if (probe_occ.x != 0) { + weight_index |= 1; + } + if (probe_occ.y != 0) { + weight_index |= 2; + } + if (probe_occ.z != 0) { + weight_index |= 4; + } + + float weight = occ_weights[weight_index]; + + weight = max(0.000001, weight); // make sure not zero (only trilinear can be zero) + + vec3 trilinear = vec3(1.0) - abs(probe_to_pos / float(PROBE_CELLS)); + + weight *= trilinear.x * trilinear.y * trilinear.z; + occlusion_blend[i] = weight; + occlusion_total += weight; + } + + for (int i = 0; i < 8; i++) { + if (occlusion_total == 0.0) { + occlusion_blend[i] = 0; + } else { + occlusion_blend[i] /= occlusion_total; + } + } + } + + groupMemoryBarrier(); + barrier(); + + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); + ivec2 base_tex_pos = probe_to_texp(probe); + ivec2 tex_pos = base_tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1) + local_pos; + ivec3 tex_array_pos = ivec3(tex_pos, upper_cascade); + specular_light += rgbe_decode(imageLoad(lightprobe_specular_data, tex_array_pos).r) * occlusion_blend[i]; + diffuse_light += rgbe_decode(imageLoad(lightprobe_diffuse_data, tex_array_pos).r) * occlusion_blend[i]; + + if (local_pos == ivec2(0)) { + tex_array_pos = ivec3(base_tex_pos, upper_cascade); + ambient_light += rgbe_decode(imageLoad(lightprobe_ambient_data, tex_array_pos).r) * occlusion_blend[i]; + } + } + } + + ivec3 probe_from = (params.offset / PROBE_CELLS) + ivec3(gl_WorkGroupID.xyz); + ivec2 probe_tex_pos = probe_to_tex(probe_from); + + ivec3 dst_tex_uv = ivec3(probe_tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1), params.cascade); + + ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2)); + copy_to[0] = dst_tex_uv + ivec3(local_pos, 0); + + if (local_pos == ivec2(0, 0)) { + copy_to[1] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE - 1, -1, 0); + copy_to[2] = dst_tex_uv + ivec3(-1, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, 0)) { + copy_to[1] = dst_tex_uv + ivec3(0, -1, 0); + copy_to[2] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE, LIGHTPROBE_OCT_SIZE - 1, 0); + copy_to[3] = dst_tex_uv + ivec3(-1, LIGHTPROBE_OCT_SIZE, 0); + } else if (local_pos == ivec2(0, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = dst_tex_uv + ivec3(-1, 0, 0); + copy_to[2] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE, 0); + copy_to[3] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE, -1, 0); + } else if (local_pos == ivec2(LIGHTPROBE_OCT_SIZE - 1, LIGHTPROBE_OCT_SIZE - 1)) { + copy_to[1] = dst_tex_uv + ivec3(0, LIGHTPROBE_OCT_SIZE, 0); + copy_to[2] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE, 0, 0); + copy_to[3] = dst_tex_uv + ivec3(-1, -1, 0); + } else if (local_pos.y == 0) { + copy_to[1] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0); + } else if (local_pos.x == 0) { + copy_to[1] = dst_tex_uv + ivec3(local_pos.x - 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } else if (local_pos.y == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = dst_tex_uv + ivec3(LIGHTPROBE_OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0); + } else if (local_pos.x == LIGHTPROBE_OCT_SIZE - 1) { + copy_to[1] = dst_tex_uv + ivec3(local_pos.x + 1, LIGHTPROBE_OCT_SIZE - local_pos.y - 1, 0); + } + + uint specular_rgbe = rgbe_encode(specular_light); + uint diffuse_rgbe = rgbe_encode(diffuse_light); + + for (int i = 0; i < 4; i++) { + if (copy_to[i] == ivec3(-2, -2, -2)) { + continue; + } + imageStore(lightprobe_specular_data, copy_to[i], uvec4(specular_rgbe)); + imageStore(lightprobe_diffuse_data, copy_to[i], uvec4(diffuse_rgbe)); + } + + if (local_pos == ivec2(0)) { + imageStore(lightprobe_ambient_data, ivec3(probe_tex_pos, params.cascade), uvec4(rgbe_encode(ambient_light))); + } + + // Cache and history invalidation + probe_tex_pos = probe_tex_pos * LIGHTPROBE_OCT_SIZE + local_pos; + + for (int i = 0; i < params.ray_hit_cache_frames; i++) { + ivec3 history_pos = ivec3(probe_tex_pos, params.cascade * params.ray_hit_cache_frames + i); + // Completely invalidate cache frame. + imageStore(ray_hit_cache, history_pos, uvec4(0)); + imageStore(lightprobe_moving_average_history, history_pos, uvec4(specular_rgbe)); + } + + uvec3 moving_average = rgbe_decode_fp(specular_rgbe, FP_BITS); + moving_average *= params.ray_hit_cache_frames; + + ivec3 ma_pos = ivec3(probe_tex_pos, params.cascade); + ma_pos.x *= 3; + + imageStore(lightprobe_moving_average, ma_pos + ivec3(0, 0, 0), uvec4(moving_average.r)); + imageStore(lightprobe_moving_average, ma_pos + ivec3(1, 0, 0), uvec4(moving_average.g)); + imageStore(lightprobe_moving_average, ma_pos + ivec3(2, 0, 0), uvec4(moving_average.b)); + +#endif +} diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl deleted file mode 100644 index 177dab16c763..000000000000 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl +++ /dev/null @@ -1,185 +0,0 @@ -#[compute] - -#version 450 - -#VERSION_DEFINES - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -#define MAX_CASCADES 8 - -layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES]; -layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES]; -layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES]; -layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES]; -layout(set = 0, binding = 5) uniform texture3D occlusion_texture; - -layout(set = 0, binding = 8) uniform sampler linear_sampler; - -struct CascadeData { - vec3 offset; //offset of (0,0,0) in world coordinates - float to_cell; // 1/bounds * grid_size - ivec3 probe_world_offset; - uint pad; - vec4 pad2; -}; - -layout(set = 0, binding = 9, std140) uniform Cascades { - CascadeData data[MAX_CASCADES]; -} -cascades; - -layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D screen_buffer; - -layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture; - -layout(push_constant, std430) uniform Params { - vec3 grid_size; - uint max_cascades; - - ivec2 screen_size; - float y_mult; - - float z_near; - - mat3x4 inv_projection; - // We pack these more tightly than mat3 and vec3, which will require some reconstruction trickery. - float cam_basis[3][3]; - float cam_origin[3]; -} -params; - -vec3 linear_to_srgb(vec3 color) { - //if going to srgb, clamp from 0 to 1. - color = clamp(color, vec3(0.0), vec3(1.0)); - const vec3 a = vec3(0.055f); - return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); -} - -vec2 octahedron_wrap(vec2 v) { - vec2 signVal; - signVal.x = v.x >= 0.0 ? 1.0 : -1.0; - signVal.y = v.y >= 0.0 ? 1.0 : -1.0; - return (1.0 - abs(v.yx)) * signVal; -} - -vec2 octahedron_encode(vec3 n) { - // https://twitter.com/Stubbesaurus/status/937994790553227264 - n /= (abs(n.x) + abs(n.y) + abs(n.z)); - n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); - n.xy = n.xy * 0.5 + 0.5; - return n.xy; -} - -void main() { - // Pixel being shaded - ivec2 screen_pos = ivec2(gl_GlobalInvocationID.xy); - if (any(greaterThanEqual(screen_pos, params.screen_size))) { //too large, do nothing - return; - } - - vec3 ray_pos; - vec3 ray_dir; - { - ray_pos = vec3(params.cam_origin[0], params.cam_origin[1], params.cam_origin[2]); - - ray_dir.xy = ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0); - ray_dir.z = params.z_near; - - ray_dir = (vec4(ray_dir, 1.0) * mat4(params.inv_projection)).xyz; - - mat3 cam_basis; - { - vec3 c0 = vec3(params.cam_basis[0][0], params.cam_basis[0][1], params.cam_basis[0][2]); - vec3 c1 = vec3(params.cam_basis[1][0], params.cam_basis[1][1], params.cam_basis[1][2]); - vec3 c2 = vec3(params.cam_basis[2][0], params.cam_basis[2][1], params.cam_basis[2][2]); - cam_basis = mat3(c0, c1, c2); - } - ray_dir = normalize(cam_basis * ray_dir); - } - - ray_pos.y *= params.y_mult; - ray_dir.y *= params.y_mult; - ray_dir = normalize(ray_dir); - - vec3 pos_to_uvw = 1.0 / params.grid_size; - - vec3 light = vec3(0.0); - float blend = 0.0; - -#if 1 - // No interpolation - - vec3 inv_dir = 1.0 / ray_dir; - - float rough = 0.5; - bool hit = false; - - for (uint i = 0; i < params.max_cascades; i++) { - //convert to local bounds - vec3 pos = ray_pos - cascades.data[i].offset; - pos *= cascades.data[i].to_cell; - - // Should never happen for debug, since we start mostly at the bounds center, - // but add anyway. - //if (any(lessThan(pos,vec3(0.0))) || any(greaterThanEqual(pos,params.grid_size))) { - // continue; //already past bounds for this cascade, goto next - //} - - //find maximum advance distance (until reaching bounds) - vec3 t0 = -pos * inv_dir; - vec3 t1 = (params.grid_size - pos) * inv_dir; - vec3 tmax = max(t0, t1); - float max_advance = min(tmax.x, min(tmax.y, tmax.z)); - - float advance = 0.0; - vec3 uvw; - hit = false; - - while (advance < max_advance) { - //read how much to advance from SDF - uvw = (pos + ray_dir * advance) * pos_to_uvw; - - float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), uvw).r * 255.0 - 1.7; - - if (distance < 0.001) { - //consider hit - hit = true; - break; - } - - advance += distance; - } - - if (!hit) { - pos += ray_dir * min(advance, max_advance); - pos /= cascades.data[i].to_cell; - pos += cascades.data[i].offset; - ray_pos = pos; - continue; - } - - //compute albedo, emission and normal at hit point - - const float EPSILON = 0.001; - vec3 hit_normal = normalize(vec3( - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r, - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r, - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r)); - - vec3 hit_light = texture(sampler3D(light_cascades[i], linear_sampler), uvw).rgb; - vec4 aniso0 = texture(sampler3D(aniso0_cascades[i], linear_sampler), uvw); - vec3 hit_aniso0 = aniso0.rgb; - vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[i], linear_sampler), uvw).rg); - - hit_light *= (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0))); - - light = hit_light; - - break; - } - -#endif - - imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0)); -} diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl deleted file mode 100644 index a0ef169f037e..000000000000 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl +++ /dev/null @@ -1,268 +0,0 @@ -#[vertex] - -#version 450 - -#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) -#extension GL_EXT_multiview : enable -#endif - -#ifdef USE_MULTIVIEW -#ifdef has_VK_KHR_multiview -#define ViewIndex gl_ViewIndex -#else // has_VK_KHR_multiview -// !BAS! This needs to become an input once we implement our fallback! -#define ViewIndex 0 -#endif // has_VK_KHR_multiview -#else // USE_MULTIVIEW -// Set to zero, not supported in non stereo -#define ViewIndex 0 -#endif //USE_MULTIVIEW - -#VERSION_DEFINES - -#define MAX_CASCADES 8 -#define MAX_VIEWS 2 - -layout(push_constant, std430) uniform Params { - uint band_power; - uint sections_in_band; - uint band_mask; - float section_arc; - - vec3 grid_size; - uint cascade; - - uint pad; - float y_mult; - uint probe_debug_index; - int probe_axis_size; -} -params; - -// https://in4k.untergrund.net/html_articles/hugi_27_-_coding_corner_polaris_sphere_tessellation_101.htm - -vec3 get_sphere_vertex(uint p_vertex_id) { - float x_angle = float(p_vertex_id & 1u) + (p_vertex_id >> params.band_power); - - float y_angle = - float((p_vertex_id & params.band_mask) >> 1) + ((p_vertex_id >> params.band_power) * params.sections_in_band); - - x_angle *= params.section_arc * 0.5f; // remember - 180AA x rot not 360 - y_angle *= -params.section_arc; - - vec3 point = vec3(sin(x_angle) * sin(y_angle), cos(x_angle), sin(x_angle) * cos(y_angle)); - - return point; -} - -#ifdef MODE_PROBES - -layout(location = 0) out vec3 normal_interp; -layout(location = 1) out flat uint probe_index; - -#endif - -#ifdef MODE_VISIBILITY - -layout(location = 0) out float visibility; - -#endif - -struct CascadeData { - vec3 offset; //offset of (0,0,0) in world coordinates - float to_cell; // 1/bounds * grid_size - ivec3 probe_world_offset; - uint pad; - vec4 pad2; -}; - -layout(set = 0, binding = 1, std140) uniform Cascades { - CascadeData data[MAX_CASCADES]; -} -cascades; - -layout(set = 0, binding = 4) uniform texture3D occlusion_texture; -layout(set = 0, binding = 3) uniform sampler linear_sampler; - -layout(set = 0, binding = 5, std140) uniform SceneData { - mat4 projection[MAX_VIEWS]; -} -scene_data; - -void main() { -#ifdef MODE_PROBES - probe_index = gl_InstanceIndex; - - normal_interp = get_sphere_vertex(gl_VertexIndex); - - vec3 vertex = normal_interp * 0.2; - - float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell; - - ivec3 probe_cell; - probe_cell.x = int(probe_index % params.probe_axis_size); - probe_cell.y = int(probe_index / (params.probe_axis_size * params.probe_axis_size)); - probe_cell.z = int((probe_index / params.probe_axis_size) % params.probe_axis_size); - - vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0); - - gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0); -#endif - -#ifdef MODE_VISIBILITY - - int probe_index = int(params.probe_debug_index); - - vec3 vertex = get_sphere_vertex(gl_VertexIndex) * 0.01; - - float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell; - - ivec3 probe_cell; - probe_cell.x = int(probe_index % params.probe_axis_size); - probe_cell.y = int((probe_index % (params.probe_axis_size * params.probe_axis_size)) / params.probe_axis_size); - probe_cell.z = int(probe_index / (params.probe_axis_size * params.probe_axis_size)); - - vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0); - - int probe_voxels = int(params.grid_size.x) / int(params.probe_axis_size - 1); - int occluder_index = int(gl_InstanceIndex); - - int diameter = probe_voxels * 2; - ivec3 occluder_pos; - occluder_pos.x = int(occluder_index % diameter); - occluder_pos.y = int(occluder_index / (diameter * diameter)); - occluder_pos.z = int((occluder_index / diameter) % diameter); - - float cell_size = 1.0 / cascades.data[params.cascade].to_cell; - - ivec3 occluder_offset = occluder_pos - ivec3(diameter / 2); - vertex += ((vec3(occluder_offset) + vec3(0.5)) * cell_size) / vec3(1.0, params.y_mult, 1.0); - - ivec3 global_cell = probe_cell + cascades.data[params.cascade].probe_world_offset; - uint occlusion_layer = 0; - if ((global_cell.x & 1) != 0) { - occlusion_layer |= 1; - } - if ((global_cell.y & 1) != 0) { - occlusion_layer |= 2; - } - if ((global_cell.z & 1) != 0) { - occlusion_layer |= 4; - } - ivec3 tex_pos = probe_cell * probe_voxels + occluder_offset; - - const vec4 layer_axis[4] = vec4[]( - vec4(1, 0, 0, 0), - vec4(0, 1, 0, 0), - vec4(0, 0, 1, 0), - vec4(0, 0, 0, 1)); - - tex_pos.z += int(params.cascade) * int(params.grid_size); - if (occlusion_layer >= 4) { - tex_pos.x += int(params.grid_size.x); - occlusion_layer &= 3; - } - - visibility = dot(texelFetch(sampler3D(occlusion_texture, linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]); - - gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0); - -#endif -} - -#[fragment] - -#version 450 - -#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview) -#extension GL_EXT_multiview : enable -#endif - -#ifdef USE_MULTIVIEW -#ifdef has_VK_KHR_multiview -#define ViewIndex gl_ViewIndex -#else // has_VK_KHR_multiview -// !BAS! This needs to become an input once we implement our fallback! -#define ViewIndex 0 -#endif // has_VK_KHR_multiview -#else // USE_MULTIVIEW -// Set to zero, not supported in non stereo -#define ViewIndex 0 -#endif //USE_MULTIVIEW - -#VERSION_DEFINES - -#define MAX_VIEWS 2 - -layout(location = 0) out vec4 frag_color; - -layout(set = 0, binding = 2) uniform texture2DArray lightprobe_texture; -layout(set = 0, binding = 3) uniform sampler linear_sampler; - -layout(push_constant, std430) uniform Params { - uint band_power; - uint sections_in_band; - uint band_mask; - float section_arc; - - vec3 grid_size; - uint cascade; - - uint pad; - float y_mult; - uint probe_debug_index; - int probe_axis_size; -} -params; - -#ifdef MODE_PROBES - -layout(location = 0) in vec3 normal_interp; -layout(location = 1) in flat uint probe_index; - -#endif - -#ifdef MODE_VISIBILITY -layout(location = 0) in float visibility; -#endif - -vec2 octahedron_wrap(vec2 v) { - vec2 signVal; - signVal.x = v.x >= 0.0 ? 1.0 : -1.0; - signVal.y = v.y >= 0.0 ? 1.0 : -1.0; - return (1.0 - abs(v.yx)) * signVal; -} - -vec2 octahedron_encode(vec3 n) { - // https://twitter.com/Stubbesaurus/status/937994790553227264 - n /= (abs(n.x) + abs(n.y) + abs(n.z)); - n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); - n.xy = n.xy * 0.5 + 0.5; - return n.xy; -} - -void main() { -#ifdef MODE_PROBES - - ivec3 tex_pos; - tex_pos.x = int(probe_index) % params.probe_axis_size; //x - tex_pos.y = int(probe_index) / (params.probe_axis_size * params.probe_axis_size); - tex_pos.x += params.probe_axis_size * ((int(probe_index) / params.probe_axis_size) % params.probe_axis_size); //z - tex_pos.z = int(params.cascade); - - vec3 tex_pos_ofs = vec3(octahedron_encode(normal_interp) * float(OCT_SIZE), 0.0); - vec3 tex_posf = vec3(vec2(tex_pos.xy * (OCT_SIZE + 2) + ivec2(1)), float(tex_pos.z)) + tex_pos_ofs; - - tex_posf.xy /= vec2(ivec2(params.probe_axis_size * params.probe_axis_size * (OCT_SIZE + 2), params.probe_axis_size * (OCT_SIZE + 2))); - - vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), tex_posf, 0.0); - - frag_color = indirect_light; - -#endif - -#ifdef MODE_VISIBILITY - - frag_color = vec4(vec3(1, visibility, visibility), 1.0); -#endif -} diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl deleted file mode 100644 index 06709f65d39f..000000000000 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl +++ /dev/null @@ -1,507 +0,0 @@ -#[compute] - -#version 450 - -#VERSION_DEFINES - -layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; - -#define MAX_CASCADES 8 - -layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES]; -layout(set = 0, binding = 2) uniform sampler linear_sampler; - -layout(set = 0, binding = 3, std430) restrict readonly buffer DispatchData { - uint x; - uint y; - uint z; - uint total_count; -} -dispatch_data; - -struct ProcessVoxel { - uint position; // xyz 7 bit packed, extra 11 bits for neighbors. - uint albedo; // rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbors. - uint light; // rgbe8985 encoded total saved light, extra 2 bits for neighbors. - uint light_aniso; // 55555 light anisotropy, extra 2 bits for neighbors. - //total neighbors: 26 -}; - -#ifdef MODE_PROCESS_STATIC -layout(set = 0, binding = 4, std430) restrict buffer ProcessVoxels { -#else -layout(set = 0, binding = 4, std430) restrict buffer readonly ProcessVoxels { -#endif - ProcessVoxel data[]; -} -process_voxels; - -layout(r32ui, set = 0, binding = 5) uniform restrict uimage3D dst_light; -layout(rgba8, set = 0, binding = 6) uniform restrict image3D dst_aniso0; -layout(rg8, set = 0, binding = 7) uniform restrict image3D dst_aniso1; - -struct CascadeData { - vec3 offset; //offset of (0,0,0) in world coordinates - float to_cell; // 1/bounds * grid_size - ivec3 probe_world_offset; - uint pad; - vec4 pad2; -}; - -layout(set = 0, binding = 8, std140) uniform Cascades { - CascadeData data[MAX_CASCADES]; -} -cascades; - -#define LIGHT_TYPE_DIRECTIONAL 0 -#define LIGHT_TYPE_OMNI 1 -#define LIGHT_TYPE_SPOT 2 - -struct Light { - vec3 color; - float energy; - - vec3 direction; - bool has_shadow; - - vec3 position; - float attenuation; - - uint type; - float cos_spot_angle; - float inv_spot_attenuation; - float radius; -}; - -layout(set = 0, binding = 9, std140) buffer restrict readonly Lights { - Light data[]; -} -lights; - -layout(set = 0, binding = 10) uniform texture2DArray lightprobe_texture; -layout(set = 0, binding = 11) uniform texture3D occlusion_texture; - -layout(push_constant, std430) uniform Params { - vec3 grid_size; - uint max_cascades; - - uint cascade; - uint light_count; - uint process_offset; - uint process_increment; - - int probe_axis_size; - float bounce_feedback; - float y_mult; - bool use_occlusion; -} -params; - -vec2 octahedron_wrap(vec2 v) { - vec2 signVal; - signVal.x = v.x >= 0.0 ? 1.0 : -1.0; - signVal.y = v.y >= 0.0 ? 1.0 : -1.0; - return (1.0 - abs(v.yx)) * signVal; -} - -vec2 octahedron_encode(vec3 n) { - // https://twitter.com/Stubbesaurus/status/937994790553227264 - n /= (abs(n.x) + abs(n.y) + abs(n.z)); - n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy); - n.xy = n.xy * 0.5 + 0.5; - return n.xy; -} - -float get_omni_attenuation(float distance, float inv_range, float decay) { - float nd = distance * inv_range; - nd *= nd; - nd *= nd; // nd^4 - nd = max(1.0 - nd, 0.0); - nd *= nd; // nd^2 - return nd * pow(max(distance, 0.0001), -decay); -} - -void main() { - uint voxel_index = uint(gl_GlobalInvocationID.x); - - //used for skipping voxels every N frames - if (params.process_increment > 1) { - voxel_index *= params.process_increment; - voxel_index += params.process_offset; - } - - if (voxel_index >= dispatch_data.total_count) { - return; - } - - uint voxel_position = process_voxels.data[voxel_index].position; - - //keep for storing to texture - ivec3 positioni = ivec3((uvec3(voxel_position, voxel_position, voxel_position) >> uvec3(0, 7, 14)) & uvec3(0x7F)); - - vec3 position = vec3(positioni) + vec3(0.5); - position /= cascades.data[params.cascade].to_cell; - position += cascades.data[params.cascade].offset; - - uint voxel_albedo = process_voxels.data[voxel_index].albedo; - - vec3 albedo = vec3(uvec3(voxel_albedo >> 10, voxel_albedo >> 5, voxel_albedo) & uvec3(0x1F)) / float(0x1F); - vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); - uint valid_aniso = (voxel_albedo >> 15) & 0x3F; - - const vec3 aniso_dir[6] = vec3[]( - vec3(1, 0, 0), - vec3(0, 1, 0), - vec3(0, 0, 1), - vec3(-1, 0, 0), - vec3(0, -1, 0), - vec3(0, 0, -1)); - - // Add indirect light first, in order to save computation resources -#ifdef MODE_PROCESS_DYNAMIC - if (params.bounce_feedback > 0.001) { - vec3 feedback = (params.bounce_feedback < 1.0) ? (albedo * params.bounce_feedback) : mix(albedo, vec3(1.0), params.bounce_feedback - 1.0); - vec3 pos = (vec3(positioni) + vec3(0.5)) * float(params.probe_axis_size - 1) / params.grid_size; - ivec3 probe_base_pos = ivec3(pos); - - float weight_accum[6] = float[](0, 0, 0, 0, 0, 0); - - ivec3 tex_pos = ivec3(probe_base_pos.xy, int(params.cascade)); - tex_pos.x += probe_base_pos.z * int(params.probe_axis_size); - - tex_pos.xy = tex_pos.xy * (OCT_SIZE + 2) + ivec2(1); - - vec3 base_tex_posf = vec3(tex_pos); - vec2 tex_pixel_size = 1.0 / vec2(ivec2((OCT_SIZE + 2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE + 2) * params.probe_axis_size)); - vec3 probe_uv_offset = vec3(ivec3(OCT_SIZE + 2, OCT_SIZE + 2, (OCT_SIZE + 2) * params.probe_axis_size)) * tex_pixel_size.xyx; - - for (uint j = 0; j < 8; j++) { - ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); - ivec3 probe_posi = probe_base_pos; - probe_posi += offset; - - // Compute weight - - vec3 probe_pos = vec3(probe_posi); - vec3 probe_to_pos = pos - probe_pos; - vec3 probe_dir = normalize(-probe_to_pos); - - // Compute lightprobe texture position - - vec3 trilinear = vec3(1.0) - abs(probe_to_pos); - - for (uint k = 0; k < 6; k++) { - if (bool(valid_aniso & (1 << k))) { - vec3 n = aniso_dir[k]; - float weight = trilinear.x * trilinear.y * trilinear.z * max(0, dot(n, probe_dir)); - - if (weight > 0.0 && params.use_occlusion) { - ivec3 occ_indexv = abs((cascades.data[params.cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); - vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); - - vec3 occ_pos = (vec3(positioni) + aniso_dir[k] + vec3(0.5)) / params.grid_size; - occ_pos.z += float(params.cascade); - if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures - occ_pos.x += 1.0; - } - occ_pos *= vec3(0.5, 1.0, 1.0 / float(params.max_cascades)); //renormalize - float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask); - - weight *= occlusion; - } - - if (weight > 0.0) { - vec3 tex_posf = base_tex_posf + vec3(octahedron_encode(n) * float(OCT_SIZE), 0.0); - tex_posf.xy *= tex_pixel_size; - - vec3 pos_uvw = tex_posf; - pos_uvw.xy += vec2(offset.xy) * probe_uv_offset.xy; - pos_uvw.x += float(offset.z) * probe_uv_offset.z; - vec3 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb; - - light_accum[k] += indirect_light * weight; - weight_accum[k] += weight; - } - } - } - } - - for (uint k = 0; k < 6; k++) { - if (weight_accum[k] > 0.0) { - light_accum[k] /= weight_accum[k]; - light_accum[k] *= feedback; - } - } - } - -#endif - - { - uint rgbe = process_voxels.data[voxel_index].light; - - //read rgbe8985 - float r = float((rgbe & 0xff) << 1); - float g = float((rgbe >> 8) & 0x1ff); - float b = float(((rgbe >> 17) & 0xff) << 1); - float e = float((rgbe >> 25) & 0x1F); - float m = pow(2.0, e - 15.0 - 9.0); - - vec3 l = vec3(r, g, b) * m; - - uint aniso = process_voxels.data[voxel_index].light_aniso; - for (uint i = 0; i < 6; i++) { - float strength = ((aniso >> (i * 5)) & 0x1F) / float(0x1F); - light_accum[i] += l * strength; - } - } - - // Raytrace light - - vec3 pos_to_uvw = 1.0 / params.grid_size; - vec3 uvw_ofs = pos_to_uvw * 0.5; - - for (uint i = 0; i < params.light_count; i++) { - float attenuation = 1.0; - vec3 direction; - float light_distance = 1e20; - - switch (lights.data[i].type) { - case LIGHT_TYPE_DIRECTIONAL: { - direction = -lights.data[i].direction; - } break; - case LIGHT_TYPE_OMNI: { - vec3 rel_vec = lights.data[i].position - position; - direction = normalize(rel_vec); - light_distance = length(rel_vec); - rel_vec.y /= params.y_mult; - attenuation = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, lights.data[i].attenuation); - - } break; - case LIGHT_TYPE_SPOT: { - vec3 rel_vec = lights.data[i].position - position; - direction = normalize(rel_vec); - light_distance = length(rel_vec); - rel_vec.y /= params.y_mult; - attenuation = get_omni_attenuation(light_distance, 1.0 / lights.data[i].radius, lights.data[i].attenuation); - - float cos_spot_angle = lights.data[i].cos_spot_angle; - float cos_angle = dot(-direction, lights.data[i].direction); - - if (cos_angle < cos_spot_angle) { - continue; - } - - float scos = max(cos_angle, cos_spot_angle); - float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle)); - attenuation *= 1.0 - pow(spot_rim, lights.data[i].inv_spot_attenuation); - } break; - } - - if (attenuation < 0.001) { - continue; - } - - bool hit = false; - - vec3 ray_pos = position; - vec3 ray_dir = direction; - vec3 inv_dir = 1.0 / ray_dir; - - //this is how to properly bias outgoing rays - float cell_size = 1.0 / cascades.data[params.cascade].to_cell; - ray_pos += sign(direction) * cell_size * 0.48; // go almost to the box edge but remain inside - ray_pos += ray_dir * 0.4 * cell_size; //apply a small bias from there - - for (uint j = params.cascade; j < params.max_cascades; j++) { - //convert to local bounds - vec3 pos = ray_pos - cascades.data[j].offset; - pos *= cascades.data[j].to_cell; - float local_distance = light_distance * cascades.data[j].to_cell; - - if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) { - continue; //already past bounds for this cascade, goto next - } - - //find maximum advance distance (until reaching bounds) - vec3 t0 = -pos * inv_dir; - vec3 t1 = (params.grid_size - pos) * inv_dir; - vec3 tmax = max(t0, t1); - float max_advance = min(tmax.x, min(tmax.y, tmax.z)); - - max_advance = min(local_distance, max_advance); - - float advance = 0.0; - float occlusion = 1.0; - - while (advance < max_advance) { - //read how much to advance from SDF - vec3 uvw = (pos + ray_dir * advance) * pos_to_uvw; - - float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0; - if (distance < 0.001) { - //consider hit - hit = true; - break; - } - - occlusion = min(occlusion, distance); - - advance += distance; - } - - if (hit) { - attenuation *= occlusion; - break; - } - - if (advance >= local_distance) { - break; //past light distance, abandon search - } - //change ray origin to collision with bounds - pos += ray_dir * max_advance; - pos /= cascades.data[j].to_cell; - pos += cascades.data[j].offset; - light_distance -= max_advance / cascades.data[j].to_cell; - ray_pos = pos; - } - - if (!hit) { - vec3 light = albedo * lights.data[i].color.rgb * lights.data[i].energy * attenuation; - - for (int j = 0; j < 6; j++) { - if (bool(valid_aniso & (1 << j))) { - light_accum[j] += max(0.0, dot(aniso_dir[j], direction)) * light; - } - } - } - } - - // Store the light in the light texture - - float lumas[6]; - vec3 light_total = vec3(0); - - for (int i = 0; i < 6; i++) { - light_total += light_accum[i]; - lumas[i] = max(light_accum[i].r, max(light_accum[i].g, light_accum[i].b)); - } - - float luma_total = max(light_total.r, max(light_total.g, light_total.b)); - - uint light_total_rgbe; - - { - //compress to RGBE9995 to save space - - const float pow2to9 = 512.0f; - const float B = 15.0f; - const float N = 9.0f; - const float LN2 = 0.6931471805599453094172321215; - - float cRed = clamp(light_total.r, 0.0, 65408.0); - float cGreen = clamp(light_total.g, 0.0, 65408.0); - float cBlue = clamp(light_total.b, 0.0, 65408.0); - - float cMax = max(cRed, max(cGreen, cBlue)); - - float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B; - - float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f); - - float exps = expp + 1.0f; - - if (0.0 <= sMax && sMax < pow2to9) { - exps = expp; - } - - float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); - float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); - float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); -#ifdef MODE_PROCESS_STATIC - //since its self-save, use RGBE8985 - light_total_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25); - -#else - light_total_rgbe = (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27); -#endif - } - -#ifdef MODE_PROCESS_DYNAMIC - - vec4 aniso0; - aniso0.r = lumas[0] / luma_total; - aniso0.g = lumas[1] / luma_total; - aniso0.b = lumas[2] / luma_total; - aniso0.a = lumas[3] / luma_total; - - vec2 aniso1; - aniso1.r = lumas[4] / luma_total; - aniso1.g = lumas[5] / luma_total; - - //save to 3D textures - imageStore(dst_aniso0, positioni, aniso0); - imageStore(dst_aniso1, positioni, vec4(aniso1, 0.0, 0.0)); - imageStore(dst_light, positioni, uvec4(light_total_rgbe)); - - //also fill neighbors, so light interpolation during the indirect pass works - - //recover the neighbor list from the leftover bits - uint neighbors = (voxel_albedo >> 21) | ((voxel_position >> 21) << 11) | ((process_voxels.data[voxel_index].light >> 30) << 22) | ((process_voxels.data[voxel_index].light_aniso >> 30) << 24); - - const uint max_neighbours = 26; - const ivec3 neighbour_positions[max_neighbours] = ivec3[]( - ivec3(-1, -1, -1), - ivec3(-1, -1, 0), - ivec3(-1, -1, 1), - ivec3(-1, 0, -1), - ivec3(-1, 0, 0), - ivec3(-1, 0, 1), - ivec3(-1, 1, -1), - ivec3(-1, 1, 0), - ivec3(-1, 1, 1), - ivec3(0, -1, -1), - ivec3(0, -1, 0), - ivec3(0, -1, 1), - ivec3(0, 0, -1), - ivec3(0, 0, 1), - ivec3(0, 1, -1), - ivec3(0, 1, 0), - ivec3(0, 1, 1), - ivec3(1, -1, -1), - ivec3(1, -1, 0), - ivec3(1, -1, 1), - ivec3(1, 0, -1), - ivec3(1, 0, 0), - ivec3(1, 0, 1), - ivec3(1, 1, -1), - ivec3(1, 1, 0), - ivec3(1, 1, 1)); - - for (uint i = 0; i < max_neighbours; i++) { - if (bool(neighbors & (1 << i))) { - ivec3 neighbour_pos = positioni + neighbour_positions[i]; - imageStore(dst_light, neighbour_pos, uvec4(light_total_rgbe)); - imageStore(dst_aniso0, neighbour_pos, aniso0); - imageStore(dst_aniso1, neighbour_pos, vec4(aniso1, 0.0, 0.0)); - } - } - -#endif - -#ifdef MODE_PROCESS_STATIC - - //save back the anisotropic - - uint light = process_voxels.data[voxel_index].light & (3 << 30); - light |= light_total_rgbe; - process_voxels.data[voxel_index].light = light; //replace - - uint light_aniso = process_voxels.data[voxel_index].light_aniso & (3 << 30); - for (int i = 0; i < 6; i++) { - light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5); - } - - process_voxels.data[voxel_index].light_aniso = light_aniso; - -#endif -} diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl deleted file mode 100644 index 4bdb0dcc722c..000000000000 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl +++ /dev/null @@ -1,613 +0,0 @@ -#[compute] - -#version 450 - -#VERSION_DEFINES - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -#define MAX_CASCADES 8 - -layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES]; -layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES]; -layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES]; -layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES]; - -layout(set = 0, binding = 6) uniform sampler linear_sampler; - -struct CascadeData { - vec3 offset; //offset of (0,0,0) in world coordinates - float to_cell; // 1/bounds * grid_size - ivec3 probe_world_offset; - uint pad; - vec4 pad2; -}; - -layout(set = 0, binding = 7, std140) uniform Cascades { - CascadeData data[MAX_CASCADES]; -} -cascades; - -layout(r32ui, set = 0, binding = 8) uniform restrict uimage2DArray lightprobe_texture_data; -layout(rgba16i, set = 0, binding = 9) uniform restrict iimage2DArray lightprobe_history_texture; -layout(rgba32i, set = 0, binding = 10) uniform restrict iimage2D lightprobe_average_texture; - -//used for scrolling -layout(rgba16i, set = 0, binding = 11) uniform restrict iimage2DArray lightprobe_history_scroll_texture; -layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_average_scroll_texture; - -layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture; - -layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture; - -#ifdef USE_CUBEMAP_ARRAY -layout(set = 1, binding = 0) uniform textureCubeArray sky_irradiance; -#else -layout(set = 1, binding = 0) uniform textureCube sky_irradiance; -#endif -layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps; - -#define HISTORY_BITS 10 - -#define SKY_MODE_DISABLED 0 -#define SKY_MODE_COLOR 1 -#define SKY_MODE_SKY 2 - -layout(push_constant, std430) uniform Params { - vec3 grid_size; - uint max_cascades; - - uint probe_axis_size; - uint cascade; - uint history_index; - uint history_size; - - uint ray_count; - float ray_bias; - ivec2 image_size; - - ivec3 world_offset; - uint sky_mode; - - ivec3 scroll; - float sky_energy; - - vec3 sky_color; - float y_mult; - - bool store_ambient_texture; - uint pad[3]; -} -params; - -const float PI = 3.14159265f; -const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0)); - -vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) { - float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count)); - float theta = float(p_index) * GOLDEN_ANGLE + p_offset; - float y = cos(r * PI * 0.5); - float l = sin(r * PI * 0.5); - return vec3(l * cos(theta), l * sin(theta), y * (float(p_index & 1) * 2.0 - 1.0)); -} - -uvec3 hash3(uvec3 x) { - x = ((x >> 16) ^ x) * 0x45d9f3b; - x = ((x >> 16) ^ x) * 0x45d9f3b; - x = (x >> 16) ^ x; - return x; -} - -float hashf3(vec3 co) { - return fract(sin(dot(co, vec3(12.9898, 78.233, 137.13451))) * 43758.5453); -} - -vec3 octahedron_encode(vec2 f) { - // https://twitter.com/Stubbesaurus/status/937994790553227264 - f = f * 2.0 - 1.0; - vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y)); - float t = clamp(-n.z, 0.0, 1.0); - n.x += n.x >= 0 ? -t : t; - n.y += n.y >= 0 ? -t : t; - return normalize(n); -} - -uint rgbe_encode(vec3 color) { - const float pow2to9 = 512.0f; - const float B = 15.0f; - const float N = 9.0f; - const float LN2 = 0.6931471805599453094172321215; - - float cRed = clamp(color.r, 0.0, 65408.0); - float cGreen = clamp(color.g, 0.0, 65408.0); - float cBlue = clamp(color.b, 0.0, 65408.0); - - float cMax = max(cRed, max(cGreen, cBlue)); - - float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B; - - float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f); - - float exps = expp + 1.0f; - - if (0.0 <= sMax && sMax < pow2to9) { - exps = expp; - } - - float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); - float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); - float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); - return (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27); -} - -struct SH { -#if (SH_SIZE == 16) - float c[48]; -#else - float c[28]; -#endif -}; - -shared SH sh_accum[64]; //8x8 - -void main() { - ivec2 pos = ivec2(gl_GlobalInvocationID.xy); - if (any(greaterThanEqual(pos, params.image_size))) { //too large, do nothing - return; - } - - uint probe_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 8; - -#ifdef MODE_PROCESS - - float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell; - - ivec3 probe_cell; - probe_cell.x = pos.x % int(params.probe_axis_size); - probe_cell.y = pos.y; - probe_cell.z = pos.x / int(params.probe_axis_size); - - vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size; - vec3 pos_to_uvw = 1.0 / params.grid_size; - - for (uint i = 0; i < SH_SIZE * 3; i++) { - sh_accum[probe_index].c[i] = 0.0; - } - - // quickly ensure each probe has a different "offset" for the vogel function, based on integer world position - uvec3 h3 = hash3(uvec3(params.world_offset + probe_cell)); - float offset = hashf3(vec3(h3 & uvec3(0xFFFFF))); - - //for a more homogeneous hemisphere, alternate based on history frames - uint ray_offset = params.history_index; - uint ray_mult = params.history_size; - uint ray_total = ray_mult * params.ray_count; - - for (uint i = 0; i < params.ray_count; i++) { - vec3 ray_dir = vogel_hemisphere(ray_offset + i * ray_mult, ray_total, offset); - ray_dir.y *= params.y_mult; - ray_dir = normalize(ray_dir); - - //needs to be visible - vec3 ray_pos = probe_pos; - vec3 inv_dir = 1.0 / ray_dir; - - bool hit = false; - uint hit_cascade; - - float bias = params.ray_bias; - vec3 abs_ray_dir = abs(ray_dir); - ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell; - vec3 uvw; - - for (uint j = params.cascade; j < params.max_cascades; j++) { - //convert to local bounds - vec3 pos = ray_pos - cascades.data[j].offset; - pos *= cascades.data[j].to_cell; - - if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) { - continue; //already past bounds for this cascade, goto next - } - - //find maximum advance distance (until reaching bounds) - vec3 t0 = -pos * inv_dir; - vec3 t1 = (params.grid_size - pos) * inv_dir; - vec3 tmax = max(t0, t1); - float max_advance = min(tmax.x, min(tmax.y, tmax.z)); - - float advance = 0.0; - - while (advance < max_advance) { - //read how much to advance from SDF - uvw = (pos + ray_dir * advance) * pos_to_uvw; - - float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0; - if (distance < 0.05) { - //consider hit - hit = true; - break; - } - - advance += distance; - } - - if (hit) { - hit_cascade = j; - break; - } - - //change ray origin to collision with bounds - pos += ray_dir * max_advance; - pos /= cascades.data[j].to_cell; - pos += cascades.data[j].offset; - ray_pos = pos; - } - - vec4 light; - if (hit) { - //avoid reading different texture from different threads - for (uint j = params.cascade; j < params.max_cascades; j++) { - if (j == hit_cascade) { - const float EPSILON = 0.001; - vec3 hit_normal = normalize(vec3( - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r, - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r, - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[hit_cascade], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r)); - - vec3 hit_light = texture(sampler3D(light_cascades[hit_cascade], linear_sampler), uvw).rgb; - vec4 aniso0 = texture(sampler3D(aniso0_cascades[hit_cascade], linear_sampler), uvw); - vec3 hit_aniso0 = aniso0.rgb; - vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[hit_cascade], linear_sampler), uvw).rg); - - //one liner magic - light.rgb = hit_light * (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0))); - light.a = 1.0; - } - } - - } else if (params.sky_mode == SKY_MODE_SKY) { -#ifdef USE_CUBEMAP_ARRAY - light.rgb = textureLod(samplerCubeArray(sky_irradiance, linear_sampler_mipmaps), vec4(ray_dir, 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates. -#else - light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates. -#endif - light.rgb *= params.sky_energy; - light.a = 0.0; - - } else if (params.sky_mode == SKY_MODE_COLOR) { - light.rgb = params.sky_color; - light.rgb *= params.sky_energy; - light.a = 0.0; - } else { - light = vec4(0, 0, 0, 0); - } - - vec3 ray_dir2 = ray_dir * ray_dir; - -#define SH_ACCUM(m_idx, m_value) \ - { \ - vec3 l = light.rgb * (m_value); \ - sh_accum[probe_index].c[m_idx * 3 + 0] += l.r; \ - sh_accum[probe_index].c[m_idx * 3 + 1] += l.g; \ - sh_accum[probe_index].c[m_idx * 3 + 2] += l.b; \ - } - SH_ACCUM(0, 0.282095); //l0 - SH_ACCUM(1, 0.488603 * ray_dir.y); //l1n1 - SH_ACCUM(2, 0.488603 * ray_dir.z); //l1n0 - SH_ACCUM(3, 0.488603 * ray_dir.x); //l1p1 - SH_ACCUM(4, 1.092548 * ray_dir.x * ray_dir.y); //l2n2 - SH_ACCUM(5, 1.092548 * ray_dir.y * ray_dir.z); //l2n1 - SH_ACCUM(6, 0.315392 * (3.0 * ray_dir2.z - 1.0)); //l20 - SH_ACCUM(7, 1.092548 * ray_dir.x * ray_dir.z); //l2p1 - SH_ACCUM(8, 0.546274 * (ray_dir2.x - ray_dir2.y)); //l2p2 -#if (SH_SIZE == 16) - SH_ACCUM(9, 0.590043 * ray_dir.y * (3.0f * ray_dir2.x - ray_dir2.y)); - SH_ACCUM(10, 2.890611 * ray_dir.y * ray_dir.x * ray_dir.z); - SH_ACCUM(11, 0.646360 * ray_dir.y * (-1.0f + 5.0f * ray_dir2.z)); - SH_ACCUM(12, 0.373176 * (5.0f * ray_dir2.z * ray_dir.z - 3.0f * ray_dir.z)); - SH_ACCUM(13, 0.457045 * ray_dir.x * (-1.0f + 5.0f * ray_dir2.z)); - SH_ACCUM(14, 1.445305 * (ray_dir2.x - ray_dir2.y) * ray_dir.z); - SH_ACCUM(15, 0.590043 * ray_dir.x * (ray_dir2.x - 3.0f * ray_dir2.y)); - -#endif - } - - for (uint i = 0; i < SH_SIZE; i++) { - // store in history texture - ivec3 prev_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(params.history_index)); - ivec2 average_pos = prev_pos.xy; - - vec4 value = vec4(sh_accum[probe_index].c[i * 3 + 0], sh_accum[probe_index].c[i * 3 + 1], sh_accum[probe_index].c[i * 3 + 2], 1.0) * 4.0 / float(params.ray_count); - - ivec4 ivalue = clamp(ivec4(value * float(1 << HISTORY_BITS)), -32768, 32767); //clamp to 16 bits, so higher values don't break average - - ivec4 prev_value = imageLoad(lightprobe_history_texture, prev_pos); - ivec4 average = imageLoad(lightprobe_average_texture, average_pos); - - average -= prev_value; - average += ivalue; - - imageStore(lightprobe_history_texture, prev_pos, ivalue); - imageStore(lightprobe_average_texture, average_pos, average); - - if (params.store_ambient_texture && i == 0) { - ivec3 ambient_pos = ivec3(pos, int(params.cascade)); - vec4 ambient_light = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS); - ambient_light *= 0.88622; // SHL0 - imageStore(lightprobe_ambient_texture, ambient_pos, ambient_light); - } - } -#endif // MODE PROCESS - -#ifdef MODE_STORE - - // converting to octahedral in this step is required because - // octahedral is much faster to read from the screen than spherical harmonics, - // despite the very slight quality loss - - ivec2 sh_pos = (pos / OCT_SIZE) * ivec2(1, SH_SIZE); - ivec2 oct_pos = (pos / OCT_SIZE) * (OCT_SIZE + 2) + ivec2(1); - ivec2 local_pos = pos % OCT_SIZE; - - //compute the octahedral normal for this texel - vec3 normal = octahedron_encode(vec2(local_pos) / float(OCT_SIZE)); - - // read the spherical harmonic - - vec3 normal2 = normal * normal; - float c[SH_SIZE] = float[]( - - 0.282095, //l0 - 0.488603 * normal.y, //l1n1 - 0.488603 * normal.z, //l1n0 - 0.488603 * normal.x, //l1p1 - 1.092548 * normal.x * normal.y, //l2n2 - 1.092548 * normal.y * normal.z, //l2n1 - 0.315392 * (3.0 * normal2.z - 1.0), //l20 - 1.092548 * normal.x * normal.z, //l2p1 - 0.546274 * (normal2.x - normal2.y) //l2p2 -#if (SH_SIZE == 16) - , - 0.590043 * normal.y * (3.0f * normal2.x - normal2.y), - 2.890611 * normal.y * normal.x * normal.z, - 0.646360 * normal.y * (-1.0f + 5.0f * normal2.z), - 0.373176 * (5.0f * normal2.z * normal.z - 3.0f * normal.z), - 0.457045 * normal.x * (-1.0f + 5.0f * normal2.z), - 1.445305 * (normal2.x - normal2.y) * normal.z, - 0.590043 * normal.x * (normal2.x - 3.0f * normal2.y) - -#endif - ); - - const float l_mult[SH_SIZE] = float[]( - 1.0, - 2.0 / 3.0, - 2.0 / 3.0, - 2.0 / 3.0, - 1.0 / 4.0, - 1.0 / 4.0, - 1.0 / 4.0, - 1.0 / 4.0, - 1.0 / 4.0 -#if (SH_SIZE == 16) - , // l4 does not contribute to irradiance - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 -#endif - ); - - vec3 irradiance = vec3(0.0); - vec3 radiance = vec3(0.0); - - for (uint i = 0; i < SH_SIZE; i++) { - // store in history texture - ivec2 average_pos = sh_pos + ivec2(0, i); - ivec4 average = imageLoad(lightprobe_average_texture, average_pos); - - vec4 sh = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS); - - vec3 m = sh.rgb * c[i] * 4.0; - - irradiance += m * l_mult[i]; - radiance += m; - } - - //encode RGBE9995 for the final texture - - uint irradiance_rgbe = rgbe_encode(irradiance); - uint radiance_rgbe = rgbe_encode(radiance); - - //store in octahedral map - - ivec3 texture_pos = ivec3(oct_pos, int(params.cascade)); - ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2)); - copy_to[0] = texture_pos + ivec3(local_pos, 0); - - if (local_pos == ivec2(0, 0)) { - copy_to[1] = texture_pos + ivec3(OCT_SIZE - 1, -1, 0); - copy_to[2] = texture_pos + ivec3(-1, OCT_SIZE - 1, 0); - copy_to[3] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE, 0); - } else if (local_pos == ivec2(OCT_SIZE - 1, 0)) { - copy_to[1] = texture_pos + ivec3(0, -1, 0); - copy_to[2] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE - 1, 0); - copy_to[3] = texture_pos + ivec3(-1, OCT_SIZE, 0); - } else if (local_pos == ivec2(0, OCT_SIZE - 1)) { - copy_to[1] = texture_pos + ivec3(-1, 0, 0); - copy_to[2] = texture_pos + ivec3(OCT_SIZE - 1, OCT_SIZE, 0); - copy_to[3] = texture_pos + ivec3(OCT_SIZE, -1, 0); - } else if (local_pos == ivec2(OCT_SIZE - 1, OCT_SIZE - 1)) { - copy_to[1] = texture_pos + ivec3(0, OCT_SIZE, 0); - copy_to[2] = texture_pos + ivec3(OCT_SIZE, 0, 0); - copy_to[3] = texture_pos + ivec3(-1, -1, 0); - } else if (local_pos.y == 0) { - copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0); - } else if (local_pos.x == 0) { - copy_to[1] = texture_pos + ivec3(local_pos.x - 1, OCT_SIZE - local_pos.y - 1, 0); - } else if (local_pos.y == OCT_SIZE - 1) { - copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0); - } else if (local_pos.x == OCT_SIZE - 1) { - copy_to[1] = texture_pos + ivec3(local_pos.x + 1, OCT_SIZE - local_pos.y - 1, 0); - } - - for (int i = 0; i < 4; i++) { - if (copy_to[i] == ivec3(-2, -2, -2)) { - continue; - } - imageStore(lightprobe_texture_data, copy_to[i], uvec4(irradiance_rgbe)); - imageStore(lightprobe_texture_data, copy_to[i] + ivec3(0, 0, int(params.max_cascades)), uvec4(radiance_rgbe)); - } - -#endif - -#ifdef MODE_SCROLL - - ivec3 probe_cell; - probe_cell.x = pos.x % int(params.probe_axis_size); - probe_cell.y = pos.y; - probe_cell.z = pos.x / int(params.probe_axis_size); - - ivec3 read_probe = probe_cell - params.scroll; - - if (all(greaterThanEqual(read_probe, ivec3(0))) && all(lessThan(read_probe, ivec3(params.probe_axis_size)))) { - // can scroll - ivec2 tex_pos; - tex_pos = read_probe.xy; - tex_pos.x += read_probe.z * int(params.probe_axis_size); - - //scroll - for (uint j = 0; j < params.history_size; j++) { - for (int i = 0; i < SH_SIZE; i++) { - // copy from history texture - ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j)); - ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j)); - ivec4 value = imageLoad(lightprobe_history_texture, src_pos); - imageStore(lightprobe_history_scroll_texture, dst_pos, value); - } - } - - for (int i = 0; i < SH_SIZE; i++) { - // copy from average texture - ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + i); - ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i); - ivec4 value = imageLoad(lightprobe_average_texture, src_pos); - imageStore(lightprobe_average_scroll_texture, dst_pos, value); - } - } else if (params.cascade < params.max_cascades - 1) { - //can't scroll, must look for position in parent cascade - - //to global coords - float cell_to_probe = float(params.grid_size.x / float(params.probe_axis_size - 1)); - - float probe_cell_size = cell_to_probe / cascades.data[params.cascade].to_cell; - vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size; - - //to parent local coords - float probe_cell_size_next = cell_to_probe / cascades.data[params.cascade + 1].to_cell; - probe_pos -= cascades.data[params.cascade + 1].offset; - probe_pos /= probe_cell_size_next; - - ivec3 probe_posi = ivec3(probe_pos); - //add up all light, no need to use occlusion here, since occlusion will do its work afterwards - - vec4 average_light[SH_SIZE] = vec4[](vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0) -#if (SH_SIZE == 16) - , - vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0) -#endif - ); - float total_weight = 0.0; - - for (int i = 0; i < 8; i++) { - ivec3 offset = probe_posi + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - - vec3 trilinear = vec3(1.0) - abs(probe_pos - vec3(offset)); - float weight = trilinear.x * trilinear.y * trilinear.z; - - ivec2 tex_pos; - tex_pos = offset.xy; - tex_pos.x += offset.z * int(params.probe_axis_size); - - for (int j = 0; j < SH_SIZE; j++) { - // copy from history texture - ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + j); - ivec4 average = imageLoad(lightprobe_average_parent_texture, src_pos); - vec4 value = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS); - average_light[j] += value * weight; - } - - total_weight += weight; - } - - if (total_weight > 0.0) { - total_weight = 1.0 / total_weight; - } - //store the averaged values everywhere - - for (int i = 0; i < SH_SIZE; i++) { - ivec4 ivalue = clamp(ivec4(average_light[i] * total_weight * float(1 << HISTORY_BITS)), ivec4(-32768), ivec4(32767)); //clamp to 16 bits, so higher values don't break average - // copy from history texture - ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, 0); - for (uint j = 0; j < params.history_size; j++) { - dst_pos.z = int(j); - imageStore(lightprobe_history_scroll_texture, dst_pos, ivalue); - } - - ivalue *= int(params.history_size); //average needs to have all history added up - imageStore(lightprobe_average_scroll_texture, dst_pos.xy, ivalue); - } - - } else { - //scroll at the edge of the highest cascade, just copy what is there, - //since its the closest we have anyway - - for (uint j = 0; j < params.history_size; j++) { - ivec2 tex_pos; - tex_pos = probe_cell.xy; - tex_pos.x += probe_cell.z * int(params.probe_axis_size); - - for (int i = 0; i < SH_SIZE; i++) { - // copy from history texture - ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j)); - ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j)); - ivec4 value = imageLoad(lightprobe_history_texture, dst_pos); - imageStore(lightprobe_history_scroll_texture, dst_pos, value); - } - } - - for (int i = 0; i < SH_SIZE; i++) { - // copy from average texture - ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i); - ivec4 average = imageLoad(lightprobe_average_texture, spos); - imageStore(lightprobe_average_scroll_texture, spos, average); - } - } - -#endif - -#ifdef MODE_SCROLL_STORE - - //do not update probe texture, as these will be updated later - - for (uint j = 0; j < params.history_size; j++) { - for (int i = 0; i < SH_SIZE; i++) { - // copy from history texture - ivec3 spos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j)); - ivec4 value = imageLoad(lightprobe_history_scroll_texture, spos); - imageStore(lightprobe_history_texture, spos, value); - } - } - - for (int i = 0; i < SH_SIZE; i++) { - // copy from average texture - ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i); - ivec4 average = imageLoad(lightprobe_average_scroll_texture, spos); - imageStore(lightprobe_average_texture, spos, average); - } - -#endif -} diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl deleted file mode 100644 index dd35ae3b7308..000000000000 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl +++ /dev/null @@ -1,1056 +0,0 @@ -#[compute] - -#version 450 - -#VERSION_DEFINES - -#ifdef MODE_JUMPFLOOD_OPTIMIZED -#define GROUP_SIZE 8 - -layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = GROUP_SIZE) in; - -#elif defined(MODE_OCCLUSION) || defined(MODE_SCROLL) -//buffer layout -layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; - -#else -//grid layout -layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; - -#endif - -#if defined(MODE_INITIALIZE_JUMP_FLOOD) || defined(MODE_INITIALIZE_JUMP_FLOOD_HALF) -layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color; -layout(rgba8ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_positions; -#endif - -#ifdef MODE_UPSCALE_JUMP_FLOOD -layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color; -layout(rgba8ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_positions_half; -layout(rgba8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D dst_positions; -#endif - -#if defined(MODE_JUMPFLOOD) || defined(MODE_JUMPFLOOD_OPTIMIZED) -layout(rgba8ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_positions; -layout(rgba8ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_positions; -#endif - -#ifdef MODE_JUMPFLOOD_OPTIMIZED - -shared uvec4 group_positions[(GROUP_SIZE + 2) * (GROUP_SIZE + 2) * (GROUP_SIZE + 2)]; //4x4x4 with margins - -void group_store(ivec3 p_pos, uvec4 p_value) { - uint offset = uint(p_pos.z * (GROUP_SIZE + 2) * (GROUP_SIZE + 2) + p_pos.y * (GROUP_SIZE + 2) + p_pos.x); - group_positions[offset] = p_value; -} - -uvec4 group_load(ivec3 p_pos) { - uint offset = uint(p_pos.z * (GROUP_SIZE + 2) * (GROUP_SIZE + 2) + p_pos.y * (GROUP_SIZE + 2) + p_pos.x); - return group_positions[offset]; -} - -#endif - -#ifdef MODE_OCCLUSION - -layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color; -layout(r8, set = 0, binding = 2) uniform restrict image3D dst_occlusion[8]; -layout(r32ui, set = 0, binding = 3) uniform restrict readonly uimage3D src_facing; - -const uvec2 group_size_offset[11] = uvec2[](uvec2(1, 0), uvec2(3, 1), uvec2(6, 4), uvec2(10, 10), uvec2(15, 20), uvec2(21, 35), uvec2(28, 56), uvec2(36, 84), uvec2(42, 120), uvec2(46, 162), uvec2(48, 208)); -const uint group_pos[256] = uint[](0, - 65536, 256, 1, - 131072, 65792, 512, 65537, 257, 2, - 196608, 131328, 66048, 768, 131073, 65793, 513, 65538, 258, 3, - 262144, 196864, 131584, 66304, 1024, 196609, 131329, 66049, 769, 131074, 65794, 514, 65539, 259, 4, - 327680, 262400, 197120, 131840, 66560, 1280, 262145, 196865, 131585, 66305, 1025, 196610, 131330, 66050, 770, 131075, 65795, 515, 65540, 260, 5, - 393216, 327936, 262656, 197376, 132096, 66816, 1536, 327681, 262401, 197121, 131841, 66561, 1281, 262146, 196866, 131586, 66306, 1026, 196611, 131331, 66051, 771, 131076, 65796, 516, 65541, 261, 6, - 458752, 393472, 328192, 262912, 197632, 132352, 67072, 1792, 393217, 327937, 262657, 197377, 132097, 66817, 1537, 327682, 262402, 197122, 131842, 66562, 1282, 262147, 196867, 131587, 66307, 1027, 196612, 131332, 66052, 772, 131077, 65797, 517, 65542, 262, 7, - 459008, 393728, 328448, 263168, 197888, 132608, 67328, 458753, 393473, 328193, 262913, 197633, 132353, 67073, 1793, 393218, 327938, 262658, 197378, 132098, 66818, 1538, 327683, 262403, 197123, 131843, 66563, 1283, 262148, 196868, 131588, 66308, 1028, 196613, 131333, 66053, 773, 131078, 65798, 518, 65543, 263, - 459264, 393984, 328704, 263424, 198144, 132864, 459009, 393729, 328449, 263169, 197889, 132609, 67329, 458754, 393474, 328194, 262914, 197634, 132354, 67074, 1794, 393219, 327939, 262659, 197379, 132099, 66819, 1539, 327684, 262404, 197124, 131844, 66564, 1284, 262149, 196869, 131589, 66309, 1029, 196614, 131334, 66054, 774, 131079, 65799, 519, - 459520, 394240, 328960, 263680, 198400, 459265, 393985, 328705, 263425, 198145, 132865, 459010, 393730, 328450, 263170, 197890, 132610, 67330, 458755, 393475, 328195, 262915, 197635, 132355, 67075, 1795, 393220, 327940, 262660, 197380, 132100, 66820, 1540, 327685, 262405, 197125, 131845, 66565, 1285, 262150, 196870, 131590, 66310, 1030, 196615, 131335, 66055, 775); - -shared uint occlusion_facing[((OCCLUSION_SIZE * 2) * (OCCLUSION_SIZE * 2) * (OCCLUSION_SIZE * 2)) / 4]; - -uint get_facing(ivec3 p_pos) { - uint ofs = uint(p_pos.z * OCCLUSION_SIZE * 2 * OCCLUSION_SIZE * 2 + p_pos.y * OCCLUSION_SIZE * 2 + p_pos.x); - uint v = occlusion_facing[ofs / 4]; - return (v >> ((ofs % 4) * 8)) & 0xFF; -} - -#endif - -#ifdef MODE_STORE - -layout(rgba8ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_positions; -layout(r16ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_albedo; -layout(r8, set = 0, binding = 3) uniform restrict readonly image3D src_occlusion[8]; -layout(r32ui, set = 0, binding = 4) uniform restrict readonly uimage3D src_light; -layout(r32ui, set = 0, binding = 5) uniform restrict readonly uimage3D src_light_aniso; -layout(r32ui, set = 0, binding = 6) uniform restrict readonly uimage3D src_facing; - -layout(r8, set = 0, binding = 7) uniform restrict writeonly image3D dst_sdf; -layout(r16ui, set = 0, binding = 8) uniform restrict writeonly uimage3D dst_occlusion; - -layout(set = 0, binding = 10, std430) restrict buffer DispatchData { - uint x; - uint y; - uint z; - uint total_count; -} -dispatch_data; - -struct ProcessVoxel { - uint position; // xyz 7 bit packed, extra 11 bits for neighbors. - uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbors - uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbors - uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbors - //total neighbors: 26 -}; - -layout(set = 0, binding = 11, std430) restrict buffer writeonly ProcessVoxels { - ProcessVoxel data[]; -} -dst_process_voxels; - -shared ProcessVoxel store_positions[4 * 4 * 4]; -shared uint store_position_count; -shared uint store_from_index; -#endif - -#ifdef MODE_SCROLL - -layout(r16ui, set = 0, binding = 1) uniform restrict writeonly uimage3D dst_albedo; -layout(r32ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_facing; -layout(r32ui, set = 0, binding = 3) uniform restrict writeonly uimage3D dst_light; -layout(r32ui, set = 0, binding = 4) uniform restrict writeonly uimage3D dst_light_aniso; - -layout(set = 0, binding = 5, std430) restrict buffer readonly DispatchData { - uint x; - uint y; - uint z; - uint total_count; -} -dispatch_data; - -struct ProcessVoxel { - uint position; // xyz 7 bit packed, extra 11 bits for neighbors. - uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbors - uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbors - uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbors - //total neighbors: 26 -}; - -layout(set = 0, binding = 6, std430) restrict buffer readonly ProcessVoxels { - ProcessVoxel data[]; -} -src_process_voxels; - -#endif - -#ifdef MODE_SCROLL_OCCLUSION - -layout(r8, set = 0, binding = 1) uniform restrict image3D dst_occlusion[8]; -layout(r16ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_occlusion; - -#endif - -layout(push_constant, std430) uniform Params { - ivec3 scroll; - - int grid_size; - - ivec3 probe_offset; - int step_size; - - bool half_size; - uint occlusion_index; - int cascade; - uint pad; -} -params; - -void main() { -#ifdef MODE_SCROLL - - // Pixel being shaded - int index = int(gl_GlobalInvocationID.x); - if (index >= dispatch_data.total_count) { //too big - return; - } - - ivec3 read_pos = (ivec3(src_process_voxels.data[index].position) >> ivec3(0, 7, 14)) & ivec3(0x7F); - ivec3 write_pos = read_pos + params.scroll; - - if (any(lessThan(write_pos, ivec3(0))) || any(greaterThanEqual(write_pos, ivec3(params.grid_size)))) { - return; // Fits outside the 3D texture, don't do anything. - } - - uint albedo = ((src_process_voxels.data[index].albedo & 0x7FFF) << 1) | 1; //add solid bit - imageStore(dst_albedo, write_pos, uvec4(albedo)); - - uint facing = (src_process_voxels.data[index].albedo >> 15) & 0x3F; //6 anisotropic facing bits - imageStore(dst_facing, write_pos, uvec4(facing)); - - uint light = src_process_voxels.data[index].light & 0x3fffffff; //30 bits of RGBE8985 - imageStore(dst_light, write_pos, uvec4(light)); - - uint light_aniso = src_process_voxels.data[index].light_aniso & 0x3fffffff; //30 bits of 6 anisotropic 5 bits values - imageStore(dst_light_aniso, write_pos, uvec4(light_aniso)); - -#endif - -#ifdef MODE_SCROLL_OCCLUSION - - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - if (any(greaterThanEqual(pos, ivec3(params.grid_size) - abs(params.scroll)))) { //too large, do nothing - return; - } - - ivec3 read_pos = pos + max(ivec3(0), -params.scroll); - ivec3 write_pos = pos + max(ivec3(0), params.scroll); - - read_pos.z += params.cascade * params.grid_size; - uint occlusion = imageLoad(src_occlusion, read_pos).r; - read_pos.x += params.grid_size; - occlusion |= imageLoad(src_occlusion, read_pos).r << 16; - - const uint occlusion_shift[8] = uint[](12, 8, 4, 0, 28, 24, 20, 16); - - for (uint i = 0; i < 8; i++) { - float o = float((occlusion >> occlusion_shift[i]) & 0xF) / 15.0; - imageStore(dst_occlusion[i], write_pos, vec4(o)); - } - -#endif - -#ifdef MODE_INITIALIZE_JUMP_FLOOD - - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - - uint c = imageLoad(src_color, pos).r; - uvec4 v; - if (bool(c & 0x1)) { - //bit set means this is solid - v.xyz = uvec3(pos); - v.w = 255; //not zero means used - } else { - v.xyz = uvec3(0); - v.w = 0; // zero means unused - } - - imageStore(dst_positions, pos, v); -#endif - -#ifdef MODE_INITIALIZE_JUMP_FLOOD_HALF - - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - ivec3 base_pos = pos * 2; - - //since we store in half size, lets kind of randomize what we store, so - //the half size jump flood has a bit better chance to find something - uvec4 closest[8]; - int closest_count = 0; - - for (uint i = 0; i < 8; i++) { - ivec3 src_pos = base_pos + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - uint c = imageLoad(src_color, src_pos).r; - if (bool(c & 1)) { - uvec4 v = uvec4(uvec3(src_pos), 255); - closest[closest_count] = v; - closest_count++; - } - } - - if (closest_count == 0) { - imageStore(dst_positions, pos, uvec4(0)); - } else { - ivec3 indexv = (pos & ivec3(1, 1, 1)) * ivec3(1, 2, 4); - int index = (indexv.x | indexv.y | indexv.z) % closest_count; - imageStore(dst_positions, pos, closest[index]); - } - -#endif - -#ifdef MODE_JUMPFLOOD - - //regular jumpflood, efficient for large steps, inefficient for small steps - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - - vec3 posf = vec3(pos); - - if (params.half_size) { - posf = posf * 2.0 + 0.5; - } - - uvec4 p = imageLoad(src_positions, pos); - - if (!params.half_size && p == uvec4(uvec3(pos), 255)) { - imageStore(dst_positions, pos, p); - return; //points to itself and valid, nothing better can be done, just pass - } - - float p_dist; - - if (p.w != 0) { - p_dist = distance(posf, vec3(p.xyz)); - } else { - p_dist = 0.0; //should not matter - } - - const uint offset_count = 26; - const ivec3 offsets[offset_count] = ivec3[]( - ivec3(-1, -1, -1), - ivec3(-1, -1, 0), - ivec3(-1, -1, 1), - ivec3(-1, 0, -1), - ivec3(-1, 0, 0), - ivec3(-1, 0, 1), - ivec3(-1, 1, -1), - ivec3(-1, 1, 0), - ivec3(-1, 1, 1), - ivec3(0, -1, -1), - ivec3(0, -1, 0), - ivec3(0, -1, 1), - ivec3(0, 0, -1), - ivec3(0, 0, 1), - ivec3(0, 1, -1), - ivec3(0, 1, 0), - ivec3(0, 1, 1), - ivec3(1, -1, -1), - ivec3(1, -1, 0), - ivec3(1, -1, 1), - ivec3(1, 0, -1), - ivec3(1, 0, 0), - ivec3(1, 0, 1), - ivec3(1, 1, -1), - ivec3(1, 1, 0), - ivec3(1, 1, 1)); - - for (uint i = 0; i < offset_count; i++) { - ivec3 ofs = pos + offsets[i] * params.step_size; - if (any(lessThan(ofs, ivec3(0))) || any(greaterThanEqual(ofs, ivec3(params.grid_size)))) { - continue; - } - uvec4 q = imageLoad(src_positions, ofs); - - if (q.w == 0) { - continue; //was not initialized yet, ignore - } - - float q_dist = distance(posf, vec3(q.xyz)); - if (p.w == 0 || q_dist < p_dist) { - p = q; //just replace because current is unused - p_dist = q_dist; - } - } - - imageStore(dst_positions, pos, p); -#endif - -#ifdef MODE_JUMPFLOOD_OPTIMIZED - //optimized version using shared compute memory - - ivec3 group_offset = ivec3(gl_WorkGroupID.xyz) % params.step_size; - ivec3 group_pos = group_offset + (ivec3(gl_WorkGroupID.xyz) / params.step_size) * ivec3(GROUP_SIZE * params.step_size); - - //load data into local group memory - - if (all(lessThan(ivec3(gl_LocalInvocationID.xyz), ivec3((GROUP_SIZE + 2) / 2)))) { - //use this thread for loading, this method uses less threads for this but its simpler and less divergent - ivec3 base_pos = ivec3(gl_LocalInvocationID.xyz) * 2; - for (uint i = 0; i < 8; i++) { - ivec3 load_pos = base_pos + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - ivec3 load_global_pos = group_pos + (load_pos - ivec3(1)) * params.step_size; - uvec4 q; - if (all(greaterThanEqual(load_global_pos, ivec3(0))) && all(lessThan(load_global_pos, ivec3(params.grid_size)))) { - q = imageLoad(src_positions, load_global_pos); - } else { - q = uvec4(0); //unused - } - - group_store(load_pos, q); - } - } - - ivec3 global_pos = group_pos + ivec3(gl_LocalInvocationID.xyz) * params.step_size; - - if (any(lessThan(global_pos, ivec3(0))) || any(greaterThanEqual(global_pos, ivec3(params.grid_size)))) { - return; //do nothing else, end here because outside range - } - - //sync - groupMemoryBarrier(); - barrier(); - - ivec3 local_pos = ivec3(gl_LocalInvocationID.xyz) + ivec3(1); - - const uint offset_count = 27; - const ivec3 offsets[offset_count] = ivec3[]( - ivec3(-1, -1, -1), - ivec3(-1, -1, 0), - ivec3(-1, -1, 1), - ivec3(-1, 0, -1), - ivec3(-1, 0, 0), - ivec3(-1, 0, 1), - ivec3(-1, 1, -1), - ivec3(-1, 1, 0), - ivec3(-1, 1, 1), - ivec3(0, -1, -1), - ivec3(0, -1, 0), - ivec3(0, -1, 1), - ivec3(0, 0, -1), - ivec3(0, 0, 0), - ivec3(0, 0, 1), - ivec3(0, 1, -1), - ivec3(0, 1, 0), - ivec3(0, 1, 1), - ivec3(1, -1, -1), - ivec3(1, -1, 0), - ivec3(1, -1, 1), - ivec3(1, 0, -1), - ivec3(1, 0, 0), - ivec3(1, 0, 1), - ivec3(1, 1, -1), - ivec3(1, 1, 0), - ivec3(1, 1, 1)); - - //only makes sense if point is inside screen - uvec4 closest = uvec4(0); - float closest_dist = 0.0; - - vec3 posf = vec3(global_pos); - - if (params.half_size) { - posf = posf * 2.0 + 0.5; - } - - for (uint i = 0; i < offset_count; i++) { - uvec4 point = group_load(local_pos + offsets[i]); - - if (point.w == 0) { - continue; //was not initialized yet, ignore - } - - float dist = distance(posf, vec3(point.xyz)); - if (closest.w == 0 || dist < closest_dist) { - closest = point; - closest_dist = dist; - } - } - - imageStore(dst_positions, global_pos, closest); - -#endif - -#ifdef MODE_UPSCALE_JUMP_FLOOD - - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - - uint c = imageLoad(src_color, pos).r; - uvec4 v; - if (bool(c & 1)) { - //bit set means this is solid - v.xyz = uvec3(pos); - v.w = 255; //not zero means used - } else { - v = imageLoad(src_positions_half, pos >> 1); - float d = length(vec3(ivec3(v.xyz) - pos)); - - ivec3 vbase = ivec3(v.xyz - (v.xyz & uvec3(1))); - - //search around if there is a better candidate from the same block - for (int i = 0; i < 8; i++) { - ivec3 bits = ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - ivec3 p = vbase + bits; - - float d2 = length(vec3(p - pos)); - if (d2 < d) { //check valid distance before test so we avoid a read - uint c2 = imageLoad(src_color, p).r; - if (bool(c2 & 1)) { - v.xyz = uvec3(p); - d = d2; - } - } - } - - //could validate better position.. - } - - imageStore(dst_positions, pos, v); - -#endif - -#ifdef MODE_OCCLUSION - - uint invocation_idx = uint(gl_LocalInvocationID.x); - ivec3 region = ivec3(gl_WorkGroupID); - - ivec3 region_offset = -ivec3(OCCLUSION_SIZE); - region_offset += region * OCCLUSION_SIZE * 2; - region_offset += params.probe_offset * OCCLUSION_SIZE; - - if (params.scroll != ivec3(0)) { - //validate scroll region - ivec3 region_offset_to = region_offset + ivec3(OCCLUSION_SIZE * 2); - uvec3 scroll_mask = uvec3(notEqual(params.scroll, ivec3(0))); //save which axes acre scrolling - ivec3 scroll_from = mix(ivec3(0), ivec3(params.grid_size) + params.scroll, lessThan(params.scroll, ivec3(0))); - ivec3 scroll_to = mix(ivec3(params.grid_size), params.scroll, greaterThan(params.scroll, ivec3(0))); - - if ((uvec3(lessThanEqual(region_offset_to, scroll_from)) | uvec3(greaterThanEqual(region_offset, scroll_to))) * scroll_mask == scroll_mask) { //all axes that scroll are out, exit - return; //region outside scroll bounds, quit - } - } - -#define OCC_HALF_SIZE (OCCLUSION_SIZE / 2) - - ivec3 local_ofs = ivec3(uvec3(invocation_idx % OCC_HALF_SIZE, (invocation_idx % (OCC_HALF_SIZE * OCC_HALF_SIZE)) / OCC_HALF_SIZE, invocation_idx / (OCC_HALF_SIZE * OCC_HALF_SIZE))) * 4; - - /* for(int i=0;i<64;i++) { - ivec3 offset = region_offset + local_ofs + ((ivec3(i) >> ivec3(0,2,4)) & ivec3(3,3,3)); - uint facig = - if (all(greaterThanEqual(offset,ivec3(0))) && all(lessThan(offset,ivec3(params.grid_size)))) {*/ - - for (int i = 0; i < 16; i++) { //skip x, so it can be packed - - ivec3 offset = local_ofs + ((ivec3(i * 4) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3)); - - uint facing_pack = 0; - for (int j = 0; j < 4; j++) { - ivec3 foffset = region_offset + offset + ivec3(j, 0, 0); - if (all(greaterThanEqual(foffset, ivec3(0))) && all(lessThan(foffset, ivec3(params.grid_size)))) { - uint f = imageLoad(src_facing, foffset).r; - facing_pack |= f << (j * 8); - } - } - - occlusion_facing[(offset.z * (OCCLUSION_SIZE * 2 * OCCLUSION_SIZE * 2) + offset.y * (OCCLUSION_SIZE * 2) + offset.x) / 4] = facing_pack; - } - - //sync occlusion saved - groupMemoryBarrier(); - barrier(); - - //process occlusion - -#define OCC_STEPS (OCCLUSION_SIZE * 3 - 2) -#define OCC_HALF_STEPS (OCC_STEPS / 2) - - for (int step = 0; step < OCC_STEPS; step++) { - bool shrink = step >= OCC_HALF_STEPS; - int occ_step = shrink ? OCC_HALF_STEPS - (step - OCC_HALF_STEPS) - 1 : step; - - if (invocation_idx < group_size_offset[occ_step].x) { - uint pv = group_pos[group_size_offset[occ_step].y + invocation_idx]; - ivec3 proc_abs = (ivec3(int(pv)) >> ivec3(0, 8, 16)) & ivec3(0xFF); - - if (shrink) { - proc_abs = ivec3(OCCLUSION_SIZE) - proc_abs - ivec3(1); - } - - for (int i = 0; i < 8; i++) { - ivec3 bits = ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - ivec3 proc_sign = bits * 2 - 1; - ivec3 local_offset = ivec3(OCCLUSION_SIZE) + proc_abs * proc_sign - (ivec3(1) - bits); - ivec3 offset = local_offset + region_offset; - if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) { - float occ; - - uint facing = get_facing(local_offset); - - if (facing != 0) { //solid - occ = 0.0; - } else if (step == 0) { -#if 0 - occ = 0.0; - if (get_facing(local_offset - ivec3(proc_sign.x,0,0))==0) { - occ+=1.0; - } - if (get_facing(local_offset - ivec3(0,proc_sign.y,0))==0) { - occ+=1.0; - } - if (get_facing(local_offset - ivec3(0,0,proc_sign.z))==0) { - occ+=1.0; - } - /* - if (get_facing(local_offset - proc_sign)==0) { - occ+=1.0; - }*/ - - occ/=3.0; -#endif - occ = 1.0; - - } else { - ivec3 read_dir = -proc_sign; - - ivec3 major_axis; - if (proc_abs.x < proc_abs.y) { - if (proc_abs.z < proc_abs.y) { - major_axis = ivec3(0, 1, 0); - } else { - major_axis = ivec3(0, 0, 1); - } - } else { - if (proc_abs.z < proc_abs.x) { - major_axis = ivec3(1, 0, 0); - } else { - major_axis = ivec3(0, 0, 1); - } - } - - float avg = 0.0; - occ = 0.0; - - ivec3 read_x = offset + ivec3(read_dir.x, 0, 0) + (proc_abs.x == 0 ? major_axis * read_dir : ivec3(0)); - ivec3 read_y = offset + ivec3(0, read_dir.y, 0) + (proc_abs.y == 0 ? major_axis * read_dir : ivec3(0)); - ivec3 read_z = offset + ivec3(0, 0, read_dir.z) + (proc_abs.z == 0 ? major_axis * read_dir : ivec3(0)); - - uint facing_x = get_facing(read_x - region_offset); - if (facing_x == 0) { - if (all(greaterThanEqual(read_x, ivec3(0))) && all(lessThan(read_x, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_x).r; - avg += 1.0; - } - } else { - if (proc_abs.x != 0) { //do not occlude from voxels in the opposite octant - avg += 1.0; - } - } - - uint facing_y = get_facing(read_y - region_offset); - if (facing_y == 0) { - if (all(greaterThanEqual(read_y, ivec3(0))) && all(lessThan(read_y, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_y).r; - avg += 1.0; - } - } else { - if (proc_abs.y != 0) { - avg += 1.0; - } - } - - uint facing_z = get_facing(read_z - region_offset); - if (facing_z == 0) { - if (all(greaterThanEqual(read_z, ivec3(0))) && all(lessThan(read_z, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_z).r; - avg += 1.0; - } - } else { - if (proc_abs.z != 0) { - avg += 1.0; - } - } - - if (avg > 0.0) { - occ /= avg; - } - } - - imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ)); - } - } - } - - groupMemoryBarrier(); - barrier(); - } -#if 1 - //bias solid voxels away - - for (int i = 0; i < 64; i++) { - ivec3 local_offset = local_ofs + ((ivec3(i) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3)); - ivec3 offset = region_offset + local_offset; - - if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) { - uint facing = get_facing(local_offset); - - if (facing != 0) { - //only work on solids - - ivec3 proc_pos = local_offset - ivec3(OCCLUSION_SIZE); - proc_pos += mix(ivec3(0), ivec3(1), greaterThanEqual(proc_pos, ivec3(0))); - - float avg = 0.0; - float occ = 0.0; - - ivec3 read_dir = -sign(proc_pos); - ivec3 read_dir_x = ivec3(read_dir.x, 0, 0); - ivec3 read_dir_y = ivec3(0, read_dir.y, 0); - ivec3 read_dir_z = ivec3(0, 0, read_dir.z); - //solid -#if 0 - - uvec3 facing_pos_base = (uvec3(facing) >> uvec3(0,1,2)) & uvec3(1,1,1); - uvec3 facing_neg_base = (uvec3(facing) >> uvec3(3,4,5)) & uvec3(1,1,1); - uvec3 facing_pos= facing_pos_base &((~facing_neg_base)&uvec3(1,1,1)); - uvec3 facing_neg= facing_neg_base &((~facing_pos_base)&uvec3(1,1,1)); -#else - uvec3 facing_pos = (uvec3(facing) >> uvec3(0, 1, 2)) & uvec3(1, 1, 1); - uvec3 facing_neg = (uvec3(facing) >> uvec3(3, 4, 5)) & uvec3(1, 1, 1); -#endif - bvec3 read_valid = bvec3(mix(facing_neg, facing_pos, greaterThan(read_dir, ivec3(0)))); - - //sides - if (read_valid.x) { - ivec3 read_offset = local_offset + read_dir_x; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - if (read_valid.y) { - ivec3 read_offset = local_offset + read_dir_y; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - if (read_valid.z) { - ivec3 read_offset = local_offset + read_dir_z; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - //adjacents - - if (all(read_valid.yz)) { - ivec3 read_offset = local_offset + read_dir_y + read_dir_z; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - if (all(read_valid.xz)) { - ivec3 read_offset = local_offset + read_dir_x + read_dir_z; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - if (all(read_valid.xy)) { - ivec3 read_offset = local_offset + read_dir_x + read_dir_y; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - //diagonal - - if (all(read_valid)) { - ivec3 read_offset = local_offset + read_dir; - uint f = get_facing(read_offset); - if (f == 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r; - avg += 1.0; - } - } - } - - if (avg > 0.0) { - occ /= avg; - } - - imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ)); - } - } - } - -#endif - -#if 1 - groupMemoryBarrier(); - barrier(); - - for (int i = 0; i < 64; i++) { - ivec3 local_offset = local_ofs + ((ivec3(i) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3)); - ivec3 offset = region_offset + local_offset; - - if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) { - uint facing = get_facing(local_offset); - - if (facing == 0) { - ivec3 proc_pos = local_offset - ivec3(OCCLUSION_SIZE); - proc_pos += mix(ivec3(0), ivec3(1), greaterThanEqual(proc_pos, ivec3(0))); - - ivec3 proc_abs = abs(proc_pos); - - ivec3 read_dir = sign(proc_pos); //opposite direction - ivec3 read_dir_x = ivec3(read_dir.x, 0, 0); - ivec3 read_dir_y = ivec3(0, read_dir.y, 0); - ivec3 read_dir_z = ivec3(0, 0, read_dir.z); - //solid - uvec3 read_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), greaterThan(read_dir, ivec3(0))); //match positive with negative normals - uvec3 block_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), lessThan(read_dir, ivec3(0))); //match positive with negative normals - - block_mask = uvec3(0); - - float visible = 0.0; - float occlude_total = 0.0; - - if (proc_abs.x < OCCLUSION_SIZE) { - ivec3 read_offset = local_offset + read_dir_x; - uint x_mask = get_facing(read_offset); - if (x_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(x_mask & read_mask.x) && !bool(x_mask & block_mask.x)) { - visible += 1.0; - } - } - } - } - - if (proc_abs.y < OCCLUSION_SIZE) { - ivec3 read_offset = local_offset + read_dir_y; - uint y_mask = get_facing(read_offset); - if (y_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(y_mask & read_mask.y) && !bool(y_mask & block_mask.y)) { - visible += 1.0; - } - } - } - } - - if (proc_abs.z < OCCLUSION_SIZE) { - ivec3 read_offset = local_offset + read_dir_z; - uint z_mask = get_facing(read_offset); - if (z_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(z_mask & read_mask.z) && !bool(z_mask & block_mask.z)) { - visible += 1.0; - } - } - } - } - - //if near the cartesian plane, test in opposite direction too - - read_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), lessThan(read_dir, ivec3(0))); //match negative with positive normals - block_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), greaterThan(read_dir, ivec3(0))); //match negative with positive normals - block_mask = uvec3(0); - - if (proc_abs.x == 1) { - ivec3 read_offset = local_offset - read_dir_x; - uint x_mask = get_facing(read_offset); - if (x_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(x_mask & read_mask.x) && !bool(x_mask & block_mask.x)) { - visible += 1.0; - } - } - } - } - - if (proc_abs.y == 1) { - ivec3 read_offset = local_offset - read_dir_y; - uint y_mask = get_facing(read_offset); - if (y_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(y_mask & read_mask.y) && !bool(y_mask & block_mask.y)) { - visible += 1.0; - } - } - } - } - - if (proc_abs.z == 1) { - ivec3 read_offset = local_offset - read_dir_z; - uint z_mask = get_facing(read_offset); - if (z_mask != 0) { - read_offset += region_offset; - if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) { - occlude_total += 1.0; - if (bool(z_mask & read_mask.z) && !bool(z_mask & block_mask.z)) { - visible += 1.0; - } - } - } - } - - if (occlude_total > 0.0) { - float occ = imageLoad(dst_occlusion[params.occlusion_index], offset).r; - occ *= visible / occlude_total; - imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ)); - } - } - } - } - -#endif - - /* - for(int i=0;i<8;i++) { - ivec3 local_offset = local_pos + ((ivec3(i) >> ivec3(2,1,0)) & ivec3(1,1,1)) * OCCLUSION_SIZE; - ivec3 offset = local_offset - ivec3(OCCLUSION_SIZE); //looking around probe, so starts negative - offset += region * OCCLUSION_SIZE * 2; //offset by region - offset += params.probe_offset * OCCLUSION_SIZE; // offset by probe offset - if (all(greaterThanEqual(offset,ivec3(0))) && all(lessThan(offset,ivec3(params.grid_size)))) { - imageStore(dst_occlusion[params.occlusion_index],offset,vec4( occlusion_data[ to_linear(local_offset) ] )); - //imageStore(dst_occlusion[params.occlusion_index],offset,vec4( occlusion_solid[ to_linear(local_offset) ] )); - } - } -*/ - -#endif - -#ifdef MODE_STORE - - ivec3 local = ivec3(gl_LocalInvocationID.xyz); - ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); - // store SDF - uvec4 p = imageLoad(src_positions, pos); - - bool solid = false; - float d; - if (ivec3(p.xyz) == pos) { - //solid block - d = 0; - solid = true; - } else { - //distance block - d = 1.0 + length(vec3(p.xyz) - vec3(pos)); - } - - d /= 255.0; - - imageStore(dst_sdf, pos, vec4(d)); - - // STORE OCCLUSION - - uint occlusion = 0; - const uint occlusion_shift[8] = uint[](12, 8, 4, 0, 28, 24, 20, 16); - for (int i = 0; i < 8; i++) { - float occ = imageLoad(src_occlusion[i], pos).r; - occlusion |= uint(clamp(occ * 15.0, 0.0, 15.0)) << occlusion_shift[i]; - } - { - ivec3 occ_pos = pos; - occ_pos.z += params.cascade * params.grid_size; - imageStore(dst_occlusion, occ_pos, uvec4(occlusion & 0xFFFF)); - occ_pos.x += params.grid_size; - imageStore(dst_occlusion, occ_pos, uvec4(occlusion >> 16)); - } - - // STORE POSITIONS - - if (local == ivec3(0)) { - store_position_count = 0; //base one stores as zero, the others wait - } - - groupMemoryBarrier(); - barrier(); - - if (solid) { - uint index = atomicAdd(store_position_count, 1); - // At least do the conversion work in parallel - store_positions[index].position = uint(pos.x | (pos.y << 7) | (pos.z << 14)); - - //see around which voxels point to this one, add them to the list - uint bit_index = 0; - uint neighbour_bits = 0; - for (int i = -1; i <= 1; i++) { - for (int j = -1; j <= 1; j++) { - for (int k = -1; k <= 1; k++) { - if (i == 0 && j == 0 && k == 0) { - continue; - } - ivec3 npos = pos + ivec3(i, j, k); - if (all(greaterThanEqual(npos, ivec3(0))) && all(lessThan(npos, ivec3(params.grid_size)))) { - p = imageLoad(src_positions, npos); - if (ivec3(p.xyz) == pos) { - neighbour_bits |= (1 << bit_index); - } - } - bit_index++; - } - } - } - - uint rgb = imageLoad(src_albedo, pos).r; - uint facing = imageLoad(src_facing, pos).r; - - store_positions[index].albedo = rgb >> 1; //store as it comes (555) to avoid precision loss (and move away the alpha bit) - store_positions[index].albedo |= (facing & 0x3F) << 15; // store facing in bits 15-21 - - store_positions[index].albedo |= neighbour_bits << 21; //store lower 11 bits of neighbors with remaining albedo - store_positions[index].position |= (neighbour_bits >> 11) << 21; //store 11 bits more of neighbors with position - - store_positions[index].light = imageLoad(src_light, pos).r; - store_positions[index].light_aniso = imageLoad(src_light_aniso, pos).r; - //add neighbors - store_positions[index].light |= (neighbour_bits >> 22) << 30; //store 2 bits more of neighbors with light - store_positions[index].light_aniso |= (neighbour_bits >> 24) << 30; //store 2 bits more of neighbors with aniso - } - - groupMemoryBarrier(); - barrier(); - - // global increment only once per group, to reduce pressure - - if (local == ivec3(0) && store_position_count > 0) { - store_from_index = atomicAdd(dispatch_data.total_count, store_position_count); - uint group_count = (store_from_index + store_position_count - 1) / 64 + 1; - atomicMax(dispatch_data.x, group_count); - } - - groupMemoryBarrier(); - barrier(); - - uint read_index = uint(local.z * 4 * 4 + local.y * 4 + local.x); - uint write_index = store_from_index + read_index; - - if (read_index < store_position_count) { - dst_process_voxels.data[write_index] = store_positions[read_index]; - } - - if (pos == ivec3(0)) { - //this thread clears y and z - dispatch_data.y = 1; - dispatch_data.z = 1; - } -#endif -} diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl index d0cfe6a3b879..b565fb9e4f03 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl @@ -98,53 +98,46 @@ layout(set = 0, binding = 12) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_I layout(set = 0, binding = 13) uniform sampler linear_sampler_with_mipmaps; -#ifdef ENABLE_SDFGI +#ifdef ENABLE_HDDAGI -// SDFGI Integration on set 1 -#define SDFGI_MAX_CASCADES 8 +// HDDAGI Integration on set 1 +#define HDDAGI_MAX_CASCADES 8 -struct SDFVoxelGICascadeData { +struct ProbeCascadeData { vec3 position; float to_probe; - ivec3 probe_world_offset; + + ivec3 region_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; float exposure_normalization; + + uvec4 pad2; }; -layout(set = 1, binding = 0, std140) uniform SDFGI { - vec3 grid_size; - uint max_cascades; +layout(set = 1, binding = 0, std140) uniform HDDAGI { + ivec3 grid_size; + int max_cascades; - bool use_occlusion; - int probe_axis_size; - float probe_to_uvw; float normal_bias; - - vec3 lightprobe_tex_pixel_size; float energy; - - vec3 lightprobe_uv_offset; float y_mult; + float reflection_bias; - vec3 occlusion_clamp; - uint pad3; - - vec3 occlusion_renormalize; - uint pad4; + ivec3 probe_axis_size; + float esm_strength; - vec3 cascade_probe_size; - uint pad5; + uvec4 pad3; - SDFVoxelGICascadeData cascades[SDFGI_MAX_CASCADES]; + ProbeCascadeData cascades[HDDAGI_MAX_CASCADES]; } -sdfgi; - -layout(set = 1, binding = 1) uniform texture2DArray sdfgi_ambient_texture; +hddagi; -layout(set = 1, binding = 2) uniform texture3D sdfgi_occlusion_texture; +layout(set = 1, binding = 1) uniform texture2DArray hddagi_ambient_texture; +layout(set = 1, binding = 2) uniform texture3D hddagi_occlusion[2]; -#endif //SDFGI +#endif //HDDAGI layout(set = 0, binding = 14, std140) uniform Params { vec2 fog_frustum_size_begin; @@ -278,6 +271,24 @@ const vec3 halton_map[TEMPORAL_FRAMES] = vec3[]( // Higher values will make light in volumetric fog fade out sooner when it's occluded by shadow. const float INV_FOG_FADE = 10.0; +#ifdef ENABLE_HDDAGI + +#define PROBE_CELLS 8 + +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} + +ivec2 probe_to_tex(ivec3 local_probe, int p_cascade) { + ivec3 cell = modi(hddagi.cascades[p_cascade].region_world_offset + local_probe, hddagi.probe_axis_size); + return cell.xy + ivec2(0, cell.z * int(hddagi.probe_axis_size.y)); +} + +#endif + void main() { vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size); @@ -627,68 +638,75 @@ void main() { } } - //sdfgi -#ifdef ENABLE_SDFGI + //hddagi +#ifdef ENABLE_HDDAGI { - float blend = -1.0; vec3 ambient_total = vec3(0.0); - for (uint i = 0; i < sdfgi.max_cascades; i++) { - vec3 cascade_pos = (world_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe; + int cascade = HDDAGI_MAX_CASCADES; + vec3 cascade_pos; + for (int i = 0; i < hddagi.max_cascades; i++) { + cascade_pos = (world_pos - hddagi.cascades[i].position) * hddagi.cascades[i].to_cell; - if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) { + if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, vec3(hddagi.grid_size)))) { continue; //skip cascade } - vec3 base_pos = floor(cascade_pos); - ivec3 probe_base_pos = ivec3(base_pos); - - vec4 ambient_accum = vec4(0.0); + cascade = i; + break; + } - ivec3 tex_pos = ivec3(probe_base_pos.xy, int(i)); - tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size; + if (cascade < HDDAGI_MAX_CASCADES) { + ivec3 occ_pos = ivec3(cascade_pos); // faster and numerically safer to do this computation as ints + vec3 pos_fract = cascade_pos - vec3(occ_pos); + occ_pos = (occ_pos + hddagi.cascades[cascade].region_world_offset * PROBE_CELLS) & (hddagi.grid_size - ivec3(1)); + occ_pos.y += (hddagi.grid_size.y + 2) * cascade; + occ_pos += ivec3(1); + ivec3 occ_total_size = hddagi.grid_size + ivec3(2); + occ_total_size.y *= hddagi.max_cascades; + vec3 occ_posf = (vec3(occ_pos) + pos_fract) / vec3(occ_total_size); - for (uint j = 0; j < 8; j++) { - ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); - ivec3 probe_posi = probe_base_pos; - probe_posi += offset; + vec4 occ_0 = texture(sampler3D(hddagi_occlusion[0], linear_sampler), occ_posf); + vec4 occ_1 = texture(sampler3D(hddagi_occlusion[1], linear_sampler), occ_posf); - // Compute weight + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); - vec3 probe_pos = vec3(probe_posi); - vec3 probe_to_pos = cascade_pos - probe_pos; + ivec3 base_probe = ivec3(cascade_pos) / PROBE_CELLS; - vec3 trilinear = vec3(1.0) - abs(probe_to_pos); - float weight = trilinear.x * trilinear.y * trilinear.z; + vec4 ambient_accum = vec4(0.0); - // Compute lightprobe occlusion + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - if (sdfgi.use_occlusion) { - ivec3 occ_indexv = abs((sdfgi.cascades[i].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); - vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); + vec3 probe_pos = vec3(probe * PROBE_CELLS); + vec3 probe_to_pos = cascade_pos - probe_pos; - vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw; - occ_pos.z += float(i); - if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures - occ_pos.x += 1.0; - } + float weight = 1.0; - occ_pos *= sdfgi.occlusion_renormalize; - float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask); + ivec3 probe_occ = (hddagi.cascades[cascade].region_world_offset + probe) & ivec3(1); - weight *= max(occlusion, 0.01); + uint weight_index = 0; + if (probe_occ.x != 0) { + weight_index |= 1; + } + if (probe_occ.y != 0) { + weight_index |= 2; } + if (probe_occ.z != 0) { + weight_index |= 4; + } + + weight *= max(0.01, occ_weights[weight_index]); - // Compute ambient texture position + vec3 trilinear = vec3(1.0) - abs(probe_to_pos / float(PROBE_CELLS)); - ivec3 uvw = tex_pos; - uvw.xy += offset.xy; - uvw.x += offset.z * sdfgi.probe_axis_size; + weight *= trilinear.x * trilinear.y * trilinear.z; - vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb; + ivec2 tex_pos = probe_to_tex(probe, cascade); - ambient_accum.rgb += ambient * weight * sdfgi.cascades[i].exposure_normalization; + vec3 probe_light = texelFetch(sampler2DArray(hddagi_ambient_texture, linear_sampler), ivec3(tex_pos, cascade), 0).rgb; + ambient_accum.rgb += probe_light * weight * hddagi.cascades[i].exposure_normalization; ambient_accum.a += weight; } @@ -696,7 +714,6 @@ void main() { ambient_accum.rgb /= ambient_accum.a; } ambient_total = ambient_accum.rgb; - break; } total_light += ambient_total * params.gi_inject; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index ed6060a10bf3..53d5e56170d8 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1633,7 +1633,7 @@ void fragment_shader(in SceneData scene_data) { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); #ifndef MODE_UNSHADED - // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + // Used in regular draw pass and when drawing SDFs for HDDAGI and materials for VoxelGI. emission *= scene_data.emissive_exposure_normalization; #endif @@ -1908,76 +1908,19 @@ void fragment_shader(in SceneData scene_data) { #else - if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SDFGI)) { //has lightmap capture + if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_HDDAGI)) { //has lightmap capture //make vertex orientation the world one, but still align to camera - vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex; + vec3 cam_vertex = mat3(scene_data.inv_view_matrix) * vertex; vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normal; vec3 cam_reflection = mat3(scene_data.inv_view_matrix) * reflect(-view, normal); - //apply y-mult - cam_pos.y *= sdfgi.y_mult; - cam_normal.y *= sdfgi.y_mult; - cam_normal = normalize(cam_normal); - cam_reflection.y *= sdfgi.y_mult; - cam_normal = normalize(cam_normal); - cam_reflection = normalize(cam_reflection); - - vec4 light_accum = vec4(0.0); - float weight_accum = 0.0; - - vec4 light_blend_accum = vec4(0.0); - float weight_blend_accum = 0.0; - - float blend = -1.0; - - // helper constants, compute once - - uint cascade = 0xFFFFFFFF; - vec3 cascade_pos; - vec3 cascade_normal; - - for (uint i = 0; i < sdfgi.max_cascades; i++) { - cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe; - - if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) { - continue; //skip cascade - } - - cascade = i; - break; - } + vec4 ret_ambient; + vec4 ret_reflection; + hddagi_process(cam_vertex, cam_normal, cam_reflection, roughness, ret_ambient, ret_reflection); - if (cascade < SDFGI_MAX_CASCADES) { - bool use_specular = true; - float blend; - vec3 diffuse, specular; - sdfgi_process(cascade, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse, specular, blend); - - if (blend > 0.0) { - //blend - if (cascade == sdfgi.max_cascades - 1) { - diffuse = mix(diffuse, ambient_light, blend); - if (use_specular) { - specular = mix(specular, specular_light, blend); - } - } else { - vec3 diffuse2, specular2; - float blend2; - cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe; - sdfgi_process(cascade + 1, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse2, specular2, blend2); - diffuse = mix(diffuse, diffuse2, blend); - if (use_specular) { - specular = mix(specular, specular2, blend); - } - } - } - - ambient_light = diffuse; - if (use_specular) { - specular_light = specular; - } - } + ambient_light = mix(ambient_light, ret_ambient.rgb, ret_ambient.a); + specular_light = mix(specular_light, ret_reflection.rgb, ret_reflection.a); } if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances @@ -2017,47 +1960,71 @@ void fragment_shader(in SceneData scene_data) { if (!sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers - vec2 coord; + ivec2 coord = ivec2(gl_FragCoord.xy); + + if (implementation_data.gi_upscale) { + if (implementation_data.gi_upscale_shift > 0) { + coord -= coord & 1; + } + + ivec2 closest_coord = coord; - if (implementation_data.gi_upscale_for_msaa) { - vec2 base_coord = screen_uv; - vec2 closest_coord = base_coord; #ifdef USE_MULTIVIEW - float closest_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0)); + vec4 closest_nr = texelFetch(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), ivec3(coord, ViewIndex), 0); #else // USE_MULTIVIEW - float closest_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0)); + vec4 closest_nr = texelFetch(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), coord, 0); #endif // USE_MULTIVIEW + float closest_r = closest_nr.a; + bool dynamic_object = closest_r > 0.5; + if (dynamic_object) { + closest_r = 1.0 - closest_r; + } + closest_r /= (127.0 / 255.0); + + float closest_ang = dot(normal, normalize(closest_nr.xyz * 2.0 - 1.0)); + closest_ang -= abs(closest_r - roughness) * 0.5; + for (int i = 0; i < 4; i++) { - const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1)); - vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size; + const ivec2 neighbours[4] = ivec2[](ivec2(1, 0), ivec2(0, 1), ivec2(-1, 0), ivec2(0, -1)); + ivec2 neighbour_coord = coord + (neighbours[i] << implementation_data.gi_upscale_shift); #ifdef USE_MULTIVIEW - float neighbour_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0)); + closest_nr = texelFetch(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), ivec3(neighbour_coord, ViewIndex), 0); #else // USE_MULTIVIEW - float neighbour_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0)); + closest_nr = texelFetch(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0); #endif // USE_MULTIVIEW - if (neighbour_ang > closest_ang) { - closest_ang = neighbour_ang; + + float r = closest_nr.a; + dynamic_object = r > 0.5; + if (dynamic_object) { + r = 1.0 - r; + } + r /= (127.0 / 255.0); + + float ang = dot(normal, normalize(closest_nr.xyz * 2.0 - 1.0)); + ang -= abs(r - roughness) * 0.5; + + if (ang > closest_ang) { + closest_ang = ang; closest_coord = neighbour_coord; } } - coord = closest_coord; - - } else { - coord = screen_uv; + coord = closest_coord >> implementation_data.gi_upscale_shift; } #ifdef USE_MULTIVIEW - vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); - vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); + vec3 buffer_ambient = texelFetch(sampler2DArray(ambient_buffer, SAMPLER_LINEAR_CLAMP), ivec3(coord, ViewIndex), 0).rgb; + vec3 buffer_reflection = texelFetch(sampler2DArray(reflection_buffer, SAMPLER_LINEAR_CLAMP), ivec3(coord, ViewIndex), 0).rgb; + vec2 buffer_blend = texelFetch(sampler2DArray(ambient_reflection_blend_buffer, SAMPLER_LINEAR_CLAMP), ivec3(coord, ViewIndex), 0).rg; #else // USE_MULTIVIEW - vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0); - vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0); + vec3 buffer_ambient = texelFetch(sampler2D(ambient_buffer, SAMPLER_LINEAR_CLAMP), coord, 0).rgb; + vec3 buffer_reflection = texelFetch(sampler2D(reflection_buffer, SAMPLER_LINEAR_CLAMP), coord, 0).rgb; + vec2 buffer_blend = texelFetch(sampler2D(ambient_reflection_blend_buffer, SAMPLER_LINEAR_CLAMP), coord, 0).rg; #endif // USE_MULTIVIEW - ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a); - specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a); + ambient_light = mix(ambient_light, buffer_ambient, buffer_blend.r); + specular_light = mix(specular_light, buffer_reflection, buffer_blend.g); } #endif // !USE_LIGHTMAP @@ -2749,42 +2716,94 @@ void fragment_shader(in SceneData scene_data) { #ifdef MODE_RENDER_SDF { + // Compute geometric normal + vec3 ddx_vertex = dFdx(vertex); + vec3 ddy_vertex = dFdy(vertex); + vec3 geometric_normal = normalize(cross(ddx_vertex, ddy_vertex)); + + /* This optimization needs more benchmark, does not seem to make a difference. + if (abs(dot(vec3(0,0,1),geometric_normal)) < 0.55) { + // Conservative value to discard this fragment if not belonging to this view + discard; + }*/ + + /* This optimization breaks the image somehow, I have no idea why. + if (gl_HelperInvocation) { + return; + }*/ + + vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normalize(normal_interp); + vec3 cam_geom_normal = mat3(scene_data.inv_view_matrix) * normalize(geometric_normal); + if (gl_FrontFacing) { + cam_geom_normal = -cam_geom_normal; + } + vec3 local_pos = (implementation_data.sdf_to_bounds * vec4(vertex, 1.0)).xyz; - ivec3 grid_pos = implementation_data.sdf_offset + ivec3(local_pos * vec3(implementation_data.sdf_size)); + vec3 grid_pos = vec3(implementation_data.sdf_offset) + local_pos * vec3(implementation_data.sdf_size); + ivec3 igrid_pos = ivec3(grid_pos); - uint albedo16 = 0x1; //solid flag - albedo16 |= clamp(uint(albedo.r * 31.0), 0, 31) << 11; - albedo16 |= clamp(uint(albedo.g * 31.0), 0, 31) << 6; - albedo16 |= clamp(uint(albedo.b * 31.0), 0, 31) << 1; + // Compute solid bits - imageStore(albedo_volume_grid, grid_pos, uvec4(albedo16)); + // Compute normal bits. + // Lower 6 are inclusive (depending on axis vector. - uint facing_bits = 0; - const vec3 aniso_dir[6] = vec3[]( - vec3(1, 0, 0), - vec3(0, 1, 0), - vec3(0, 0, 1), - vec3(-1, 0, 0), - vec3(0, -1, 0), - vec3(0, 0, -1)); + // upper 26 are exclusive (With some margin), used to save something closer to the normal. - vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normalize(normal_interp); + const int facing_direction_count = 26; + const vec3 facing_directions[26] = vec3[](vec3(-1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(0.0, 0.0, 1.0), vec3(-0.5773502691896258, -0.5773502691896258, -0.5773502691896258), vec3(-0.7071067811865475, -0.7071067811865475, 0.0), vec3(-0.5773502691896258, -0.5773502691896258, 0.5773502691896258), vec3(-0.7071067811865475, 0.0, -0.7071067811865475), vec3(-0.7071067811865475, 0.0, 0.7071067811865475), vec3(-0.5773502691896258, 0.5773502691896258, -0.5773502691896258), vec3(-0.7071067811865475, 0.7071067811865475, 0.0), vec3(-0.5773502691896258, 0.5773502691896258, 0.5773502691896258), vec3(0.0, -0.7071067811865475, -0.7071067811865475), vec3(0.0, -0.7071067811865475, 0.7071067811865475), vec3(0.0, 0.7071067811865475, -0.7071067811865475), vec3(0.0, 0.7071067811865475, 0.7071067811865475), vec3(0.5773502691896258, -0.5773502691896258, -0.5773502691896258), vec3(0.7071067811865475, -0.7071067811865475, 0.0), vec3(0.5773502691896258, -0.5773502691896258, 0.5773502691896258), vec3(0.7071067811865475, 0.0, -0.7071067811865475), vec3(0.7071067811865475, 0.0, 0.7071067811865475), vec3(0.5773502691896258, 0.5773502691896258, -0.5773502691896258), vec3(0.7071067811865475, 0.7071067811865475, 0.0), vec3(0.5773502691896258, 0.5773502691896258, 0.5773502691896258)); - float closest_dist = -1e20; + uint bit_normal = 0; - for (uint i = 0; i < 6; i++) { - float d = dot(cam_normal, aniso_dir[i]); - if (d > closest_dist) { - closest_dist = d; - facing_bits = (1 << i); + //const float exclusive_threshold = 0.86; // given min cos is 0.70710676908493 + const float exclusive_threshold = 0.7; // given min cos is 0.70710676908493 + const float inclusive_threshold = 0.001; + + for (int i = 0; i < facing_direction_count; i++) { + float dp = dot(cam_geom_normal, facing_directions[i]); + + if (i < 6 && dp > inclusive_threshold) { + bit_normal |= uint(1 << i); + } + + if (dp > exclusive_threshold) { + bit_normal |= uint(1 << (i + 6)); } } + // Subgroup merge and store normal bits + + { #ifdef MOLTENVK_USED - imageStore(geom_facing_grid, grid_pos, uvec4(imageLoad(geom_facing_grid, grid_pos).r | facing_bits)); //store facing bits + imageStore(geom_normal_bits, igrid_pos, uvec4(imageLoad(geom_normal_bits, igrid_pos).r | (1 << bit_ofs))); //store solid bits #else - imageAtomicOr(geom_facing_grid, grid_pos, facing_bits); //store facing bits + imageAtomicOr(geom_normal_bits, igrid_pos, bit_normal); //store solid bits #endif + } + + // Compute aniso albedo + + const vec3 aniso_dir[6] = vec3[]( + vec3(-1, 0, 0), + vec3(1, 0, 0), + vec3(0, -1, 0), + vec3(0, 1, 0), + vec3(0, 0, -1), + vec3(0, 0, 1)); + + for (int i = 0; i < 6; i++) { + float d = dot(cam_normal, aniso_dir[i]); + if (d > 0.0) { + vec4 aniso_albedo = vec4(albedo, 1.0); + + uint albedo16 = 0; + albedo16 |= clamp(uint(aniso_albedo.r * 31.0), 0, 31) << 0; + albedo16 |= clamp(uint(aniso_albedo.g * 63.0), 0, 63) << 5; + albedo16 |= clamp(uint(aniso_albedo.b * 31.0), 0, 31) << 11; + ivec3 store_pos = igrid_pos >> 1; + store_pos.z = store_pos.z * 6 + i; + imageStore(albedo_volume_grid, store_pos, uvec4(albedo16)); + } + } if (length(emission) > 0.001) { float lumas[6]; @@ -2807,35 +2826,27 @@ void fragment_shader(in SceneData scene_data) { //compress to RGBE9995 to save space - const float pow2to9 = 512.0f; - const float B = 15.0f; - const float N = 9.0f; - const float LN2 = 0.6931471805599453094172321215; + uint light_rgbe; - float cRed = clamp(light_total.r, 0.0, 65408.0); - float cGreen = clamp(light_total.g, 0.0, 65408.0); - float cBlue = clamp(light_total.b, 0.0, 65408.0); + { + vec3 rgb = light_total.rgb; - float cMax = max(cRed, max(cGreen, cBlue)); + const float rgbe_max = uintBitsToFloat(0x477F8000); + const float rgbe_min = uintBitsToFloat(0x37800000); - float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B; + rgb = clamp(rgb, 0, rgbe_max); - float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f); + float max_channel = max(max(rgbe_min, rgb.r), max(rgb.g, rgb.b)); - float exps = expp + 1.0f; + float bias = uintBitsToFloat((floatBitsToUint(max_channel) + 0x07804000) & 0x7F800000); - if (0.0 <= sMax && sMax < pow2to9) { - exps = expp; + uvec3 urgb = floatBitsToUint(rgb + bias); + uint e = (floatBitsToUint(bias) << 4) + 0x10000000; + light_rgbe = e | (urgb.b << 18) | (urgb.g << 9) | (urgb.r & 0x1FF); } - float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); - float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); - float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); - //store as 8985 to have 2 extra neighbor bits - uint light_rgbe = ((uint(sRed) & 0x1FFu) >> 1) | ((uint(sGreen) & 0x1FFu) << 8) | (((uint(sBlue) & 0x1FFu) >> 1) << 17) | ((uint(exps) & 0x1Fu) << 25); - - imageStore(emission_grid, grid_pos, uvec4(light_rgbe)); - imageStore(emission_aniso_grid, grid_pos, uvec4(light_aniso)); + imageStore(emission_grid, igrid_pos >> 1, uvec4(light_rgbe)); + imageStore(emission_aniso_grid, igrid_pos >> 1, uvec4(light_aniso)); } } diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 441cf3c80c92..03afd5551422 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -40,7 +40,7 @@ layout(push_constant, std430) uniform DrawCall { } draw_call; -#define SDFGI_MAX_CASCADES 8 +#define HDDAGI_MAX_CASCADES 8 /* Set 0: Base Pass (never changes) */ @@ -51,7 +51,7 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler; #define INSTANCE_FLAGS_DYNAMIC (1 << 3) #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) -#define INSTANCE_FLAGS_USE_SDFGI (1 << 6) +#define INSTANCE_FLAGS_USE_HDDAGI (1 << 6) #define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7) #define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8) #define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9) @@ -125,42 +125,36 @@ layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUnifo } global_shader_uniforms; -struct SDFVoxelGICascadeData { +struct HDDAGIProbeCascadeData { vec3 position; float to_probe; - ivec3 probe_world_offset; + + ivec3 region_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; float exposure_normalization; + + uvec4 pad2; }; -layout(set = 0, binding = 13, std140) uniform SDFGI { - vec3 grid_size; - uint max_cascades; +layout(set = 0, binding = 13, std140) uniform HDDAGI { + ivec3 grid_size; + int max_cascades; - bool use_occlusion; - int probe_axis_size; - float probe_to_uvw; float normal_bias; - - vec3 lightprobe_tex_pixel_size; float energy; - - vec3 lightprobe_uv_offset; float y_mult; + float reflection_bias; - vec3 occlusion_clamp; - uint pad3; + ivec3 probe_axis_size; + float esm_strength; - vec3 occlusion_renormalize; - uint pad4; + uvec4 pad3; - vec3 cascade_probe_size; - uint pad5; - - SDFVoxelGICascadeData cascades[SDFGI_MAX_CASCADES]; + HDDAGIProbeCascadeData cascades[HDDAGI_MAX_CASCADES]; } -sdfgi; +hddagi; layout(set = 0, binding = 14) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; @@ -188,10 +182,10 @@ struct ImplementationData { mat4 sdf_to_bounds; ivec3 sdf_offset; - uint pad2; + int gi_upscale_shift; ivec3 sdf_size; - bool gi_upscale_for_msaa; + bool gi_upscale; bool volumetric_fog_enabled; float volumetric_fog_inv_length; @@ -271,7 +265,7 @@ layout(set = 1, binding = 12 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_A layout(r16ui, set = 1, binding = 24) uniform restrict writeonly uimage3D albedo_volume_grid; layout(r32ui, set = 1, binding = 25) uniform restrict writeonly uimage3D emission_grid; layout(r32ui, set = 1, binding = 26) uniform restrict writeonly uimage3D emission_aniso_grid; -layout(r32ui, set = 1, binding = 27) uniform restrict uimage3D geom_facing_grid; +layout(r32ui, set = 1, binding = 27) uniform restrict uimage3D geom_normal_bits; //still need to be present for shaders that use it, so remap them to something #define depth_buffer shadow_atlas @@ -282,12 +276,15 @@ layout(r32ui, set = 1, binding = 27) uniform restrict uimage3D geom_facing_grid; #else #ifdef USE_MULTIVIEW + layout(set = 1, binding = 24) uniform texture2DArray depth_buffer; layout(set = 1, binding = 25) uniform texture2DArray color_buffer; layout(set = 1, binding = 26) uniform texture2DArray normal_roughness_buffer; layout(set = 1, binding = 27) uniform texture2DArray ao_buffer; layout(set = 1, binding = 28) uniform texture2DArray ambient_buffer; layout(set = 1, binding = 29) uniform texture2DArray reflection_buffer; +layout(set = 1, binding = 30) uniform texture2DArray ambient_reflection_blend_buffer; + #define multiviewSampler sampler2DArray #else // USE_MULTIVIEW layout(set = 1, binding = 24) uniform texture2D depth_buffer; @@ -296,10 +293,13 @@ layout(set = 1, binding = 26) uniform texture2D normal_roughness_buffer; layout(set = 1, binding = 27) uniform texture2D ao_buffer; layout(set = 1, binding = 28) uniform texture2D ambient_buffer; layout(set = 1, binding = 29) uniform texture2D reflection_buffer; +layout(set = 1, binding = 30) uniform texture2D ambient_reflection_blend_buffer; #define multiviewSampler sampler2D #endif -layout(set = 1, binding = 30) uniform texture2DArray sdfgi_lightprobe_texture; -layout(set = 1, binding = 31) uniform texture3D sdfgi_occlusion_cascades; + +layout(set = 1, binding = 31) uniform texture2DArray hddagi_lightprobe_specular; +layout(set = 1, binding = 32) uniform texture2DArray hddagi_lightprobe_diffuse; +layout(set = 1, binding = 33) uniform texture3D hddagi_occlusion[2]; struct VoxelGIData { mat4 xform; // 64 - 64 @@ -316,17 +316,18 @@ struct VoxelGIData { float exposure_normalization; // 4 - 112 }; -layout(set = 1, binding = 32, std140) uniform VoxelGIs { +layout(set = 1, binding = 34, std140) uniform VoxelGIs { VoxelGIData data[MAX_VOXEL_GI_INSTANCES]; } voxel_gi_instances; -layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture; +layout(set = 1, binding = 35) uniform texture3D volumetric_fog_texture; #ifdef USE_MULTIVIEW -layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer; +layout(set = 1, binding = 36) uniform texture2DArray ssil_buffer; #else -layout(set = 1, binding = 34) uniform texture2D ssil_buffer; +layout(set = 1, binding = 36) uniform texture2D ssil_buffer; + #endif // USE_MULTIVIEW #endif diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 27769f1c17d7..36b6c786858b 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1323,7 +1323,7 @@ void main() { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); #ifndef MODE_UNSHADED - // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + // Used in regular draw pass and when drawing SDFs for HDDAGI and materials for VoxelGI. emission *= scene_data.emissive_exposure_normalization; #endif diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 7674e905e1d4..f9c8c5ae5a6f 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -32,7 +32,7 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler; #define INSTANCE_FLAGS_DYNAMIC (1 << 3) #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) -#define INSTANCE_FLAGS_USE_SDFGI (1 << 6) +#define INSTANCE_FLAGS_USE_HDDAGI (1 << 6) #define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7) #define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8) #define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9) diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl index 4cb0e4c52d51..d11f47b24bc0 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl @@ -1,4 +1,4 @@ -// Functions related to gi/sdfgi for our forward renderer +// Functions related to gi/hddagi for our forward renderer //standard voxel cone trace vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { @@ -13,7 +13,7 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); + vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); float a = (1.0 - color.a); color += a * scolor; dist += half_diameter; @@ -35,7 +35,7 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); + vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); lod_level += 1.0; float a = (1.0 - color.a); @@ -153,120 +153,215 @@ vec2 octahedron_encode(vec3 n) { return n.xy; } -void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, bool use_specular, float roughness, out vec3 diffuse_light, out vec3 specular_light, out float blend) { - cascade_pos += cam_normal * sdfgi.normal_bias; +#define PROBE_CELLS 8 +#define OCC16_DISTANCE_MAX 256.0 +#define ROUGHNESS_TO_REFLECTION_TRESHOOLD 0.2 - vec3 base_pos = floor(cascade_pos); - //cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal; - ivec3 probe_base_pos = ivec3(base_pos); +ivec3 modi(ivec3 value, ivec3 p_y) { + // GLSL Specification says: + // "Results are undefined if one or both operands are negative." + // So.. + return mix(value % p_y, p_y - ((abs(value) - ivec3(1)) % p_y) - 1, lessThan(sign(value), ivec3(0))); +} - vec4 diffuse_accum = vec4(0.0); - vec3 specular_accum; +ivec2 probe_to_tex(ivec3 local_probe, int p_cascade) { + ivec3 cell = modi(hddagi.cascades[p_cascade].region_world_offset + local_probe, hddagi.probe_axis_size); + return cell.xy + ivec2(0, cell.z * int(hddagi.probe_axis_size.y)); +} - ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade)); - tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size; - tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1); +void sdfvoxel_gi_process(int cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) { + // vec3 posf = cascade_pos + cam_normal * hddagi.normal_bias; + vec3 posf = cascade_pos + cam_normal; - vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size; + ivec3 posi = ivec3(posf); + ivec3 base_probe = posi / PROBE_CELLS; - vec3 specular_posf; + vec3 diffuse_accum = vec3(0.0); + vec3 specular_accum = vec3(0.0); + float weight_accum = 0.0; - if (use_specular) { - specular_accum = vec3(0.0); - specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size; - } + ivec3 occ_pos = posi; // faster and numerically safer to do this computation as ints + vec3 pos_fract = posf - vec3(posi); + occ_pos = (occ_pos + hddagi.cascades[cascade].region_world_offset * PROBE_CELLS) & (hddagi.grid_size - 1); + occ_pos.y += (hddagi.grid_size.y + 2) * cascade; + occ_pos += ivec3(1); + ivec3 occ_total_size = hddagi.grid_size + ivec3(2); + occ_total_size.y *= hddagi.max_cascades; + vec3 occ_posf = (vec3(occ_pos) + pos_fract) / vec3(occ_total_size); - vec4 light_accum = vec4(0.0); - float weight_accum = 0.0; + vec4 occ_0 = texture(sampler3D(hddagi_occlusion[0], SAMPLER_LINEAR_CLAMP), occ_posf); + vec4 occ_1 = texture(sampler3D(hddagi_occlusion[1], SAMPLER_LINEAR_CLAMP), occ_posf); - for (uint j = 0; j < 8; j++) { - ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); - ivec3 probe_posi = probe_base_pos; - probe_posi += offset; + float occ_weights[8] = float[](occ_0.x, occ_0.y, occ_0.z, occ_0.w, occ_1.x, occ_1.y, occ_1.z, occ_1.w); - // Compute weight + vec4 accum_light = vec4(0.0); - vec3 probe_pos = vec3(probe_posi); - vec3 probe_to_pos = cascade_pos - probe_pos; - vec3 probe_dir = normalize(-probe_to_pos); + vec2 light_probe_tex_to_uv = 1.0 / vec2((LIGHTPROBE_OCT_SIZE + 2) * hddagi.probe_axis_size.x, (LIGHTPROBE_OCT_SIZE + 2) * hddagi.probe_axis_size.y * hddagi.probe_axis_size.z); + vec2 light_uv = octahedron_encode(vec3(cam_normal)) * float(LIGHTPROBE_OCT_SIZE); + vec2 light_uv_spec = octahedron_encode(vec3(cam_specular_normal)) * float(LIGHTPROBE_OCT_SIZE); - vec3 trilinear = vec3(1.0) - abs(probe_to_pos); - float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir)); + for (int i = 0; i < 8; i++) { + ivec3 probe = base_probe + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1)); - // Compute lightprobe occlusion + vec3 probe_pos = vec3(probe * PROBE_CELLS); - if (sdfgi.use_occlusion) { - ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); - vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); + vec3 probe_to_pos = posf - probe_pos; + vec3 n = normalize(probe_to_pos); + float d = length(probe_to_pos); - vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw; - occ_pos.z += float(cascade); - if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures - occ_pos.x += 1.0; - } + float weight = 1.0; + // Dynamic objects don't need this visibility optimization, and this makes them wobbly when they move. + // weight *= pow(max(0.0001, (dot(-n, cam_normal) + 1.0) * 0.5), 2.0) + 0.2; + // weight *= max(0.005, (dot(-n, cam_normal))); - occ_pos *= sdfgi.occlusion_renormalize; - float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, SAMPLER_LINEAR_CLAMP), occ_pos, 0.0), occ_mask); + ivec3 probe_occ = (hddagi.cascades[cascade].region_world_offset + probe) & ivec3(1); - weight *= max(occlusion, 0.01); + uint weight_index = 0; + if (probe_occ.x != 0) { + weight_index |= 1; + } + if (probe_occ.y != 0) { + weight_index |= 2; + } + if (probe_occ.z != 0) { + weight_index |= 4; } - // Compute lightprobe texture position + weight *= max(0.2, occ_weights[weight_index]); - vec3 diffuse; - vec3 pos_uvw = diffuse_posf; - pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; - pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; - diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb; + vec3 trilinear = vec3(1.0) - abs(probe_to_pos / float(PROBE_CELLS)); - diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); + weight *= trilinear.x * trilinear.y * trilinear.z; - if (use_specular) { - vec3 specular = vec3(0.0); - vec3 pos_uvw = specular_posf; - pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; - pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; - if (roughness < 0.99) { - specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb; - } - if (roughness > 0.5) { - specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0); - } + ivec2 tex_pos = probe_to_tex(probe, cascade); + vec2 base_tex_uv = vec2(ivec2(tex_pos * (LIGHTPROBE_OCT_SIZE + 2) + ivec2(1))); + vec2 tex_uv = base_tex_uv + light_uv; + tex_uv *= light_probe_tex_to_uv; + + vec3 probe_light = texture(sampler2DArray(hddagi_lightprobe_diffuse, SAMPLER_LINEAR_CLAMP), vec3(tex_uv, float(cascade))).rgb; + diffuse_accum += probe_light * weight; - specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; + tex_uv = base_tex_uv + light_uv_spec; + tex_uv *= light_probe_tex_to_uv; + + vec3 probe_ref_light; + if (roughness < 0.99) { + probe_ref_light = texture(sampler2DArray(hddagi_lightprobe_specular, SAMPLER_LINEAR_CLAMP), vec3(tex_uv, float(cascade))).rgb; + } else { + probe_ref_light = vec3(0.0); + } + + vec3 probe_ref_full_light; + if (roughness > ROUGHNESS_TO_REFLECTION_TRESHOOLD) { + probe_ref_full_light = texture(sampler2DArray(hddagi_lightprobe_diffuse, SAMPLER_LINEAR_CLAMP), vec3(tex_uv, float(cascade))).rgb; + } else { + probe_ref_full_light = vec3(0.0); } - } - if (diffuse_accum.a > 0.0) { - diffuse_accum.rgb /= diffuse_accum.a; + probe_ref_light = mix(probe_ref_light, probe_ref_full_light, smoothstep(ROUGHNESS_TO_REFLECTION_TRESHOOLD, 1.0, roughness)); + + specular_accum += probe_ref_light * weight; + + weight_accum += weight; } - diffuse_light = diffuse_accum.rgb; + diffuse_light = diffuse_accum / weight_accum; + specular_light = specular_accum / weight_accum; +} + +void hddagi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, out vec4 ambient_light, out vec4 reflection_light) { + //make vertex orientation the world one, but still align to camera + vertex.y *= hddagi.y_mult; + normal.y *= hddagi.y_mult; + reflection.y *= hddagi.y_mult; + + //renormalize + normal = normalize(normal); + reflection = normalize(reflection); - if (use_specular) { - if (diffuse_accum.a > 0.0) { - specular_accum /= diffuse_accum.a; + vec3 cam_pos = vertex; + vec3 cam_normal = normal; + + vec4 light_accum = vec4(0.0); + float weight_accum = 0.0; + + vec4 light_blend_accum = vec4(0.0); + float weight_blend_accum = 0.0; + + float blend = -1.0; + + // helper constants, compute once + + int cascade = 0x7FFFFFFF; + vec3 cascade_pos; + vec3 cascade_normal; + + for (int i = 0; i < hddagi.max_cascades; i++) { + cascade_pos = (cam_pos - hddagi.cascades[i].position) * hddagi.cascades[i].to_cell; + + if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, vec3(hddagi.grid_size)))) { + continue; //skip cascade } - specular_light = specular_accum; + cascade = i; + break; } - { - //process blend - float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5; - float blend_to = blend_from + 2.0; + if (cascade < HDDAGI_MAX_CASCADES) { + ambient_light = vec4(0, 0, 0, 1); + reflection_light = vec4(0, 0, 0, 1); - vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe; + float blend; + vec3 diffuse, specular; + sdfvoxel_gi_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse, specular); - float len = length(inner_pos); + { + //process blend + vec3 blend_from = ((vec3(hddagi.probe_axis_size) - 1) / 2.0); - inner_pos = abs(normalize(inner_pos)); - len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + vec3 inner_pos = cam_pos * hddagi.cascades[cascade].to_probe; - if (len >= blend_from) { - blend = smoothstep(blend_from, blend_to, len); - } else { - blend = 0.0; + vec3 inner_dist = blend_from - abs(inner_pos); + + float min_d = min(inner_dist.x, min(inner_dist.y, inner_dist.z)); + + blend = clamp(1.0 - smoothstep(0.5, 2.5, min_d), 0, 1); } + + if (blend > 0.0) { +#if 0 +// debug + const vec3 to_color[HDDAGI_MAX_CASCADES] = vec3[] ( + vec3(1,0,0),vec3(0,1,0),vec3(0,0,1),vec3(1,1,0),vec3(1,0,1),vec3(0,1,1),vec3(0,0,0),vec3(1,1,1) ); + + diffuse = mix(diffuse,to_color[cascade],blend); + specular = mix(specular,to_color[cascade],blend); +#else + + if (cascade == hddagi.max_cascades - 1) { + ambient_light.a = 1.0 - blend; + reflection_light.a = 1.0 - blend; + + } else { + vec3 diffuse2, specular2; + cascade_pos = (cam_pos - hddagi.cascades[cascade + 1].position) * hddagi.cascades[cascade + 1].to_cell; + + sdfvoxel_gi_process(cascade + 1, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse2, specular2); + + diffuse = mix(diffuse, diffuse2, blend); + specular = mix(specular, specular2, blend); + } +#endif + } + + ambient_light.rgb = diffuse; + reflection_light.rgb = specular; + + ambient_light.rgb *= hddagi.energy; + reflection_light.rgb *= hddagi.energy; + + } else { + ambient_light = vec4(0); + reflection_light = vec4(0); } } diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 8b2bb14b7667..01e8fd87b243 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -295,11 +295,11 @@ void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mod light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } -void LightStorage::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) { +void LightStorage::light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL(light); - light->max_sdfgi_cascade = p_cascade; + light->max_hddagi_cascade = p_cascade; light->version++; light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); @@ -368,11 +368,11 @@ RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(R return light->directional_shadow_mode; } -uint32_t LightStorage::light_get_max_sdfgi_cascade(RID p_light) { +uint32_t LightStorage::light_get_max_hddagi_cascade(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_NULL_V(light, 0); - return light->max_sdfgi_cascade; + return light->max_hddagi_cascade; } RS::LightBakeMode LightStorage::light_get_bake_mode(RID p_light) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index f152cc5dae63..a9aa9c9e618a 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -70,7 +70,7 @@ class LightStorage : public RendererLightStorage { bool negative = false; bool reverse_cull = false; RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC; - uint32_t max_sdfgi_cascade = 2; + uint32_t max_hddagi_cascade = 2; uint32_t cull_mask = 0xFFFFFFFF; bool distance_fade = false; real_t distance_fade_begin = 40.0; @@ -475,7 +475,7 @@ class LightStorage : public RendererLightStorage { virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override; virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override; virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override; - virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override; + virtual void light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) override; virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override; @@ -573,7 +573,7 @@ class LightStorage : public RendererLightStorage { } virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; - virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override; + virtual uint32_t light_get_max_hddagi_cascade(RID p_light) override; virtual uint64_t light_get_version(RID p_light) const override; virtual uint32_t light_get_cull_mask(RID p_light) const override; diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h index 3cd397b8ed6f..28ffec38f0cf 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -90,9 +90,9 @@ class RenderDataRD : public RenderData { LocalVector directional_shadows; /* GI info */ - const RendererSceneRender::RenderSDFGIData *render_sdfgi_regions = nullptr; - int render_sdfgi_region_count = 0; - const RendererSceneRender::RenderSDFGIUpdateData *sdfgi_update_data = nullptr; + const RendererSceneRender::RenderHDDAGIData *render_hddagi_regions = nullptr; + int render_hddagi_region_count = 0; + const RendererSceneRender::RenderHDDAGIUpdateData *hddagi_update_data = nullptr; uint32_t voxel_gi_count = 0; }; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index b02d3def88f3..a7a273a0848c 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -1594,9 +1594,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } } - uint32_t max_sdfgi_cascade = RSG::light_storage->light_get_max_sdfgi_cascade(p_instance->base); - if (light->max_sdfgi_cascade != max_sdfgi_cascade) { - light->max_sdfgi_cascade = max_sdfgi_cascade; //should most likely make sdfgi dirty in scenario + uint32_t max_hddagi_cascade = RSG::light_storage->light_get_max_hddagi_cascade(p_instance->base); + if (light->max_hddagi_cascade != max_hddagi_cascade) { + light->max_hddagi_cascade = max_hddagi_cascade; //should most likely make hddagi dirty in scenario } } else if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { InstanceReflectionProbeData *reflection_probe = static_cast(p_instance->base_data); @@ -2756,8 +2756,8 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul uint64_t frame_number = RSG::rasterizer->get_frame_number(); float lightmap_probe_update_speed = RSG::light_storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time(); - uint32_t sdfgi_last_light_index = 0xFFFFFFFF; - uint32_t sdfgi_last_light_cascade = 0xFFFFFFFF; + uint32_t hddagi_last_light_index = 0xFFFFFFFF; + uint32_t hddagi_last_light_cascade = 0xFFFFFFFF; RID instance_pair_buffer[MAX_INSTANCE_PAIRS]; @@ -2995,22 +2995,22 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul #undef VIS_CHECK #undef OCCLUSION_CULLED - for (uint32_t j = 0; j < cull_data.cull->sdfgi.region_count; j++) { - if (cull_data.scenario->instance_aabbs[i].in_aabb(cull_data.cull->sdfgi.region_aabb[j])) { + for (uint32_t j = 0; j < cull_data.cull->hddagi.region_count; j++) { + if (cull_data.scenario->instance_aabbs[i].in_aabb(cull_data.cull->hddagi.region_aabb[j])) { uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; if (base_type == RS::INSTANCE_LIGHT) { InstanceLightData *instance_light = (InstanceLightData *)idata.instance->base_data; - if (instance_light->bake_mode == RS::LIGHT_BAKE_STATIC && cull_data.cull->sdfgi.region_cascade[j] <= instance_light->max_sdfgi_cascade) { - if (sdfgi_last_light_index != i || sdfgi_last_light_cascade != cull_data.cull->sdfgi.region_cascade[j]) { - sdfgi_last_light_index = i; - sdfgi_last_light_cascade = cull_data.cull->sdfgi.region_cascade[j]; - cull_result.sdfgi_cascade_lights[sdfgi_last_light_cascade].push_back(instance_light->instance); + if (instance_light->bake_mode == RS::LIGHT_BAKE_STATIC && cull_data.cull->hddagi.region_cascade[j] <= instance_light->max_hddagi_cascade) { + if (hddagi_last_light_index != i || hddagi_last_light_cascade != cull_data.cull->hddagi.region_cascade[j]) { + hddagi_last_light_index = i; + hddagi_last_light_cascade = cull_data.cull->hddagi.region_cascade[j]; + cull_result.hddagi_cascade_lights[hddagi_last_light_cascade].push_back(instance_light->instance); } } } else if ((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) { if (idata.flags & InstanceData::FLAG_USES_BAKED_LIGHT) { - cull_result.sdfgi_region_geometry_instances[j].push_back(idata.instance_geometry); + cull_result.hddagi_region_geometry_instances[j].push_back(idata.instance_geometry); mesh_visible = true; } } @@ -3040,7 +3040,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c if (p_reflection_probe.is_null()) { //no rendering code here, this is only to set up what needs to be done, request regions, etc. - scene_render->sdfgi_update(p_render_buffers, p_environment, camera_position); //update conditions for SDFGI (whether its used or not) + scene_render->hddagi_update(p_render_buffers, p_environment, camera_position); //update conditions for HDDAGI (whether its used or not) } RENDER_TIMESTAMP("Update Visibility Dependencies"); @@ -3117,28 +3117,27 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } } - { //sdfgi - cull.sdfgi.region_count = 0; + { //hddagi + cull.hddagi.region_count = 0; if (p_reflection_probe.is_null()) { - cull.sdfgi.cascade_light_count = 0; + cull.hddagi.cascade_light_count = 0; uint32_t prev_cascade = 0xFFFFFFFF; - uint32_t pending_region_count = scene_render->sdfgi_get_pending_region_count(p_render_buffers); - + uint32_t pending_region_count = scene_render->hddagi_get_pending_region_count(p_render_buffers); for (uint32_t i = 0; i < pending_region_count; i++) { - cull.sdfgi.region_aabb[i] = scene_render->sdfgi_get_pending_region_bounds(p_render_buffers, i); - uint32_t region_cascade = scene_render->sdfgi_get_pending_region_cascade(p_render_buffers, i); - cull.sdfgi.region_cascade[i] = region_cascade; + cull.hddagi.region_aabb[i] = scene_render->hddagi_get_pending_region_bounds(p_render_buffers, i); + uint32_t region_cascade = scene_render->hddagi_get_pending_region_cascade(p_render_buffers, i); + cull.hddagi.region_cascade[i] = region_cascade; if (region_cascade != prev_cascade) { - cull.sdfgi.cascade_light_index[cull.sdfgi.cascade_light_count] = region_cascade; - cull.sdfgi.cascade_light_count++; + cull.hddagi.cascade_light_index[cull.hddagi.cascade_light_count] = region_cascade; + cull.hddagi.cascade_light_count++; prev_cascade = region_cascade; } } - cull.sdfgi.region_count = pending_region_count; + cull.hddagi.region_count = pending_region_count; } } @@ -3350,40 +3349,40 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } } - //render SDFGI + //render HDDAGI { // Q: Should this whole block be skipped if we're rendering our reflection probe? - sdfgi_update_data.update_static = false; + hddagi_update_data.update_static = false; - if (cull.sdfgi.region_count > 0) { + if (cull.hddagi.region_count > 0) { //update regions - for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) { - render_sdfgi_data[i].instances.merge_unordered(scene_cull_result.sdfgi_region_geometry_instances[i]); - render_sdfgi_data[i].region = i; + for (uint32_t i = 0; i < cull.hddagi.region_count; i++) { + render_hddagi_data[i].instances.merge_unordered(scene_cull_result.hddagi_region_geometry_instances[i]); + render_hddagi_data[i].region = i; } //check if static lights were culled bool static_lights_culled = false; - for (uint32_t i = 0; i < cull.sdfgi.cascade_light_count; i++) { - if (scene_cull_result.sdfgi_cascade_lights[i].size()) { + for (uint32_t i = 0; i < cull.hddagi.cascade_light_count; i++) { + if (scene_cull_result.hddagi_cascade_lights[i].size()) { static_lights_culled = true; break; } } if (static_lights_culled) { - sdfgi_update_data.static_cascade_count = cull.sdfgi.cascade_light_count; - sdfgi_update_data.static_cascade_indices = cull.sdfgi.cascade_light_index; - sdfgi_update_data.static_positional_lights = scene_cull_result.sdfgi_cascade_lights; - sdfgi_update_data.update_static = true; + hddagi_update_data.static_cascade_count = cull.hddagi.cascade_light_count; + hddagi_update_data.static_cascade_indices = cull.hddagi.cascade_light_index; + hddagi_update_data.static_positional_lights = scene_cull_result.hddagi_cascade_lights; + hddagi_update_data.update_static = true; } } if (p_reflection_probe.is_null()) { - sdfgi_update_data.directional_lights = &directional_lights; - sdfgi_update_data.positional_light_instances = scenario->dynamic_lights.ptr(); - sdfgi_update_data.positional_light_count = scenario->dynamic_lights.size(); + hddagi_update_data.directional_lights = &directional_lights; + hddagi_update_data.positional_light_instances = scenario->dynamic_lights.ptr(); + hddagi_update_data.positional_light_count = scenario->dynamic_lights.size(); } } @@ -3409,7 +3408,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } RENDER_TIMESTAMP("Render 3D Scene"); - scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_attributes, p_compositor, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data, r_render_info); + scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_attributes, p_compositor, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_hddagi_data, cull.hddagi.region_count, &hddagi_update_data, r_render_info); if (p_viewport.is_valid()) { RSG::viewport->viewport_set_prev_camera_data(p_viewport, p_camera_data); @@ -3420,8 +3419,8 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } max_shadows_used = 0; - for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) { - render_sdfgi_data[i].instances.clear(); + for (uint32_t i = 0; i < cull.hddagi.region_count; i++) { + render_hddagi_data[i].instances.clear(); } } @@ -4250,8 +4249,8 @@ RendererSceneCull::RendererSceneCull() { for (uint32_t i = 0; i < MAX_UPDATE_SHADOWS; i++) { render_shadow_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool); } - for (uint32_t i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - render_sdfgi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool); + for (uint32_t i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + render_hddagi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool); } scene_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); @@ -4281,8 +4280,8 @@ RendererSceneCull::~RendererSceneCull() { for (uint32_t i = 0; i < MAX_UPDATE_SHADOWS; i++) { render_shadow_data[i].instances.reset(); } - for (uint32_t i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - render_sdfgi_data[i].instances.reset(); + for (uint32_t i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + render_hddagi_data[i].instances.reset(); } scene_cull_result.reset(); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 0039d1447516..0262386a9b69 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -56,8 +56,8 @@ class RendererSceneCull : public RenderingMethod { RendererSceneRender *scene_render = nullptr; enum { - SDFGI_MAX_CASCADES = 8, - SDFGI_MAX_REGIONS_PER_CASCADE = 3, + HDDAGI_MAX_CASCADES = 8, + HDDAGI_MAX_REGIONS_PER_CASCADE = 3, MAX_INSTANCE_PAIRS = 32, MAX_UPDATE_SHADOWS = 512 }; @@ -703,7 +703,7 @@ class RendererSceneCull : public RenderingMethod { Instance *baked_light = nullptr; RS::LightBakeMode bake_mode; - uint32_t max_sdfgi_cascade = 2; + uint32_t max_hddagi_cascade = 2; private: // Instead of a single dirty flag, we maintain a count @@ -896,8 +896,8 @@ class RendererSceneCull : public RenderingMethod { PagedArray cascade_geometry_instances[RendererSceneRender::MAX_DIRECTIONAL_LIGHT_CASCADES]; } directional_shadows[RendererSceneRender::MAX_DIRECTIONAL_LIGHTS]; - PagedArray sdfgi_region_geometry_instances[SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE]; - PagedArray sdfgi_cascade_lights[SDFGI_MAX_CASCADES]; + PagedArray hddagi_region_geometry_instances[HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE]; + PagedArray hddagi_cascade_lights[HDDAGI_MAX_CASCADES]; void clear() { geometry_instances.clear(); @@ -915,12 +915,12 @@ class RendererSceneCull : public RenderingMethod { } } - for (int i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - sdfgi_region_geometry_instances[i].clear(); + for (int i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + hddagi_region_geometry_instances[i].clear(); } - for (int i = 0; i < SDFGI_MAX_CASCADES; i++) { - sdfgi_cascade_lights[i].clear(); + for (int i = 0; i < HDDAGI_MAX_CASCADES; i++) { + hddagi_cascade_lights[i].clear(); } } @@ -940,12 +940,12 @@ class RendererSceneCull : public RenderingMethod { } } - for (int i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - sdfgi_region_geometry_instances[i].reset(); + for (int i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + hddagi_region_geometry_instances[i].reset(); } - for (int i = 0; i < SDFGI_MAX_CASCADES; i++) { - sdfgi_cascade_lights[i].reset(); + for (int i = 0; i < HDDAGI_MAX_CASCADES; i++) { + hddagi_cascade_lights[i].reset(); } } @@ -966,12 +966,12 @@ class RendererSceneCull : public RenderingMethod { } } - for (int i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - sdfgi_region_geometry_instances[i].merge_unordered(p_cull_result.sdfgi_region_geometry_instances[i]); + for (int i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + hddagi_region_geometry_instances[i].merge_unordered(p_cull_result.hddagi_region_geometry_instances[i]); } - for (int i = 0; i < SDFGI_MAX_CASCADES; i++) { - sdfgi_cascade_lights[i].merge_unordered(p_cull_result.sdfgi_cascade_lights[i]); + for (int i = 0; i < HDDAGI_MAX_CASCADES; i++) { + hddagi_cascade_lights[i].merge_unordered(p_cull_result.hddagi_cascade_lights[i]); } } @@ -991,12 +991,12 @@ class RendererSceneCull : public RenderingMethod { } } - for (int i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) { - sdfgi_region_geometry_instances[i].set_page_pool(p_geometry_instance_pool); + for (int i = 0; i < HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE; i++) { + hddagi_region_geometry_instances[i].set_page_pool(p_geometry_instance_pool); } - for (int i = 0; i < SDFGI_MAX_CASCADES; i++) { - sdfgi_cascade_lights[i].set_page_pool(p_rid_pool); + for (int i = 0; i < HDDAGI_MAX_CASCADES; i++) { + hddagi_cascade_lights[i].set_page_pool(p_rid_pool); } } }; @@ -1007,8 +1007,8 @@ class RendererSceneCull : public RenderingMethod { RendererSceneRender::RenderShadowData render_shadow_data[MAX_UPDATE_SHADOWS]; uint32_t max_shadows_used = 0; - RendererSceneRender::RenderSDFGIData render_sdfgi_data[SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE]; - RendererSceneRender::RenderSDFGIUpdateData sdfgi_update_data; + RendererSceneRender::RenderHDDAGIData render_hddagi_data[HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE]; + RendererSceneRender::RenderHDDAGIUpdateData hddagi_update_data; uint32_t thread_cull_threshold = 200; @@ -1103,16 +1103,16 @@ class RendererSceneCull : public RenderingMethod { uint32_t shadow_count; - struct SDFGI { - //have arrays here because SDFGI functions expects this, plus regions can have areas - AABB region_aabb[SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE]; //max 3 regions per cascade - uint32_t region_cascade[SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE]; //max 3 regions per cascade + struct HDDAGI { + //have arrays here because HDDAGI functions expects this, plus regions can have areas + AABB region_aabb[HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE]; //max 3 regions per cascade + uint32_t region_cascade[HDDAGI_MAX_CASCADES * HDDAGI_MAX_REGIONS_PER_CASCADE]; //max 3 regions per cascade uint32_t region_count = 0; - uint32_t cascade_light_index[SDFGI_MAX_CASCADES]; + uint32_t cascade_light_index[HDDAGI_MAX_CASCADES]; uint32_t cascade_light_count = 0; - } sdfgi; + } hddagi; SpinLock lock; @@ -1334,24 +1334,29 @@ class RendererSceneCull : public RenderingMethod { PASS6(environment_set_ssil_quality, RS::EnvironmentSSILQuality, bool, float, int, float, float) - // SDFGI + // HDDAGI - PASS11(environment_set_sdfgi, RID, bool, int, float, RS::EnvironmentSDFGIYScale, bool, float, bool, float, float, float) + PASS15(environment_set_hddagi, RID, bool, int, RS::EnvironmentHDDAGICascadeFormat, float, bool, float, bool, float, float, float, float, float, bool, bool) - PASS1RC(bool, environment_get_sdfgi_enabled, RID) - PASS1RC(int, environment_get_sdfgi_cascades, RID) - PASS1RC(float, environment_get_sdfgi_min_cell_size, RID) - PASS1RC(bool, environment_get_sdfgi_use_occlusion, RID) - PASS1RC(float, environment_get_sdfgi_bounce_feedback, RID) - PASS1RC(bool, environment_get_sdfgi_read_sky_light, RID) - PASS1RC(float, environment_get_sdfgi_energy, RID) - PASS1RC(float, environment_get_sdfgi_normal_bias, RID) - PASS1RC(float, environment_get_sdfgi_probe_bias, RID) - PASS1RC(RS::EnvironmentSDFGIYScale, environment_get_sdfgi_y_scale, RID) + PASS1RC(bool, environment_get_hddagi_enabled, RID) + PASS1RC(RS::EnvironmentHDDAGICascadeFormat, environment_get_hddagi_cascade_format, RID) + PASS1RC(int, environment_get_hddagi_cascades, RID) + PASS1RC(float, environment_get_hddagi_min_cell_size, RID) + PASS1RC(bool, environment_get_hddagi_filter_probes, RID) + PASS1RC(float, environment_get_hddagi_bounce_feedback, RID) + PASS1RC(bool, environment_get_hddagi_read_sky_light, RID) + PASS1RC(float, environment_get_hddagi_energy, RID) + PASS1RC(float, environment_get_hddagi_normal_bias, RID) + PASS1RC(float, environment_get_hddagi_reflection_bias, RID) + PASS1RC(float, environment_get_hddagi_probe_bias, RID) + PASS1RC(float, environment_get_hddagi_occlusion_bias, RID) + PASS1RC(float, environment_get_hddagi_filter_ambient, RID) + PASS1RC(float, environment_get_hddagi_filter_reflection, RID) - PASS1(environment_set_sdfgi_ray_count, RS::EnvironmentSDFGIRayCount) - PASS1(environment_set_sdfgi_frames_to_converge, RS::EnvironmentSDFGIFramesToConverge) - PASS1(environment_set_sdfgi_frames_to_update_light, RS::EnvironmentSDFGIFramesToUpdateLight) + PASS1(environment_set_hddagi_frames_to_converge, RS::EnvironmentHDDAGIFramesToConverge) + PASS1(environment_set_hddagi_frames_to_update_light, RS::EnvironmentHDDAGIFramesToUpdateLight) + + PASS1(environment_set_hddagi_inactive_probe_frames, RS::EnvironmentHDDAGIInactiveProbeFrames) // Adjustment PASS7(environment_set_adjustment, RID, bool, float, float, float, bool, RID) @@ -1372,7 +1377,7 @@ class RendererSceneCull : public RenderingMethod { PASS1(positional_soft_shadow_filter_set_quality, RS::ShadowQuality) PASS1(directional_soft_shadow_filter_set_quality, RS::ShadowQuality) - PASS2(sdfgi_set_debug_probe_select, const Vector3 &, const Vector3 &) + PASS2(hddagi_set_debug_probe_select, const Vector3 &, const Vector3 &) /* Render Buffers */ diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index 76c779900fba..a4603a107eda 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -636,50 +636,66 @@ float RendererSceneRender::environment_get_ssil_normal_rejection(RID p_env) cons return environment_storage.environment_get_ssil_normal_rejection(p_env); } -// SDFGI +// HDDAGI -void RendererSceneRender::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { - environment_storage.environment_set_sdfgi(p_env, p_enable, p_cascades, p_min_cell_size, p_y_scale, p_use_occlusion, p_bounce_feedback, p_read_sky, p_energy, p_normal_bias, p_probe_bias); +void RendererSceneRender::environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, RS::EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient) { + environment_storage.environment_set_hddagi(p_env, p_enable, p_cascades, p_cascade_format, p_min_cell_size, p_filter_probes, p_bounce_feedback, p_read_sky, p_energy, p_normal_bias, p_reflection_bias, p_probe_bias, p_occlusion_bias, p_filter_reflection, p_filter_ambient); } -bool RendererSceneRender::environment_get_sdfgi_enabled(RID p_env) const { - return environment_storage.environment_get_sdfgi_enabled(p_env); +bool RendererSceneRender::environment_get_hddagi_enabled(RID p_env) const { + return environment_storage.environment_get_hddagi_enabled(p_env); } -int RendererSceneRender::environment_get_sdfgi_cascades(RID p_env) const { - return environment_storage.environment_get_sdfgi_cascades(p_env); +int RendererSceneRender::environment_get_hddagi_cascades(RID p_env) const { + return environment_storage.environment_get_hddagi_cascades(p_env); } -float RendererSceneRender::environment_get_sdfgi_min_cell_size(RID p_env) const { - return environment_storage.environment_get_sdfgi_min_cell_size(p_env); +float RendererSceneRender::environment_get_hddagi_min_cell_size(RID p_env) const { + return environment_storage.environment_get_hddagi_min_cell_size(p_env); } -bool RendererSceneRender::environment_get_sdfgi_use_occlusion(RID p_env) const { - return environment_storage.environment_get_sdfgi_use_occlusion(p_env); +bool RendererSceneRender::environment_get_hddagi_filter_probes(RID p_env) const { + return environment_storage.environment_get_hddagi_filter_probes(p_env); } -float RendererSceneRender::environment_get_sdfgi_bounce_feedback(RID p_env) const { - return environment_storage.environment_get_sdfgi_bounce_feedback(p_env); +bool RendererSceneRender::environment_get_hddagi_filter_ambient(RID p_env) const { + return environment_storage.environment_get_hddagi_filter_ambient(p_env); } -bool RendererSceneRender::environment_get_sdfgi_read_sky_light(RID p_env) const { - return environment_storage.environment_get_sdfgi_read_sky_light(p_env); +bool RendererSceneRender::environment_get_hddagi_filter_reflection(RID p_env) const { + return environment_storage.environment_get_hddagi_filter_reflection(p_env); } -float RendererSceneRender::environment_get_sdfgi_energy(RID p_env) const { - return environment_storage.environment_get_sdfgi_energy(p_env); +float RendererSceneRender::environment_get_hddagi_bounce_feedback(RID p_env) const { + return environment_storage.environment_get_hddagi_bounce_feedback(p_env); } -float RendererSceneRender::environment_get_sdfgi_normal_bias(RID p_env) const { - return environment_storage.environment_get_sdfgi_normal_bias(p_env); +bool RendererSceneRender::environment_get_hddagi_read_sky_light(RID p_env) const { + return environment_storage.environment_get_hddagi_read_sky_light(p_env); } -float RendererSceneRender::environment_get_sdfgi_probe_bias(RID p_env) const { - return environment_storage.environment_get_sdfgi_probe_bias(p_env); +float RendererSceneRender::environment_get_hddagi_energy(RID p_env) const { + return environment_storage.environment_get_hddagi_energy(p_env); } -RS::EnvironmentSDFGIYScale RendererSceneRender::environment_get_sdfgi_y_scale(RID p_env) const { - return environment_storage.environment_get_sdfgi_y_scale(p_env); +float RendererSceneRender::environment_get_hddagi_normal_bias(RID p_env) const { + return environment_storage.environment_get_hddagi_normal_bias(p_env); +} + +float RendererSceneRender::environment_get_hddagi_reflection_bias(RID p_env) const { + return environment_storage.environment_get_hddagi_reflection_bias(p_env); +} + +float RendererSceneRender::environment_get_hddagi_probe_bias(RID p_env) const { + return environment_storage.environment_get_hddagi_probe_bias(p_env); +} + +float RendererSceneRender::environment_get_hddagi_occlusion_bias(RID p_env) const { + return environment_storage.environment_get_hddagi_occlusion_bias(p_env); +} + +RS::EnvironmentHDDAGICascadeFormat RendererSceneRender::environment_get_hddagi_cascade_format(RID p_env) const { + return environment_storage.environment_get_hddagi_cascade_format(p_env); } // Adjustments diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 719efa4df228..32758ec9083d 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -58,12 +58,12 @@ class RendererSceneRender { virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) = 0; virtual uint32_t geometry_instance_get_pair_mask() = 0; - /* SDFGI UPDATE */ + /* HDDAGI UPDATE */ - virtual void sdfgi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0; - virtual int sdfgi_get_pending_region_count(const Ref &p_render_buffers) const = 0; - virtual AABB sdfgi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const = 0; - virtual uint32_t sdfgi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const = 0; + virtual void hddagi_update(const Ref &p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0; + virtual int hddagi_get_pending_region_count(const Ref &p_render_buffers) const = 0; + virtual AABB hddagi_get_pending_region_bounds(const Ref &p_render_buffers, int p_region) const = 0; + virtual uint32_t hddagi_get_pending_region_cascade(const Ref &p_render_buffers, int p_region) const = 0; /* SKY API */ @@ -227,22 +227,27 @@ class RendererSceneRender { virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - // SDFGI - void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias); - bool environment_get_sdfgi_enabled(RID p_env) const; - int environment_get_sdfgi_cascades(RID p_env) const; - float environment_get_sdfgi_min_cell_size(RID p_env) const; - bool environment_get_sdfgi_use_occlusion(RID p_env) const; - float environment_get_sdfgi_bounce_feedback(RID p_env) const; - bool environment_get_sdfgi_read_sky_light(RID p_env) const; - float environment_get_sdfgi_energy(RID p_env) const; - float environment_get_sdfgi_normal_bias(RID p_env) const; - float environment_get_sdfgi_probe_bias(RID p_env) const; - RS::EnvironmentSDFGIYScale environment_get_sdfgi_y_scale(RID p_env) const; - - virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0; - virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; - virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0; + // HDDAGI + void environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, RS::EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient); + bool environment_get_hddagi_enabled(RID p_env) const; + int environment_get_hddagi_cascades(RID p_env) const; + float environment_get_hddagi_min_cell_size(RID p_env) const; + bool environment_get_hddagi_filter_probes(RID p_env) const; + float environment_get_hddagi_bounce_feedback(RID p_env) const; + bool environment_get_hddagi_read_sky_light(RID p_env) const; + float environment_get_hddagi_energy(RID p_env) const; + float environment_get_hddagi_normal_bias(RID p_env) const; + float environment_get_hddagi_reflection_bias(RID p_env) const; + float environment_get_hddagi_probe_bias(RID p_env) const; + float environment_get_hddagi_occlusion_bias(RID p_env) const; + RS::EnvironmentHDDAGICascadeFormat environment_get_hddagi_cascade_format(RID p_env) const; + bool environment_get_hddagi_filter_ambient(RID p_env) const; + bool environment_get_hddagi_filter_reflection(RID p_env) const; + + virtual void environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) = 0; + virtual void environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) = 0; + + virtual void environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) = 0; // Adjustment void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction); @@ -277,12 +282,12 @@ class RendererSceneRender { PagedArray instances; }; - struct RenderSDFGIData { + struct RenderHDDAGIData { int region = 0; PagedArray instances; }; - struct RenderSDFGIUpdateData { + struct RenderHDDAGIUpdateData { bool update_static = false; uint32_t static_cascade_count; uint32_t *static_cascade_indices = nullptr; @@ -312,7 +317,7 @@ class RendererSceneRender { void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect); }; - virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) = 0; + virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderHDDAGIData *p_render_hddagi_regions, int p_render_hddagi_region_count, const RenderHDDAGIUpdateData *p_hddagi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) = 0; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) = 0; @@ -334,7 +339,7 @@ class RendererSceneRender { virtual bool free(RID p_rid) = 0; - virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; + virtual void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index aa5e7d83cc84..ce43a37558fe 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -282,23 +282,28 @@ class RenderingMethod { virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - // SDFGI - virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; - - virtual bool environment_get_sdfgi_enabled(RID p_env) const = 0; - virtual int environment_get_sdfgi_cascades(RID p_env) const = 0; - virtual float environment_get_sdfgi_min_cell_size(RID p_env) const = 0; - virtual bool environment_get_sdfgi_use_occlusion(RID p_env) const = 0; - virtual float environment_get_sdfgi_bounce_feedback(RID p_env) const = 0; - virtual bool environment_get_sdfgi_read_sky_light(RID p_env) const = 0; - virtual float environment_get_sdfgi_energy(RID p_env) const = 0; - virtual float environment_get_sdfgi_normal_bias(RID p_env) const = 0; - virtual float environment_get_sdfgi_probe_bias(RID p_env) const = 0; - virtual RS::EnvironmentSDFGIYScale environment_get_sdfgi_y_scale(RID p_env) const = 0; - - virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0; - virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; - virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0; + // HDDAGI + virtual void environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, RS::EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient) = 0; + + virtual bool environment_get_hddagi_enabled(RID p_env) const = 0; + virtual int environment_get_hddagi_cascades(RID p_env) const = 0; + virtual float environment_get_hddagi_min_cell_size(RID p_env) const = 0; + virtual bool environment_get_hddagi_filter_probes(RID p_env) const = 0; + virtual float environment_get_hddagi_bounce_feedback(RID p_env) const = 0; + virtual bool environment_get_hddagi_read_sky_light(RID p_env) const = 0; + virtual float environment_get_hddagi_energy(RID p_env) const = 0; + virtual float environment_get_hddagi_normal_bias(RID p_env) const = 0; + virtual float environment_get_hddagi_reflection_bias(RID p_env) const = 0; + virtual float environment_get_hddagi_probe_bias(RID p_env) const = 0; + virtual float environment_get_hddagi_occlusion_bias(RID p_env) const = 0; + virtual float environment_get_hddagi_filter_ambient(RID p_env) const = 0; + virtual float environment_get_hddagi_filter_reflection(RID p_env) const = 0; + virtual RS::EnvironmentHDDAGICascadeFormat environment_get_hddagi_cascade_format(RID p_env) const = 0; + + virtual void environment_set_hddagi_frames_to_converge(RS::EnvironmentHDDAGIFramesToConverge p_frames) = 0; + virtual void environment_set_hddagi_frames_to_update_light(RS::EnvironmentHDDAGIFramesToUpdateLight p_update) = 0; + + virtual void environment_set_hddagi_inactive_probe_frames(RS::EnvironmentHDDAGIInactiveProbeFrames p_frames) = 0; virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; @@ -331,7 +336,7 @@ class RenderingMethod { virtual TypedArray bake_render_uv2(RID p_base, const TypedArray &p_material_overrides, const Size2i &p_image_size) = 0; virtual void voxel_gi_set_quality(RS::VoxelGIQuality) = 0; - virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; + virtual void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void render_empty_scene(const Ref &p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 51ff009eaf5d..44a86c308862 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -319,8 +319,8 @@ bool RenderingServerDefault::has_feature(Features p_feature) const { } #endif -void RenderingServerDefault::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { - RSG::scene->sdfgi_set_debug_probe_select(p_position, p_dir); +void RenderingServerDefault::hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { + RSG::scene->hddagi_set_debug_probe_select(p_position, p_dir); } void RenderingServerDefault::set_print_gpu_profile(bool p_enable) { diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 164ec3cc09fe..4feee056a402 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -378,7 +378,7 @@ class RenderingServerDefault : public RenderingServer { FUNC5(light_set_distance_fade, RID, bool, float, float, float) FUNC2(light_set_reverse_cull_face_mode, RID, bool) FUNC2(light_set_bake_mode, RID, LightBakeMode) - FUNC2(light_set_max_sdfgi_cascade, RID, uint32_t) + FUNC2(light_set_max_hddagi_cascade, RID, uint32_t) FUNC2(light_omni_set_shadow_mode, RID, LightOmniShadowMode) @@ -478,7 +478,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2(voxel_gi_set_interior, RID, bool) FUNC2(voxel_gi_set_use_two_bounces, RID, bool) - FUNC0(sdfgi_reset) + FUNC0(hddagi_reset) /* PARTICLES */ @@ -746,10 +746,11 @@ class RenderingServerDefault : public RenderingServer { FUNC2(environment_set_volumetric_fog_volume_size, int, int) FUNC1(environment_set_volumetric_fog_filter_active, bool) - FUNC11(environment_set_sdfgi, RID, bool, int, float, EnvironmentSDFGIYScale, bool, float, bool, float, float, float) - FUNC1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount) - FUNC1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge) - FUNC1(environment_set_sdfgi_frames_to_update_light, EnvironmentSDFGIFramesToUpdateLight) + FUNC15(environment_set_hddagi, RID, bool, int, EnvironmentHDDAGICascadeFormat, float, bool, float, bool, float, float, float, float, float, bool, bool) + FUNC1(environment_set_hddagi_frames_to_converge, EnvironmentHDDAGIFramesToConverge) + FUNC1(environment_set_hddagi_frames_to_update_light, EnvironmentHDDAGIFramesToUpdateLight) + + FUNC1(environment_set_hddagi_inactive_probe_frames, EnvironmentHDDAGIInactiveProbeFrames) FUNC3R(Ref, environment_bake_panorama, RID, bool, const Size2i &) @@ -1090,7 +1091,7 @@ class RenderingServerDefault : public RenderingServer { virtual bool is_low_end() const override; - virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; + virtual void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override; virtual void set_print_gpu_profile(bool p_enable) override; diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 1bbb5da6bb91..4ecd50801a6f 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -686,86 +686,114 @@ float RendererEnvironmentStorage::environment_get_ssil_normal_rejection(RID p_en return env->ssil_normal_rejection; } -// SDFGI +// HDDAGI -void RendererEnvironmentStorage::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { +void RendererEnvironmentStorage::environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, RS::EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL(env); #ifdef DEBUG_ENABLED if (OS::get_singleton()->get_current_rendering_method() != "forward_plus" && p_enable) { - WARN_PRINT_ONCE_ED("SDFGI can only be enabled when using the Forward+ rendering backend."); + WARN_PRINT_ONCE_ED("HDDAGI can only be enabled when using the Forward+ rendering backend."); } #endif - env->sdfgi_enabled = p_enable; - env->sdfgi_cascades = p_cascades; - env->sdfgi_min_cell_size = p_min_cell_size; - env->sdfgi_use_occlusion = p_use_occlusion; - env->sdfgi_bounce_feedback = p_bounce_feedback; - env->sdfgi_read_sky_light = p_read_sky; - env->sdfgi_energy = p_energy; - env->sdfgi_normal_bias = p_normal_bias; - env->sdfgi_probe_bias = p_probe_bias; - env->sdfgi_y_scale = p_y_scale; -} - -bool RendererEnvironmentStorage::environment_get_sdfgi_enabled(RID p_env) const { + env->hddagi_enabled = p_enable; + env->hddagi_cascades = p_cascades; + env->hddagi_cascade_format = p_cascade_format; + env->hddagi_min_cell_size = p_min_cell_size; + env->hddagi_filter_probes = p_filter_probes; + env->hddagi_bounce_feedback = p_bounce_feedback; + env->hddagi_read_sky_light = p_read_sky; + env->hddagi_energy = p_energy; + env->hddagi_normal_bias = p_normal_bias; + env->hddagi_reflection_bias = p_reflection_bias; + env->hddagi_probe_bias = p_probe_bias; + env->hddagi_occlusion_bias = p_occlusion_bias; + env->hddagi_filter_ambient = p_filter_ambient; + env->hddagi_filter_reflection = p_filter_reflection; +} + +bool RendererEnvironmentStorage::environment_get_hddagi_enabled(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, false); - return env->sdfgi_enabled; + return env->hddagi_enabled; } -int RendererEnvironmentStorage::environment_get_sdfgi_cascades(RID p_env) const { +int RendererEnvironmentStorage::environment_get_hddagi_cascades(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 4); - return env->sdfgi_cascades; + return env->hddagi_cascades; } -float RendererEnvironmentStorage::environment_get_sdfgi_min_cell_size(RID p_env) const { +float RendererEnvironmentStorage::environment_get_hddagi_min_cell_size(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 0.2); - return env->sdfgi_min_cell_size; + return env->hddagi_min_cell_size; +} + +bool RendererEnvironmentStorage::environment_get_hddagi_filter_probes(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_NULL_V(env, false); + return env->hddagi_filter_probes; +} + +bool RendererEnvironmentStorage::environment_get_hddagi_filter_reflection(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_NULL_V(env, false); + return env->hddagi_filter_reflection; } -bool RendererEnvironmentStorage::environment_get_sdfgi_use_occlusion(RID p_env) const { +bool RendererEnvironmentStorage::environment_get_hddagi_filter_ambient(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, false); - return env->sdfgi_use_occlusion; + return env->hddagi_filter_ambient; } -float RendererEnvironmentStorage::environment_get_sdfgi_bounce_feedback(RID p_env) const { +float RendererEnvironmentStorage::environment_get_hddagi_bounce_feedback(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 0.5); - return env->sdfgi_bounce_feedback; + return env->hddagi_bounce_feedback; } -bool RendererEnvironmentStorage::environment_get_sdfgi_read_sky_light(RID p_env) const { +bool RendererEnvironmentStorage::environment_get_hddagi_read_sky_light(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, true); - return env->sdfgi_read_sky_light; + return env->hddagi_read_sky_light; } -float RendererEnvironmentStorage::environment_get_sdfgi_energy(RID p_env) const { +float RendererEnvironmentStorage::environment_get_hddagi_energy(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 1.0); - return env->sdfgi_energy; + return env->hddagi_energy; +} + +float RendererEnvironmentStorage::environment_get_hddagi_normal_bias(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_NULL_V(env, 1.1); + return env->hddagi_normal_bias; +} + +float RendererEnvironmentStorage::environment_get_hddagi_reflection_bias(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_NULL_V(env, 1.1); + return env->hddagi_reflection_bias; } -float RendererEnvironmentStorage::environment_get_sdfgi_normal_bias(RID p_env) const { +float RendererEnvironmentStorage::environment_get_hddagi_probe_bias(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 1.1); - return env->sdfgi_normal_bias; + return env->hddagi_probe_bias; } -float RendererEnvironmentStorage::environment_get_sdfgi_probe_bias(RID p_env) const { +float RendererEnvironmentStorage::environment_get_hddagi_occlusion_bias(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL_V(env, 1.1); - return env->sdfgi_probe_bias; + return env->hddagi_occlusion_bias; } -RS::EnvironmentSDFGIYScale RendererEnvironmentStorage::environment_get_sdfgi_y_scale(RID p_env) const { +RS::EnvironmentHDDAGICascadeFormat RendererEnvironmentStorage::environment_get_hddagi_cascade_format(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_NULL_V(env, RS::ENV_SDFGI_Y_SCALE_75_PERCENT); - return env->sdfgi_y_scale; + ERR_FAIL_NULL_V(env, RS::ENV_HDDAGI_CASCADE_FORMAT_16x8x16); + return env->hddagi_cascade_format; } // Adjustments diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h index 9f78808ff757..4015fb35fe3c 100644 --- a/servers/rendering/storage/environment_storage.h +++ b/servers/rendering/storage/environment_storage.h @@ -134,17 +134,21 @@ class RendererEnvironmentStorage { float ssil_sharpness = 0.98; float ssil_normal_rejection = 1.0; - // SDFGI - bool sdfgi_enabled = false; - int sdfgi_cascades = 4; - float sdfgi_min_cell_size = 0.2; - bool sdfgi_use_occlusion = false; - float sdfgi_bounce_feedback = 0.5; - bool sdfgi_read_sky_light = true; - float sdfgi_energy = 1.0; - float sdfgi_normal_bias = 1.1; - float sdfgi_probe_bias = 1.1; - RS::EnvironmentSDFGIYScale sdfgi_y_scale = RS::ENV_SDFGI_Y_SCALE_75_PERCENT; + // HDDAGI + bool hddagi_enabled = false; + int hddagi_cascades = 4; + float hddagi_min_cell_size = 0.2; + bool hddagi_filter_probes = true; + bool hddagi_filter_reflection = false; + bool hddagi_filter_ambient = false; + float hddagi_bounce_feedback = 1.0; + bool hddagi_read_sky_light = true; + float hddagi_energy = 1.0; + float hddagi_normal_bias = 1.1; + float hddagi_reflection_bias = 2.0; + float hddagi_probe_bias = 2.0; + float hddagi_occlusion_bias = 0.1; + RS::EnvironmentHDDAGICascadeFormat hddagi_cascade_format = RS::ENV_HDDAGI_CASCADE_FORMAT_16x8x16; // Adjustments bool adjustments_enabled = false; @@ -284,18 +288,22 @@ class RendererEnvironmentStorage { float environment_get_ssil_sharpness(RID p_env) const; float environment_get_ssil_normal_rejection(RID p_env) const; - // SDFGI - void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias); - bool environment_get_sdfgi_enabled(RID p_env) const; - int environment_get_sdfgi_cascades(RID p_env) const; - float environment_get_sdfgi_min_cell_size(RID p_env) const; - bool environment_get_sdfgi_use_occlusion(RID p_env) const; - float environment_get_sdfgi_bounce_feedback(RID p_env) const; - bool environment_get_sdfgi_read_sky_light(RID p_env) const; - float environment_get_sdfgi_energy(RID p_env) const; - float environment_get_sdfgi_normal_bias(RID p_env) const; - float environment_get_sdfgi_probe_bias(RID p_env) const; - RS::EnvironmentSDFGIYScale environment_get_sdfgi_y_scale(RID p_env) const; + // HDDAGI + void environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, RS::EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient); + bool environment_get_hddagi_enabled(RID p_env) const; + int environment_get_hddagi_cascades(RID p_env) const; + float environment_get_hddagi_min_cell_size(RID p_env) const; + bool environment_get_hddagi_filter_probes(RID p_env) const; + float environment_get_hddagi_bounce_feedback(RID p_env) const; + bool environment_get_hddagi_read_sky_light(RID p_env) const; + float environment_get_hddagi_energy(RID p_env) const; + float environment_get_hddagi_normal_bias(RID p_env) const; + float environment_get_hddagi_reflection_bias(RID p_env) const; + float environment_get_hddagi_probe_bias(RID p_env) const; + float environment_get_hddagi_occlusion_bias(RID p_env) const; + bool environment_get_hddagi_filter_ambient(RID p_env) const; + bool environment_get_hddagi_filter_reflection(RID p_env) const; + RS::EnvironmentHDDAGICascadeFormat environment_get_hddagi_cascade_format(RID p_env) const; // Adjustment void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction); diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index 6a0adfa59662..f55901eb068c 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -60,7 +60,7 @@ class RendererLightStorage { virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) = 0; virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0; virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) = 0; - virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0; + virtual void light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) = 0; virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) = 0; @@ -83,7 +83,7 @@ class RendererLightStorage { virtual Color light_get_color(RID p_light) = 0; virtual bool light_get_reverse_cull_face_mode(RID p_light) const = 0; virtual RS::LightBakeMode light_get_bake_mode(RID p_light) = 0; - virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) = 0; + virtual uint32_t light_get_max_hddagi_cascade(RID p_light) = 0; virtual uint64_t light_get_version(RID p_light) const = 0; virtual uint32_t light_get_cull_mask(RID p_light) const = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 6509c426d66a..88023622e9a0 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2468,7 +2468,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("light_set_distance_fade", "decal", "enabled", "begin", "shadow", "length"), &RenderingServer::light_set_distance_fade); ClassDB::bind_method(D_METHOD("light_set_reverse_cull_face_mode", "light", "enabled"), &RenderingServer::light_set_reverse_cull_face_mode); ClassDB::bind_method(D_METHOD("light_set_bake_mode", "light", "bake_mode"), &RenderingServer::light_set_bake_mode); - ClassDB::bind_method(D_METHOD("light_set_max_sdfgi_cascade", "light", "cascade"), &RenderingServer::light_set_max_sdfgi_cascade); + ClassDB::bind_method(D_METHOD("light_set_max_hddagi_cascade", "light", "cascade"), &RenderingServer::light_set_max_hddagi_cascade); ClassDB::bind_method(D_METHOD("light_omni_set_shadow_mode", "light", "mode"), &RenderingServer::light_omni_set_shadow_mode); @@ -2593,7 +2593,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC); BIND_ENUM_CONSTANT(DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC); - /* GI API (affects VoxelGI and SDFGI) */ + /* GI API (affects VoxelGI and HDDAGI) */ ClassDB::bind_method(D_METHOD("gi_set_use_half_resolution", "half_resolution"), &RenderingServer::gi_set_use_half_resolution); @@ -2901,8 +2901,8 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SSIL); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_PSSM_SPLITS); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_DECAL_ATLAS); - BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI); - BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI_PROBES); + BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_HDDAGI); + BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_HDDAGI_PROBES); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_GI_BUFFER); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_DISABLE_LOD); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS); @@ -2978,17 +2978,21 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr); ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "power", "detail", "horizon", "sharpness", "light_affect", "ao_channel_affect"), &RenderingServer::environment_set_ssao); + ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect", "fog_mode"), &RenderingServer::environment_set_fog, DEFVAL(RS::ENV_FOG_MODE_EXPONENTIAL)); - ClassDB::bind_method(D_METHOD("environment_set_sdfgi", "env", "enable", "cascades", "min_cell_size", "y_scale", "use_occlusion", "bounce_feedback", "read_sky", "energy", "normal_bias", "probe_bias"), &RenderingServer::environment_set_sdfgi); + ClassDB::bind_method(D_METHOD("environment_set_hddagi", "env", "enable", "cascades", "cascade_format", "min_cell_size", "filter_probes", "bounce_feedback", "read_sky", "energy", "normal_bias", "reflection_bias", "probe_bias", "occlusion_bias", "filter_reflection", "filter_ambient"), &RenderingServer::environment_set_hddagi); + ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject", "sky_affect"), &RenderingServer::environment_set_volumetric_fog); ClassDB::bind_method(D_METHOD("environment_glow_set_use_bicubic_upscale", "enable"), &RenderingServer::environment_glow_set_use_bicubic_upscale); ClassDB::bind_method(D_METHOD("environment_set_ssr_roughness_quality", "quality"), &RenderingServer::environment_set_ssr_roughness_quality); ClassDB::bind_method(D_METHOD("environment_set_ssao_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssao_quality); ClassDB::bind_method(D_METHOD("environment_set_ssil_quality", "quality", "half_size", "adaptive_target", "blur_passes", "fadeout_from", "fadeout_to"), &RenderingServer::environment_set_ssil_quality); - ClassDB::bind_method(D_METHOD("environment_set_sdfgi_ray_count", "ray_count"), &RenderingServer::environment_set_sdfgi_ray_count); - ClassDB::bind_method(D_METHOD("environment_set_sdfgi_frames_to_converge", "frames"), &RenderingServer::environment_set_sdfgi_frames_to_converge); - ClassDB::bind_method(D_METHOD("environment_set_sdfgi_frames_to_update_light", "frames"), &RenderingServer::environment_set_sdfgi_frames_to_update_light); + ClassDB::bind_method(D_METHOD("environment_set_hddagi_frames_to_converge", "frames"), &RenderingServer::environment_set_hddagi_frames_to_converge); + ClassDB::bind_method(D_METHOD("environment_set_hddagi_frames_to_update_light", "frames"), &RenderingServer::environment_set_hddagi_frames_to_update_light); + + ClassDB::bind_method(D_METHOD("environment_set_hddagi_inactive_probe_frames", "frames"), &RenderingServer::environment_set_hddagi_inactive_probe_frames); + ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog_volume_size", "size", "depth"), &RenderingServer::environment_set_volumetric_fog_volume_size); ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog_filter_active", "active"), &RenderingServer::environment_set_volumetric_fog_filter_active); @@ -3048,33 +3052,29 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_HIGH); BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_ULTRA); - BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_50_PERCENT); - BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_75_PERCENT); - BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_100_PERCENT); - - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_4); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_8); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_16); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_32); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_64); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_96); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_128); - BIND_ENUM_CONSTANT(ENV_SDFGI_RAY_COUNT_MAX); - - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_5_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_10_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_15_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_20_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_25_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_IN_30_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_CONVERGE_MAX); - - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME); - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_IN_2_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_IN_8_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_IN_16_FRAMES); - BIND_ENUM_CONSTANT(ENV_SDFGI_UPDATE_LIGHT_MAX); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CASCADE_FORMAT_16x8x16); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CASCADE_FORMAT_16x16x16); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CASCADE_FORMAT_MAX); + + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_IN_6_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_IN_12_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_IN_18_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_IN_24_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_IN_32_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_CONVERGE_MAX); + + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_IN_1_FRAME); + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_IN_2_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_IN_4_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_IN_8_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_IN_16_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_UPDATE_LIGHT_MAX); + + BIND_ENUM_CONSTANT(ENV_HDDAGI_INACTIVE_PROBE_1_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_INACTIVE_PROBE_2_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_INACTIVE_PROBE_4_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_INACTIVE_PROBE_8_FRAMES); + BIND_ENUM_CONSTANT(ENV_HDDAGI_INACTIVE_PROBE_MAX); BIND_ENUM_CONSTANT(SUB_SURFACE_SCATTERING_QUALITY_DISABLED); BIND_ENUM_CONSTANT(SUB_SURFACE_SCATTERING_QUALITY_LOW); @@ -3558,7 +3558,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/reflection_atlas/reflection_size.mobile", PROPERTY_HINT_RANGE, "0,2048,1"), 128); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/reflections/reflection_atlas/reflection_count", PROPERTY_HINT_RANGE, "0,256,1"), 64); - GLOBAL_DEF("rendering/global_illumination/gi/use_half_resolution", false); + GLOBAL_DEF("rendering/global_illumination/gi/use_half_resolution", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/voxel_gi/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"), 0); @@ -3620,9 +3620,9 @@ void RenderingServer::init() { GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/lightmapping/specular_occlusion/mode", PROPERTY_HINT_ENUM, "Disabled (Fastest),Reduce (Faster),Conservative (Fast)"), 2); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2); - GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"), 1); - GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"), 5); - GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/frames_to_update_lights", PROPERTY_HINT_ENUM, "1 (Slower),2,4,8,16 (Faster)"), 2); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/hddagi/frames_to_converge", PROPERTY_HINT_ENUM, "6 (Less Latency/Mem usage & Low Quality),12,18,24,32 (More Latency / Mem Usage & High Quality)"), 1); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/hddagi/frames_to_update_lights", PROPERTY_HINT_ENUM, "1 (Faster),2,4,8,16 (Slower)"), 2); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/hddagi/frames_to_update_inactive_probes", PROPERTY_HINT_ENUM, "1 (Faster),2,4,8,16 (Slower)"), 3); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/volumetric_fog/volume_size", PROPERTY_HINT_RANGE, "16,512,1"), 64); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/environment/volumetric_fog/volume_depth", PROPERTY_HINT_RANGE, "16,512,1"), 64); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index c801c65b12a4..1a4e2aa5978f 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -516,7 +516,7 @@ class RenderingServer : public Object { }; virtual void light_set_bake_mode(RID p_light, LightBakeMode p_bake_mode) = 0; - virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0; + virtual void light_set_max_hddagi_cascade(RID p_light, uint32_t p_cascade) = 0; // Omni light enum LightOmniShadowMode { @@ -669,7 +669,7 @@ class RenderingServer : public Object { virtual void voxel_gi_set_quality(VoxelGIQuality) = 0; - virtual void sdfgi_reset() = 0; + virtual void hddagi_reset() = 0; /* LIGHTMAP */ @@ -1022,8 +1022,8 @@ class RenderingServer : public Object { VIEWPORT_DEBUG_DRAW_SSIL, VIEWPORT_DEBUG_DRAW_PSSM_SPLITS, VIEWPORT_DEBUG_DRAW_DECAL_ATLAS, - VIEWPORT_DEBUG_DRAW_SDFGI, - VIEWPORT_DEBUG_DRAW_SDFGI_PROBES, + VIEWPORT_DEBUG_DRAW_HDDAGI, + VIEWPORT_DEBUG_DRAW_HDDAGI_PROBES, VIEWPORT_DEBUG_DRAW_GI_BUFFER, VIEWPORT_DEBUG_DRAW_DISABLE_LOD, VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS, @@ -1202,49 +1202,45 @@ class RenderingServer : public Object { virtual void environment_set_ssil_quality(EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - enum EnvironmentSDFGIYScale { - ENV_SDFGI_Y_SCALE_50_PERCENT, - ENV_SDFGI_Y_SCALE_75_PERCENT, - ENV_SDFGI_Y_SCALE_100_PERCENT, + enum EnvironmentHDDAGICascadeFormat { + ENV_HDDAGI_CASCADE_FORMAT_16x8x16, + ENV_HDDAGI_CASCADE_FORMAT_16x16x16, + ENV_HDDAGI_CASCADE_FORMAT_MAX, }; - virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; + virtual void environment_set_hddagi(RID p_env, bool p_enable, int p_cascades, EnvironmentHDDAGICascadeFormat p_cascade_format, float p_min_cell_size, bool p_filter_probes, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_reflection_bias, float p_probe_bias, float p_occlusion_bias, bool p_filter_reflection, bool p_filter_ambient) = 0; - enum EnvironmentSDFGIRayCount { - ENV_SDFGI_RAY_COUNT_4, - ENV_SDFGI_RAY_COUNT_8, - ENV_SDFGI_RAY_COUNT_16, - ENV_SDFGI_RAY_COUNT_32, - ENV_SDFGI_RAY_COUNT_64, - ENV_SDFGI_RAY_COUNT_96, - ENV_SDFGI_RAY_COUNT_128, - ENV_SDFGI_RAY_COUNT_MAX, + enum EnvironmentHDDAGIFramesToConverge { + ENV_HDDAGI_CONVERGE_IN_6_FRAMES, + ENV_HDDAGI_CONVERGE_IN_12_FRAMES, + ENV_HDDAGI_CONVERGE_IN_18_FRAMES, + ENV_HDDAGI_CONVERGE_IN_24_FRAMES, + ENV_HDDAGI_CONVERGE_IN_32_FRAMES, + ENV_HDDAGI_CONVERGE_MAX }; - virtual void environment_set_sdfgi_ray_count(EnvironmentSDFGIRayCount p_ray_count) = 0; + virtual void environment_set_hddagi_frames_to_converge(EnvironmentHDDAGIFramesToConverge p_frames) = 0; - enum EnvironmentSDFGIFramesToConverge { - ENV_SDFGI_CONVERGE_IN_5_FRAMES, - ENV_SDFGI_CONVERGE_IN_10_FRAMES, - ENV_SDFGI_CONVERGE_IN_15_FRAMES, - ENV_SDFGI_CONVERGE_IN_20_FRAMES, - ENV_SDFGI_CONVERGE_IN_25_FRAMES, - ENV_SDFGI_CONVERGE_IN_30_FRAMES, - ENV_SDFGI_CONVERGE_MAX + enum EnvironmentHDDAGIFramesToUpdateLight { + ENV_HDDAGI_UPDATE_LIGHT_IN_1_FRAME, + ENV_HDDAGI_UPDATE_LIGHT_IN_2_FRAMES, + ENV_HDDAGI_UPDATE_LIGHT_IN_4_FRAMES, + ENV_HDDAGI_UPDATE_LIGHT_IN_8_FRAMES, + ENV_HDDAGI_UPDATE_LIGHT_IN_16_FRAMES, + ENV_HDDAGI_UPDATE_LIGHT_MAX, }; - virtual void environment_set_sdfgi_frames_to_converge(EnvironmentSDFGIFramesToConverge p_frames) = 0; + virtual void environment_set_hddagi_frames_to_update_light(EnvironmentHDDAGIFramesToUpdateLight p_update) = 0; - enum EnvironmentSDFGIFramesToUpdateLight { - ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME, - ENV_SDFGI_UPDATE_LIGHT_IN_2_FRAMES, - ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES, - ENV_SDFGI_UPDATE_LIGHT_IN_8_FRAMES, - ENV_SDFGI_UPDATE_LIGHT_IN_16_FRAMES, - ENV_SDFGI_UPDATE_LIGHT_MAX, + enum EnvironmentHDDAGIInactiveProbeFrames { + ENV_HDDAGI_INACTIVE_PROBE_1_FRAMES, + ENV_HDDAGI_INACTIVE_PROBE_2_FRAMES, + ENV_HDDAGI_INACTIVE_PROBE_4_FRAMES, + ENV_HDDAGI_INACTIVE_PROBE_8_FRAMES, + ENV_HDDAGI_INACTIVE_PROBE_MAX }; - virtual void environment_set_sdfgi_frames_to_update_light(EnvironmentSDFGIFramesToUpdateLight p_update) = 0; + virtual void environment_set_hddagi_inactive_probe_frames(EnvironmentHDDAGIInactiveProbeFrames p_frames) = 0; enum EnvironmentFogMode { ENV_FOG_MODE_EXPONENTIAL, @@ -1701,7 +1697,7 @@ class RenderingServer : public Object { virtual RID get_test_texture(); virtual RID get_white_texture(); - virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; + virtual void hddagi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual RID make_sphere_mesh(int p_lats, int p_lons, real_t p_radius); @@ -1832,10 +1828,10 @@ VARIANT_ENUM_CAST(RenderingServer::EnvironmentToneMapper); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSRRoughnessQuality); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSAOQuality); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSILQuality); -VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToConverge); -VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIRayCount); -VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToUpdateLight); -VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIYScale); +VARIANT_ENUM_CAST(RenderingServer::EnvironmentHDDAGIFramesToConverge); +VARIANT_ENUM_CAST(RenderingServer::EnvironmentHDDAGICascadeFormat); +VARIANT_ENUM_CAST(RenderingServer::EnvironmentHDDAGIFramesToUpdateLight); +VARIANT_ENUM_CAST(RenderingServer::EnvironmentHDDAGIInactiveProbeFrames); VARIANT_ENUM_CAST(RenderingServer::SubSurfaceScatteringQuality); VARIANT_ENUM_CAST(RenderingServer::DOFBlurQuality); VARIANT_ENUM_CAST(RenderingServer::DOFBokehShape);