Merge pull request #77496 from clayjohn/GLES3-shadows

Implement 3D shadows in the GL Compatibility renderer
This commit is contained in:
Yuri Sizov 2023-09-28 20:03:53 +02:00
commit 7ae0fa1083
10 changed files with 2385 additions and 396 deletions

View file

@ -134,6 +134,16 @@ void CopyEffects::copy_screen() {
draw_screen_triangle();
}
void CopyEffects::copy_cube_to_rect(const Rect2 &p_rect) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_OCTAHEDRAL);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION);
draw_screen_quad();
}
// Intended for efficiently mipmapping textures.
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
GLuint framebuffers[2];

View file

@ -63,6 +63,7 @@ public:
// These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array.
void copy_to_rect(const Rect2 &p_rect);
void copy_screen();
void copy_cube_to_rect(const Rect2 &p_rect);
void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
void gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size);
void set_color(const Color &p_color, const Rect2i &p_region);

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,6 @@ enum RenderListType {
enum PassMode {
PASS_MODE_COLOR,
PASS_MODE_COLOR_TRANSPARENT,
PASS_MODE_COLOR_ADDITIVE,
PASS_MODE_SHADOW,
PASS_MODE_DEPTH,
};
@ -75,6 +74,8 @@ enum SceneUniformLocation {
SCENE_SPOTLIGHT_UNIFORM_LOCATION,
SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
SCENE_MULTIVIEW_UNIFORM_LOCATION,
SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION,
SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION,
};
enum SkyUniformLocation {
@ -109,6 +110,7 @@ struct RenderDataGLES3 {
const PagedArray<RID> *reflection_probes = nullptr;
RID environment;
RID camera_attributes;
RID shadow_atlas;
RID reflection_probe;
int reflection_probe_pass = 0;
@ -116,10 +118,16 @@ struct RenderDataGLES3 {
float screen_mesh_lod_threshold = 0.0;
uint32_t directional_light_count = 0;
uint32_t directional_shadow_count = 0;
uint32_t spot_light_count = 0;
uint32_t omni_light_count = 0;
RenderingMethod::RenderInfo *render_info = nullptr;
/* Shadow data */
const RendererSceneRender::RenderShadowData *render_shadows = nullptr;
int render_shadow_count = 0;
};
class RasterizerCanvasGLES3;
@ -173,11 +181,36 @@ private:
float size;
uint32_t enabled; // For use by SkyShaders
float pad[2];
float pad;
float shadow_opacity;
float specular;
};
static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes");
struct ShadowData {
float shadow_matrix[16];
float light_position[3];
float shadow_normal_bias;
float pad[3];
float shadow_atlas_pixel_size;
};
static_assert(sizeof(ShadowData) % 16 == 0, "ShadowData size must be a multiple of 16 bytes");
struct DirectionalShadowData {
float direction[3];
float shadow_atlas_pixel_size;
float shadow_normal_bias[4];
float shadow_split_offsets[4];
float shadow_matrices[4][16];
float fade_from;
float fade_to;
uint32_t blend_splits; // Not exposed to the shader.
uint32_t pad;
};
static_assert(sizeof(DirectionalShadowData) % 16 == 0, "DirectionalShadowData size must be a multiple of 16 bytes");
class GeometryInstanceGLES3;
// Cached data for drawing surfaces
@ -221,6 +254,8 @@ private:
uint32_t surface_index = 0;
uint32_t lod_index = 0;
uint32_t index_count = 0;
int32_t light_pass_index = -1;
bool finished_base_pass = false;
void *surface = nullptr;
GLES3::SceneShaderData *shader = nullptr;
@ -245,14 +280,23 @@ private:
bool using_projectors = false;
bool using_softshadows = false;
uint32_t omni_light_count = 0;
LocalVector<RID> omni_lights;
uint32_t spot_light_count = 0;
LocalVector<RID> spot_lights;
struct LightPass {
int32_t light_id = -1; // Position in the light uniform buffer.
int32_t shadow_id = -1; // Position in the shadow uniform buffer.
RID light_instance_rid;
bool is_omni = false;
};
LocalVector<LightPass> light_passes;
uint32_t paired_omni_light_count = 0;
uint32_t paired_spot_light_count = 0;
LocalVector<RID> paired_omni_lights;
LocalVector<RID> paired_spot_lights;
LocalVector<uint32_t> omni_light_gl_cache;
LocalVector<uint32_t> spot_light_gl_cache;
//used during setup
// Used during setup.
GeometryInstanceSurface *surface_caches = nullptr;
SelfList<GeometryInstanceGLES3> dirty_list_element;
@ -336,10 +380,11 @@ private:
float fog_light_color[3];
float fog_sun_scatter;
float shadow_bias;
float pad;
uint32_t camera_visible_layers;
uint32_t pad1;
uint32_t pad2;
uint32_t pad3;
bool pancake_shadows;
};
static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
@ -378,16 +423,22 @@ private:
LightData *omni_lights = nullptr;
LightData *spot_lights = nullptr;
ShadowData *positional_shadows = nullptr;
InstanceSort<GLES3::LightInstance> *omni_light_sort;
InstanceSort<GLES3::LightInstance> *spot_light_sort;
GLuint omni_light_buffer = 0;
GLuint spot_light_buffer = 0;
GLuint positional_shadow_buffer = 0;
uint32_t omni_light_count = 0;
uint32_t spot_light_count = 0;
RS::ShadowQuality positional_shadow_quality = RS::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
DirectionalLightData *directional_lights = nullptr;
GLuint directional_light_buffer = 0;
DirectionalShadowData *directional_shadows = nullptr;
GLuint directional_shadow_buffer = 0;
RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
} scene_state;
struct RenderListParameters {
@ -462,9 +513,11 @@ private:
RenderList render_list[RENDER_LIST_MAX];
void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count);
void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows);
void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count, uint32_t &r_directional_shadow_count);
void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias = 0.0);
void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false);
void _render_shadows(const RenderDataGLES3 *p_render_data);
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr);
template <PassMode p_pass_mode>
_FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false);
@ -477,7 +530,7 @@ protected:
float screen_space_roughness_limiter_amount = 0.25;
float screen_space_roughness_limiter_limit = 0.18;
void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer);
void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas);
/* Camera Attributes */

View file

