From d674c9e2891111a001d170f0b74f015128dd0c41 Mon Sep 17 00:00:00 2001 From: Apples <2352020+apples@users.noreply.github.com> Date: Fri, 17 Feb 2023 15:20:26 -0600 Subject: [PATCH] Add stencil support for spatial materials --- doc/classes/BaseMaterial3D.xml | 62 ++++ drivers/gles3/rasterizer_gles3.cpp | 4 + drivers/gles3/rasterizer_gles3.h | 1 + drivers/gles3/rasterizer_scene_gles3.cpp | 92 +++++- drivers/gles3/rasterizer_scene_gles3.h | 56 ++++ drivers/gles3/storage/material_storage.cpp | 25 ++ drivers/gles3/storage/material_storage.h | 22 ++ .../storage/render_scene_buffers_gles3.cpp | 42 +-- drivers/gles3/storage/texture_storage.cpp | 50 ++- drivers/gles3/storage/texture_storage.h | 3 + editor/plugins/text_shader_editor.cpp | 66 ++-- .../plugins/visual_shader_editor_plugin.cpp | 1 + scene/resources/material.cpp | 287 +++++++++++++++- scene/resources/material.h | 71 ++++ scene/resources/visual_shader.cpp | 132 ++++++++ scene/resources/visual_shader.h | 5 + .../render_forward_clustered.cpp | 22 +- .../render_forward_clustered.h | 2 + .../scene_shader_forward_clustered.cpp | 67 ++++ .../scene_shader_forward_clustered.h | 22 ++ .../forward_mobile/render_forward_mobile.cpp | 11 + .../scene_shader_forward_mobile.cpp | 66 ++++ .../scene_shader_forward_mobile.h | 22 ++ servers/rendering/shader_compiler.cpp | 18 ++ servers/rendering/shader_compiler.h | 2 + servers/rendering/shader_language.cpp | 306 +++++++++++++----- servers/rendering/shader_language.h | 9 +- servers/rendering/shader_types.cpp | 8 + servers/rendering/shader_types.h | 2 + 29 files changed, 1335 insertions(+), 141 deletions(-) diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 9cc1451d9ab..a25e1afcfcc 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -378,6 +378,24 @@ The method for rendering the specular blob. [b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead. + + The primary color of the stencil effect. + + + The comparison operator to use for stencil masking operations. See [enum StencilCompare]. + + + The flags dictating how the stencil operation behaves. See [enum StencilFlags]. + + + The stencil effect mode. See [enum StencilMode]. + + + The outline thickness for [constant STENCIL_MODE_OUTLINE]. + + + The stencil reference value (0-255). Typically a power of 2. + If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. Subsurface scattering quality is controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality]. @@ -828,5 +846,49 @@ Smoothly fades the object out based on the object's distance from the camera using a dithering approach. Dithering discards pixels based on a set pattern to smoothly fade without enabling transparency. On certain hardware, this can be faster than [constant DISTANCE_FADE_PIXEL_ALPHA] and [constant DISTANCE_FADE_PIXEL_DITHER]. + + Disables stencil operations. + + + Stencil preset which applies an outline to the object. + [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead. + + + Stencil preset which shows a silhouette of the object behind walls. + [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead. + + + Enables stencil operations without a preset. + + + The material will only be rendered where it passes a stencil comparison with existing stencil buffer values. See [enum StencilCompare]. + + + The material will write the reference value to the stencil buffer where it passes the depth test. + + + The material will write the reference value to the stencil buffer where it fails the depth test. + + + Always passes the stencil test. + + + Passes the stencil test when the reference value is less than the existing stencil value. + + + Passes the stencil test when the reference value is equal to the existing stencil value. + + + Passes the stencil test when the reference value is less than or equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than the existing stencil value. + + + Passes the stencil test when the reference value is not equal to the existing stencil value. + + + Passes the stencil test when the reference value is greater than or equal to the existing stencil value. + diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 4bdcdd49cdf..d75f34ab166 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -138,6 +138,10 @@ void RasterizerGLES3::clear_depth(float p_depth) { #endif // GLES_API_ENABLED } +void RasterizerGLES3::clear_stencil(int32_t p_stencil) { + glClearStencil(p_stencil); +} + #ifdef CAN_DEBUG static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) { // These are ultimately annoying, so removing for now. diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 563d2c2b9c0..1d2f29d0d50 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -116,6 +116,7 @@ public: static bool is_gles_over_gl() { return gles_over_gl; } static void clear_depth(float p_depth); + static void clear_stencil(int32_t p_stencil); static void make_current(bool p_gles_over_gl) { gles_over_gl = p_gles_over_gl; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 84be199a0f5..46bbad17a67 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -224,6 +224,10 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS; } + if (p_material->shader_data->stencil_enabled) { + flags |= GeometryInstanceSurface::FLAG_USES_STENCIL; + } + if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) { //material is only meant for alpha pass flags |= GeometryInstanceSurface::FLAG_PASS_ALPHA; @@ -237,6 +241,17 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW; } + if (p_material->shader_data->stencil_enabled) { + if (p_material->shader_data->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) { + // Stencil materials which read from the stencil buffer must be in the alpha pass. + // This is critical to preserve compatibility once we'll have the compositor. + if (!(flags & GeometryInstanceSurface::FLAG_PASS_ALPHA)) { + String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; + ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path)); + } + } + } + GLES3::SceneMaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) { @@ -1234,6 +1249,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const scene_state.used_screen_texture = false; scene_state.used_normal_texture = false; scene_state.used_depth_texture = false; + scene_state.used_opaque_stencil = false; } Plane near_plane; @@ -1427,6 +1443,9 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const if (surf->flags & GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE) { scene_state.used_depth_texture = true; } + if ((surf->flags & GeometryInstanceSurface::FLAG_USES_STENCIL) && !force_alpha && (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE))) { + scene_state.used_opaque_stencil = true; + } } else if (p_pass_mode == PASS_MODE_SHADOW) { if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) { @@ -2490,6 +2509,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ // Do depth prepass if it's explicitly enabled bool use_depth_prepass = config->use_depth_prepass; + // Forcibly enable depth prepass if opaque stencil writes are used. + use_depth_prepass = use_depth_prepass || scene_state.used_opaque_stencil; + // Don't do depth prepass we are rendering overdraw use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW; @@ -2506,10 +2528,13 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_blend(false); scene_state.set_gl_depth_func(GL_GEQUAL); scene_state.enable_gl_scissor_test(false); + scene_state.enable_gl_stencil_test(false); + scene_state.set_gl_stencil_write_mask(255); glColorMask(0, 0, 0, 0); RasterizerGLES3::clear_depth(0.0); - glClear(GL_DEPTH_BUFFER_BIT); + RasterizerGLES3::clear_stencil(0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Some desktop GL implementations fall apart when using Multiview with GL_NONE. GLuint db = p_camera_data->view_count > 1 ? GL_COLOR_ATTACHMENT0 : GL_NONE; glDrawBuffers(1, &db); @@ -2549,9 +2574,12 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glDrawBuffers(1, &db); } + scene_state.enable_gl_stencil_test(false); + if (!fb_cleared) { RasterizerGLES3::clear_depth(0.0); - glClear(GL_DEPTH_BUFFER_BIT); + RasterizerGLES3::clear_stencil(0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } // Need to clear framebuffer unless: @@ -2631,6 +2659,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); scene_state.enable_gl_depth_draw(false); + scene_state.enable_gl_stencil_test(false); if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); @@ -2689,7 +2718,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ if (scene_state.used_depth_texture) { glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); glBindTexture(GL_TEXTURE_2D, backbuffer_depth); } @@ -2707,6 +2736,8 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _render_list_template(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); + scene_state.enable_gl_stencil_test(false); + if (!flip_y) { // Restore the default winding order. glFrontFace(GL_CCW); @@ -2836,7 +2867,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend // Copy depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); @@ -2865,10 +2896,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend for (uint32_t v = 0; v < view_count; v++) { glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } } @@ -2910,10 +2941,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); for (uint32_t v = 0; v < view_count; v++) { - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); } } @@ -3052,6 +3083,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + // Stencil. + if (shader->stencil_enabled) { + static const GLenum stencil_compare_table[GLES3::SceneShaderData::STENCIL_COMPARE_MAX] = { + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, + }; + + GLenum stencil_compare = stencil_compare_table[shader->stencil_compare]; + GLuint stencil_compare_mask = 0; + GLuint stencil_write_mask = 0; + GLenum stencil_op_dpfail = GL_KEEP; + GLenum stencil_op_dppass = GL_KEEP; + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) { + stencil_compare_mask = 255; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE) { + stencil_op_dppass = GL_REPLACE; + stencil_write_mask = 255; + } + + if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE_DEPTH_FAIL) { + stencil_op_dpfail = GL_REPLACE; + stencil_write_mask = 255; + } + + scene_state.enable_gl_stencil_test(true); + scene_state.set_gl_stencil_func(stencil_compare, shader->stencil_reference, stencil_compare_mask); + scene_state.set_gl_stencil_write_mask(stencil_write_mask); + scene_state.set_gl_stencil_op(GL_KEEP, stencil_op_dpfail, stencil_op_dppass); + } else { + scene_state.enable_gl_stencil_test(false); + scene_state.set_gl_stencil_write_mask(255); + } + if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { if (!uses_additive_lighting && pass == 1) { // Don't render additive passes if not using additive lighting. diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index d0653eb7036..3a70501037a 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -247,6 +247,7 @@ private: FLAG_USES_DEPTH_TEXTURE = 4096, FLAG_USES_NORMAL_TEXTURE = 8192, FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384, + FLAG_USES_STENCIL = 32768, }; union { @@ -488,6 +489,15 @@ private: glDepthFunc(GL_GEQUAL); current_depth_function = GL_GEQUAL; + + glDisable(GL_STENCIL_TEST); + current_stencil_test_enabled = false; + glStencilMask(255); + current_stencil_write_mask = 255; + glStencilFunc(GL_ALWAYS, 0, 255); + current_stencil_compare = GL_ALWAYS; + current_stencil_reference = 0; + current_stencil_compare_mask = 255; } void set_gl_cull_mode(RS::CullMode p_mode) { @@ -552,10 +562,56 @@ private: } } + void enable_gl_stencil_test(bool p_enabled) { + if (current_stencil_test_enabled != p_enabled) { + if (p_enabled) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + current_stencil_test_enabled = p_enabled; + } + } + + void set_gl_stencil_func(GLenum p_compare, GLint p_reference, GLenum p_compare_mask) { + if (current_stencil_compare != p_compare || current_stencil_reference != p_reference || current_stencil_compare_mask != p_compare_mask) { + glStencilFunc(p_compare, p_reference, p_compare_mask); + current_stencil_compare = p_compare; + current_stencil_reference = p_reference; + current_stencil_compare_mask = p_compare_mask; + } + } + + void set_gl_stencil_write_mask(GLuint p_mask) { + if (current_stencil_write_mask != p_mask) { + glStencilMask(p_mask); + current_stencil_write_mask = p_mask; + } + } + + void set_gl_stencil_op(GLenum p_op_fail, GLenum p_op_dpfail, GLenum p_op_dppass) { + if (current_stencil_op_fail != p_op_fail || current_stencil_op_dpfail != p_op_dpfail || current_stencil_op_dppass != p_op_dppass) { + glStencilOp(p_op_fail, p_op_dpfail, p_op_dppass); + current_stencil_op_fail = p_op_fail; + current_stencil_op_dpfail = p_op_dpfail; + current_stencil_op_dppass = p_op_dppass; + } + } + + GLenum current_stencil_compare = GL_ALWAYS; + GLuint current_stencil_compare_mask = 255; + GLuint current_stencil_write_mask = 255; + GLint current_stencil_reference = 0; + GLenum current_stencil_op_fail = GL_KEEP; + GLenum current_stencil_op_dpfail = GL_KEEP; + GLenum current_stencil_op_dppass = GL_KEEP; + bool current_stencil_test_enabled = false; + bool texscreen_copied = false; bool used_screen_texture = false; bool used_normal_texture = false; bool used_depth_texture = false; + bool used_opaque_stencil = false; LightData *omni_lights = nullptr; LightData *spot_lights = nullptr; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 5661e8eaecf..6a89e9d39d4 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2947,6 +2947,12 @@ void SceneShaderData::set_code(const String &p_code) { int cull_modei = RS::CULL_MODE_BACK; int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -3016,6 +3022,20 @@ void SceneShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones; actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code); @@ -3056,6 +3076,11 @@ void SceneShaderData::set_code(const String &p_code) { uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #ifdef DEBUG_ENABLED if (uses_particle_trails) { WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile renderers."); diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index dfd469354a9..ec93b8fbdc5 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -261,6 +261,23 @@ struct SceneShaderData : public ShaderData { DEPTH_TEST_ENABLED_INVERTED, }; + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // not an actual operator, just the amount of operators + }; + + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + enum AlphaAntiAliasing { ALPHA_ANTIALIASING_OFF, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE, @@ -286,6 +303,11 @@ struct SceneShaderData : public ShaderData { DepthTest depth_test; RS::CullMode cull_mode; + StencilCompare stencil_compare; + uint32_t stencil_flags; + int32_t stencil_reference; + bool stencil_enabled; + bool uses_point_size; bool uses_alpha; bool uses_alpha_clip; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 7d899fad778..0feb650a320 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -63,14 +63,14 @@ void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth if (p_samples > 1) { #if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count); - glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); #else ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform."); #endif } else { #ifndef IOS_ENABLED glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, p_depth, 0, 0, p_view_count); #else ERR_PRINT_ONCE("Multiview isn't supported on this platform."); #endif @@ -79,13 +79,13 @@ void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth if (p_samples > 1) { #ifdef ANDROID_ENABLED glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples); - glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); #else ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform."); #endif } else { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0); } } } @@ -196,7 +196,8 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { ERR_FAIL_COND(view_count == 0); bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF || apply_color_adjustments_in_post; - uint32_t depth_format_size = 3; + GLenum depth_format = GL_DEPTH24_STENCIL8; + uint32_t depth_format_size = 4; bool use_multiview = view_count > 1; if ((!use_internal_buffer || internal3d.color != 0) && (msaa3d.mode == RS::VIEWPORT_MSAA_DISABLED || msaa3d.color != 0)) { @@ -230,9 +231,9 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindTexture(texture_target, internal3d.depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -250,13 +251,13 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { #ifndef IOS_ENABLED if (use_multiview) { glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, internal3d.depth, 0, 0, view_count); } else { #else { #endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, internal3d.depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -299,15 +300,15 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glGenRenderbuffers(1, &msaa3d.depth); glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y); - GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer"); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, depth_format, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth render buffer"); // Create our MSAA 3D FBO. glGenFramebuffers(1, &msaa3d.fbo); glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -341,9 +342,9 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth); #ifdef ANDROID_ENABLED - glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE); #else - glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE); #endif GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth texture"); @@ -353,7 +354,7 @@ void RenderSceneBuffersGLES3::_check_render_buffers() { glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count); - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, msaa3d.depth, 0, 0, view_count); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -474,7 +475,8 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; - uint32_t depth_format_size = 3; + GLenum depth_format = GL_DEPTH24_STENCIL8; + uint32_t depth_format_size = 4; if (backbuffer3d.color == 0 && p_need_color) { glGenTextures(1, &backbuffer3d.color); @@ -509,9 +511,9 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de glBindTexture(texture_target, backbuffer3d.depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -523,12 +525,12 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, backbuffer3d.depth, 0); } } diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 7c52c14ea5d..6fa8f42b3f3 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -2183,14 +2183,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { ERR_FAIL_NULL(texture); rt->depth = texture->tex_id; + rt->depth_has_stencil = rt->overridden.depth_has_stencil; } else { glGenTextures(1, &rt->depth); glBindTexture(texture_target, rt->depth); if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage3D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + glTexImage2D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); } glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -2198,16 +2199,19 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target depth texture"); + rt->depth_has_stencil = true; + + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 4, "Render target depth texture"); } + #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -2354,12 +2358,33 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s if (rt->backbuffer_depth == 0 && uses_depth_texture) { glGenTextures(1, &rt->backbuffer_depth); glBindTexture(texture_target, rt->backbuffer_depth); - if (use_multiview) { - glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + GLint internal_format; + GLenum format; + GLenum type; + GLenum attachment; + int element_size; + + if (rt->depth_has_stencil) { + internal_format = GL_DEPTH24_STENCIL8; + format = GL_DEPTH_STENCIL; + type = GL_UNSIGNED_INT_24_8; + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + element_size = 4; } else { - glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + internal_format = GL_DEPTH_COMPONENT24; + format = GL_DEPTH_COMPONENT; + type = GL_UNSIGNED_INT; + attachment = GL_DEPTH_ATTACHMENT; + element_size = 3; } - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target backbuffer depth texture"); + + if (use_multiview) { + glTexImage3D(texture_target, 0, internal_format, rt->size.x, rt->size.y, rt->view_count, 0, format, type, nullptr); + } else { + glTexImage2D(texture_target, 0, internal_format, rt->size.x, rt->size.y, 0, format, type, nullptr); + } + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * element_size, "Render target backbuffer depth texture"); glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -2367,12 +2392,12 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #ifndef IOS_ENABLED if (use_multiview) { - glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, rt->backbuffer_depth, 0, 0, rt->view_count); } else { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, rt->backbuffer_depth, 0); } } } @@ -2546,6 +2571,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->overridden.color = p_color_texture; rt->overridden.depth = p_depth_texture; + rt->overridden.depth_has_stencil = p_depth_texture.is_null(); rt->overridden.is_overridden = true; uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); @@ -2557,6 +2583,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->fbo = cache->get().fbo; rt->color = cache->get().color; rt->depth = cache->get().depth; + rt->depth_has_stencil = cache->get().depth_has_stencil; rt->size = cache->get().size; rt->texture = p_color_texture; return; @@ -2568,6 +2595,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color new_entry.fbo = rt->fbo; new_entry.color = rt->color; new_entry.depth = rt->depth; + new_entry.depth_has_stencil = rt->depth_has_stencil; new_entry.size = rt->size; // Keep track of any textures we had to allocate because they weren't overridden. if (p_color_texture.is_null()) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index afa548b4b50..405f07ca1b4 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -347,6 +347,7 @@ struct RenderTarget { GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + bool depth_has_stencil = true; bool hdr = false; // For Compatibility this effects both 2D and 3D rendering! GLuint color_internal_format = GL_RGBA8; @@ -375,6 +376,7 @@ struct RenderTarget { struct RTOverridden { bool is_overridden = false; + bool depth_has_stencil = false; RID color; RID depth; RID velocity; @@ -385,6 +387,7 @@ struct RenderTarget { GLuint depth; Size2i size; Vector allocated_textures; + bool depth_has_stencil; }; RBMap fbo_cache; } overridden; diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 07a4ed5ad4d..f16a2960876 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -269,17 +269,31 @@ void ShaderTextEditor::_load_theme_settings() { } } - const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + { + const Vector &render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); - for (int j = 0; j < modes.size(); j++) { - const ShaderLanguage::ModeInfo &mode_info = modes[j]; - - if (!mode_info.options.is_empty()) { - for (int k = 0; k < mode_info.options.size(); k++) { - built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k])); + for (const ShaderLanguage::ModeInfo &mode_info : render_modes) { + if (!mode_info.options.is_empty()) { + for (const StringName &option : mode_info.options) { + built_ins.push_back(String(mode_info.name) + "_" + String(option)); + } + } else { + built_ins.push_back(String(mode_info.name)); + } + } + } + + { + const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)); + + for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) { + if (!mode_info.options.is_empty()) { + for (const StringName &option : mode_info.options) { + built_ins.push_back(String(mode_info.name) + "_" + String(option)); + } + } else { + built_ins.push_back(String(mode_info.name)); } - } else { - built_ins.push_back(String(mode_info.name)); } } } @@ -290,17 +304,31 @@ void ShaderTextEditor::_load_theme_settings() { } } - const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + { + const Vector &shader_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); - for (int i = 0; i < modes.size(); i++) { - const ShaderLanguage::ModeInfo &mode_info = modes[i]; - - if (!mode_info.options.is_empty()) { - for (int j = 0; j < mode_info.options.size(); j++) { - built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[j])); + for (const ShaderLanguage::ModeInfo &mode_info : shader_modes) { + if (!mode_info.options.is_empty()) { + for (const StringName &option : mode_info.options) { + built_ins.push_back(String(mode_info.name) + "_" + String(option)); + } + } else { + built_ins.push_back(String(mode_info.name)); + } + } + } + + { + const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode())); + + for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) { + if (!mode_info.options.is_empty()) { + for (const StringName &option : mode_info.options) { + built_ins.push_back(String(mode_info.name) + "_" + String(option)); + } + } else { + built_ins.push_back(String(mode_info.name)); } - } else { - built_ins.push_back(String(mode_info.name)); } } } @@ -437,6 +465,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, Listget_functions(RenderingServer::ShaderMode(shader->get_mode())); comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())); + comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode())); comp_info.shader_types = ShaderTypes::get_singleton()->get_types(); sl.complete(code, comp_info, r_options, calltip); @@ -541,6 +570,7 @@ void ShaderTextEditor::_validate_script() { Shader::Mode mode = shader->get_mode(); comp_info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); + comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(mode)); comp_info.shader_types = ShaderTypes::get_singleton()->get_types(); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 2d22a4c4b82..38050d71aca 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -6297,6 +6297,7 @@ void VisualShaderEditor::_update_preview() { ShaderLanguage::ShaderCompileInfo info; info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())); info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())); + info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(visual_shader->get_mode())); info.shader_types = ShaderTypes::get_singleton()->get_types(); info.global_shader_uniform_type_func = _visual_shader_editor_get_global_shader_uniform_type; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b5d54a1eaae..09fe1859e0a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -912,6 +912,56 @@ void BaseMaterial3D::_update_shader() { code += ";\n"; + if (stencil_mode != STENCIL_MODE_DISABLED && stencil_flags != 0) { + code += "stencil_mode "; + + if (stencil_flags & STENCIL_FLAG_READ) { + code += "read"; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + if (stencil_flags & STENCIL_FLAG_READ) { + code += ", "; + } + code += "write"; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + if (stencil_flags & (STENCIL_FLAG_READ | STENCIL_FLAG_WRITE)) { + code += ", "; + } + code += "write_depth_fail"; + } + + switch (stencil_compare) { + case STENCIL_COMPARE_ALWAYS: + code += ", compare_always"; + break; + case STENCIL_COMPARE_LESS: + code += ", compare_less"; + break; + case STENCIL_COMPARE_EQUAL: + code += ", compare_equal"; + break; + case STENCIL_COMPARE_LESS_OR_EQUAL: + code += ", compare_less_or_equal"; + break; + case STENCIL_COMPARE_GREATER: + code += ", compare_greater"; + break; + case STENCIL_COMPARE_NOT_EQUAL: + code += ", compare_not_equal"; + break; + case STENCIL_COMPARE_GREATER_OR_EQUAL: + code += ", compare_greater_or_equal"; + break; + case STENCIL_COMPARE_MAX: + break; + } + + code += vformat(", %s;\n", stencil_reference); + } + // Generate list of uniforms. code += vformat(R"( uniform vec4 albedo : source_color; @@ -2591,7 +2641,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { } if (p_property.name == "depth_test" && flags[FLAG_DISABLE_DEPTH_TEST]) { - p_property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (p_property.name == "stencil_reference" && stencil_mode == STENCIL_MODE_DISABLED) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if ((p_property.name == "stencil_flags" || p_property.name == "stencil_compare") && stencil_mode != STENCIL_MODE_CUSTOM) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (p_property.name == "stencil_color" && stencil_mode != STENCIL_MODE_OUTLINE && stencil_mode != STENCIL_MODE_XRAY) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (p_property.name == "stencil_outline_thickness" && stencil_mode != STENCIL_MODE_OUTLINE) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (p_property.name == "subsurf_scatter_transmittance_color" || p_property.name == "subsurf_scatter_transmittance_texture")) { @@ -3066,6 +3132,179 @@ RID BaseMaterial3D::get_rid() const { return _get_material(); } +void BaseMaterial3D::_prepare_stencil_effect() { + const Ref current_next_pass = get_next_pass(); + + if (stencil_mode == STENCIL_MODE_DISABLED || stencil_mode == STENCIL_MODE_CUSTOM) { + if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) { + set_next_pass(current_next_pass->get_next_pass()); + } + return; + } + + Ref stencil_next_pass; + + if (current_next_pass.is_null() || !current_next_pass->has_meta("_stencil_owned")) { + stencil_next_pass = Ref(memnew(StandardMaterial3D)); + stencil_next_pass->set_meta("_stencil_owned", true); + stencil_next_pass->set_next_pass(current_next_pass); + set_next_pass(stencil_next_pass); + } else { + stencil_next_pass = current_next_pass; + } + + switch (stencil_mode) { + case STENCIL_MODE_DISABLED: + break; + case STENCIL_MODE_OUTLINE: + set_stencil_flags(STENCIL_FLAG_WRITE); + set_stencil_compare(STENCIL_COMPARE_ALWAYS); + stencil_next_pass->set_render_priority(-1); + stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED); + stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA); + stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, false); + stencil_next_pass->set_grow_enabled(true); + stencil_next_pass->set_grow(stencil_effect_outline_thickness); + stencil_next_pass->set_albedo(stencil_effect_color); + stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM); + stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE); + stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL); + stencil_next_pass->set_stencil_reference(stencil_reference); + break; + case STENCIL_MODE_XRAY: + set_stencil_flags(STENCIL_FLAG_WRITE); + set_stencil_compare(STENCIL_COMPARE_ALWAYS); + stencil_next_pass->set_render_priority(-1); + stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED); + stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA); + stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, true); + stencil_next_pass->set_grow_enabled(false); + stencil_next_pass->set_grow(0); + stencil_next_pass->set_albedo(stencil_effect_color); + stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM); + stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE); + stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL); + stencil_next_pass->set_stencil_reference(stencil_reference); + break; + case STENCIL_MODE_CUSTOM: + break; + case STENCIL_MODE_MAX: + break; + } +} + +Ref BaseMaterial3D::_get_stencil_next_pass() const { + const Ref current_next_pass = get_next_pass(); + Ref stencil_next_pass; + + if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) { + stencil_next_pass = current_next_pass; + } + + return stencil_next_pass; +} + +void BaseMaterial3D::set_stencil_mode(StencilMode p_stencil_mode) { + if (stencil_mode == p_stencil_mode) { + return; + } + + stencil_mode = p_stencil_mode; + _prepare_stencil_effect(); + _queue_shader_change(); + notify_property_list_changed(); +} + +BaseMaterial3D::StencilMode BaseMaterial3D::get_stencil_mode() const { + return stencil_mode; +} + +void BaseMaterial3D::set_stencil_flags(int p_stencil_flags) { + if (stencil_flags == p_stencil_flags) { + return; + } + + if ((p_stencil_flags & STENCIL_FLAG_READ) && (stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL))) { + p_stencil_flags = p_stencil_flags & STENCIL_FLAG_READ; + } + + if ((p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL)) && (stencil_flags & STENCIL_FLAG_READ)) { + p_stencil_flags = p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL); + } + + stencil_flags = p_stencil_flags; + _queue_shader_change(); +} + +int BaseMaterial3D::get_stencil_flags() const { + return stencil_flags; +} + +void BaseMaterial3D::set_stencil_compare(BaseMaterial3D::StencilCompare p_op) { + if (stencil_compare == p_op) { + return; + } + + stencil_compare = p_op; + _queue_shader_change(); +} + +BaseMaterial3D::StencilCompare BaseMaterial3D::get_stencil_compare() const { + return stencil_compare; +} + +void BaseMaterial3D::set_stencil_reference(int p_reference) { + if (stencil_reference == p_reference) { + return; + } + + stencil_reference = p_reference; + _queue_shader_change(); + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_stencil_reference(p_reference); + } +} + +int BaseMaterial3D::get_stencil_reference() const { + return stencil_reference; +} + +void BaseMaterial3D::set_stencil_effect_color(const Color &p_color) { + if (stencil_effect_color == p_color) { + return; + } + + stencil_effect_color = p_color; + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_albedo(p_color); + } +} + +Color BaseMaterial3D::get_stencil_effect_color() const { + return stencil_effect_color; +} + +void BaseMaterial3D::set_stencil_effect_outline_thickness(float p_outline_thickness) { + if (stencil_effect_outline_thickness == p_outline_thickness) { + return; + } + + stencil_effect_outline_thickness = p_outline_thickness; + + Ref stencil_next_pass = _get_stencil_next_pass(); + if (stencil_next_pass.is_valid()) { + stencil_next_pass->set_grow(p_outline_thickness); + } +} + +float BaseMaterial3D::get_stencil_effect_outline_thickness() const { + return stencil_effect_outline_thickness; +} + RID BaseMaterial3D::get_shader_rid() const { const_cast(this)->_update_shader(); return shader_rid; @@ -3291,6 +3530,24 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fov_override", "scale"), &BaseMaterial3D::set_fov_override); ClassDB::bind_method(D_METHOD("get_fov_override"), &BaseMaterial3D::get_fov_override); + ClassDB::bind_method(D_METHOD("set_stencil_mode", "stencil_mode"), &BaseMaterial3D::set_stencil_mode); + ClassDB::bind_method(D_METHOD("get_stencil_mode"), &BaseMaterial3D::get_stencil_mode); + + ClassDB::bind_method(D_METHOD("set_stencil_flags", "stencil_flags"), &BaseMaterial3D::set_stencil_flags); + ClassDB::bind_method(D_METHOD("get_stencil_flags"), &BaseMaterial3D::get_stencil_flags); + + ClassDB::bind_method(D_METHOD("set_stencil_compare", "stencil_compare"), &BaseMaterial3D::set_stencil_compare); + ClassDB::bind_method(D_METHOD("get_stencil_compare"), &BaseMaterial3D::get_stencil_compare); + + ClassDB::bind_method(D_METHOD("set_stencil_reference", "stencil_reference"), &BaseMaterial3D::set_stencil_reference); + ClassDB::bind_method(D_METHOD("get_stencil_reference"), &BaseMaterial3D::get_stencil_reference); + + ClassDB::bind_method(D_METHOD("set_stencil_effect_color", "stencil_color"), &BaseMaterial3D::set_stencil_effect_color); + ClassDB::bind_method(D_METHOD("get_stencil_effect_color"), &BaseMaterial3D::get_stencil_effect_color); + + ClassDB::bind_method(D_METHOD("set_stencil_effect_outline_thickness", "stencil_outline_thickness"), &BaseMaterial3D::set_stencil_effect_outline_thickness); + ClassDB::bind_method(D_METHOD("get_stencil_effect_outline_thickness"), &BaseMaterial3D::get_stencil_effect_outline_thickness); + ADD_GROUP("Transparency", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); @@ -3478,6 +3735,15 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_max_distance", "get_distance_fade_max_distance"); + ADD_GROUP("Stencil", "stencil_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_mode", PROPERTY_HINT_ENUM, "Disabled,Outline,X-Ray,Custom"), "set_stencil_mode", "get_stencil_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_flags", PROPERTY_HINT_FLAGS, "Read,Write,Write Depth Fail"), "set_stencil_flags", "get_stencil_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_compare", PROPERTY_HINT_ENUM, "Always,Less,Equal,Less Or Equal,Greater,Not Equal,Greater Or Equal"), "set_stencil_compare", "get_stencil_compare"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_reference", PROPERTY_HINT_RANGE, "0,255,1"), "set_stencil_reference", "get_stencil_reference"); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "stencil_color", PROPERTY_HINT_NONE), "set_stencil_effect_color", "get_stencil_effect_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stencil_outline_thickness", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_stencil_effect_outline_thickness", "get_stencil_effect_outline_thickness"); + BIND_ENUM_CONSTANT(TEXTURE_ALBEDO); BIND_ENUM_CONSTANT(TEXTURE_METALLIC); BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS); @@ -3612,6 +3878,23 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_ALPHA); BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_DITHER); BIND_ENUM_CONSTANT(DISTANCE_FADE_OBJECT_DITHER); + + BIND_ENUM_CONSTANT(STENCIL_MODE_DISABLED); + BIND_ENUM_CONSTANT(STENCIL_MODE_OUTLINE); + BIND_ENUM_CONSTANT(STENCIL_MODE_XRAY); + BIND_ENUM_CONSTANT(STENCIL_MODE_CUSTOM); + + BIND_ENUM_CONSTANT(STENCIL_FLAG_READ); + BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE); + BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE_DEPTH_FAIL); + + BIND_ENUM_CONSTANT(STENCIL_COMPARE_ALWAYS); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS_OR_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_NOT_EQUAL); + BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER_OR_EQUAL); } BaseMaterial3D::BaseMaterial3D(bool p_orm) : @@ -3680,6 +3963,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_z_clip_scale(1.0); set_fov_override(75.0); + set_stencil_mode(STENCIL_MODE_DISABLED); + flags[FLAG_ALBEDO_TEXTURE_MSDF] = false; flags[FLAG_USE_TEXTURE_REPEAT] = true; diff --git a/scene/resources/material.h b/scene/resources/material.h index 8a5141eb416..081a2356fbd 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -326,6 +326,33 @@ public: DISTANCE_FADE_MAX }; + enum StencilMode { + STENCIL_MODE_DISABLED, + STENCIL_MODE_OUTLINE, + STENCIL_MODE_XRAY, + STENCIL_MODE_CUSTOM, + STENCIL_MODE_MAX // Not an actual mode, just the amount of modes. + }; + + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + + STENCIL_FLAG_NUM_BITS = 3 // Not an actual mode, just the amount of bits. + }; + + enum StencilCompare { + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + private: struct MaterialKey { // enum values @@ -345,6 +372,13 @@ private: uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1); uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1); uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1); + + // stencil + uint64_t stencil_mode : get_num_bits(STENCIL_MODE_MAX - 1); + uint64_t stencil_flags : STENCIL_FLAG_NUM_BITS; + uint64_t stencil_compare : get_num_bits(STENCIL_COMPARE_MAX - 1); + uint64_t stencil_reference : 8; + // booleans uint64_t invalid_key : 1; uint64_t deep_parallax : 1; @@ -406,6 +440,11 @@ private: mk.alpha_antialiasing_mode = alpha_antialiasing_mode; mk.orm = orm; + mk.stencil_mode = stencil_mode; + mk.stencil_flags = stencil_flags; + mk.stencil_compare = stencil_compare; + mk.stencil_reference = stencil_reference; + for (int i = 0; i < FEATURE_MAX; i++) { if (features[i]) { mk.feature_mask |= ((uint64_t)1 << i); @@ -579,10 +618,21 @@ private: float z_clip_scale = 1.0; float fov_override = 75.0; + StencilMode stencil_mode = STENCIL_MODE_DISABLED; + int stencil_flags = 0; + StencilCompare stencil_compare = STENCIL_COMPARE_ALWAYS; + int stencil_reference = 1; + + Color stencil_effect_color; + float stencil_effect_outline_thickness = 0.01f; + bool features[FEATURE_MAX] = {}; Ref textures[TEXTURE_MAX]; + void _prepare_stencil_effect(); + Ref _get_stencil_next_pass() const; + static HashMap> materials_for_2d; //used by Sprite3D, Label3D and other stuff protected: @@ -790,6 +840,24 @@ public: void set_emission_operator(EmissionOperator p_op); EmissionOperator get_emission_operator() const; + void set_stencil_mode(StencilMode p_stencil_mode); + StencilMode get_stencil_mode() const; + + void set_stencil_flags(int p_stencil_flags); + int get_stencil_flags() const; + + void set_stencil_compare(StencilCompare p_op); + StencilCompare get_stencil_compare() const; + + void set_stencil_reference(int p_reference); + int get_stencil_reference() const; + + void set_stencil_effect_color(const Color &p_color); + Color get_stencil_effect_color() const; + + void set_stencil_effect_outline_thickness(float p_outline_thickness); + float get_stencil_effect_outline_thickness() const; + void set_metallic_texture_channel(TextureChannel p_channel); TextureChannel get_metallic_texture_channel() const; void set_roughness_texture_channel(TextureChannel p_channel); @@ -837,6 +905,9 @@ VARIANT_ENUM_CAST(BaseMaterial3D::BillboardMode) VARIANT_ENUM_CAST(BaseMaterial3D::TextureChannel) VARIANT_ENUM_CAST(BaseMaterial3D::EmissionOperator) VARIANT_ENUM_CAST(BaseMaterial3D::DistanceFadeMode) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilMode) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilFlags) +VARIANT_ENUM_CAST(BaseMaterial3D::StencilCompare) class StandardMaterial3D : public BaseMaterial3D { GDCLASS(StandardMaterial3D, BaseMaterial3D) diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index a76dd797ae7..4f23de672b0 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1715,6 +1715,41 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } _queue_update(); return true; + } else if (prop_name == "stencil/enabled") { + stencil_enabled = bool(p_value); + _queue_update(); + notify_property_list_changed(); + return true; + } else if (prop_name == "stencil/reference") { + stencil_reference = int(p_value); + _queue_update(); + return true; + } else if (prop_name.begins_with("stencil_flags/")) { + StringName flag = prop_name.get_slicec('/', 1); + bool enable = p_value; + if (enable) { + stencil_flags.insert(flag); + if (flag == "read") { + stencil_flags.erase("write"); + stencil_flags.erase("write_depth_fail"); + } else if (flag == "write" || flag == "write_depth_fail") { + stencil_flags.erase("read"); + } + } else { + stencil_flags.erase(flag); + } + _queue_update(); + return true; + } else if (prop_name.begins_with("stencil_modes/")) { + String mode_name = prop_name.get_slicec('/', 1); + int value = p_value; + if (value == 0) { + stencil_modes.erase(mode_name); // It's default anyway, so don't store it. + } else { + stencil_modes[mode_name] = value; + } + _queue_update(); + return true; } else if (prop_name.begins_with("varyings/")) { String var_name = prop_name.get_slicec('/', 1); Varying value = Varying(); @@ -1798,6 +1833,24 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { r_ret = 0; } return true; + } else if (prop_name == "stencil/enabled") { + r_ret = stencil_enabled; + return true; + } else if (prop_name == "stencil/reference") { + r_ret = stencil_reference; + return true; + } else if (prop_name.begins_with("stencil_flags/")) { + StringName flag = prop_name.get_slicec('/', 1); + r_ret = stencil_flags.has(flag); + return true; + } else if (prop_name.begins_with("stencil_modes/")) { + String mode_name = prop_name.get_slicec('/', 1); + if (stencil_modes.has(mode_name)) { + r_ret = stencil_modes[mode_name]; + } else { + r_ret = 0; + } + return true; } else if (prop_name.begins_with("varyings/")) { String var_name = prop_name.get_slicec('/', 1); if (varyings.has(var_name)) { @@ -1934,6 +1987,45 @@ void VisualShader::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E))); } + const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode)); + + if (smodes.size() > 0) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil"), PNAME("enabled")))); + + uint32_t stencil_prop_usage = stencil_enabled ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE; + + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil"), PNAME("reference")), PROPERTY_HINT_RANGE, "0,255,1", stencil_prop_usage)); + + HashMap stencil_enums; + HashSet stencil_toggles; + + for (const ShaderLanguage::ModeInfo &info : smodes) { + if (!info.options.is_empty()) { + const String begin = String(info.name); + + for (int j = 0; j < info.options.size(); j++) { + const String option = String(info.options[j]).capitalize(); + + if (!stencil_enums.has(begin)) { + stencil_enums[begin] = option; + } else { + stencil_enums[begin] += "," + option; + } + } + } else { + stencil_toggles.insert(String(info.name)); + } + } + + for (const KeyValue &E : stencil_enums) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil_modes"), E.key), PROPERTY_HINT_ENUM, E.value, stencil_prop_usage)); + } + + for (const String &E : stencil_toggles) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil_flags"), E), PROPERTY_HINT_NONE, "", stencil_prop_usage)); + } + } + for (const KeyValue &E : varyings) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "varyings", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } @@ -2621,6 +2713,46 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } + const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode)); + + if (stencil_enabled && smodes.size() > 0 && (stencil_flags.has("read") || stencil_flags.has("write") || stencil_flags.has("write_depth_fail"))) { + String stencil_mode; + + Vector flag_names; + + // Add enum modes first. + for (const ShaderLanguage::ModeInfo &info : smodes) { + const String temp = String(info.name); + + if (!info.options.is_empty()) { + if (stencil_modes.has(temp) && stencil_modes[temp] < info.options.size()) { + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += temp + "_" + info.options[stencil_modes[temp]]; + } + } else if (stencil_flags.has(temp)) { + flag_names.push_back(temp); + } + } + + // Add flags afterward. + for (const String &flag_name : flag_names) { + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += flag_name; + } + + // Add reference value. + if (!stencil_mode.is_empty()) { + stencil_mode += ", "; + } + stencil_mode += itos(stencil_reference); + + global_code += "stencil_mode " + stencil_mode + ";\n\n"; + } + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 7d7b0996c17..4a24e147cf7 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -138,6 +138,11 @@ private: HashMap modes; HashSet flags; + bool stencil_enabled = false; + HashMap stencil_modes; + HashSet stencil_flags; + int stencil_reference = 1; + HashMap varyings; #ifdef TOOLS_ENABLED HashMap preview_params; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 7d841f34ddf..456661d7126 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -881,6 +881,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con scene_state.used_normal_texture = false; scene_state.used_depth_texture = false; scene_state.used_lightmap = false; + scene_state.used_opaque_stencil = false; } uint32_t lightmap_captures_used = 0; @@ -1126,6 +1127,9 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DEPTH_TEXTURE) { scene_state.used_depth_texture = true; } + if ((surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_STENCIL) && !force_alpha && (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE))) { + scene_state.used_opaque_stencil = true; + } } else if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) { if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) { rl->add_element(surf); @@ -2041,7 +2045,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool debug_voxelgis = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_ALBEDO || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION; bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; - bool depth_pre_pass = bool(GLOBAL_GET_CACHED(bool, "rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid(); + bool force_depth_pre_pass = scene_state.used_opaque_stencil; + bool depth_pre_pass = (force_depth_pre_pass || bool(GLOBAL_GET_CACHED(bool, "rendering/driver/depth_prepass/enable"))) && depth_framebuffer.is_valid(); SceneShaderForwardClustered::ShaderSpecialization base_specialization = scene_shader.default_specialization; base_specialization.use_depth_fog = p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH; @@ -3984,6 +3989,10 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS; } + if (p_material->shader_data->stencil_enabled) { + flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_STENCIL; + } + if (p_material->shader_data->uses_alpha_pass()) { flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA; if (p_material->shader_data->uses_depth_in_alpha_pass()) { @@ -4004,6 +4013,17 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_MOTION_VECTOR; } + if (p_material->shader_data->stencil_enabled) { + if (p_material->shader_data->stencil_flags & SceneShaderForwardClustered::ShaderData::STENCIL_FLAG_READ) { + // Stencil materials which read from the stencil buffer must be in the alpha pass. + // This is critical to preserve compatibility once we'll have the compositor. + if (!(flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { + String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; + ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path)); + } + } + } + SceneShaderForwardClustered::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (p_material->shader_data->uses_shared_shadow_material()) { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 0626cef6d31..b5c92ec1fe2 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -408,6 +408,7 @@ private: bool used_depth_texture = false; bool used_sss = false; bool used_lightmap = false; + bool used_opaque_stencil = false; struct ShadowPass { uint32_t element_from; @@ -485,6 +486,7 @@ private: FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768, FLAG_USES_PARTICLE_TRAILS = 65536, FLAG_USES_MOTION_VECTOR = 131072, + FLAG_USES_STENCIL = 262144, }; union { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index e75302f345e..3a4abf41699 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -84,6 +84,12 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -145,6 +151,20 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.write_flag_pointers["POSITION"] = &uses_position; actions.write_flag_pointers["Z_CLIP_SCALE"] = &uses_z_clip_scale; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; Error err = OK; @@ -185,6 +205,11 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_tangent |= uses_normal_map; uses_tangent |= uses_bent_normal_map; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #if 0 print_line("**compiling shader:"); print_line("**defines:\n"); @@ -333,6 +358,48 @@ void SceneShaderForwardClustered::ShaderData::_create_pipeline(PipelineKey p_pip depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS; } } + + depth_stencil_state.enable_stencil = stencil_enabled; + if (stencil_enabled) { + static const RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = { + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + }; + + uint32_t stencil_mask = 255; + + RD::PipelineDepthStencilState::StencilOperationState op; + op.fail = RD::STENCIL_OP_KEEP; + op.pass = RD::STENCIL_OP_KEEP; + op.depth_fail = RD::STENCIL_OP_KEEP; + op.compare = stencil_compare_rd_table[stencil_compare]; + op.compare_mask = 0; + op.write_mask = 0; + op.reference = stencil_reference; + + if (stencil_flags & STENCIL_FLAG_READ) { + op.compare_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + op.pass = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + op.depth_fail = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + depth_stencil_state.front_op = op; + depth_stencil_state.back_op = op; + } + bool depth_pre_pass_enabled = bool(GLOBAL_GET_CACHED(bool, "rendering/driver/depth_prepass/enable")); RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 5090051b56b..e646916a7fd 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -168,6 +168,23 @@ public: ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE }; + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + struct PipelineKey { RD::VertexFormatID vertex_format_id; RD::FramebufferFormatID framebuffer_format_id; @@ -250,6 +267,11 @@ public: bool uses_z_clip_scale = false; RS::CullMode cull_mode = RS::CULL_MODE_DISABLED; + bool stencil_enabled = false; + uint32_t stencil_flags = 0; + StencilCompare stencil_compare = STENCIL_COMPARE_LESS; + uint32_t stencil_reference = 0; + uint64_t last_pass = 0; uint32_t index = 0; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index c9d0d7b63c9..8881b423e1c 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2675,6 +2675,17 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS; } + if (p_material->shader_data->stencil_enabled) { + if (p_material->shader_data->stencil_flags & SceneShaderForwardMobile::ShaderData::STENCIL_FLAG_READ) { + // Stencil materials which read from the stencil buffer must be in the alpha pass. + // This is critical to preserve compatibility once we'll have the compositor. + if (!(flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) { + String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")"; + ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path)); + } + } + } + SceneShaderForwardMobile::MaterialData *material_shadow = nullptr; void *surface_shadow = nullptr; if (p_material->shader_data->uses_shared_shadow_material()) { diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 223cfd181cc..f38f223971e 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -84,6 +84,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { int depth_drawi = DEPTH_DRAW_OPAQUE; + int stencil_readi = 0; + int stencil_writei = 0; + int stencil_write_depth_faili = 0; + int stencil_comparei = STENCIL_COMPARE_ALWAYS; + int stencil_referencei = -1; + ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; @@ -143,6 +149,20 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["VERTEX"] = &uses_vertex; + actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ); + actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE); + actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL); + + actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS); + actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL); + actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL); + actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER); + actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL); + actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL); + actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS); + + actions.stencil_reference = &stencil_referencei; + actions.uniforms = &uniforms; MutexLock lock(SceneShaderForwardMobile::singleton_mutex); @@ -179,6 +199,11 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_tangent |= uses_normal_map; uses_tangent |= uses_bent_normal_map; + stencil_enabled = stencil_referencei != -1; + stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili; + stencil_compare = StencilCompare(stencil_comparei); + stencil_reference = stencil_referencei; + #ifdef DEBUG_ENABLED if (uses_sss) { WARN_PRINT_ONCE_ED("Subsurface scattering is only available when using the Forward+ renderer."); @@ -300,6 +325,47 @@ void SceneShaderForwardMobile::ShaderData::_create_pipeline(PipelineKey p_pipeli RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, }; + depth_stencil_state.enable_stencil = stencil_enabled; + if (stencil_enabled) { + static const RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = { + RD::COMPARE_OP_LESS, + RD::COMPARE_OP_EQUAL, + RD::COMPARE_OP_LESS_OR_EQUAL, + RD::COMPARE_OP_GREATER, + RD::COMPARE_OP_NOT_EQUAL, + RD::COMPARE_OP_GREATER_OR_EQUAL, + RD::COMPARE_OP_ALWAYS, + }; + + uint32_t stencil_mask = 255; + + RD::PipelineDepthStencilState::StencilOperationState op; + op.fail = RD::STENCIL_OP_KEEP; + op.pass = RD::STENCIL_OP_KEEP; + op.depth_fail = RD::STENCIL_OP_KEEP; + op.compare = stencil_compare_rd_table[stencil_compare]; + op.compare_mask = 0; + op.write_mask = 0; + op.reference = stencil_reference; + + if (stencil_flags & STENCIL_FLAG_READ) { + op.compare_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE) { + op.pass = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) { + op.depth_fail = RD::STENCIL_OP_REPLACE; + op.write_mask = stencil_mask; + } + + depth_stencil_state.front_op = op; + depth_stencil_state.back_op = op; + } + RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[p_pipeline_key.primitive_type]; RD::PipelineRasterizationState raster_state; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 18af561e23a..7b3194822e9 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -166,6 +166,23 @@ public: ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE }; + enum StencilFlags { + STENCIL_FLAG_READ = 1, + STENCIL_FLAG_WRITE = 2, + STENCIL_FLAG_WRITE_DEPTH_FAIL = 4, + }; + + enum StencilCompare { + STENCIL_COMPARE_LESS, + STENCIL_COMPARE_EQUAL, + STENCIL_COMPARE_LESS_OR_EQUAL, + STENCIL_COMPARE_GREATER, + STENCIL_COMPARE_NOT_EQUAL, + STENCIL_COMPARE_GREATER_OR_EQUAL, + STENCIL_COMPARE_ALWAYS, + STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators. + }; + struct PipelineKey { RD::VertexFormatID vertex_format_id; RD::FramebufferFormatID framebuffer_format_id; @@ -246,6 +263,11 @@ public: bool writes_modelview_or_projection = false; bool uses_world_coordinates = false; + bool stencil_enabled = false; + uint32_t stencil_flags = 0; + StencilCompare stencil_compare = STENCIL_COMPARE_LESS; + uint32_t stencil_reference = 0; + uint64_t last_pass = 0; uint32_t index = 0; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 397da80b9f0..c23ba8ee010 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -453,6 +453,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene case SL::Node::NODE_TYPE_SHADER: { SL::ShaderNode *pnode = (SL::ShaderNode *)p_node; + // Render modes. + for (int i = 0; i < pnode->render_modes.size(); i++) { if (p_default_actions.render_mode_defines.has(pnode->render_modes[i]) && !used_rmode_defines.has(pnode->render_modes[i])) { r_gen_code.defines.push_back(p_default_actions.render_mode_defines[pnode->render_modes[i]]); @@ -469,6 +471,21 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } } + // Stencil modes. + + for (int i = 0; i < pnode->stencil_modes.size(); i++) { + if (p_actions.stencil_mode_values.has(pnode->stencil_modes[i])) { + Pair &p = p_actions.stencil_mode_values[pnode->stencil_modes[i]]; + *p.first = p.second; + } + } + + // Stencil reference value. + + if (p_actions.stencil_reference && pnode->stencil_reference != -1) { + *p_actions.stencil_reference = pnode->stencil_reference; + } + // structs for (int i = 0; i < pnode->vstructs.size(); i++) { @@ -1463,6 +1480,7 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident SL::ShaderCompileInfo info; info.functions = ShaderTypes::get_singleton()->get_functions(p_mode); info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode); + info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(p_mode); info.shader_types = ShaderTypes::get_singleton()->get_types(); info.global_shader_uniform_type_func = _get_global_shader_uniform_type; info.base_varying_index = actions.base_varying_index; diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 7fb873a65f1..16c1a034bfe 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -50,6 +50,8 @@ public: HashMap render_mode_flags; HashMap usage_flag_pointers; HashMap write_flag_pointers; + HashMap> stencil_mode_values; + int *stencil_reference = nullptr; HashMap *uniforms = nullptr; }; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index c428d92e6c4..884f4022922 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -339,6 +339,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} }, { TK_SHADER_TYPE, "shader_type", CF_SHADER_TYPE, {}, {} }, { TK_RENDER_MODE, "render_mode", CF_GLOBAL_SPACE, {}, {} }, + { TK_STENCIL_MODE, "stencil_mode", CF_GLOBAL_SPACE, {}, {} }, // uniform qualifiers @@ -4128,7 +4129,7 @@ bool ShaderLanguage::is_token_operator_assign(TokenType p_type) { } bool ShaderLanguage::is_token_hint(TokenType p_type) { - return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE); + return int(p_type) > int(TK_STENCIL_MODE) && int(p_type) < int(TK_SHADER_TYPE); } bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) { @@ -9110,7 +9111,7 @@ bool ShaderLanguage::_parse_numeric_constant_expression(const FunctionInfo &p_fu return true; } -Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types) { +Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types) { Token tk; TkPos prev_pos; Token next; @@ -9173,7 +9174,8 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo(); - HashMap defined_modes; + HashMap defined_render_modes; + HashMap defined_stencil_modes; while (tk.type != TK_EOF) { switch (tk.type) { @@ -9182,83 +9184,64 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f keyword_completion_context = CF_UNSPECIFIED; #endif // DEBUG_ENABLED while (true) { - StringName mode; - _get_completable_identifier(nullptr, COMPLETION_RENDER_MODE, mode); - - if (mode == StringName()) { - _set_error(RTR("Expected an identifier for render mode.")); - return ERR_PARSE_ERROR; + Error error = _parse_shader_mode(false, p_render_modes, defined_render_modes); + if (error != OK) { + return error; } - const String smode = String(mode); - - if (shader->render_modes.has(mode)) { - _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode)); - return ERR_PARSE_ERROR; - } - - bool found = false; - - if (is_shader_inc) { - for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { - const Vector modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); - - for (int j = 0; j < modes.size(); j++) { - const ModeInfo &info = modes[j]; - const String name = String(info.name); - - if (smode.begins_with(name)) { - if (!info.options.is_empty()) { - if (info.options.has(smode.substr(name.length() + 1))) { - found = true; - - if (defined_modes.has(name)) { - _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name])); - return ERR_PARSE_ERROR; - } - defined_modes.insert(name, smode); - break; - } - } else { - found = true; - break; - } - } - } - } - } else { - for (int i = 0; i < p_render_modes.size(); i++) { - const ModeInfo &info = p_render_modes[i]; - const String name = String(info.name); - - if (smode.begins_with(name)) { - if (!info.options.is_empty()) { - if (info.options.has(smode.substr(name.length() + 1))) { - found = true; - - if (defined_modes.has(name)) { - _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name])); - return ERR_PARSE_ERROR; - } - defined_modes.insert(name, smode); - break; - } - } else { - found = true; - break; - } - } - } - } - - if (!found) { - _set_error(vformat(RTR("Invalid render mode: '%s'."), smode)); - return ERR_PARSE_ERROR; - } - - shader->render_modes.push_back(mode); - tk = _get_token(); + + if (tk.type == TK_COMMA) { + // All good, do nothing. + } else if (tk.type == TK_SEMICOLON) { + break; // Done. + } else { + _set_error(vformat(RTR("Unexpected token: '%s'."), get_token_text(tk))); + return ERR_PARSE_ERROR; + } + } +#ifdef DEBUG_ENABLED + keyword_completion_context = CF_GLOBAL_SPACE; +#endif // DEBUG_ENABLED + } break; + case TK_STENCIL_MODE: { +#ifdef DEBUG_ENABLED + keyword_completion_context = CF_UNSPECIFIED; +#endif // DEBUG_ENABLED + while (true) { + TkPos pos = _get_tkpos(); + tk = _get_token(); + + if (tk.is_integer_constant()) { + const int reference_value = tk.constant; + + if (shader->stencil_reference != -1) { + _set_error(vformat(RTR("Duplicated stencil mode reference value: '%s'."), reference_value)); + return ERR_PARSE_ERROR; + } + + if (reference_value < 0) { + _set_error(vformat(RTR("Stencil mode reference value cannot be negative: '%s'."), reference_value)); + return ERR_PARSE_ERROR; + } + + if (reference_value > 255) { + _set_error(vformat(RTR("Stencil mode reference value cannot be greater than 255: '%s'."), reference_value)); + return ERR_PARSE_ERROR; + } + + shader->stencil_reference = reference_value; + } else { + _set_tkpos(pos); + + Error error = _parse_shader_mode(true, p_stencil_modes, defined_stencil_modes); + if (error != OK) { + return error; + } + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { //all good, do nothing } else if (tk.type == TK_SEMICOLON) { @@ -11076,6 +11059,110 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat return FAILED; } +Error ShaderLanguage::_parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes) { + StringName mode; + _get_completable_identifier(nullptr, p_is_stencil ? COMPLETION_STENCIL_MODE : COMPLETION_RENDER_MODE, mode); + + if (mode == StringName()) { + if (p_is_stencil) { + _set_error(RTR("Expected an identifier for stencil mode.")); + } else { + _set_error(RTR("Expected an identifier for render mode.")); + } + return ERR_PARSE_ERROR; + } + + const String smode = String(mode); + + Vector ¤t_modes = p_is_stencil ? shader->stencil_modes : shader->render_modes; + + if (current_modes.has(mode)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Duplicated stencil mode: '%s'."), smode)); + } else { + _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode)); + } + return ERR_PARSE_ERROR; + } + + bool found = false; + + if (is_shader_inc) { + for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { + const Vector modes = p_is_stencil ? ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)) : ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i)); + + for (const ModeInfo &info : modes) { + const String name = String(info.name); + + if (smode.begins_with(name)) { + if (!info.options.is_empty()) { + if (info.options.has(smode.substr(name.length() + 1))) { + found = true; + + if (r_defined_modes.has(name)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } else { + _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } + return ERR_PARSE_ERROR; + } + r_defined_modes.insert(name, smode); + break; + } + } else { + found = true; + break; + } + } + } + } + } else { + for (const ModeInfo &info : p_modes) { + const String name = String(info.name); + + if (smode.begins_with(name)) { + if (!info.options.is_empty()) { + if (info.options.has(smode.substr(name.length() + 1))) { + found = true; + + if (r_defined_modes.has(name)) { + if (p_is_stencil) { + _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } else { + _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name])); + } + return ERR_PARSE_ERROR; + } + r_defined_modes.insert(name, smode); + break; + } + } else { + found = true; + break; + } + } + } + } + + if (!found) { + if (p_is_stencil) { + _set_error(vformat(RTR("Invalid stencil mode: '%s'."), smode)); + } else { + _set_error(vformat(RTR("Invalid render mode: '%s'."), smode)); + } + return ERR_PARSE_ERROR; + } + + if (p_is_stencil) { + shader->stencil_modes.push_back(mode); + } else { + shader->render_modes.push_back(mode); + } + + return OK; +} + // skips over whitespace and /* */ and // comments static int _get_first_ident_pos(const String &p_code) { int idx = 0; @@ -11226,7 +11313,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i nodes = nullptr; shader = alloc_node(); - Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types); + Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types); #ifdef DEBUG_ENABLED if (check_warnings) { @@ -11251,7 +11338,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ global_shader_uniform_get_type_func = p_info.global_shader_uniform_type_func; shader = alloc_node(); - _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types); + _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types); #ifdef DEBUG_ENABLED // Adds context keywords. @@ -11349,6 +11436,67 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ return OK; } break; + case COMPLETION_STENCIL_MODE: { + if (is_shader_inc) { + for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { + const Vector modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)); + + for (const ModeInfo &info : modes) { + if (!info.options.is_empty()) { + bool found = false; + + for (const StringName &option : info.options) { + if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) { + found = true; + } + } + + if (!found) { + for (const StringName &option : info.options) { + ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(completion_option); + } + } + } else { + const String name = String(info.name); + + if (!shader->stencil_modes.has(name)) { + ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } + } + } else { + for (const ModeInfo &info : p_info.stencil_modes) { + if (!info.options.is_empty()) { + bool found = false; + + for (const StringName &option : info.options) { + if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) { + found = true; + } + } + + if (!found) { + for (const StringName &option : info.options) { + ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(completion_option); + } + } + } else { + const String name = String(info.name); + + if (!shader->stencil_modes.has(name)) { + ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_options->push_back(option); + } + } + } + } + + return OK; + } break; case COMPLETION_STRUCT: { if (shader->structs.has(completion_struct)) { StructNode *node = shader->structs[completion_struct].shader_struct; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index bee2f5bdc45..fed8550ab96 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -164,6 +164,7 @@ public: TK_ARG_OUT, TK_ARG_INOUT, TK_RENDER_MODE, + TK_STENCIL_MODE, TK_HINT_DEFAULT_WHITE_TEXTURE, TK_HINT_DEFAULT_BLACK_TEXTURE, TK_HINT_DEFAULT_TRANSPARENT_TEXTURE, @@ -723,6 +724,8 @@ public: HashMap structs; HashMap functions; Vector render_modes; + Vector stencil_modes; + int stencil_reference = -1; Vector vfunctions; Vector vconstants; @@ -799,6 +802,7 @@ public: COMPLETION_NONE, COMPLETION_SHADER_TYPE, COMPLETION_RENDER_MODE, + COMPLETION_STENCIL_MODE, COMPLETION_MAIN_FUNCTION, COMPLETION_IDENTIFIER, COMPLETION_FUNCTION_CALL, @@ -1222,11 +1226,13 @@ private: String _get_qualifier_str(ArgumentQualifier p_qualifier) const; bool _parse_numeric_constant_expression(const FunctionInfo &p_function_info, float &r_constant); - Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types); + Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types); Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op); Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op); + Error _parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes); + public: #ifdef DEBUG_ENABLED List::Element *get_warnings_ptr(); @@ -1248,6 +1254,7 @@ public: struct ShaderCompileInfo { HashMap functions; Vector render_modes; + Vector stencil_modes; VaryingFunctionNames varying_function_names; HashSet shader_types; GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index c64df961ba7..5efa66b4e67 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -38,6 +38,10 @@ const Vector &ShaderTypes::get_modes(RS::ShaderMode p_ return shader_modes[p_mode].modes; } +const Vector &ShaderTypes::get_stencil_modes(RS::ShaderMode p_mode) const { + return shader_modes[p_mode].stencil_modes; +} + const HashSet &ShaderTypes::get_types() const { return shader_types; } @@ -246,6 +250,10 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("debug_shadow_splits") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("fog_disabled") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("specular_occlusion_disabled") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("read") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write_depth_fail") }); + shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("compare"), { "always", "less", "equal", "less_or_equal", "greater", "not_equal", "greater_or_equal" } }); } /************ CANVAS ITEM **************************/ diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h index 3d943429480..cf42549d014 100644 --- a/servers/rendering/shader_types.h +++ b/servers/rendering/shader_types.h @@ -37,6 +37,7 @@ class ShaderTypes { struct Type { HashMap functions; Vector modes; + Vector stencil_modes; }; HashMap shader_modes; @@ -51,6 +52,7 @@ public: const HashMap &get_functions(RS::ShaderMode p_mode) const; const Vector &get_modes(RS::ShaderMode p_mode) const; + const Vector &get_stencil_modes(RS::ShaderMode p_mode) const; const HashSet &get_types() const; const List &get_types_list() const;