diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 6fd439674519..a25e1afcfcc2 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -159,6 +159,10 @@
Determines when depth rendering takes place. See also [member transparency].
+
+ Determines which comparison operator is used when testing depth. See [enum DepthTest].
+ [b]Note:[/b] Changing [member depth_test] to a non-default value only has a visible effect when used on a transparent material, or a material that has [member depth_draw_mode] set to [constant DEPTH_DRAW_DISABLED].
+
Texture that specifies the color of the detail overlay. [member detail_albedo]'s alpha channel is used as a mask, even when the material is opaque. To use a dedicated texture as a mask, see [member detail_mask].
[b]Note:[/b] [member detail_albedo] is [i]not[/i] modulated by [member albedo_color].
@@ -374,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].
@@ -661,6 +683,12 @@
Objects will not write their depth to the depth buffer, even during the depth prepass (if enabled).
+
+ Depth test will discard the pixel if it is behind other pixels.
+
+
+ Depth test will discard the pixel if it is in front of other pixels. Useful for stencil effects.
+
Default cull mode. The back of the object is culled when not visible. Back face triangles will be culled when facing the camera. This results in only the front side of triangles being drawn. For closed-surface meshes, this means that only the exterior of the mesh will be visible.
@@ -818,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 4bdcdd49cdf6..d75f34ab1665 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 563d2c2b9c0d..1d2f29d0d500 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -116,6 +116,7 @@ class RasterizerGLES3 : public RendererCompositor {
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 6d0d59ce4266..46bbad17a67c 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -32,6 +32,7 @@
#include "drivers/gles3/effects/copy_effects.h"
#include "drivers/gles3/effects/feed_effects.h"
+#include "drivers/gles3/storage/material_storage.h"
#include "rasterizer_gles3.h"
#include "storage/config.h"
#include "storage/mesh_storage.h"
@@ -223,10 +224,14 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}
- 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_DISABLED) {
+ 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;
- if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED)) {
+ if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED)) {
flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH;
flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW;
}
@@ -236,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) {
@@ -1233,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;
@@ -1426,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) {
@@ -2184,7 +2204,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_GREATER);
+ scene_state.set_gl_depth_func(GL_GREATER);
glColorMask(0, 0, 0, 0);
glDrawBuffers(0, nullptr);
@@ -2489,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;
@@ -2503,12 +2526,15 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
scene_state.enable_gl_blend(false);
- glDepthFunc(GL_GEQUAL);
+ 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);
@@ -2541,16 +2567,19 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
scene_state.enable_gl_scissor_test(false);
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_GEQUAL);
+ scene_state.set_gl_depth_func(GL_GEQUAL);
{
GLuint db = GL_COLOR_ATTACHMENT0;
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:
@@ -2630,11 +2659,13 @@ 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");
scene_state.enable_gl_depth_test(true);
+ scene_state.set_gl_depth_func(GL_GEQUAL);
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK);
@@ -2687,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);
}
@@ -2705,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);
@@ -2834,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);
@@ -2863,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);
}
}
@@ -2908,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);
}
}
@@ -3016,7 +3049,13 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
- scene_state.enable_gl_depth_test(shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED);
+ scene_state.enable_gl_depth_test(shader->depth_test != GLES3::SceneShaderData::DEPTH_TEST_DISABLED);
+ }
+
+ if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED_INVERTED) {
+ scene_state.set_gl_depth_func(GL_LESS);
+ } else {
+ scene_state.set_gl_depth_func(GL_GEQUAL);
}
if constexpr (p_pass_mode != PASS_MODE_SHADOW) {
@@ -3044,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.
@@ -3713,7 +3793,7 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_GREATER);
+ scene_state.set_gl_depth_func(GL_GREATER);
glDrawBuffers(0, nullptr);
@@ -3759,7 +3839,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray draw_buffers;
draw_buffers.push_back(GL_COLOR_ATTACHMENT0);
@@ -3852,7 +3932,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref(&depth_drawi, DEPTH_DRAW_OPAQUE);
actions.render_mode_values["depth_draw_always"] = Pair(&depth_drawi, DEPTH_DRAW_ALWAYS);
- actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED);
+ actions.render_mode_values["depth_test_disabled"] = Pair(&depth_test_disabledi, 1);
+ actions.render_mode_values["depth_test_inverted"] = Pair(&depth_test_invertedi, 1);
actions.render_mode_values["cull_disabled"] = Pair(&cull_modei, RS::CULL_MODE_DISABLED);
actions.render_mode_values["cull_front"] = Pair(&cull_modei, RS::CULL_MODE_FRONT);
@@ -3014,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);
@@ -3026,7 +3048,13 @@ void SceneShaderData::set_code(const String &p_code) {
blend_mode = BlendMode(blend_modei);
alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
depth_draw = DepthDraw(depth_drawi);
- depth_test = DepthTest(depth_testi);
+ if (depth_test_disabledi) {
+ depth_test = DEPTH_TEST_DISABLED;
+ } else if (depth_test_invertedi) {
+ depth_test = DEPTH_TEST_ENABLED_INVERTED;
+ } else {
+ depth_test = DEPTH_TEST_ENABLED;
+ }
cull_mode = RS::CullMode(cull_modei);
vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals.
@@ -3048,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.");
@@ -3114,7 +3147,7 @@ bool SceneShaderData::casts_shadows() const {
bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
- return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+ return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test != DEPTH_TEST_ENABLED));
}
RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const {
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index 9f2b214f3e75..ec93b8fbdc5e 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -257,7 +257,25 @@ struct SceneShaderData : public ShaderData {
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ 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 {
@@ -285,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 7d899fad7789..0feb650a320b 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 7c52c14ea5d5..6fa8f42b3f3f 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);
+
+ 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 {
+ internal_format = GL_DEPTH_COMPONENT24;
+ format = GL_DEPTH_COMPONENT;
+ type = GL_UNSIGNED_INT;
+ attachment = GL_DEPTH_ATTACHMENT;
+ element_size = 3;
+ }
+
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, internal_format, rt->size.x, rt->size.y, rt->view_count, 0, format, type, 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, 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 * 3, "Render target backbuffer depth texture");
+ 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 afa548b4b502..405f07ca1b45 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 07a4ed5ad4d3..f16a29608765 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 (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));
+ }
+ }
+ }
- for (int j = 0; j < modes.size(); j++) {
- const ShaderLanguage::ModeInfo &mode_info = modes[j];
+ {
+ const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i));
- 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 : 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];
+ 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));
+ }
+ }
+ }
- 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]));
+ {
+ 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 2d22a4c4b82f..38050d71aca8 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 0bd2805c8cbf..09fe1859e0ac 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -861,6 +861,17 @@ void BaseMaterial3D::_update_shader() {
}
if (flags[FLAG_DISABLE_DEPTH_TEST]) {
code += ", depth_test_disabled";
+ } else {
+ switch (depth_test) {
+ case DEPTH_TEST_DEFAULT:
+ // depth_test_default is the default behavior, no need to emit it here.
+ break;
+ case DEPTH_TEST_INVERTED:
+ code += ", depth_test_inverted";
+ break;
+ case DEPTH_TEST_MAX:
+ break; // Internal value, skip.
+ }
}
if (flags[FLAG_PARTICLE_TRAILS_MODE]) {
code += ", particle_trails";
@@ -901,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;
@@ -2354,6 +2415,19 @@ BaseMaterial3D::DepthDrawMode BaseMaterial3D::get_depth_draw_mode() const {
return depth_draw_mode;
}
+void BaseMaterial3D::set_depth_test(DepthTest p_func) {
+ if (depth_test == p_func) {
+ return;
+ }
+
+ depth_test = p_func;
+ _queue_shader_change();
+}
+
+BaseMaterial3D::DepthTest BaseMaterial3D::get_depth_test() const {
+ return depth_test;
+}
+
void BaseMaterial3D::set_cull_mode(CullMode p_mode) {
if (cull_mode == p_mode) {
return;
@@ -2410,7 +2484,8 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) {
p_flag == FLAG_UV1_USE_TRIPLANAR ||
p_flag == FLAG_UV2_USE_TRIPLANAR ||
p_flag == FLAG_USE_Z_CLIP_SCALE ||
- p_flag == FLAG_USE_FOV_OVERRIDE) {
+ p_flag == FLAG_USE_FOV_OVERRIDE ||
+ p_flag == FLAG_DISABLE_DEPTH_TEST) {
notify_property_list_changed();
}
@@ -2565,6 +2640,26 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
p_property.usage = PROPERTY_USAGE_NONE;
}
+ if (p_property.name == "depth_test" && flags[FLAG_DISABLE_DEPTH_TEST]) {
+ 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")) {
p_property.usage = PROPERTY_USAGE_NONE;
}
@@ -3037,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;
@@ -3133,6 +3401,9 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_depth_draw_mode", "depth_draw_mode"), &BaseMaterial3D::set_depth_draw_mode);
ClassDB::bind_method(D_METHOD("get_depth_draw_mode"), &BaseMaterial3D::get_depth_draw_mode);
+ ClassDB::bind_method(D_METHOD("set_depth_test", "depth_test"), &BaseMaterial3D::set_depth_test);
+ ClassDB::bind_method(D_METHOD("get_depth_test"), &BaseMaterial3D::get_depth_test);
+
ClassDB::bind_method(D_METHOD("set_cull_mode", "cull_mode"), &BaseMaterial3D::set_cull_mode);
ClassDB::bind_method(D_METHOD("get_cull_mode"), &BaseMaterial3D::get_cull_mode);
@@ -3259,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");
@@ -3269,6 +3558,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_test", PROPERTY_HINT_ENUM, "Default,Inverted"), "set_depth_test", "get_depth_test");
ADD_GROUP("Shading", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode");
@@ -3445,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);
@@ -3518,6 +3817,9 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(DEPTH_DRAW_ALWAYS);
BIND_ENUM_CONSTANT(DEPTH_DRAW_DISABLED);
+ BIND_ENUM_CONSTANT(DEPTH_TEST_DEFAULT);
+ BIND_ENUM_CONSTANT(DEPTH_TEST_INVERTED);
+
BIND_ENUM_CONSTANT(CULL_BACK);
BIND_ENUM_CONSTANT(CULL_FRONT);
BIND_ENUM_CONSTANT(CULL_DISABLED);
@@ -3576,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) :
@@ -3644,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 aca637713ed5..081a2356fbdc 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -238,6 +238,12 @@ class BaseMaterial3D : public Material {
DEPTH_DRAW_MAX
};
+ enum DepthTest {
+ DEPTH_TEST_DEFAULT,
+ DEPTH_TEST_INVERTED,
+ DEPTH_TEST_MAX
+ };
+
enum CullMode {
CULL_BACK,
CULL_FRONT,
@@ -320,6 +326,33 @@ class BaseMaterial3D : public Material {
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
@@ -330,6 +363,7 @@ class BaseMaterial3D : public Material {
uint64_t shading_mode : get_num_bits(SHADING_MODE_MAX - 1);
uint64_t blend_mode : get_num_bits(BLEND_MODE_MAX - 1);
uint64_t depth_draw_mode : get_num_bits(DEPTH_DRAW_MAX - 1);
+ uint64_t depth_test : get_num_bits(DEPTH_TEST_MAX - 1);
uint64_t cull_mode : get_num_bits(CULL_MAX - 1);
uint64_t diffuse_mode : get_num_bits(DIFFUSE_MAX - 1);
uint64_t specular_mode : get_num_bits(SPECULAR_MAX - 1);
@@ -338,6 +372,13 @@ class BaseMaterial3D : public Material {
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;
@@ -381,6 +422,7 @@ class BaseMaterial3D : public Material {
mk.detail_uv = detail_uv;
mk.blend_mode = blend_mode;
mk.depth_draw_mode = depth_draw_mode;
+ mk.depth_test = depth_test;
mk.cull_mode = cull_mode;
mk.texture_filter = texture_filter;
mk.transparency = transparency;
@@ -398,6 +440,11 @@ class BaseMaterial3D : public Material {
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);
@@ -553,6 +600,7 @@ class BaseMaterial3D : public Material {
BlendMode blend_mode = BLEND_MODE_MIX;
BlendMode detail_blend_mode = BLEND_MODE_MIX;
DepthDrawMode depth_draw_mode = DEPTH_DRAW_OPAQUE_ONLY;
+ DepthTest depth_test = DEPTH_TEST_DEFAULT;
CullMode cull_mode = CULL_BACK;
bool flags[FLAG_MAX] = {};
SpecularMode specular_mode = SPECULAR_SCHLICK_GGX;
@@ -570,10 +618,21 @@ class BaseMaterial3D : public Material {
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:
@@ -688,6 +747,9 @@ class BaseMaterial3D : public Material {
void set_depth_draw_mode(DepthDrawMode p_mode);
DepthDrawMode get_depth_draw_mode() const;
+ void set_depth_test(DepthTest p_func);
+ DepthTest get_depth_test() const;
+
void set_cull_mode(CullMode p_mode);
CullMode get_cull_mode() const;
@@ -778,6 +840,24 @@ class BaseMaterial3D : public Material {
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);
@@ -816,6 +896,7 @@ VARIANT_ENUM_CAST(BaseMaterial3D::DetailUV)
VARIANT_ENUM_CAST(BaseMaterial3D::Feature)
VARIANT_ENUM_CAST(BaseMaterial3D::BlendMode)
VARIANT_ENUM_CAST(BaseMaterial3D::DepthDrawMode)
+VARIANT_ENUM_CAST(BaseMaterial3D::DepthTest)
VARIANT_ENUM_CAST(BaseMaterial3D::CullMode)
VARIANT_ENUM_CAST(BaseMaterial3D::Flags)
VARIANT_ENUM_CAST(BaseMaterial3D::DiffuseMode)
@@ -824,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 733771df29de..4f23de672b00 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)) {
@@ -1886,6 +1939,29 @@ void VisualShader::_get_property_list(List *p_list) const {
for (int i = 0; i < rmodes.size(); i++) {
const ShaderLanguage::ModeInfo &info = rmodes[i];
+ // Special handling for depth_test.
+ if (info.name == "depth_test") {
+ toggles.insert("depth_test_disabled");
+
+ const String begin = String(info.name);
+
+ for (int j = 0; j < info.options.size(); j++) {
+ if (info.options[j] == "disabled") {
+ continue;
+ }
+
+ const String option = String(info.options[j]).capitalize();
+
+ if (!blend_mode_enums.has(begin)) {
+ blend_mode_enums[begin] = vformat("%s:%s", option, j);
+ } else {
+ blend_mode_enums[begin] += "," + vformat("%s:%s", option, j);
+ }
+ }
+
+ continue;
+ }
+
if (!info.options.is_empty()) {
const String begin = String(info.name);
@@ -1911,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));
}
@@ -2550,6 +2665,23 @@ void VisualShader::_update_shader() const {
const ShaderLanguage::ModeInfo &info = rmodes[i];
const String temp = String(info.name);
+ // Special handling for depth_test.
+ if (temp == "depth_test") {
+ if (flags.has("depth_test_disabled")) {
+ flag_names.push_back("depth_test_disabled");
+ } else {
+ if (!render_mode.is_empty()) {
+ render_mode += ", ";
+ }
+ if (modes.has(temp) && modes[temp] < info.options.size()) {
+ render_mode += temp + "_" + info.options[modes[temp]];
+ } else {
+ render_mode += temp + "_" + info.options[0];
+ }
+ }
+ continue;
+ }
+
if (!info.options.is_empty()) {
if (!render_mode.is_empty()) {
render_mode += ", ";
@@ -2581,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 7d7b0996c17d..4a24e147cf72 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -138,6 +138,11 @@ class VisualShader : public Shader {
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 7d841f34ddfc..456661d71267 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 0626cef6d311..b5c92ec1fe20 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 @@ class RenderForwardClustered : public RendererSceneRenderRD {
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 @@ class RenderForwardClustered : public RendererSceneRenderRD {
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 dfb1634f1f3e..3a4abf41699b 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
@@ -52,7 +52,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
ShaderCompiler::GeneratedCode gen_code;
blend_mode = BLEND_MODE_MIX;
- depth_testi = DEPTH_TEST_ENABLED;
+ depth_test_disabledi = 0;
+ depth_test_invertedi = 0;
alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
int cull_modei = RS::CULL_MODE_BACK;
@@ -83,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;
@@ -101,7 +108,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["depth_draw_opaque"] = Pair(&depth_drawi, DEPTH_DRAW_OPAQUE);
actions.render_mode_values["depth_draw_always"] = Pair(&depth_drawi, DEPTH_DRAW_ALWAYS);
- actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED);
+ actions.render_mode_values["depth_test_disabled"] = Pair(&depth_test_disabledi, 1);
+ actions.render_mode_values["depth_test_inverted"] = Pair(&depth_test_invertedi, 1);
actions.render_mode_values["cull_disabled"] = Pair(&cull_modei, RS::CULL_MODE_DISABLED);
actions.render_mode_values["cull_front"] = Pair(&cull_modei, RS::CULL_MODE_FRONT);
@@ -143,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;
@@ -164,7 +186,13 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
}
depth_draw = DepthDraw(depth_drawi);
- depth_test = DepthTest(depth_testi);
+ if (depth_test_disabledi) {
+ depth_test = DEPTH_TEST_DISABLED;
+ } else if (depth_test_invertedi) {
+ depth_test = DEPTH_TEST_ENABLED_INVERTED;
+ } else {
+ depth_test = DEPTH_TEST_ENABLED;
+ }
cull_mode = RS::CullMode(cull_modei);
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
uses_screen_texture = gen_code.uses_screen_texture;
@@ -177,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");
@@ -219,7 +252,7 @@ bool SceneShaderForwardClustered::ShaderData::casts_shadows() const {
bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
- return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+ return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test != DEPTH_TEST_ENABLED));
}
RS::ShaderNativeSourceCode SceneShaderForwardClustered::ShaderData::get_native_source_code() const {
@@ -318,9 +351,55 @@ void SceneShaderForwardClustered::ShaderData::_create_pipeline(PipelineKey p_pip
if (depth_test != DEPTH_TEST_DISABLED) {
depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
+
+ if (depth_test == DEPTH_TEST_ENABLED_INVERTED) {
+ 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 f9d09b1c40f0..e646916a7fd3 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
@@ -150,7 +150,8 @@ class SceneShaderForwardClustered {
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ DEPTH_TEST_ENABLED_INVERTED,
};
enum CullVariant {
@@ -167,6 +168,23 @@ class SceneShaderForwardClustered {
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;
@@ -213,7 +231,8 @@ class SceneShaderForwardClustered {
DepthTest depth_test = DEPTH_TEST_ENABLED;
int blend_mode = BLEND_MODE_MIX;
- int depth_testi = DEPTH_TEST_ENABLED;
+ int depth_test_disabledi = 0;
+ int depth_test_invertedi = 0;
int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
bool uses_point_size = false;
@@ -248,6 +267,11 @@ class SceneShaderForwardClustered {
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;
@@ -257,13 +281,13 @@ class SceneShaderForwardClustered {
bool has_blend_alpha = uses_blend_alpha;
bool has_alpha = has_base_alpha || has_blend_alpha;
bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
- bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ bool no_depth_test = depth_test != DEPTH_TEST_ENABLED;
return has_alpha || has_read_screen_alpha || no_depth_draw || no_depth_test;
}
_FORCE_INLINE_ bool uses_depth_in_alpha_pass() const {
bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
- bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ bool no_depth_test = depth_test != DEPTH_TEST_ENABLED;
return (uses_depth_prepass_alpha || uses_alpha_antialiasing) && !(no_depth_draw || no_depth_test);
}
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 c9d0d7b63c9b..8881b423e1c8 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 732d4305316f..f38f223971ee 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
@@ -54,7 +54,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
ShaderCompiler::GeneratedCode gen_code;
blend_mode = BLEND_MODE_MIX;
- depth_testi = DEPTH_TEST_ENABLED;
+ depth_test_disabledi = 0;
+ depth_test_invertedi = 0;
alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
cull_mode = RS::CULL_MODE_BACK;
@@ -83,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;
@@ -101,7 +108,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["depth_draw_opaque"] = Pair(&depth_drawi, DEPTH_DRAW_OPAQUE);
actions.render_mode_values["depth_draw_always"] = Pair(&depth_drawi, DEPTH_DRAW_ALWAYS);
- actions.render_mode_values["depth_test_disabled"] = Pair(&depth_testi, DEPTH_TEST_DISABLED);
+ actions.render_mode_values["depth_test_disabled"] = Pair(&depth_test_disabledi, 1);
+ actions.render_mode_values["depth_test_inverted"] = Pair(&depth_test_invertedi, 1);
actions.render_mode_values["cull_disabled"] = Pair(&cull_mode, RS::CULL_MODE_DISABLED);
actions.render_mode_values["cull_front"] = Pair(&cull_mode, RS::CULL_MODE_FRONT);
@@ -141,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);
@@ -159,7 +181,13 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
}
depth_draw = DepthDraw(depth_drawi);
- depth_test = DepthTest(depth_testi);
+ if (depth_test_disabledi) {
+ depth_test = DEPTH_TEST_DISABLED;
+ } else if (depth_test_invertedi) {
+ depth_test = DEPTH_TEST_ENABLED_INVERTED;
+ } else {
+ depth_test = DEPTH_TEST_ENABLED;
+ }
uses_vertex_time = gen_code.uses_vertex_time;
uses_fragment_time = gen_code.uses_fragment_time;
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
@@ -171,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.");
@@ -224,7 +257,7 @@ bool SceneShaderForwardMobile::ShaderData::casts_shadows() const {
bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
- return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+ return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test != DEPTH_TEST_ENABLED));
}
RS::ShaderNativeSourceCode SceneShaderForwardMobile::ShaderData::get_native_source_code() const {
@@ -276,8 +309,12 @@ void SceneShaderForwardMobile::ShaderData::_create_pipeline(PipelineKey p_pipeli
if (depth_test != DEPTH_TEST_DISABLED) {
depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
+
+ if (depth_test == DEPTH_TEST_ENABLED_INVERTED) {
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS;
+ }
}
RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
@@ -288,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 29fc5bb2a524..7b3194822e98 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
@@ -148,7 +148,8 @@ class SceneShaderForwardMobile {
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ DEPTH_TEST_ENABLED_INVERTED,
};
enum CullVariant {
@@ -165,6 +166,23 @@ class SceneShaderForwardMobile {
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;
@@ -211,7 +229,8 @@ class SceneShaderForwardMobile {
DepthTest depth_test;
int blend_mode = BLEND_MODE_MIX;
- int depth_testi = DEPTH_TEST_ENABLED;
+ int depth_test_disabledi = 0;
+ int depth_test_invertedi = 0;
int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
int cull_mode = RS::CULL_MODE_BACK;
@@ -244,6 +263,11 @@ class SceneShaderForwardMobile {
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;
@@ -253,13 +277,13 @@ class SceneShaderForwardMobile {
bool has_blend_alpha = uses_blend_alpha;
bool has_alpha = has_base_alpha || has_blend_alpha;
bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
- bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ bool no_depth_test = depth_test != DEPTH_TEST_ENABLED;
return has_alpha || has_read_screen_alpha || no_depth_draw || no_depth_test;
}
_FORCE_INLINE_ bool uses_depth_in_alpha_pass() const {
bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
- bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ bool no_depth_test = depth_test != DEPTH_TEST_ENABLED;
return (uses_depth_prepass_alpha || uses_alpha_antialiasing) && !(no_depth_draw || no_depth_test);
}
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 397da80b9f07..c23ba8ee010d 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 7fb873a65f1a..16c1a034bfe5 100644
--- a/servers/rendering/shader_compiler.h
+++ b/servers/rendering/shader_compiler.h
@@ -50,6 +50,8 @@ class ShaderCompiler {
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 c428d92e6c42..884f4022922f 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);
+ tk = _get_token();
- if (shader->render_modes.has(mode)) {
- _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode));
+ 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();
- 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));
+ if (tk.is_integer_constant()) {
+ const int reference_value = tk.constant;
- for (int j = 0; j < modes.size(); j++) {
- const ModeInfo &info = modes[j];
- const String name = String(info.name);
+ if (shader->stencil_reference != -1) {
+ _set_error(vformat(RTR("Duplicated stencil mode reference value: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
+ }
- if (smode.begins_with(name)) {
- if (!info.options.is_empty()) {
- if (info.options.has(smode.substr(name.length() + 1))) {
- found = true;
+ if (reference_value < 0) {
+ _set_error(vformat(RTR("Stencil mode reference value cannot be negative: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
+ }
- 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 (reference_value > 255) {
+ _set_error(vformat(RTR("Stencil mode reference value cannot be greater than 255: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
}
- } 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;
+ shader->stencil_reference = reference_value;
+ } else {
+ _set_tkpos(pos);
- 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;
- }
- }
+ Error error = _parse_shader_mode(true, p_stencil_modes, defined_stencil_modes);
+ if (error != OK) {
+ return error;
}
}
- 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) {
@@ -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 233221735a8c..fed8550ab96b 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -164,6 +164,7 @@ class ShaderLanguage {
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 @@ class ShaderLanguage {
HashMap structs;
HashMap functions;
Vector render_modes;
+ Vector stencil_modes;
+ int stencil_reference = -1;
Vector vfunctions;
Vector vconstants;
@@ -799,6 +802,7 @@ class ShaderLanguage {
COMPLETION_NONE,
COMPLETION_SHADER_TYPE,
COMPLETION_RENDER_MODE,
+ COMPLETION_STENCIL_MODE,
COMPLETION_MAIN_FUNCTION,
COMPLETION_IDENTIFIER,
COMPLETION_FUNCTION_CALL,
@@ -937,6 +941,13 @@ class ShaderLanguage {
options.push_back(p_arg5);
options.push_back(p_arg6);
}
+
+ ModeInfo(const StringName &p_name, std::initializer_list p_args) :
+ name(p_name) {
+ for (const StringName &arg : p_args) {
+ options.push_back(arg);
+ }
+ }
};
struct FunctionInfo {
@@ -1215,11 +1226,13 @@ class ShaderLanguage {
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();
@@ -1241,6 +1254,7 @@ class ShaderLanguage {
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 fc90b50966f9..5efa66b4e671 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;
}
@@ -226,7 +230,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul", "premul_alpha" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_draw"), "opaque", "always", "never" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_prepass_alpha") });
- shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_test_disabled") });
+ shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("depth_test"), { "default", "disabled", "inverted" } });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("sss_mode_skin") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("cull"), "back", "front", "disabled" });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("unshaded") });
@@ -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 3d943429480a..cf42549d0145 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 @@ class ShaderTypes {
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;