diff options
author | Juan Linietsky <reduzio@gmail.com> | 2020-04-14 17:05:45 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2020-04-14 17:05:45 -0300 |
commit | 6f293ed7958d56fb4a8033f04e5ef59e037d2ae7 (patch) | |
tree | 5facee58c1dc6ede57f1b8ef3c0c0effed927103 | |
parent | 5e5103f460f406899582e4307e56882401fd37d5 (diff) |
Add support for projectors in spot and omni lights.
-rw-r--r-- | scene/3d/light_3d.cpp | 42 | ||||
-rw-r--r-- | scene/3d/light_3d.h | 6 | ||||
-rw-r--r-- | servers/rendering/rasterizer.h | 4 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp | 5 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_effects_rd.h | 3 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp | 32 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h | 1 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp | 27 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/rasterizer_storage_rd.h | 13 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl | 28 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl | 125 | ||||
-rw-r--r-- | servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl | 3 |
12 files changed, 253 insertions, 36 deletions
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 81a5c6f4f3..bd95a0bfdc 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -70,7 +70,7 @@ void Light3D::set_shadow(bool p_enable) { shadow = p_enable; RS::get_singleton()->light_set_shadow(light, p_enable); - if (type == RenderingServer::LIGHT_SPOT) { + if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) { update_configuration_warning(); } } @@ -166,6 +166,18 @@ Light3D::BakeMode Light3D::get_bake_mode() const { return bake_mode; } +void Light3D::set_projector(const Ref<Texture> &p_texture) { + + projector = p_texture; + RID tex_id = projector.is_valid() ? projector->get_rid() : RID(); + RS::get_singleton()->light_set_projector(light, tex_id); + update_configuration_warning(); +} + +Ref<Texture2D> Light3D::get_projector() const { + return projector; +} + void Light3D::_update_visibility() { if (!is_inside_tree()) @@ -221,6 +233,10 @@ void Light3D::_validate_property(PropertyInfo &property) const { property.usage = 0; } + if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") { + property.usage = 0; + } + if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") { property.usage = 0; } @@ -255,10 +271,14 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode); ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode); + ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector); + ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector); + ADD_GROUP("Light", "light_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); @@ -444,6 +464,19 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const { return shadow_mode; } +String OmniLight3D::get_configuration_warning() const { + String warning = Light3D::get_configuration_warning(); + + if (!has_shadow() && get_projector().is_valid()) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("Projector texture only works with shadows active."); + } + + return warning; +} + void OmniLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &OmniLight3D::set_shadow_mode); @@ -475,6 +508,13 @@ String SpotLight3D::get_configuration_warning() const { warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."); } + if (!has_shadow() && get_projector().is_valid()) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("Projector texture only works with shadows active."); + } + return warning; } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 21e14f0e8b..f939d56a70 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -81,6 +81,7 @@ private: bool editor_only; void _update_visibility(); BakeMode bake_mode; + Ref<Texture2D> projector; // bind helpers @@ -125,6 +126,9 @@ public: void set_bake_mode(BakeMode p_mode); BakeMode get_bake_mode() const; + void set_projector(const Ref<Texture> &p_texture); + Ref<Texture2D> get_projector() const; + virtual AABB get_aabb() const; virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const; @@ -196,6 +200,8 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; + virtual String get_configuration_warning() const; + OmniLight3D(); }; diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 23b7b509ba..5da9c2aeea 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -327,8 +327,8 @@ public: virtual Size2 texture_size_with_proxy(RID p_proxy) = 0; - virtual void texture_add_to_decal_atlas(RID p_texture) = 0; - virtual void texture_remove_from_decal_atlas(RID p_texture) = 0; + virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0; + virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0; /* SHADER API */ diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index 5dc0e04a94..d469dd97ca 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -204,7 +204,7 @@ RID RasterizerEffectsRD::_get_compute_uniform_set_from_image_pair(RID p_texture1 return uniform_set; } -void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y) { +void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) { zeromem(©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); @@ -219,7 +219,7 @@ void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_f } RD::DrawListID draw_list = p_draw_list; - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); RD::get_singleton()->draw_list_set_push_constant(draw_list, ©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); @@ -1238,6 +1238,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() { { Vector<String> copy_modes; copy_modes.push_back("\n"); + copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n"); copy_to_fb.shader.initialize(copy_modes); diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index 7c955622c3..531591442b 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -113,6 +113,7 @@ class RasterizerEffectsRD { enum CopyToFBMode { COPY_TO_FB_COPY, + COPY_TO_FB_COPY_PANORAMA_TO_DP, COPY_TO_FB_MAX, }; @@ -565,7 +566,7 @@ public: void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false); void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false); void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far); - void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false); + void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false); void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false); void gaussian_glow(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 2c23decd1f..b335300ad0 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -1841,6 +1841,30 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig light_data.atlas_rect[2] = 0; light_data.atlas_rect[3] = 0; + RID projector = storage->light_get_projector(base); + + if (projector.is_valid()) { + Rect2 rect = storage->decal_atlas_get_texture_rect(projector); + + if (type == RS::LIGHT_SPOT) { + + light_data.projector_rect[0] = rect.position.x; + light_data.projector_rect[1] = rect.position.y + rect.size.height; //flip because shadow is flipped + light_data.projector_rect[2] = rect.size.width; + light_data.projector_rect[3] = -rect.size.height; + } else { + light_data.projector_rect[0] = rect.position.x; + light_data.projector_rect[1] = rect.position.y; + light_data.projector_rect[2] = rect.size.width; + light_data.projector_rect[3] = rect.size.height * 0.5; //used by dp, so needs to be half + } + } else { + light_data.projector_rect[0] = 0; + light_data.projector_rect[1] = 0; + light_data.projector_rect[2] = 0; + light_data.projector_rect[3] = 0; + } + if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) { // fill in the shadow information @@ -1892,17 +1916,11 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig } else if (type == RS::LIGHT_SPOT) { - //used for clamping in this light type - light_data.atlas_rect[2] += light_data.atlas_rect[0]; - light_data.atlas_rect[3] += light_data.atlas_rect[1]; - Transform modelview = (p_camera_inverse_transform * light_transform).inverse(); CameraMatrix bias; bias.set_light_bias(); - CameraMatrix rectm; - rectm.set_light_atlas_rect(rect); - CameraMatrix shadow_mtx = rectm * bias * light_instance_get_shadow_camera(li, 0) * modelview; + CameraMatrix shadow_mtx = bias * light_instance_get_shadow_camera(li, 0) * modelview; store_camera(shadow_mtx, light_data.shadow_matrix); if (size > 0.0) { diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h index bfc21e492a..83ff46ca7e 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -279,6 +279,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { float soft_shadow_scale; uint32_t mask; uint32_t pad[2]; + float projector_rect[4]; }; struct DirectionalLightData { diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index fe48321e59..148494692c 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -3180,7 +3180,19 @@ void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) { Light *light = light_owner.getornull(p_light); ERR_FAIL_COND(!light); + if (light->projector == p_texture) { + return; + } + + if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { + texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); + } + light->projector = p_texture; + + if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { + texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); + } } void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) { @@ -4381,22 +4393,30 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const { return RS::INSTANCE_NONE; } -void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture) { +void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp) { if (!decal_atlas.textures.has(p_texture)) { DecalAtlas::Texture t; t.users = 1; + t.panorama_to_dp_users = p_panorama_to_dp ? 1 : 0; decal_atlas.textures[p_texture] = t; decal_atlas.dirty = true; } else { DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture); t->users++; + if (p_panorama_to_dp) { + t->panorama_to_dp_users++; + } } } -void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture) { +void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture); ERR_FAIL_COND(!t); t->users--; + if (p_panorama_to_dp) { + ERR_FAIL_COND(t->panorama_to_dp_users == 0); + t->panorama_to_dp_users--; + } if (t->users == 0) { decal_atlas.textures.erase(p_texture); //do not mark it dirty, there is no need to since it remains working @@ -4590,7 +4610,7 @@ void RasterizerStorageRD::_update_decal_atlas() { while ((K = decal_atlas.textures.next(K))) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K); Texture *src_tex = texture_owner.getornull(*K); - effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list); + effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0); } RD::get_singleton()->draw_list_end(); @@ -4732,6 +4752,7 @@ bool RasterizerStorageRD::free(RID p_rid) { } else if (light_owner.owns(p_rid)) { + light_set_projector(p_rid, RID()); //clear projector // delete the texture Light *light = light_owner.getornull(p_rid); light->instance_dependency.instance_notify_deleted(p_rid); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index e8ee135f2b..1980f043a0 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -178,6 +178,7 @@ private: struct DecalAtlas { struct Texture { + int panorama_to_dp_users; int users; Rect2 uv_rect; }; @@ -599,8 +600,8 @@ public: virtual Size2 texture_size_with_proxy(RID p_proxy); - virtual void texture_add_to_decal_atlas(RID p_texture); - virtual void texture_remove_from_decal_atlas(RID p_texture); + virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false); + virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false); RID decal_atlas_get_texture() const; RID decal_atlas_get_texture_srgb() const; @@ -964,6 +965,14 @@ public: return light->param[p_param]; } + _FORCE_INLINE_ RID light_get_projector(RID p_light) { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, RID()); + + return light->projector; + } + _FORCE_INLINE_ Color light_get_color(RID p_light) { const Light *light = light_owner.getornull(p_light); diff --git a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl b/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl index 0f8688ee34..07f8d09743 100644 --- a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl +++ b/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl @@ -65,6 +65,34 @@ layout(location = 0) out vec4 frag_color; void main() { vec2 uv = uv_interp; + +#ifdef MODE_PANORAMA_TO_DP + + //obtain normal from dual paraboloid uv +#define M_PI 3.14159265359 + + float side; + uv.y = modf(uv.y * 2.0, side); + side = side * 2.0 - 1.0; + vec3 normal = vec3(uv * 2.0 - 1.0, 0.0); + normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y)); + normal *= -side; + normal = normalize(normal); + + //now convert normal to panorama uv + + vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y)); + + if (st.x < 0.0) + st.x += M_PI * 2.0; + + uv = st / vec2(M_PI * 2.0, M_PI); + + if (side < 0.0) { + //uv.y = 1.0 - uv.y; + uv = 1.0 - uv; + } +#endif vec4 color = textureLod(source_color, uv, 0.0); if (params.force_luminance) { color.rgb = vec3(max(max(color.r, color.g), color.b)); diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl index c6a0e5aaaf..ec47887036 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl @@ -784,7 +784,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex #endif //USE_NO_SHADOWS -void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, +void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif @@ -976,8 +976,8 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a //redo shadowmapping, but shrink the model a bit to avoid arctifacts splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0)); - shadow_len = length(splane); - splane = normalize(splane); + shadow_len = length(splane.xyz); + splane = normalize(splane.xyz); if (splane.z >= 0.0) { @@ -999,7 +999,70 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a } #endif - shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + vec3 no_shadow = vec3(1.0); + + if (lights.data[idx].projector_rect != vec4(0.0)) { + + vec3 local_v = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)).xyz; + local_v = normalize(local_v); + + vec4 atlas_rect = lights.data[idx].projector_rect; + + if (local_v.z >= 0.0) { + + local_v.z += 1.0; + atlas_rect.y += atlas_rect.w; + + } else { + + local_v.z = 1.0 - local_v.z; + } + + local_v.xy /= local_v.z; + local_v.xy = local_v.xy * 0.5 + 0.5; + vec2 proj_uv = local_v.xy * atlas_rect.zw; + + vec2 proj_uv_ddx; + vec2 proj_uv_ddy; + { + vec3 local_v_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0)).xyz; + local_v_ddx = normalize(local_v_ddx); + + if (local_v_ddx.z >= 0.0) { + + local_v_ddx.z += 1.0; + } else { + + local_v_ddx.z = 1.0 - local_v_ddx.z; + } + + local_v_ddx.xy /= local_v_ddx.z; + local_v_ddx.xy = local_v_ddx.xy * 0.5 + 0.5; + + proj_uv_ddx = local_v_ddx.xy * atlas_rect.zw - proj_uv; + + vec3 local_v_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0)).xyz; + local_v_ddy = normalize(local_v_ddy); + + if (local_v_ddy.z >= 0.0) { + + local_v_ddy.z += 1.0; + } else { + + local_v_ddy.z = 1.0 - local_v_ddy.z; + } + + local_v_ddy.xy /= local_v_ddy.z; + local_v_ddy.xy = local_v_ddy.xy * 0.5 + 0.5; + + proj_uv_ddy = local_v_ddy.xy * atlas_rect.zw - proj_uv; + } + + vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + atlas_rect.xy, proj_uv_ddx, proj_uv_ddy); + no_shadow = mix(no_shadow, proj.rgb, proj.a); + } + + shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow); } #endif //USE_NO_SHADOWS @@ -1030,7 +1093,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a specular_light); } -void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, #endif @@ -1114,6 +1177,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a //find blocker + vec2 shadow_uv = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy; + float blocker_count = 0.0; float blocker_average = 0.0; @@ -1126,10 +1191,11 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a } float uv_size = lights.data[idx].soft_shadow_size * z_norm * lights.data[idx].soft_shadow_scale; + vec2 clamp_max = lights.data[idx].atlas_rect.xy + lights.data[idx].atlas_rect.zw; for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) { - vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size; - suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw); + vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size; + suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max); float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r; if (d < z_norm) { blocker_average += d; @@ -1146,8 +1212,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a shadow = 0.0; for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) { - vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size; - suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw); + vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size; + suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max); shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(suv, z_norm, 1.0)); } @@ -1160,17 +1226,41 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a } else { //hard shadow - splane.z = z_norm; - shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane); + vec4 shadow_uv = vec4(splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy, z_norm, 1.0); + + shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, shadow_uv); } - shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + vec3 no_shadow = vec3(1.0); + + if (lights.data[idx].projector_rect != vec4(0.0)) { + + splane = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)); + splane /= splane.w; + + vec2 proj_uv = splane.xy * lights.data[idx].projector_rect.zw; + + //ensure we have proper mipmaps + vec4 splane_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0)); + splane_ddx /= splane_ddx.w; + vec2 proj_uv_ddx = splane_ddx.xy * lights.data[idx].projector_rect.zw - proj_uv; + + vec4 splane_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0)); + splane_ddy /= splane_ddy.w; + vec2 proj_uv_ddy = splane_ddy.xy * lights.data[idx].projector_rect.zw - proj_uv; + + vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + lights.data[idx].projector_rect.xy, proj_uv_ddx, proj_uv_ddy); + no_shadow = mix(no_shadow, proj.rgb, proj.a); + } + + shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow); #ifdef LIGHT_TRANSMITTANCE_USED { - vec4 splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0)); + splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0)); splane /= splane.w; + splane.xy = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy; float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r; //reconstruct depth @@ -1691,6 +1781,9 @@ FRAGMENT_SHADER_CODE #ifndef MODE_RENDER_DEPTH uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near))); + //used for interpolating anything cluster related + vec3 vertex_ddx = dFdx(vertex); + vec3 vertex_ddy = dFdy(vertex); { // process decals @@ -1698,8 +1791,6 @@ FRAGMENT_SHADER_CODE uint decal_pointer = cluster_cell.w & CLUSTER_POINTER_MASK; //do outside for performance and avoiding arctifacts - vec3 vertex_ddx = dFdx(vertex); - vec3 vertex_ddy = dFdy(vertex); for (uint i = 0; i < decal_count; i++) { @@ -2195,7 +2286,7 @@ FRAGMENT_SHADER_CODE continue; //not masked } - light_process_omni(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity, + light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif @@ -2234,7 +2325,7 @@ FRAGMENT_SHADER_CODE continue; //not masked } - light_process_spot(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity, + light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity, #ifdef LIGHT_BACKLIGHT_USED backlight, #endif diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl index 329bfe7760..b5e3de5e82 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -153,7 +153,7 @@ struct LightData { //this structure needs to be as packed as possible uint color_specular; //rgb color, a specular (8 bit unorm) uint cone_attenuation_angle; // attenuation and angle, (16bit float) uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) - vec4 atlas_rect; // used for spot + vec4 atlas_rect; // rect in the shadow atlas mat4 shadow_matrix; float shadow_bias; float shadow_normal_bias; @@ -162,6 +162,7 @@ struct LightData { //this structure needs to be as packed as possible float soft_shadow_scale; // scales the shadow kernel for blurrier shadows uint mask; uint pad[2]; + vec4 projector_rect; //projector rect in srgb decal atlas }; layout(set = 0, binding = 5, std430) restrict readonly buffer Lights { |