@ -6,6 +6,7 @@ mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY
mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR
mode_mipmap = #define MODE_MIPMAP
mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION
mode_cube_to_octahedral = #define CUBE_TO_OCTAHEDRAL \n#define USE_COPY_SECTION
#[specializations]
@ -50,8 +51,20 @@ uniform vec4 color_in;
uniform highp vec2 pixel_size;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
uniform samplerCube source_cube; // texunit:0
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
#else
uniform sampler2D source; // texunit:0
#endif
layout(location = 0) out vec4 frag_color;
void main() {
@ -90,4 +103,11 @@ void main() {
frag_color += (F + G + L + K) * lesser_weight;
frag_color += (G + H + M + L) * lesser_weight;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
// Treat the UV coordinates as 0-1 encoded octahedral coordinates.
vec3 dir = oct_to_vec3(uv_interp * 2.0 - 1.0);
frag_color = texture(source_cube, dir);
#endif
}

View file

@ -1,10 +1,8 @@
/* clang-format off */
#[modes]
mode_color = #define BASE_PASS
mode_color_instancing = #define BASE_PASS \n#define USE_INSTANCING
mode_additive = #define USE_ADDITIVE_LIGHTING
mode_additive_instancing = #define USE_ADDITIVE_LIGHTING \n#define USE_INSTANCING
mode_color =
mode_color_instancing = \n#define USE_INSTANCING
mode_depth = #define MODE_RENDER_DEPTH
mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING
@ -17,6 +15,19 @@ DISABLE_LIGHT_SPOT = false
DISABLE_FOG = false
USE_RADIANCE_MAP = true
USE_MULTIVIEW = false
RENDER_SHADOWS = false
RENDER_SHADOWS_LINEAR = false
SHADOW_MODE_PCF_5 = false
SHADOW_MODE_PCF_13 = false
LIGHT_USE_PSSM2 = false
LIGHT_USE_PSSM4 = false
LIGHT_USE_PSSM_BLEND = false
BASE_PASS = true
USE_ADDITIVE_LIGHTING = false
// We can only use one type of light per additive pass. This means that if USE_ADDITIVE_LIGHTING is defined, and
// these are false, we are doing a directional light pass.
ADDITIVE_OMNI = false
ADDITIVE_SPOT = false
#[vertex]
@ -33,6 +44,12 @@ USE_MULTIVIEW = false
#endif
#endif
#ifdef MODE_UNSHADED
#ifdef USE_ADDITIVE_LIGHTING
#undef USE_ADDITIVE_LIGHTING
#endif
#endif // MODE_UNSHADED
/*
from RenderingServer:
ARRAY_VERTEX = 0, // RG32F or RGB32F (depending on 2D bit)
@ -151,13 +168,56 @@ layout(std140) uniform SceneData { // ubo:2
vec3 fog_light_color;
float fog_sun_scatter;
float shadow_bias;
float pad;
uint camera_visible_layers;
uint pad3;
uint pad4;
uint pad5;
bool pancake_shadows;
}
scene_data;
#ifdef USE_ADDITIVE_LIGHTING
#if defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
struct PositionalShadowData {
highp mat4 shadow_matrix;
highp vec3 light_position;
highp float shadow_normal_bias;
vec3 pad;
highp float shadow_atlas_pixel_size;
};
layout(std140) uniform PositionalShadows { // ubo:9
PositionalShadowData positional_shadows[MAX_LIGHT_DATA_STRUCTS];
};
uniform lowp uint positional_shadow_index;
#else // ADDITIVE_DIRECTIONAL
struct DirectionalShadowData {
highp vec3 direction;
highp float shadow_atlas_pixel_size;
highp vec4 shadow_normal_bias;
highp vec4 shadow_split_offsets;
highp mat4 shadow_matrix1;
highp mat4 shadow_matrix2;
highp mat4 shadow_matrix3;
highp mat4 shadow_matrix4;
mediump float fade_from;
mediump float fade_to;
mediump vec2 pad;
};
layout(std140) uniform DirectionalShadows { // ubo:10
DirectionalShadowData directional_shadows[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
};
uniform lowp uint directional_shadow_index;
#endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
#endif // USE_ADDITIVE_LIGHTING
#ifdef USE_MULTIVIEW
layout(std140) uniform MultiviewData { // ubo:8
highp mat4 projection_matrix_view[MAX_VIEWS];
@ -201,6 +261,19 @@ out vec3 tangent_interp;
out vec3 binormal_interp;
#endif
#ifdef USE_ADDITIVE_LIGHTING
out highp vec4 shadow_coord;
#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
out highp vec4 shadow_coord2;
#endif
#ifdef LIGHT_USE_PSSM4
out highp vec4 shadow_coord3;
out highp vec4 shadow_coord4;
#endif //LIGHT_USE_PSSM4
#endif
#ifdef MATERIAL_UNIFORMS_USED
/* clang-format off */
@ -351,6 +424,50 @@ void main() {
binormal_interp = binormal;
#endif
// Calculate shadows.
#ifdef USE_ADDITIVE_LIGHTING
#if defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
// Apply normal bias at draw time to avoid issues with scaling non-fused geometry.
vec3 light_rel_vec = positional_shadows[positional_shadow_index].light_position - vertex_interp;
float light_length = length(light_rel_vec);
float aNdotL = abs(dot(normalize(normal_interp), normalize(light_rel_vec)));
vec3 normal_offset = (1.0 - aNdotL) * positional_shadows[positional_shadow_index].shadow_normal_bias * light_length * normal_interp;
#ifdef ADDITIVE_SPOT
// Calculate coord here so we can take advantage of prefetch.
shadow_coord = positional_shadows[positional_shadow_index].shadow_matrix * vec4(vertex_interp + normal_offset, 1.0);
#endif
#ifdef ADDITIVE_OMNI
// Can't interpolate unit direction nicely, so forget about prefetch.
shadow_coord = vec4(vertex_interp + normal_offset, 1.0);
#endif
#else // ADDITIVE_DIRECTIONAL
vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(directional_shadows[directional_shadow_index].direction, -normalize(normal_interp))));
vec3 normal_offset = base_normal_bias * directional_shadows[directional_shadow_index].shadow_normal_bias.x;
shadow_coord = directional_shadows[directional_shadow_index].shadow_matrix1 * vec4(vertex_interp + normal_offset, 1.0);
#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
normal_offset = base_normal_bias * directional_shadows[directional_shadow_index].shadow_normal_bias.y;
shadow_coord2 = directional_shadows[directional_shadow_index].shadow_matrix2 * vec4(vertex_interp + normal_offset, 1.0);
#endif
#ifdef LIGHT_USE_PSSM4
normal_offset = base_normal_bias * directional_shadows[directional_shadow_index].shadow_normal_bias.z;
shadow_coord3 = directional_shadows[directional_shadow_index].shadow_matrix3 * vec4(vertex_interp + normal_offset, 1.0);
normal_offset = base_normal_bias * directional_shadows[directional_shadow_index].shadow_normal_bias.w;
shadow_coord4 = directional_shadows[directional_shadow_index].shadow_matrix4 * vec4(vertex_interp + normal_offset, 1.0);
#endif //LIGHT_USE_PSSM4
#endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
#endif // USE_ADDITIVE_LIGHTING
#if defined(RENDER_SHADOWS) && !defined(RENDER_SHADOWS_LINEAR)
// This is an optimized version of normalize(vertex_interp) * scene_data.shadow_bias / length(vertex_interp).
float light_length_sq = dot(vertex_interp, vertex_interp);
vertex_interp += vertex_interp * scene_data.shadow_bias / light_length_sq;
#endif
#if defined(OVERRIDE_POSITION)
gl_Position = position;
#else
@ -372,17 +489,22 @@ void main() {
#endif
#endif
#ifdef MODE_UNSHADED
#ifdef USE_ADDITIVE_LIGHTING
#undef USE_ADDITIVE_LIGHTING
#endif
#endif // MODE_UNSHADED
#ifndef MODE_RENDER_DEPTH
#include "tonemap_inc.glsl"
#endif
#include "stdlib_inc.glsl"
/* texture unit usage, N is max_texture_unity-N
/* texture unit usage, N is max_texture_unit-N
1-color correction // In tonemap_inc.glsl
2-radiance
3-directional_shadow
4-positional_shadow
3-shadow
5-screen
6-depth
@ -422,6 +544,19 @@ in vec3 normal_interp;
in highp vec3 vertex_interp;
#ifdef USE_ADDITIVE_LIGHTING
in highp vec4 shadow_coord;
#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4)
in highp vec4 shadow_coord2;
#endif
#ifdef LIGHT_USE_PSSM4
in highp vec4 shadow_coord3;
in highp vec4 shadow_coord4;
#endif //LIGHT_USE_PSSM4
#endif
#ifdef USE_RADIANCE_MAP
#define RADIANCE_MAX_LOD 5.0
@ -483,10 +618,11 @@ layout(std140) uniform SceneData { // ubo:2
vec3 fog_light_color;
float fog_sun_scatter;
float shadow_bias;
float pad;
uint camera_visible_layers;
uint pad3;
uint pad4;
uint pad5;
bool pancake_shadows;
}
scene_data;
@ -505,15 +641,17 @@ multiview_data;
/* clang-format on */
#ifndef MODE_RENDER_DEPTH
// Directional light data.
#ifndef DISABLE_LIGHT_DIRECTIONAL
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
struct DirectionalLightData {
mediump vec3 direction;
mediump float energy;
mediump vec3 color;
mediump float size;
mediump vec3 pad;
mediump vec2 pad;
mediump float shadow_opacity;
mediump float specular;
};
@ -521,10 +659,15 @@ layout(std140) uniform DirectionalLights { // ubo:7
DirectionalLightData directional_lights[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
};
#if defined(USE_ADDITIVE_LIGHTING) && (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
// Directional shadows can be in the base pass or in the additive passes
uniform highp sampler2DShadow directional_shadow_atlas; // texunit:-3
#endif // defined(USE_ADDITIVE_LIGHTING) && (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
#endif // !DISABLE_LIGHT_DIRECTIONAL
// Omni and spot light data.
#if !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
#if !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
struct LightData { // This structure needs to be as packed as possible.
highp vec3 position;
@ -542,27 +685,119 @@ struct LightData { // This structure needs to be as packed as possible.
mediump float shadow_opacity;
};
#ifndef DISABLE_LIGHT_OMNI
#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
layout(std140) uniform OmniLightData { // ubo:5
LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
};
#ifdef BASE_PASS
uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
uniform uint omni_light_count;
#endif
#endif // BASE_PASS
#endif // DISABLE_LIGHT_OMNI
#ifndef DISABLE_LIGHT_SPOT
#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
layout(std140) uniform SpotLightData { // ubo:6
LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
};
#ifdef BASE_PASS
uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
uniform uint spot_light_count;
#endif
#endif // BASE_PASS
#endif // DISABLE_LIGHT_SPOT
#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
#ifdef USE_ADDITIVE_LIGHTING
uniform highp samplerCubeShadow positional_shadow; // texunit:-4
#ifdef ADDITIVE_OMNI
uniform highp samplerCubeShadow omni_shadow_texture; // texunit:-3
uniform lowp uint omni_light_index;
#endif
#ifdef ADDITIVE_SPOT
uniform highp sampler2DShadow spot_shadow_texture; // texunit:-3
uniform lowp uint spot_light_index;
#endif
#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
#if defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
struct PositionalShadowData {
highp mat4 shadow_matrix;
highp vec3 light_position;
highp float shadow_normal_bias;
vec3 pad;
highp float shadow_atlas_pixel_size;
};
layout(std140) uniform PositionalShadows { // ubo:9
PositionalShadowData positional_shadows[MAX_LIGHT_DATA_STRUCTS];
};
uniform lowp uint positional_shadow_index;
#else // ADDITIVE_DIRECTIONAL
struct DirectionalShadowData {
highp vec3 direction;
highp float shadow_atlas_pixel_size;
highp vec4 shadow_normal_bias;
highp vec4 shadow_split_offsets;
highp mat4 shadow_matrix1;
highp mat4 shadow_matrix2;
highp mat4 shadow_matrix3;
highp mat4 shadow_matrix4;
mediump float fade_from;
mediump float fade_to;
mediump vec2 pad;
};
layout(std140) uniform DirectionalShadows { // ubo:10
DirectionalShadowData directional_shadows[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
};
uniform lowp uint directional_shadow_index;
#endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
#if !defined(ADDITIVE_OMNI)
float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4 pos) {
float avg = textureProj(shadow, pos);
#ifdef SHADOW_MODE_PCF_13
pos /= pos.w;
avg += textureProj(shadow, vec4(pos.xy + vec2(shadow_pixel_size * 2.0, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(-shadow_pixel_size * 2.0, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, shadow_pixel_size * 2.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, -shadow_pixel_size * 2.0), pos.zw));
// Early bail if distant samples are fully shaded (or none are shaded) to improve performance.
if (avg <= 0.000001) {
// None shaded at all.
return 0.0;
} else if (avg >= 4.999999) {
// All fully shaded.
return 1.0;
}
avg += textureProj(shadow, vec4(pos.xy + vec2(shadow_pixel_size, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(-shadow_pixel_size, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, -shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(shadow_pixel_size, shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(-shadow_pixel_size, shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(shadow_pixel_size, -shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(-shadow_pixel_size, -shadow_pixel_size), pos.zw));
return avg * (1.0 / 13.0);
#endif
#ifdef SHADOW_MODE_PCF_5
pos /= pos.w;
avg += textureProj(shadow, vec4(pos.xy + vec2(shadow_pixel_size, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(-shadow_pixel_size, 0.0), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, shadow_pixel_size), pos.zw));
avg += textureProj(shadow, vec4(pos.xy + vec2(0.0, -shadow_pixel_size), pos.zw));
return avg * (1.0 / 5.0);
#endif
return avg;
}
#endif //!defined(ADDITIVE_OMNI)
#endif // USE_ADDITIVE_LIGHTING
#endif // !MODE_RENDER_DEPTH
#ifdef USE_MULTIVIEW
uniform highp sampler2DArray depth_buffer; // texunit:-6
@ -589,8 +824,8 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
// see https://google.github.io/filament/Filament.md.html
return mix(vec3(dielectric), albedo, vec3(metallic));
}
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
#ifndef MODE_RENDER_DEPTH
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(USE_ADDITIVE_LIGHTING)
float D_GGX(float cos_theta_m, float alpha) {
float a = cos_theta_m * alpha;
@ -787,7 +1022,7 @@ float get_omni_spot_attenuation(float distance, float inv_range, float decay) {
return nd * pow(max(distance, 0.0001), -decay);
}
#ifndef DISABLE_LIGHT_OMNI
#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
@ -813,6 +1048,8 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t));
}
omni_attenuation *= shadow;
light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@ -831,7 +1068,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
}
#endif // !DISABLE_LIGHT_OMNI
#ifndef DISABLE_LIGHT_SPOT
#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
@ -864,6 +1101,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t));
}
spot_attenuation *= shadow;
light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@ -879,11 +1118,10 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
#endif
diffuse_light, specular_light);
}
#endif // !DISABLE_LIGHT_SPOT
#endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
#endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
#ifndef MODE_RENDER_DEPTH
vec4 fog_process(vec3 vertex) {
vec3 fog_color = scene_data.fog_light_color;
@ -1191,10 +1429,7 @@ void main() {
#endif
}
#endif // BASE_PASS
#ifndef DISABLE_LIGHT_DIRECTIONAL
//diffuse_light = normal; //
for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
@ -1220,7 +1455,7 @@ void main() {
if (i >= omni_light_count) {
break;
}
light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha,
light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@ -1243,7 +1478,7 @@ void main() {
if (i >= spot_light_count) {
break;
}
light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha,
light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@ -1261,7 +1496,7 @@ void main() {
diffuse_light, specular_light);
}
#endif // !DISABLE_LIGHT_SPOT
#endif // BASE_PASS
#endif // !MODE_UNSHADED
#endif // !MODE_RENDER_DEPTH
@ -1287,9 +1522,14 @@ void main() {
#endif // USE_SHADOW_TO_OPACITY
#ifdef MODE_RENDER_DEPTH
//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
#else // !MODE_RENDER_DEPTH
#ifdef RENDER_SHADOWS_LINEAR
// Linearize the depth buffer if rendering cubemap shadows.
gl_FragDepth = (length(vertex) + scene_data.shadow_bias) / scene_data.z_far;
#endif
// Nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
#else // !MODE_RENDER_DEPTH
#ifdef BASE_PASS
#ifdef MODE_UNSHADED
frag_color = vec4(albedo, alpha);
#else
@ -1300,21 +1540,15 @@ void main() {
ambient_light *= 1.0 - metallic;
frag_color = vec4(diffuse_light + specular_light, alpha);
#ifdef BASE_PASS
frag_color.rgb += emission + ambient_light;
#endif
#endif //MODE_UNSHADED
#endif //!MODE_UNSHADED
#ifndef FOG_DISABLED
fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba));
#ifndef DISABLE_FOG
if (scene_data.fog_enabled) {
#ifdef BASE_PASS
frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a);
#else
frag_color.rgb *= (1.0 - fog.a);
#endif // BASE_PASS
}
#endif // !DISABLE_FOG
#endif // !FOG_DISABLED
@ -1331,6 +1565,223 @@ void main() {
#ifdef USE_COLOR_CORRECTION
frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction);
#endif
#else // !BASE_PASS
frag_color = vec4(0.0, 0.0, 0.0, alpha);
#endif // !BASE_PASS
/* ADDITIVE LIGHTING PASS */
#ifdef USE_ADDITIVE_LIGHTING
diffuse_light = vec3(0.0);
specular_light = vec3(0.0);
#if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
// Orthogonal shadows
#if !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4)
float directional_shadow = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
#endif // !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4)
// PSSM2 shadows
#ifdef LIGHT_USE_PSSM2
float depth_z = -vertex.z;
vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets;
//take advantage of prefetch
float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2);
float directional_shadow = 1.0;
if (depth_z < light_split_offsets.y) {
float pssm_fade = 0.0;
#ifdef LIGHT_USE_PSSM_BLEND
float directional_shadow2 = 1.0;
float pssm_blend = 0.0;
bool use_blend = true;
#endif
if (depth_z < light_split_offsets.x) {
float pssm_fade = 0.0;
directional_shadow = shadow1;
#ifdef LIGHT_USE_PSSM_BLEND
directional_shadow2 = shadow2;
pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
#endif
} else {
directional_shadow = shadow2;
pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
#ifdef LIGHT_USE_PSSM_BLEND
use_blend = false;
#endif
}
#ifdef LIGHT_USE_PSSM_BLEND
if (use_blend) {
directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend);
}
#endif
directional_shadow = mix(directional_shadow, 1.0, pssm_fade);
}
#endif //LIGHT_USE_PSSM2
// PSSM4 shadows
#ifdef LIGHT_USE_PSSM4
float depth_z = -vertex.z;
vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets;
float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2);
float shadow3 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord3);
float shadow4 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord4);
float directional_shadow = 1.0;
if (depth_z < light_split_offsets.w) {
float pssm_fade = 0.0;
#ifdef LIGHT_USE_PSSM_BLEND
float directional_shadow2 = 1.0;
float pssm_blend = 0.0;
bool use_blend = true;
#endif
if (depth_z < light_split_offsets.y) {
if (depth_z < light_split_offsets.x) {
directional_shadow = shadow1;
#ifdef LIGHT_USE_PSSM_BLEND
directional_shadow2 = shadow2;
pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
#endif
} else {
directional_shadow = shadow2;
#ifdef LIGHT_USE_PSSM_BLEND
directional_shadow2 = shadow3;
pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z);
#endif
}
} else {
if (depth_z < light_split_offsets.z) {
directional_shadow = shadow3;
#if defined(LIGHT_USE_PSSM_BLEND)
directional_shadow2 = shadow4;
pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z);
#endif
} else {
directional_shadow = shadow4;
pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z);
#if defined(LIGHT_USE_PSSM_BLEND)
use_blend = false;
#endif
}
}
#if defined(LIGHT_USE_PSSM_BLEND)
if (use_blend) {
directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend);
}
#endif
directional_shadow = mix(directional_shadow, 1.0, pssm_fade);
}
#endif //LIGHT_USE_PSSM4
directional_shadow = mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z));
directional_shadow = mix(1.0, directional_shadow, directional_lights[directional_shadow_index].shadow_opacity);
light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
#ifdef LIGHT_RIM_USED
rim, rim_tint,
#endif
#ifdef LIGHT_CLEARCOAT_USED
clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
#ifdef LIGHT_ANISOTROPY_USED
binormal,
tangent, anisotropy,
#endif
diffuse_light,
specular_light);
#endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
#ifdef ADDITIVE_OMNI
vec3 light_ray = ((positional_shadows[positional_shadow_index].shadow_matrix * vec4(shadow_coord.xyz, 1.0))).xyz;
float omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius));
omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
#ifdef LIGHT_RIM_USED
rim,
rim_tint,
#endif
#ifdef LIGHT_CLEARCOAT_USED
clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
#ifdef LIGHT_ANISOTROPY_USED
binormal, tangent, anisotropy,
#endif
diffuse_light, specular_light);
#endif // ADDITIVE_OMNI
#ifdef ADDITIVE_SPOT
float spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity);
light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
#ifdef LIGHT_RIM_USED
rim,
rim_tint,
#endif
#ifdef LIGHT_CLEARCOAT_USED
clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
#ifdef LIGHT_ANISOTROPY_USED
tangent,
binormal, anisotropy,
#endif
diffuse_light, specular_light);
#endif // ADDITIVE_SPOT
diffuse_light *= albedo;
diffuse_light *= 1.0 - metallic;
vec3 additive_light_color = diffuse_light + specular_light;
#ifndef FOG_DISABLED
fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba));
#ifndef DISABLE_FOG
if (scene_data.fog_enabled) {
additive_light_color *= (1.0 - fog.a);
}
#endif // !DISABLE_FOG
#endif // !FOG_DISABLED
// Tonemap before writing as we are writing to an sRGB framebuffer
additive_light_color *= exposure;
additive_light_color = apply_tonemapping(additive_light_color, white);
additive_light_color = linear_to_srgb(additive_light_color);
#ifdef USE_BCS
additive_light_color = apply_bcs(additive_light_color, bcs);
#endif
#ifdef USE_COLOR_CORRECTION
additive_light_color = apply_color_correction(additive_light_color, color_correction);
#endif
frag_color.rgb += additive_light_color;
#endif // USE_ADDITIVE_LIGHTING
#endif //!MODE_RENDER_DEPTH
}

View file

@ -31,6 +31,8 @@
#ifdef GLES3_ENABLED
#include "light_storage.h"
#include "../rasterizer_gles3.h"
#include "../rasterizer_scene_gles3.h"
#include "config.h"
#include "texture_storage.h"
@ -358,6 +360,20 @@ RID LightStorage::light_instance_create(RID p_light) {
void LightStorage::light_instance_free(RID p_light_instance) {
LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL(light_instance);
// Remove from shadow atlases.
for (const RID &E : light_instance->shadow_atlases) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(E);
ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_light_instance));
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t q = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t s = key & SHADOW_INDEX_MASK;
shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
shadow_atlas->shadow_owners.erase(p_light_instance);
}
light_instance_owner.free(p_light_instance);
}
@ -376,9 +392,26 @@ void LightStorage::light_instance_set_aabb(RID p_light_instance, const AABB &p_a
}
void LightStorage::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) {
LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL(light_instance);
ERR_FAIL_INDEX(p_pass, 6);
light_instance->shadow_transform[p_pass].camera = p_projection;
light_instance->shadow_transform[p_pass].transform = p_transform;
light_instance->shadow_transform[p_pass].farplane = p_far;
light_instance->shadow_transform[p_pass].split = p_split;
light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale;
light_instance->shadow_transform[p_pass].range_begin = p_range_begin;
light_instance->shadow_transform[p_pass].shadow_texel_size = p_shadow_texel_size;
light_instance->shadow_transform[p_pass].uv_scale = p_uv_scale;
}
void LightStorage::light_instance_mark_visible(RID p_light_instance) {
LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL(light_instance);
light_instance->last_scene_pass = RasterizerSceneGLES3::get_singleton()->get_scene_pass();
}
/* PROBE API */
@ -598,33 +631,466 @@ void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transfo
/* SHADOW ATLAS API */
RID LightStorage::shadow_atlas_create() {
return RID();
return shadow_atlas_owner.make_rid(ShadowAtlas());
}
void LightStorage::shadow_atlas_free(RID p_atlas) {
shadow_atlas_set_size(p_atlas, 0);
shadow_atlas_owner.free(p_atlas);
}
void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL(shadow_atlas);
ERR_FAIL_COND(p_size < 0);
p_size = next_power_of_2(p_size);
if (p_size == shadow_atlas->size && p_16_bits == shadow_atlas->use_16_bits) {
return;
}
for (uint32_t i = 0; i < 4; i++) {
// Clear all subdivisions and free shadows.
for (uint32_t j = 0; j < shadow_atlas->quadrants[i].textures.size(); j++) {
glDeleteTextures(1, &shadow_atlas->quadrants[i].textures[j]);
glDeleteFramebuffers(1, &shadow_atlas->quadrants[i].fbos[j]);
}
shadow_atlas->quadrants[i].textures.clear();
shadow_atlas->quadrants[i].fbos.clear();
shadow_atlas->quadrants[i].shadows.clear();
shadow_atlas->quadrants[i].shadows.resize(shadow_atlas->quadrants[i].subdivision * shadow_atlas->quadrants[i].subdivision);
}
// Erase shadow atlas reference from lights.
for (const KeyValue<RID, uint32_t> &E : shadow_atlas->shadow_owners) {
LightInstance *li = light_instance_owner.get_or_null(E.key);
ERR_CONTINUE(!li);
li->shadow_atlases.erase(p_atlas);
}
if (shadow_atlas->debug_texture != 0) {
glDeleteTextures(1, &shadow_atlas->debug_texture);
}
if (shadow_atlas->debug_fbo != 0) {
glDeleteFramebuffers(1, &shadow_atlas->debug_fbo);
}
// Clear owners.
shadow_atlas->shadow_owners.clear();
shadow_atlas->size = p_size;
shadow_atlas->use_16_bits = p_16_bits;
}
void LightStorage::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL(shadow_atlas);
ERR_FAIL_INDEX(p_quadrant, 4);
ERR_FAIL_INDEX(p_subdivision, 16384);
uint32_t subdiv = next_power_of_2(p_subdivision);
if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer.
subdiv <<= 1;
}
subdiv = int(Math::sqrt((float)subdiv));
if (shadow_atlas->quadrants[p_quadrant].subdivision == subdiv) {
return;
}
// Erase all data from quadrant.
for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) {
if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) {
shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
LightInstance *li = light_instance_owner.get_or_null(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
ERR_CONTINUE(!li);
li->shadow_atlases.erase(p_atlas);
}
}
for (uint32_t j = 0; j < shadow_atlas->quadrants[p_quadrant].textures.size(); j++) {
glDeleteTextures(1, &shadow_atlas->quadrants[p_quadrant].textures[j]);
glDeleteFramebuffers(1, &shadow_atlas->quadrants[p_quadrant].fbos[j]);
}
shadow_atlas->quadrants[p_quadrant].textures.clear();
shadow_atlas->quadrants[p_quadrant].fbos.clear();
shadow_atlas->quadrants[p_quadrant].shadows.clear();
shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv);
shadow_atlas->quadrants[p_quadrant].subdivision = subdiv;
// Cache the smallest subdiv (for faster allocation in light update).
shadow_atlas->smallest_subdiv = 1 << 30;
for (int i = 0; i < 4; i++) {
if (shadow_atlas->quadrants[i].subdivision) {
shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision);
}
}
if (shadow_atlas->smallest_subdiv == 1 << 30) {
shadow_atlas->smallest_subdiv = 0;
}
// Re-sort the size orders, simple bubblesort for 4 elements.
int swaps = 0;
do {
swaps = 0;
for (int i = 0; i < 3; i++) {
if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) {
SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]);
swaps++;
}
}
} while (swaps > 0);
}
bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(shadow_atlas, false);
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL_V(li, false);
if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) {
return false;
}
uint32_t quad_size = shadow_atlas->size >> 1;
int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage));
int valid_quadrants[4];
int valid_quadrant_count = 0;
int best_size = -1; // Best size found.
int best_subdiv = -1; // Subdiv for the best size.
// Find the quadrants this fits into, and the best possible size it can fit into.
for (int i = 0; i < 4; i++) {
int q = shadow_atlas->size_order[i];
int sd = shadow_atlas->quadrants[q].subdivision;
if (sd == 0) {
continue; // Unused.
}
int max_fit = quad_size / sd;
if (best_size != -1 && max_fit > best_size) {
break; // Too large.
}
valid_quadrants[valid_quadrant_count++] = q;
best_subdiv = sd;
if (max_fit >= desired_fit) {
best_size = max_fit;
}
}
ERR_FAIL_COND_V(valid_quadrant_count == 0, false);
uint64_t tick = OS::get_singleton()->get_ticks_msec();
uint32_t old_key = SHADOW_INVALID;
uint32_t old_quadrant = SHADOW_INVALID;
uint32_t old_shadow = SHADOW_INVALID;
int old_subdivision = -1;
bool should_realloc = false;
bool should_redraw = false;
if (shadow_atlas->shadow_owners.has(p_light_instance)) {
old_key = shadow_atlas->shadow_owners[p_light_instance];
old_quadrant = (old_key >> QUADRANT_SHIFT) & 0x3;
old_shadow = old_key & SHADOW_INDEX_MASK;
// Only re-allocate if a better option is available, and enough time has passed.
should_realloc = shadow_atlas->quadrants[old_quadrant].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec);
should_redraw = shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].version != p_light_version;
if (!should_realloc) {
shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = p_light_version;
// Already existing, see if it should redraw or it's just OK.
return should_redraw;
}
old_subdivision = shadow_atlas->quadrants[old_quadrant].subdivision;
}
bool is_omni = li->light_type == RS::LIGHT_OMNI;
bool found_shadow = false;
int new_quadrant = -1;
int new_shadow = -1;
found_shadow = _shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, old_subdivision, tick, is_omni, new_quadrant, new_shadow);
// For new shadows if we found an atlas.
// Or for existing shadows that found a better atlas.
if (found_shadow) {
if (old_quadrant != SHADOW_INVALID) {
shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = 0;
shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].owner = RID();
}
uint32_t new_key = new_quadrant << QUADRANT_SHIFT;
new_key |= new_shadow;
ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
_shadow_atlas_invalidate_shadow(sh, p_atlas, shadow_atlas, new_quadrant, new_shadow);
sh->owner = p_light_instance;
sh->owner_is_omni = is_omni;
sh->alloc_tick = tick;
sh->version = p_light_version;
li->shadow_atlases.insert(p_atlas);
// Update it in map.
shadow_atlas->shadow_owners[p_light_instance] = new_key;
// Make it dirty, as it should redraw anyway.
return true;
}
return should_redraw;
}
bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool is_omni, int &r_quadrant, int &r_shadow) {
for (int i = p_quadrant_count - 1; i >= 0; i--) {
int qidx = p_in_quadrants[i];
if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) {
return false;
}
// Look for an empty space.
int sc = shadow_atlas->quadrants[qidx].shadows.size();
const ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptr();
// We have a free space in this quadrant, allocate a texture and use it.
if (sc > (int)shadow_atlas->quadrants[qidx].textures.size()) {
GLuint fbo_id = 0;
glGenFramebuffers(1, &fbo_id);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id);
GLuint texture_id = 0;
glGenTextures(1, &texture_id);
glActiveTexture(GL_TEXTURE0);
int size = (shadow_atlas->size >> 1) / shadow_atlas->quadrants[qidx].subdivision;
GLenum format = shadow_atlas->use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
GLenum type = shadow_atlas->use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
if (is_omni) {
glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id);
for (int id = 0; id < 6; id++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + id, 0, format, size / 2, size / 2, 0, GL_DEPTH_COMPONENT, type, nullptr);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture_id, 0);
#ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
ERR_PRINT("Could not create omni light shadow framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
}
#endif
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
} else {
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, format, size, size, 0, GL_DEPTH_COMPONENT, type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
r_quadrant = qidx;
r_shadow = shadow_atlas->quadrants[qidx].textures.size();
shadow_atlas->quadrants[qidx].textures.push_back(texture_id);
shadow_atlas->quadrants[qidx].fbos.push_back(fbo_id);
return true;
}
int found_used_idx = -1; // Found existing one, must steal it.
uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion).
for (int j = 0; j < sc; j++) {
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
ERR_CONTINUE(!sli);
if (sli->last_scene_pass != RasterizerSceneGLES3::get_singleton()->get_scene_pass()) {
// Was just allocated, don't kill it so soon, wait a bit.
if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) {
continue;
}
if (found_used_idx == -1 || sli->last_scene_pass < min_pass) {
found_used_idx = j;
min_pass = sli->last_scene_pass;
}
}
}
if (found_used_idx != -1) {
r_quadrant = qidx;
r_shadow = found_used_idx;
return true;
}
}
return false;
}
void LightStorage::_shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx) {
if (p_shadow->owner.is_valid()) {
LightInstance *sli = light_instance_owner.get_or_null(p_shadow->owner);
p_shadow_atlas->shadow_owners.erase(p_shadow->owner);
p_shadow->version = 0;
p_shadow->owner = RID();
sli->shadow_atlases.erase(p_atlas);
}
}
void LightStorage::shadow_atlas_update(RID p_atlas) {
// Do nothing as there is no shadow atlas texture.
}
/* DIRECTIONAL SHADOW */
// Create if necessary and clear.
void LightStorage::update_directional_shadow_atlas() {
if (directional_shadow.depth == 0 && directional_shadow.size > 0) {
glGenFramebuffers(1, &directional_shadow.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
glGenTextures(1, &directional_shadow.depth);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
GLenum format = directional_shadow.use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
GLenum type = directional_shadow.use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
glTexImage2D(GL_TEXTURE_2D, 0, format, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
}
glDepthMask(GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
RasterizerGLES3::clear_depth(1.0);
glClear(GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) {
}
p_size = nearest_power_of_2_templated(p_size);
int LightStorage::get_directional_light_shadow_size(RID p_light_intance) {
return 0;
if (directional_shadow.size == p_size && directional_shadow.use_16_bits == p_16_bits) {
return;
}
directional_shadow.size = p_size;
directional_shadow.use_16_bits = p_16_bits;
if (directional_shadow.depth != 0) {
glDeleteTextures(1, &directional_shadow.depth);
directional_shadow.depth = 0;
glDeleteFramebuffers(1, &directional_shadow.fbo);
directional_shadow.fbo = 0;
}
}
void LightStorage::set_directional_shadow_count(int p_count) {
directional_shadow.light_count = p_count;
directional_shadow.current_light = 0;
}
static Rect2i _get_directional_shadow_rect(int p_size, int p_shadow_count, int p_shadow_index) {
int split_h = 1;
int split_v = 1;
while (split_h * split_v < p_shadow_count) {
if (split_h == split_v) {
split_h <<= 1;
} else {
split_v <<= 1;
}
}
Rect2i rect(0, 0, p_size, p_size);
rect.size.width /= split_h;
rect.size.height /= split_v;
rect.position.x = rect.size.width * (p_shadow_index % split_h);
rect.position.y = rect.size.height * (p_shadow_index / split_h);
return rect;
}
Rect2i LightStorage::get_directional_shadow_rect() {
return _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, directional_shadow.current_light);
}
int LightStorage::get_directional_light_shadow_size(RID p_light_instance) {
ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0);
Rect2i r = _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, 0);
LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL_V(light_instance, 0);
switch (light_directional_get_shadow_mode(light_instance->light)) {
case RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL:
break; //none
case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS:
r.size.height /= 2;
break;
case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS:
r.size /= 2;
break;
}
return MAX(r.size.width, r.size.height);
}
#endif // !GLES3_ENABLED

View file

@ -73,6 +73,19 @@ struct Light {
/* Light instance */
struct LightInstance {
struct ShadowTransform {
Projection camera;
Transform3D transform;
float farplane;
float split;
float bias_scale;
float shadow_texel_size;
float range_begin;
Rect2 atlas_rect;
Vector2 uv_scale;
};
ShadowTransform shadow_transform[6];
RS::LightType light_type = RS::LIGHT_DIRECTIONAL;
AABB aabb;
@ -80,10 +93,6 @@ struct LightInstance {
RID light;
Transform3D transform;
Vector3 light_vector;
Vector3 spot_vector;
float linear_att = 0.0;
uint64_t shadow_pass = 0;
uint64_t last_scene_pass = 0;
uint64_t last_scene_shadow_pass = 0;
@ -93,7 +102,10 @@ struct LightInstance {
Rect2 directional_rect;
uint32_t gl_id = -1;
HashSet<RID> shadow_atlases; // Shadow atlases where this light is registered.
int32_t gl_id = -1;
int32_t shadow_id = -1;
LightInstance() {}
};
@ -144,6 +156,14 @@ struct Lightmap {
};
class LightStorage : public RendererLightStorage {
public:
enum ShadowAtlastQuadrant {
QUADRANT_SHIFT = 27,
OMNI_LIGHT_FLAG = 1 << 26,
SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1,
SHADOW_INVALID = 0xFFFFFFFF
};
private:
static LightStorage *singleton;
@ -162,6 +182,63 @@ private:
mutable RID_Owner<Lightmap, true> lightmap_owner;
/* SHADOW ATLAS */
// Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its
// own texture which is the same size as it would be if it were in a real atlas.
// This allows us to maintain the same behavior as the other renderers.
struct ShadowAtlas {
struct Quadrant {
uint32_t subdivision = 0;
struct Shadow {
RID owner;
bool owner_is_omni = false;
uint64_t version = 0;
uint64_t alloc_tick = 0;
Shadow() {}
};
Vector<Shadow> shadows;
LocalVector<GLuint> textures;
LocalVector<GLuint> fbos;
Quadrant() {}
} quadrants[4];
// Ordered from smallest (worst) shadow size to largest (best).
int size_order[4] = { 0, 1, 2, 3 };
uint32_t smallest_subdiv = 0;
int size = 0;
bool use_16_bits = true;
GLuint debug_texture = 0;
GLuint debug_fbo = 0;
HashMap<RID, uint32_t> shadow_owners;
};
uint64_t shadow_atlas_realloc_tolerance_msec = 500;
RID_Owner<ShadowAtlas> shadow_atlas_owner;
void _shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx);
bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool p_omni, int &r_quadrant, int &r_shadow);
/* DIRECTIONAL SHADOW */
struct DirectionalShadow {
GLuint depth = 0;
GLuint fbo = 0;
int light_count = 0;
int size = 0;
bool use_16_bits = true;
int current_light = 0;
} directional_shadow;
public:
static LightStorage *get_singleton();
@ -307,15 +384,169 @@ public:
virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
virtual void light_instance_mark_visible(RID p_light_instance) override;
_FORCE_INLINE_ RID light_instance_get_base_light(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light;
}
_FORCE_INLINE_ Transform3D light_instance_get_base_transform(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->transform;
}
_FORCE_INLINE_ AABB light_instance_get_base_aabb(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->aabb;
}
_FORCE_INLINE_ void light_instance_set_cull_mask(RID p_light_instance, uint32_t p_cull_mask) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->cull_mask = p_cull_mask;
}
_FORCE_INLINE_ uint32_t light_instance_get_cull_mask(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->cull_mask;
}
_FORCE_INLINE_ GLuint light_instance_get_shadow_texture(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t shadow = key & SHADOW_INDEX_MASK;
ERR_FAIL_COND_V(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size(), 0);
return shadow_atlas_get_quadrant_shadow_texture(p_shadow_atlas, quadrant, shadow);
}
_FORCE_INLINE_ bool light_instance_has_shadow_atlas(RID p_light_instance, RID p_shadow_atlas) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_atlases.has(p_shadow_atlas);
}
_FORCE_INLINE_ float light_instance_get_shadow_texel_size(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t quadrant_size = shadow_atlas->size >> 1;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
return float(1.0) / shadow_size;
}
_FORCE_INLINE_ Projection light_instance_get_shadow_camera(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].camera;
}
_FORCE_INLINE_ Transform3D light_instance_get_shadow_transform(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].transform;
}
_FORCE_INLINE_ float light_instance_get_shadow_bias_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].bias_scale;
}
_FORCE_INLINE_ float light_instance_get_shadow_range(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].farplane;
}
_FORCE_INLINE_ float light_instance_get_shadow_range_begin(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].range_begin;
}
_FORCE_INLINE_ Vector2 light_instance_get_shadow_uv_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].uv_scale;
}
_FORCE_INLINE_ void light_instance_set_directional_shadow_atlas_rect(RID p_light_instance, int p_index, const Rect2 p_atlas_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->shadow_transform[p_index].atlas_rect = p_atlas_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_shadow_atlas_rect(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].atlas_rect;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_split(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].split;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_texel_size(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].shadow_texel_size;
}
_FORCE_INLINE_ void light_instance_set_render_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_render_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_pass;
}
_FORCE_INLINE_ void light_instance_set_shadow_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_scene_shadow_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_shadow_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_scene_shadow_pass;
}
_FORCE_INLINE_ void light_instance_set_directional_rect(RID p_light_instance, const Rect2 &p_directional_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->directional_rect = p_directional_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_rect(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->directional_rect;
}
_FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light_type;
}
_FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) {
_FORCE_INLINE_ int32_t light_instance_get_gl_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->gl_id;
}
_FORCE_INLINE_ int32_t light_instance_get_shadow_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_id;
}
/* PROBE API */
virtual RID reflection_probe_allocate() override;
@ -389,23 +620,6 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override;
virtual float lightmap_get_probe_capture_update_speed() const override;
/* LIGHT SHADOW MAPPING */
/*
struct CanvasOccluder {
RID self;
GLuint vertex_id; // 0 means, unconfigured
GLuint index_id; // 0 means, unconfigured
LocalVector<Vector2> lines;
int len;
};
RID_Owner<CanvasOccluder> canvas_occluder_owner;
RID canvas_light_occluder_create();
void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines);
*/
/* LIGHTMAP INSTANCE */
virtual RID lightmap_instance_create(RID p_lightmap) override;
@ -413,6 +627,7 @@ public:
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
/* SHADOW ATLAS API */
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); };
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
@ -420,11 +635,151 @@ public:
virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
return atlas->shadow_owners.has(p_light_instance);
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_light_instance_key(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, -1);
return atlas->shadow_owners[p_light_instance];
}
_FORCE_INLINE_ int shadow_atlas_get_size(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
return atlas->size;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_fb(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_fbo != 0) {
return atlas->debug_fbo;
}
glGenFramebuffers(1, &atlas->debug_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, atlas->debug_fbo);
if (atlas->debug_texture == 0) {
atlas->debug_texture = shadow_atlas_get_debug_texture(p_atlas);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, atlas->debug_texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return atlas->debug_fbo;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_texture(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_texture != 0) {
return atlas->debug_texture;
}
glGenTextures(1, &atlas->debug_texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, atlas->size, atlas->size, 0, GL_RED, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
glBindTexture(GL_TEXTURE_2D, 0);
return atlas->debug_texture;
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadows_length(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].shadows.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_shadows_allocated(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].textures.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_subdivision(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_texture(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].textures.size(), 0);
return atlas->quadrants[p_quadrant].textures[p_shadow];
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_fb(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].fbos.size(), 0);
return atlas->quadrants[p_quadrant].fbos[p_shadow];
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadow_size(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return (atlas->size >> 1) / atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ bool shadow_atlas_get_quadrant_shadow_is_omni(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, (uint32_t)atlas->quadrants[p_quadrant].shadows.size(), false);
return atlas->quadrants[p_quadrant].shadows[p_shadow].owner_is_omni;
}
virtual void shadow_atlas_update(RID p_atlas) override;
virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override;
virtual int get_directional_light_shadow_size(RID p_light_intance) override;
virtual void set_directional_shadow_count(int p_count) override;
Rect2i get_directional_shadow_rect();
void update_directional_shadow_atlas();
_FORCE_INLINE_ GLuint directional_shadow_get_texture() {
return directional_shadow.depth;
}
_FORCE_INLINE_ int directional_shadow_get_size() {
return directional_shadow.size;
}
_FORCE_INLINE_ GLuint direction_shadow_get_fb() {
return directional_shadow.fbo;
}
_FORCE_INLINE_ void directional_shadow_increase_current_light() {
directional_shadow.current_light++;
}
};
} // namespace GLES3

View file

@ -172,10 +172,6 @@ AABB Light3D::get_aabb() const {
PackedStringArray Light3D::get_configuration_warnings() const {
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (has_shadow() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("Shadows are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
}
if (!get_scale().is_equal_approx(Vector3(1, 1, 1))) {
warnings.push_back(RTR("A light's scale does not affect the visual size of the light."));
}

View file

@ -677,8 +677,7 @@ public:
return li->shadow_transform[p_index].camera;
}
_FORCE_INLINE_ Transform3D
light_instance_get_shadow_transform(RID p_light_instance, int p_index) {
_FORCE_INLINE_ Transform3D light_instance_get_shadow_transform(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].transform;
}
@ -1005,15 +1004,15 @@ public:
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override;
virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_intance) {
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
return atlas->shadow_owners.has(p_light_intance);
return atlas->shadow_owners.has(p_light_instance);
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_light_instance_key(RID p_atlas, RID p_light_intance) {
_FORCE_INLINE_ uint32_t shadow_atlas_get_light_instance_key(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, -1);
return atlas->shadow_owners[p_light_intance];
return atlas->shadow_owners[p_light_instance];
}
_FORCE_INLINE_ RID shadow_atlas_get_texture(RID p_atlas) {
@ -1053,7 +1052,7 @@ public:
/* DIRECTIONAL SHADOW */
virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override;
virtual int get_directional_light_shadow_size(RID p_light_intance) override;
virtual int get_directional_light_shadow_size(RID p_light_instance) override;
virtual void set_directional_shadow_count(int p_count) override;
Rect2i get_directional_shadow_rect();