From 7574a5dbb39ba6d7962f68a5f65b0ec222072f5c Mon Sep 17 00:00:00 2001
From: Apples <2352020+apples@users.noreply.github.com>
Date: Sun, 10 Sep 2023 02:06:38 -0500
Subject: [PATCH 1/2] Add depth function for spatial materials
---
doc/classes/BaseMaterial3D.xml | 10 +++++
drivers/gles3/rasterizer_scene_gles3.cpp | 26 +++++++-----
drivers/gles3/rasterizer_scene_gles3.h | 12 ++++++
drivers/gles3/storage/material_storage.cpp | 16 ++++++--
drivers/gles3/storage/material_storage.h | 3 +-
scene/resources/material.cpp | 38 +++++++++++++++++-
scene/resources/material.h | 13 ++++++
scene/resources/visual_shader.cpp | 40 +++++++++++++++++++
.../scene_shader_forward_clustered.cpp | 22 +++++++---
.../scene_shader_forward_clustered.h | 10 +++--
.../scene_shader_forward_mobile.cpp | 22 +++++++---
.../scene_shader_forward_mobile.h | 10 +++--
servers/rendering/shader_language.h | 7 ++++
servers/rendering/shader_types.cpp | 2 +-
14 files changed, 197 insertions(+), 34 deletions(-)
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 6fd4396745..9cc1451d9a 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].
@@ -661,6 +665,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.
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 6d0d59ce42..84be199a0f 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,10 @@ 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 (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;
}
@@ -2184,7 +2185,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);
@@ -2503,7 +2504,7 @@ 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);
glColorMask(0, 0, 0, 0);
@@ -2541,7 +2542,7 @@ 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;
@@ -2635,6 +2636,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
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);
@@ -3016,7 +3018,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) {
@@ -3713,7 +3721,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 +3767,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray draw_buffers;
draw_buffers.push_back(GL_COLOR_ATTACHMENT0);
@@ -3852,7 +3860,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);
@@ -3026,7 +3028,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.
@@ -3114,7 +3122,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 9f2b214f3e..dfd469354a 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -257,7 +257,8 @@ struct SceneShaderData : public ShaderData {
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ DEPTH_TEST_ENABLED_INVERTED,
};
enum AlphaAntiAliasing {
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 0bd2805c8c..b5d54a1eaa 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";
@@ -2354,6 +2365,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 +2434,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 +2590,10 @@ 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_NONE;
+ }
+
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;
}
@@ -3133,6 +3162,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);
@@ -3269,6 +3301,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");
@@ -3518,6 +3551,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);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index aca637713e..8a5141eb41 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -238,6 +238,12 @@ public:
DEPTH_DRAW_MAX
};
+ enum DepthTest {
+ DEPTH_TEST_DEFAULT,
+ DEPTH_TEST_INVERTED,
+ DEPTH_TEST_MAX
+ };
+
enum CullMode {
CULL_BACK,
CULL_FRONT,
@@ -330,6 +336,7 @@ private:
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);
@@ -381,6 +388,7 @@ private:
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;
@@ -553,6 +561,7 @@ private:
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;
@@ -688,6 +697,9 @@ public:
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;
@@ -816,6 +828,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)
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 733771df29..a76dd797ae 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1886,6 +1886,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);
@@ -2550,6 +2573,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 += ", ";
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 dfb1634f1f..e75302f345 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;
@@ -101,7 +102,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);
@@ -164,7 +166,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;
@@ -219,7 +227,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,8 +326,12 @@ 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;
+ }
}
bool depth_pre_pass_enabled = bool(GLOBAL_GET_CACHED(bool, "rendering/driver/depth_prepass/enable"));
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 f9d09b1c40..5090051b56 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 @@ public:
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ DEPTH_TEST_ENABLED_INVERTED,
};
enum CullVariant {
@@ -213,7 +214,8 @@ public:
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;
@@ -257,13 +259,13 @@ public:
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/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 732d430531..223cfd181c 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;
@@ -101,7 +102,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);
@@ -159,7 +161,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;
@@ -224,7 +232,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 +284,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] = {
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 29fc5bb2a5..18af561e23 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 @@ public:
enum DepthTest {
DEPTH_TEST_DISABLED,
- DEPTH_TEST_ENABLED
+ DEPTH_TEST_ENABLED,
+ DEPTH_TEST_ENABLED_INVERTED,
};
enum CullVariant {
@@ -211,7 +212,8 @@ public:
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;
@@ -253,13 +255,13 @@ public:
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_language.h b/servers/rendering/shader_language.h
index 233221735a..bee2f5bdc4 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -937,6 +937,13 @@ public:
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 {
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index fc90b50966..c64df961ba 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -226,7 +226,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") });
From d674c9e2891111a001d170f0b74f015128dd0c41 Mon Sep 17 00:00:00 2001
From: Apples <2352020+apples@users.noreply.github.com>
Date: Fri, 17 Feb 2023 15:20:26 -0600
Subject: [PATCH 2/2] Add stencil support for spatial materials
---
doc/classes/BaseMaterial3D.xml | 62 ++++
drivers/gles3/rasterizer_gles3.cpp | 4 +
drivers/gles3/rasterizer_gles3.h | 1 +
drivers/gles3/rasterizer_scene_gles3.cpp | 92 +++++-
drivers/gles3/rasterizer_scene_gles3.h | 56 ++++
drivers/gles3/storage/material_storage.cpp | 25 ++
drivers/gles3/storage/material_storage.h | 22 ++
.../storage/render_scene_buffers_gles3.cpp | 42 +--
drivers/gles3/storage/texture_storage.cpp | 50 ++-
drivers/gles3/storage/texture_storage.h | 3 +
editor/plugins/text_shader_editor.cpp | 66 ++--
.../plugins/visual_shader_editor_plugin.cpp | 1 +
scene/resources/material.cpp | 287 +++++++++++++++-
scene/resources/material.h | 71 ++++
scene/resources/visual_shader.cpp | 132 ++++++++
scene/resources/visual_shader.h | 5 +
.../render_forward_clustered.cpp | 22 +-
.../render_forward_clustered.h | 2 +
.../scene_shader_forward_clustered.cpp | 67 ++++
.../scene_shader_forward_clustered.h | 22 ++
.../forward_mobile/render_forward_mobile.cpp | 11 +
.../scene_shader_forward_mobile.cpp | 66 ++++
.../scene_shader_forward_mobile.h | 22 ++
servers/rendering/shader_compiler.cpp | 18 ++
servers/rendering/shader_compiler.h | 2 +
servers/rendering/shader_language.cpp | 306 +++++++++++++-----
servers/rendering/shader_language.h | 9 +-
servers/rendering/shader_types.cpp | 8 +
servers/rendering/shader_types.h | 2 +
29 files changed, 1335 insertions(+), 141 deletions(-)
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 9cc1451d9a..a25e1afcfc 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -378,6 +378,24 @@
The method for rendering the specular blob.
[b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead.
+
+ The primary color of the stencil effect.
+
+
+ The comparison operator to use for stencil masking operations. See [enum StencilCompare].
+
+
+ The flags dictating how the stencil operation behaves. See [enum StencilFlags].
+
+
+ The stencil effect mode. See [enum StencilMode].
+
+
+ The outline thickness for [constant STENCIL_MODE_OUTLINE].
+
+
+ The stencil reference value (0-255). Typically a power of 2.
+
If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. Subsurface scattering quality is controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality].
@@ -828,5 +846,49 @@
Smoothly fades the object out based on the object's distance from the camera using a dithering approach. Dithering discards pixels based on a set pattern to smoothly fade without enabling transparency. On certain hardware, this can be faster than [constant DISTANCE_FADE_PIXEL_ALPHA] and [constant DISTANCE_FADE_PIXEL_DITHER].
+
+ Disables stencil operations.
+
+
+ Stencil preset which applies an outline to the object.
+ [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead.
+
+
+ Stencil preset which shows a silhouette of the object behind walls.
+ [b]Note:[/b] Requires a [member Material.next_pass] material which will be automatically applied. Any manual changes made to [member Material.next_pass] will be lost when the stencil properties are modified or the scene is reloaded. To safely apply a [member Material.next_pass] material on a material that uses stencil presets, use [member GeometryInstance3D.material_overlay] instead.
+
+
+ Enables stencil operations without a preset.
+
+
+ The material will only be rendered where it passes a stencil comparison with existing stencil buffer values. See [enum StencilCompare].
+
+
+ The material will write the reference value to the stencil buffer where it passes the depth test.
+
+
+ The material will write the reference value to the stencil buffer where it fails the depth test.
+
+
+ Always passes the stencil test.
+
+
+ Passes the stencil test when the reference value is less than the existing stencil value.
+
+
+ Passes the stencil test when the reference value is equal to the existing stencil value.
+
+
+ Passes the stencil test when the reference value is less than or equal to the existing stencil value.
+
+
+ Passes the stencil test when the reference value is greater than the existing stencil value.
+
+
+ Passes the stencil test when the reference value is not equal to the existing stencil value.
+
+
+ Passes the stencil test when the reference value is greater than or equal to the existing stencil value.
+
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 4bdcdd49cd..d75f34ab16 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 563d2c2b9c..1d2f29d0d5 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -116,6 +116,7 @@ public:
static bool is_gles_over_gl() { return gles_over_gl; }
static void clear_depth(float p_depth);
+ static void clear_stencil(int32_t p_stencil);
static void make_current(bool p_gles_over_gl) {
gles_over_gl = p_gles_over_gl;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 84be199a0f..46bbad17a6 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -224,6 +224,10 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}
+ if (p_material->shader_data->stencil_enabled) {
+ flags |= GeometryInstanceSurface::FLAG_USES_STENCIL;
+ }
+
if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) {
//material is only meant for alpha pass
flags |= GeometryInstanceSurface::FLAG_PASS_ALPHA;
@@ -237,6 +241,17 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW;
}
+ if (p_material->shader_data->stencil_enabled) {
+ if (p_material->shader_data->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) {
+ // Stencil materials which read from the stencil buffer must be in the alpha pass.
+ // This is critical to preserve compatibility once we'll have the compositor.
+ if (!(flags & GeometryInstanceSurface::FLAG_PASS_ALPHA)) {
+ String shader_path = p_material->shader_data->path.is_empty() ? "" : "(" + p_material->shader_data->path + ")";
+ ERR_PRINT_ED(vformat("Attempting to use a shader %s that reads stencil but is not in the alpha queue. Ensure the material uses alpha blending or has depth_draw disabled or depth_test disabled.", shader_path));
+ }
+ }
+ }
+
GLES3::SceneMaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) {
@@ -1234,6 +1249,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
scene_state.used_screen_texture = false;
scene_state.used_normal_texture = false;
scene_state.used_depth_texture = false;
+ scene_state.used_opaque_stencil = false;
}
Plane near_plane;
@@ -1427,6 +1443,9 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
if (surf->flags & GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE) {
scene_state.used_depth_texture = true;
}
+ if ((surf->flags & GeometryInstanceSurface::FLAG_USES_STENCIL) && !force_alpha && (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE))) {
+ scene_state.used_opaque_stencil = true;
+ }
} else if (p_pass_mode == PASS_MODE_SHADOW) {
if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) {
@@ -2490,6 +2509,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
// Do depth prepass if it's explicitly enabled
bool use_depth_prepass = config->use_depth_prepass;
+ // Forcibly enable depth prepass if opaque stencil writes are used.
+ use_depth_prepass = use_depth_prepass || scene_state.used_opaque_stencil;
+
// Don't do depth prepass we are rendering overdraw
use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW;
@@ -2506,10 +2528,13 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
scene_state.enable_gl_blend(false);
scene_state.set_gl_depth_func(GL_GEQUAL);
scene_state.enable_gl_scissor_test(false);
+ scene_state.enable_gl_stencil_test(false);
+ scene_state.set_gl_stencil_write_mask(255);
glColorMask(0, 0, 0, 0);
RasterizerGLES3::clear_depth(0.0);
- glClear(GL_DEPTH_BUFFER_BIT);
+ RasterizerGLES3::clear_stencil(0);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Some desktop GL implementations fall apart when using Multiview with GL_NONE.
GLuint db = p_camera_data->view_count > 1 ? GL_COLOR_ATTACHMENT0 : GL_NONE;
glDrawBuffers(1, &db);
@@ -2549,9 +2574,12 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
glDrawBuffers(1, &db);
}
+ scene_state.enable_gl_stencil_test(false);
+
if (!fb_cleared) {
RasterizerGLES3::clear_depth(0.0);
- glClear(GL_DEPTH_BUFFER_BIT);
+ RasterizerGLES3::clear_stencil(0);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
// Need to clear framebuffer unless:
@@ -2631,6 +2659,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
_render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size());
scene_state.enable_gl_depth_draw(false);
+ scene_state.enable_gl_stencil_test(false);
if (draw_sky || draw_sky_fog_only) {
RENDER_TIMESTAMP("Render Sky");
@@ -2689,7 +2718,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
if (scene_state.used_depth_texture) {
glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y,
- GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, backbuffer_depth);
}
@@ -2707,6 +2736,8 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_
_render_list_template(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true);
+ scene_state.enable_gl_stencil_test(false);
+
if (!flip_y) {
// Restore the default winding order.
glFrontFace(GL_CCW);
@@ -2836,7 +2867,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
// Copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt);
- glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt);
@@ -2865,10 +2896,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
for (uint32_t v = 0; v < view_count; v++) {
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v);
- glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v);
+ glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v);
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v);
- glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v);
- glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v);
+ glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
}
@@ -2910,10 +2941,10 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
for (uint32_t v = 0; v < view_count; v++) {
- glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v);
- glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v);
+ glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, read_depth, 0, v);
+ glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, write_depth, 0, v);
- glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
}
@@ -3052,6 +3083,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
}
+ // Stencil.
+ if (shader->stencil_enabled) {
+ static const GLenum stencil_compare_table[GLES3::SceneShaderData::STENCIL_COMPARE_MAX] = {
+ GL_LESS,
+ GL_EQUAL,
+ GL_LEQUAL,
+ GL_GREATER,
+ GL_NOTEQUAL,
+ GL_GEQUAL,
+ GL_ALWAYS,
+ };
+
+ GLenum stencil_compare = stencil_compare_table[shader->stencil_compare];
+ GLuint stencil_compare_mask = 0;
+ GLuint stencil_write_mask = 0;
+ GLenum stencil_op_dpfail = GL_KEEP;
+ GLenum stencil_op_dppass = GL_KEEP;
+
+ if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_READ) {
+ stencil_compare_mask = 255;
+ }
+
+ if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE) {
+ stencil_op_dppass = GL_REPLACE;
+ stencil_write_mask = 255;
+ }
+
+ if (shader->stencil_flags & GLES3::SceneShaderData::STENCIL_FLAG_WRITE_DEPTH_FAIL) {
+ stencil_op_dpfail = GL_REPLACE;
+ stencil_write_mask = 255;
+ }
+
+ scene_state.enable_gl_stencil_test(true);
+ scene_state.set_gl_stencil_func(stencil_compare, shader->stencil_reference, stencil_compare_mask);
+ scene_state.set_gl_stencil_write_mask(stencil_write_mask);
+ scene_state.set_gl_stencil_op(GL_KEEP, stencil_op_dpfail, stencil_op_dppass);
+ } else {
+ scene_state.enable_gl_stencil_test(false);
+ scene_state.set_gl_stencil_write_mask(255);
+ }
+
if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
if (!uses_additive_lighting && pass == 1) {
// Don't render additive passes if not using additive lighting.
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index d0653eb703..3a70501037 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -247,6 +247,7 @@ private:
FLAG_USES_DEPTH_TEXTURE = 4096,
FLAG_USES_NORMAL_TEXTURE = 8192,
FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384,
+ FLAG_USES_STENCIL = 32768,
};
union {
@@ -488,6 +489,15 @@ private:
glDepthFunc(GL_GEQUAL);
current_depth_function = GL_GEQUAL;
+
+ glDisable(GL_STENCIL_TEST);
+ current_stencil_test_enabled = false;
+ glStencilMask(255);
+ current_stencil_write_mask = 255;
+ glStencilFunc(GL_ALWAYS, 0, 255);
+ current_stencil_compare = GL_ALWAYS;
+ current_stencil_reference = 0;
+ current_stencil_compare_mask = 255;
}
void set_gl_cull_mode(RS::CullMode p_mode) {
@@ -552,10 +562,56 @@ private:
}
}
+ void enable_gl_stencil_test(bool p_enabled) {
+ if (current_stencil_test_enabled != p_enabled) {
+ if (p_enabled) {
+ glEnable(GL_STENCIL_TEST);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ }
+ current_stencil_test_enabled = p_enabled;
+ }
+ }
+
+ void set_gl_stencil_func(GLenum p_compare, GLint p_reference, GLenum p_compare_mask) {
+ if (current_stencil_compare != p_compare || current_stencil_reference != p_reference || current_stencil_compare_mask != p_compare_mask) {
+ glStencilFunc(p_compare, p_reference, p_compare_mask);
+ current_stencil_compare = p_compare;
+ current_stencil_reference = p_reference;
+ current_stencil_compare_mask = p_compare_mask;
+ }
+ }
+
+ void set_gl_stencil_write_mask(GLuint p_mask) {
+ if (current_stencil_write_mask != p_mask) {
+ glStencilMask(p_mask);
+ current_stencil_write_mask = p_mask;
+ }
+ }
+
+ void set_gl_stencil_op(GLenum p_op_fail, GLenum p_op_dpfail, GLenum p_op_dppass) {
+ if (current_stencil_op_fail != p_op_fail || current_stencil_op_dpfail != p_op_dpfail || current_stencil_op_dppass != p_op_dppass) {
+ glStencilOp(p_op_fail, p_op_dpfail, p_op_dppass);
+ current_stencil_op_fail = p_op_fail;
+ current_stencil_op_dpfail = p_op_dpfail;
+ current_stencil_op_dppass = p_op_dppass;
+ }
+ }
+
+ GLenum current_stencil_compare = GL_ALWAYS;
+ GLuint current_stencil_compare_mask = 255;
+ GLuint current_stencil_write_mask = 255;
+ GLint current_stencil_reference = 0;
+ GLenum current_stencil_op_fail = GL_KEEP;
+ GLenum current_stencil_op_dpfail = GL_KEEP;
+ GLenum current_stencil_op_dppass = GL_KEEP;
+ bool current_stencil_test_enabled = false;
+
bool texscreen_copied = false;
bool used_screen_texture = false;
bool used_normal_texture = false;
bool used_depth_texture = false;
+ bool used_opaque_stencil = false;
LightData *omni_lights = nullptr;
LightData *spot_lights = nullptr;
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 5661e8eaec..6a89e9d39d 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -2947,6 +2947,12 @@ void SceneShaderData::set_code(const String &p_code) {
int cull_modei = RS::CULL_MODE_BACK;
int depth_drawi = DEPTH_DRAW_OPAQUE;
+ int stencil_readi = 0;
+ int stencil_writei = 0;
+ int stencil_write_depth_faili = 0;
+ int stencil_comparei = STENCIL_COMPARE_ALWAYS;
+ int stencil_referencei = -1;
+
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
@@ -3016,6 +3022,20 @@ void SceneShaderData::set_code(const String &p_code) {
actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones;
actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights;
+ actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ);
+ actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE);
+ actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
+
+ actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS);
+ actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL);
+ actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
+ actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER);
+ actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
+ actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
+ actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS);
+
+ actions.stencil_reference = &stencil_referencei;
+
actions.uniforms = &uniforms;
Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
@@ -3056,6 +3076,11 @@ void SceneShaderData::set_code(const String &p_code) {
uses_vertex_time = gen_code.uses_vertex_time;
uses_fragment_time = gen_code.uses_fragment_time;
+ stencil_enabled = stencil_referencei != -1;
+ stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili;
+ stencil_compare = StencilCompare(stencil_comparei);
+ stencil_reference = stencil_referencei;
+
#ifdef DEBUG_ENABLED
if (uses_particle_trails) {
WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile renderers.");
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index dfd469354a..ec93b8fbdc 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -261,6 +261,23 @@ struct SceneShaderData : public ShaderData {
DEPTH_TEST_ENABLED_INVERTED,
};
+ enum StencilCompare {
+ STENCIL_COMPARE_LESS,
+ STENCIL_COMPARE_EQUAL,
+ STENCIL_COMPARE_LESS_OR_EQUAL,
+ STENCIL_COMPARE_GREATER,
+ STENCIL_COMPARE_NOT_EQUAL,
+ STENCIL_COMPARE_GREATER_OR_EQUAL,
+ STENCIL_COMPARE_ALWAYS,
+ STENCIL_COMPARE_MAX // not an actual operator, just the amount of operators
+ };
+
+ enum StencilFlags {
+ STENCIL_FLAG_READ = 1,
+ STENCIL_FLAG_WRITE = 2,
+ STENCIL_FLAG_WRITE_DEPTH_FAIL = 4,
+ };
+
enum AlphaAntiAliasing {
ALPHA_ANTIALIASING_OFF,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
@@ -286,6 +303,11 @@ struct SceneShaderData : public ShaderData {
DepthTest depth_test;
RS::CullMode cull_mode;
+ StencilCompare stencil_compare;
+ uint32_t stencil_flags;
+ int32_t stencil_reference;
+ bool stencil_enabled;
+
bool uses_point_size;
bool uses_alpha;
bool uses_alpha_clip;
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
index 7d899fad77..0feb650a32 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 7c52c14ea5..6fa8f42b3f 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -2183,14 +2183,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
ERR_FAIL_NULL(texture);
rt->depth = texture->tex_id;
+ rt->depth_has_stencil = rt->overridden.depth_has_stencil;
} else {
glGenTextures(1, &rt->depth);
glBindTexture(texture_target, rt->depth);
if (use_multiview) {
- glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+ glTexImage3D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
} else {
- glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+ glTexImage2D(texture_target, 0, GL_DEPTH24_STENCIL8, rt->size.x, rt->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -2198,16 +2199,19 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target depth texture");
+ rt->depth_has_stencil = true;
+
+ GLES3::Utilities::get_singleton()->texture_allocated_data(rt->depth, rt->size.x * rt->size.y * rt->view_count * 4, "Render target depth texture");
}
+
#ifndef IOS_ENABLED
if (use_multiview) {
- glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
} else {
#else
{
#endif
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, rt->depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0);
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -2354,12 +2358,33 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s
if (rt->backbuffer_depth == 0 && uses_depth_texture) {
glGenTextures(1, &rt->backbuffer_depth);
glBindTexture(texture_target, rt->backbuffer_depth);
- if (use_multiview) {
- glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+
+ GLint internal_format;
+ GLenum format;
+ GLenum type;
+ GLenum attachment;
+ int element_size;
+
+ if (rt->depth_has_stencil) {
+ internal_format = GL_DEPTH24_STENCIL8;
+ format = GL_DEPTH_STENCIL;
+ type = GL_UNSIGNED_INT_24_8;
+ attachment = GL_DEPTH_STENCIL_ATTACHMENT;
+ element_size = 4;
} else {
- glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+ internal_format = GL_DEPTH_COMPONENT24;
+ format = GL_DEPTH_COMPONENT;
+ type = GL_UNSIGNED_INT;
+ attachment = GL_DEPTH_ATTACHMENT;
+ element_size = 3;
}
- GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * 3, "Render target backbuffer depth texture");
+
+ if (use_multiview) {
+ glTexImage3D(texture_target, 0, internal_format, rt->size.x, rt->size.y, rt->view_count, 0, format, type, nullptr);
+ } else {
+ glTexImage2D(texture_target, 0, internal_format, rt->size.x, rt->size.y, 0, format, type, nullptr);
+ }
+ GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer_depth, rt->size.x * rt->size.y * rt->view_count * element_size, "Render target backbuffer depth texture");
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -2367,12 +2392,12 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#ifndef IOS_ENABLED
if (use_multiview) {
- glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count);
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, rt->backbuffer_depth, 0, 0, rt->view_count);
} else {
#else
{
#endif
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, rt->backbuffer_depth, 0);
}
}
}
@@ -2546,6 +2571,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
rt->overridden.color = p_color_texture;
rt->overridden.depth = p_depth_texture;
+ rt->overridden.depth_has_stencil = p_depth_texture.is_null();
rt->overridden.is_overridden = true;
uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id());
@@ -2557,6 +2583,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
rt->fbo = cache->get().fbo;
rt->color = cache->get().color;
rt->depth = cache->get().depth;
+ rt->depth_has_stencil = cache->get().depth_has_stencil;
rt->size = cache->get().size;
rt->texture = p_color_texture;
return;
@@ -2568,6 +2595,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
new_entry.fbo = rt->fbo;
new_entry.color = rt->color;
new_entry.depth = rt->depth;
+ new_entry.depth_has_stencil = rt->depth_has_stencil;
new_entry.size = rt->size;
// Keep track of any textures we had to allocate because they weren't overridden.
if (p_color_texture.is_null()) {
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index afa548b4b5..405f07ca1b 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 07a4ed5ad4..f16a296087 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -269,17 +269,31 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
+ {
+ const Vector &render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
- for (int j = 0; j < modes.size(); j++) {
- const ShaderLanguage::ModeInfo &mode_info = modes[j];
-
- if (!mode_info.options.is_empty()) {
- for (int k = 0; k < mode_info.options.size(); k++) {
- built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k]));
+ for (const ShaderLanguage::ModeInfo &mode_info : render_modes) {
+ if (!mode_info.options.is_empty()) {
+ for (const StringName &option : mode_info.options) {
+ built_ins.push_back(String(mode_info.name) + "_" + String(option));
+ }
+ } else {
+ built_ins.push_back(String(mode_info.name));
+ }
+ }
+ }
+
+ {
+ const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i));
+
+ for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) {
+ if (!mode_info.options.is_empty()) {
+ for (const StringName &option : mode_info.options) {
+ built_ins.push_back(String(mode_info.name) + "_" + String(option));
+ }
+ } else {
+ built_ins.push_back(String(mode_info.name));
}
- } else {
- built_ins.push_back(String(mode_info.name));
}
}
}
@@ -290,17 +304,31 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- const Vector &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+ {
+ const Vector &shader_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
- for (int i = 0; i < modes.size(); i++) {
- const ShaderLanguage::ModeInfo &mode_info = modes[i];
-
- if (!mode_info.options.is_empty()) {
- for (int j = 0; j < mode_info.options.size(); j++) {
- built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[j]));
+ for (const ShaderLanguage::ModeInfo &mode_info : shader_modes) {
+ if (!mode_info.options.is_empty()) {
+ for (const StringName &option : mode_info.options) {
+ built_ins.push_back(String(mode_info.name) + "_" + String(option));
+ }
+ } else {
+ built_ins.push_back(String(mode_info.name));
+ }
+ }
+ }
+
+ {
+ const Vector &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode()));
+
+ for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) {
+ if (!mode_info.options.is_empty()) {
+ for (const StringName &option : mode_info.options) {
+ built_ins.push_back(String(mode_info.name) + "_" + String(option));
+ }
+ } else {
+ built_ins.push_back(String(mode_info.name));
}
- } else {
- built_ins.push_back(String(mode_info.name));
}
}
}
@@ -437,6 +465,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, Listget_functions(RenderingServer::ShaderMode(shader->get_mode()));
comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+ comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode()));
comp_info.shader_types = ShaderTypes::get_singleton()->get_types();
sl.complete(code, comp_info, r_options, calltip);
@@ -541,6 +570,7 @@ void ShaderTextEditor::_validate_script() {
Shader::Mode mode = shader->get_mode();
comp_info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode));
comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode));
+ comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(mode));
comp_info.shader_types = ShaderTypes::get_singleton()->get_types();
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 2d22a4c4b8..38050d71ac 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 b5d54a1eaa..09fe1859e0 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -912,6 +912,56 @@ void BaseMaterial3D::_update_shader() {
code += ";\n";
+ if (stencil_mode != STENCIL_MODE_DISABLED && stencil_flags != 0) {
+ code += "stencil_mode ";
+
+ if (stencil_flags & STENCIL_FLAG_READ) {
+ code += "read";
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE) {
+ if (stencil_flags & STENCIL_FLAG_READ) {
+ code += ", ";
+ }
+ code += "write";
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) {
+ if (stencil_flags & (STENCIL_FLAG_READ | STENCIL_FLAG_WRITE)) {
+ code += ", ";
+ }
+ code += "write_depth_fail";
+ }
+
+ switch (stencil_compare) {
+ case STENCIL_COMPARE_ALWAYS:
+ code += ", compare_always";
+ break;
+ case STENCIL_COMPARE_LESS:
+ code += ", compare_less";
+ break;
+ case STENCIL_COMPARE_EQUAL:
+ code += ", compare_equal";
+ break;
+ case STENCIL_COMPARE_LESS_OR_EQUAL:
+ code += ", compare_less_or_equal";
+ break;
+ case STENCIL_COMPARE_GREATER:
+ code += ", compare_greater";
+ break;
+ case STENCIL_COMPARE_NOT_EQUAL:
+ code += ", compare_not_equal";
+ break;
+ case STENCIL_COMPARE_GREATER_OR_EQUAL:
+ code += ", compare_greater_or_equal";
+ break;
+ case STENCIL_COMPARE_MAX:
+ break;
+ }
+
+ code += vformat(", %s;\n", stencil_reference);
+ }
+
// Generate list of uniforms.
code += vformat(R"(
uniform vec4 albedo : source_color;
@@ -2591,7 +2641,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
}
if (p_property.name == "depth_test" && flags[FLAG_DISABLE_DEPTH_TEST]) {
- p_property.usage = PROPERTY_USAGE_NONE;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if (p_property.name == "stencil_reference" && stencil_mode == STENCIL_MODE_DISABLED) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if ((p_property.name == "stencil_flags" || p_property.name == "stencil_compare") && stencil_mode != STENCIL_MODE_CUSTOM) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if (p_property.name == "stencil_color" && stencil_mode != STENCIL_MODE_OUTLINE && stencil_mode != STENCIL_MODE_XRAY) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if (p_property.name == "stencil_outline_thickness" && stencil_mode != STENCIL_MODE_OUTLINE) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (p_property.name == "subsurf_scatter_transmittance_color" || p_property.name == "subsurf_scatter_transmittance_texture")) {
@@ -3066,6 +3132,179 @@ RID BaseMaterial3D::get_rid() const {
return _get_material();
}
+void BaseMaterial3D::_prepare_stencil_effect() {
+ const Ref current_next_pass = get_next_pass();
+
+ if (stencil_mode == STENCIL_MODE_DISABLED || stencil_mode == STENCIL_MODE_CUSTOM) {
+ if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) {
+ set_next_pass(current_next_pass->get_next_pass());
+ }
+ return;
+ }
+
+ Ref stencil_next_pass;
+
+ if (current_next_pass.is_null() || !current_next_pass->has_meta("_stencil_owned")) {
+ stencil_next_pass = Ref(memnew(StandardMaterial3D));
+ stencil_next_pass->set_meta("_stencil_owned", true);
+ stencil_next_pass->set_next_pass(current_next_pass);
+ set_next_pass(stencil_next_pass);
+ } else {
+ stencil_next_pass = current_next_pass;
+ }
+
+ switch (stencil_mode) {
+ case STENCIL_MODE_DISABLED:
+ break;
+ case STENCIL_MODE_OUTLINE:
+ set_stencil_flags(STENCIL_FLAG_WRITE);
+ set_stencil_compare(STENCIL_COMPARE_ALWAYS);
+ stencil_next_pass->set_render_priority(-1);
+ stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED);
+ stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA);
+ stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, false);
+ stencil_next_pass->set_grow_enabled(true);
+ stencil_next_pass->set_grow(stencil_effect_outline_thickness);
+ stencil_next_pass->set_albedo(stencil_effect_color);
+ stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM);
+ stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE);
+ stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL);
+ stencil_next_pass->set_stencil_reference(stencil_reference);
+ break;
+ case STENCIL_MODE_XRAY:
+ set_stencil_flags(STENCIL_FLAG_WRITE);
+ set_stencil_compare(STENCIL_COMPARE_ALWAYS);
+ stencil_next_pass->set_render_priority(-1);
+ stencil_next_pass->set_shading_mode(SHADING_MODE_UNSHADED);
+ stencil_next_pass->set_transparency(TRANSPARENCY_ALPHA);
+ stencil_next_pass->set_flag(FLAG_DISABLE_DEPTH_TEST, true);
+ stencil_next_pass->set_grow_enabled(false);
+ stencil_next_pass->set_grow(0);
+ stencil_next_pass->set_albedo(stencil_effect_color);
+ stencil_next_pass->set_stencil_mode(STENCIL_MODE_CUSTOM);
+ stencil_next_pass->set_stencil_flags(STENCIL_FLAG_READ | STENCIL_FLAG_WRITE);
+ stencil_next_pass->set_stencil_compare(STENCIL_COMPARE_NOT_EQUAL);
+ stencil_next_pass->set_stencil_reference(stencil_reference);
+ break;
+ case STENCIL_MODE_CUSTOM:
+ break;
+ case STENCIL_MODE_MAX:
+ break;
+ }
+}
+
+Ref BaseMaterial3D::_get_stencil_next_pass() const {
+ const Ref current_next_pass = get_next_pass();
+ Ref stencil_next_pass;
+
+ if (current_next_pass.is_valid() && current_next_pass->has_meta("_stencil_owned")) {
+ stencil_next_pass = current_next_pass;
+ }
+
+ return stencil_next_pass;
+}
+
+void BaseMaterial3D::set_stencil_mode(StencilMode p_stencil_mode) {
+ if (stencil_mode == p_stencil_mode) {
+ return;
+ }
+
+ stencil_mode = p_stencil_mode;
+ _prepare_stencil_effect();
+ _queue_shader_change();
+ notify_property_list_changed();
+}
+
+BaseMaterial3D::StencilMode BaseMaterial3D::get_stencil_mode() const {
+ return stencil_mode;
+}
+
+void BaseMaterial3D::set_stencil_flags(int p_stencil_flags) {
+ if (stencil_flags == p_stencil_flags) {
+ return;
+ }
+
+ if ((p_stencil_flags & STENCIL_FLAG_READ) && (stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL))) {
+ p_stencil_flags = p_stencil_flags & STENCIL_FLAG_READ;
+ }
+
+ if ((p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL)) && (stencil_flags & STENCIL_FLAG_READ)) {
+ p_stencil_flags = p_stencil_flags & (STENCIL_FLAG_WRITE | STENCIL_FLAG_WRITE_DEPTH_FAIL);
+ }
+
+ stencil_flags = p_stencil_flags;
+ _queue_shader_change();
+}
+
+int BaseMaterial3D::get_stencil_flags() const {
+ return stencil_flags;
+}
+
+void BaseMaterial3D::set_stencil_compare(BaseMaterial3D::StencilCompare p_op) {
+ if (stencil_compare == p_op) {
+ return;
+ }
+
+ stencil_compare = p_op;
+ _queue_shader_change();
+}
+
+BaseMaterial3D::StencilCompare BaseMaterial3D::get_stencil_compare() const {
+ return stencil_compare;
+}
+
+void BaseMaterial3D::set_stencil_reference(int p_reference) {
+ if (stencil_reference == p_reference) {
+ return;
+ }
+
+ stencil_reference = p_reference;
+ _queue_shader_change();
+
+ Ref stencil_next_pass = _get_stencil_next_pass();
+ if (stencil_next_pass.is_valid()) {
+ stencil_next_pass->set_stencil_reference(p_reference);
+ }
+}
+
+int BaseMaterial3D::get_stencil_reference() const {
+ return stencil_reference;
+}
+
+void BaseMaterial3D::set_stencil_effect_color(const Color &p_color) {
+ if (stencil_effect_color == p_color) {
+ return;
+ }
+
+ stencil_effect_color = p_color;
+
+ Ref stencil_next_pass = _get_stencil_next_pass();
+ if (stencil_next_pass.is_valid()) {
+ stencil_next_pass->set_albedo(p_color);
+ }
+}
+
+Color BaseMaterial3D::get_stencil_effect_color() const {
+ return stencil_effect_color;
+}
+
+void BaseMaterial3D::set_stencil_effect_outline_thickness(float p_outline_thickness) {
+ if (stencil_effect_outline_thickness == p_outline_thickness) {
+ return;
+ }
+
+ stencil_effect_outline_thickness = p_outline_thickness;
+
+ Ref stencil_next_pass = _get_stencil_next_pass();
+ if (stencil_next_pass.is_valid()) {
+ stencil_next_pass->set_grow(p_outline_thickness);
+ }
+}
+
+float BaseMaterial3D::get_stencil_effect_outline_thickness() const {
+ return stencil_effect_outline_thickness;
+}
+
RID BaseMaterial3D::get_shader_rid() const {
const_cast(this)->_update_shader();
return shader_rid;
@@ -3291,6 +3530,24 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fov_override", "scale"), &BaseMaterial3D::set_fov_override);
ClassDB::bind_method(D_METHOD("get_fov_override"), &BaseMaterial3D::get_fov_override);
+ ClassDB::bind_method(D_METHOD("set_stencil_mode", "stencil_mode"), &BaseMaterial3D::set_stencil_mode);
+ ClassDB::bind_method(D_METHOD("get_stencil_mode"), &BaseMaterial3D::get_stencil_mode);
+
+ ClassDB::bind_method(D_METHOD("set_stencil_flags", "stencil_flags"), &BaseMaterial3D::set_stencil_flags);
+ ClassDB::bind_method(D_METHOD("get_stencil_flags"), &BaseMaterial3D::get_stencil_flags);
+
+ ClassDB::bind_method(D_METHOD("set_stencil_compare", "stencil_compare"), &BaseMaterial3D::set_stencil_compare);
+ ClassDB::bind_method(D_METHOD("get_stencil_compare"), &BaseMaterial3D::get_stencil_compare);
+
+ ClassDB::bind_method(D_METHOD("set_stencil_reference", "stencil_reference"), &BaseMaterial3D::set_stencil_reference);
+ ClassDB::bind_method(D_METHOD("get_stencil_reference"), &BaseMaterial3D::get_stencil_reference);
+
+ ClassDB::bind_method(D_METHOD("set_stencil_effect_color", "stencil_color"), &BaseMaterial3D::set_stencil_effect_color);
+ ClassDB::bind_method(D_METHOD("get_stencil_effect_color"), &BaseMaterial3D::get_stencil_effect_color);
+
+ ClassDB::bind_method(D_METHOD("set_stencil_effect_outline_thickness", "stencil_outline_thickness"), &BaseMaterial3D::set_stencil_effect_outline_thickness);
+ ClassDB::bind_method(D_METHOD("get_stencil_effect_outline_thickness"), &BaseMaterial3D::get_stencil_effect_outline_thickness);
+
ADD_GROUP("Transparency", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
@@ -3478,6 +3735,15 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_min_distance", "get_distance_fade_min_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_max_distance", "get_distance_fade_max_distance");
+ ADD_GROUP("Stencil", "stencil_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_mode", PROPERTY_HINT_ENUM, "Disabled,Outline,X-Ray,Custom"), "set_stencil_mode", "get_stencil_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_flags", PROPERTY_HINT_FLAGS, "Read,Write,Write Depth Fail"), "set_stencil_flags", "get_stencil_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_compare", PROPERTY_HINT_ENUM, "Always,Less,Equal,Less Or Equal,Greater,Not Equal,Greater Or Equal"), "set_stencil_compare", "get_stencil_compare");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stencil_reference", PROPERTY_HINT_RANGE, "0,255,1"), "set_stencil_reference", "get_stencil_reference");
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "stencil_color", PROPERTY_HINT_NONE), "set_stencil_effect_color", "get_stencil_effect_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stencil_outline_thickness", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_stencil_effect_outline_thickness", "get_stencil_effect_outline_thickness");
+
BIND_ENUM_CONSTANT(TEXTURE_ALBEDO);
BIND_ENUM_CONSTANT(TEXTURE_METALLIC);
BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS);
@@ -3612,6 +3878,23 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_ALPHA);
BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_DITHER);
BIND_ENUM_CONSTANT(DISTANCE_FADE_OBJECT_DITHER);
+
+ BIND_ENUM_CONSTANT(STENCIL_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(STENCIL_MODE_OUTLINE);
+ BIND_ENUM_CONSTANT(STENCIL_MODE_XRAY);
+ BIND_ENUM_CONSTANT(STENCIL_MODE_CUSTOM);
+
+ BIND_ENUM_CONSTANT(STENCIL_FLAG_READ);
+ BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE);
+ BIND_ENUM_CONSTANT(STENCIL_FLAG_WRITE_DEPTH_FAIL);
+
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_ALWAYS);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_EQUAL);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_LESS_OR_EQUAL);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_NOT_EQUAL);
+ BIND_ENUM_CONSTANT(STENCIL_COMPARE_GREATER_OR_EQUAL);
}
BaseMaterial3D::BaseMaterial3D(bool p_orm) :
@@ -3680,6 +3963,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_z_clip_scale(1.0);
set_fov_override(75.0);
+ set_stencil_mode(STENCIL_MODE_DISABLED);
+
flags[FLAG_ALBEDO_TEXTURE_MSDF] = false;
flags[FLAG_USE_TEXTURE_REPEAT] = true;
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 8a5141eb41..081a2356fb 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -326,6 +326,33 @@ public:
DISTANCE_FADE_MAX
};
+ enum StencilMode {
+ STENCIL_MODE_DISABLED,
+ STENCIL_MODE_OUTLINE,
+ STENCIL_MODE_XRAY,
+ STENCIL_MODE_CUSTOM,
+ STENCIL_MODE_MAX // Not an actual mode, just the amount of modes.
+ };
+
+ enum StencilFlags {
+ STENCIL_FLAG_READ = 1,
+ STENCIL_FLAG_WRITE = 2,
+ STENCIL_FLAG_WRITE_DEPTH_FAIL = 4,
+
+ STENCIL_FLAG_NUM_BITS = 3 // Not an actual mode, just the amount of bits.
+ };
+
+ enum StencilCompare {
+ STENCIL_COMPARE_ALWAYS,
+ STENCIL_COMPARE_LESS,
+ STENCIL_COMPARE_EQUAL,
+ STENCIL_COMPARE_LESS_OR_EQUAL,
+ STENCIL_COMPARE_GREATER,
+ STENCIL_COMPARE_NOT_EQUAL,
+ STENCIL_COMPARE_GREATER_OR_EQUAL,
+ STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators.
+ };
+
private:
struct MaterialKey {
// enum values
@@ -345,6 +372,13 @@ private:
uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1);
uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1);
uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1);
+
+ // stencil
+ uint64_t stencil_mode : get_num_bits(STENCIL_MODE_MAX - 1);
+ uint64_t stencil_flags : STENCIL_FLAG_NUM_BITS;
+ uint64_t stencil_compare : get_num_bits(STENCIL_COMPARE_MAX - 1);
+ uint64_t stencil_reference : 8;
+
// booleans
uint64_t invalid_key : 1;
uint64_t deep_parallax : 1;
@@ -406,6 +440,11 @@ private:
mk.alpha_antialiasing_mode = alpha_antialiasing_mode;
mk.orm = orm;
+ mk.stencil_mode = stencil_mode;
+ mk.stencil_flags = stencil_flags;
+ mk.stencil_compare = stencil_compare;
+ mk.stencil_reference = stencil_reference;
+
for (int i = 0; i < FEATURE_MAX; i++) {
if (features[i]) {
mk.feature_mask |= ((uint64_t)1 << i);
@@ -579,10 +618,21 @@ private:
float z_clip_scale = 1.0;
float fov_override = 75.0;
+ StencilMode stencil_mode = STENCIL_MODE_DISABLED;
+ int stencil_flags = 0;
+ StencilCompare stencil_compare = STENCIL_COMPARE_ALWAYS;
+ int stencil_reference = 1;
+
+ Color stencil_effect_color;
+ float stencil_effect_outline_thickness = 0.01f;
+
bool features[FEATURE_MAX] = {};
Ref textures[TEXTURE_MAX];
+ void _prepare_stencil_effect();
+ Ref _get_stencil_next_pass() const;
+
static HashMap> materials_for_2d; //used by Sprite3D, Label3D and other stuff
protected:
@@ -790,6 +840,24 @@ public:
void set_emission_operator(EmissionOperator p_op);
EmissionOperator get_emission_operator() const;
+ void set_stencil_mode(StencilMode p_stencil_mode);
+ StencilMode get_stencil_mode() const;
+
+ void set_stencil_flags(int p_stencil_flags);
+ int get_stencil_flags() const;
+
+ void set_stencil_compare(StencilCompare p_op);
+ StencilCompare get_stencil_compare() const;
+
+ void set_stencil_reference(int p_reference);
+ int get_stencil_reference() const;
+
+ void set_stencil_effect_color(const Color &p_color);
+ Color get_stencil_effect_color() const;
+
+ void set_stencil_effect_outline_thickness(float p_outline_thickness);
+ float get_stencil_effect_outline_thickness() const;
+
void set_metallic_texture_channel(TextureChannel p_channel);
TextureChannel get_metallic_texture_channel() const;
void set_roughness_texture_channel(TextureChannel p_channel);
@@ -837,6 +905,9 @@ VARIANT_ENUM_CAST(BaseMaterial3D::BillboardMode)
VARIANT_ENUM_CAST(BaseMaterial3D::TextureChannel)
VARIANT_ENUM_CAST(BaseMaterial3D::EmissionOperator)
VARIANT_ENUM_CAST(BaseMaterial3D::DistanceFadeMode)
+VARIANT_ENUM_CAST(BaseMaterial3D::StencilMode)
+VARIANT_ENUM_CAST(BaseMaterial3D::StencilFlags)
+VARIANT_ENUM_CAST(BaseMaterial3D::StencilCompare)
class StandardMaterial3D : public BaseMaterial3D {
GDCLASS(StandardMaterial3D, BaseMaterial3D)
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index a76dd797ae..4f23de672b 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1715,6 +1715,41 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
}
_queue_update();
return true;
+ } else if (prop_name == "stencil/enabled") {
+ stencil_enabled = bool(p_value);
+ _queue_update();
+ notify_property_list_changed();
+ return true;
+ } else if (prop_name == "stencil/reference") {
+ stencil_reference = int(p_value);
+ _queue_update();
+ return true;
+ } else if (prop_name.begins_with("stencil_flags/")) {
+ StringName flag = prop_name.get_slicec('/', 1);
+ bool enable = p_value;
+ if (enable) {
+ stencil_flags.insert(flag);
+ if (flag == "read") {
+ stencil_flags.erase("write");
+ stencil_flags.erase("write_depth_fail");
+ } else if (flag == "write" || flag == "write_depth_fail") {
+ stencil_flags.erase("read");
+ }
+ } else {
+ stencil_flags.erase(flag);
+ }
+ _queue_update();
+ return true;
+ } else if (prop_name.begins_with("stencil_modes/")) {
+ String mode_name = prop_name.get_slicec('/', 1);
+ int value = p_value;
+ if (value == 0) {
+ stencil_modes.erase(mode_name); // It's default anyway, so don't store it.
+ } else {
+ stencil_modes[mode_name] = value;
+ }
+ _queue_update();
+ return true;
} else if (prop_name.begins_with("varyings/")) {
String var_name = prop_name.get_slicec('/', 1);
Varying value = Varying();
@@ -1798,6 +1833,24 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = 0;
}
return true;
+ } else if (prop_name == "stencil/enabled") {
+ r_ret = stencil_enabled;
+ return true;
+ } else if (prop_name == "stencil/reference") {
+ r_ret = stencil_reference;
+ return true;
+ } else if (prop_name.begins_with("stencil_flags/")) {
+ StringName flag = prop_name.get_slicec('/', 1);
+ r_ret = stencil_flags.has(flag);
+ return true;
+ } else if (prop_name.begins_with("stencil_modes/")) {
+ String mode_name = prop_name.get_slicec('/', 1);
+ if (stencil_modes.has(mode_name)) {
+ r_ret = stencil_modes[mode_name];
+ } else {
+ r_ret = 0;
+ }
+ return true;
} else if (prop_name.begins_with("varyings/")) {
String var_name = prop_name.get_slicec('/', 1);
if (varyings.has(var_name)) {
@@ -1934,6 +1987,45 @@ void VisualShader::_get_property_list(List *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E)));
}
+ const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode));
+
+ if (smodes.size() > 0) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil"), PNAME("enabled"))));
+
+ uint32_t stencil_prop_usage = stencil_enabled ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE;
+
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil"), PNAME("reference")), PROPERTY_HINT_RANGE, "0,255,1", stencil_prop_usage));
+
+ HashMap stencil_enums;
+ HashSet stencil_toggles;
+
+ for (const ShaderLanguage::ModeInfo &info : smodes) {
+ if (!info.options.is_empty()) {
+ const String begin = String(info.name);
+
+ for (int j = 0; j < info.options.size(); j++) {
+ const String option = String(info.options[j]).capitalize();
+
+ if (!stencil_enums.has(begin)) {
+ stencil_enums[begin] = option;
+ } else {
+ stencil_enums[begin] += "," + option;
+ }
+ }
+ } else {
+ stencil_toggles.insert(String(info.name));
+ }
+ }
+
+ for (const KeyValue &E : stencil_enums) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("stencil_modes"), E.key), PROPERTY_HINT_ENUM, E.value, stencil_prop_usage));
+ }
+
+ for (const String &E : stencil_toggles) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("stencil_flags"), E), PROPERTY_HINT_NONE, "", stencil_prop_usage));
+ }
+ }
+
for (const KeyValue &E : varyings) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "varyings", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
@@ -2621,6 +2713,46 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
+ const Vector &smodes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader_mode));
+
+ if (stencil_enabled && smodes.size() > 0 && (stencil_flags.has("read") || stencil_flags.has("write") || stencil_flags.has("write_depth_fail"))) {
+ String stencil_mode;
+
+ Vector flag_names;
+
+ // Add enum modes first.
+ for (const ShaderLanguage::ModeInfo &info : smodes) {
+ const String temp = String(info.name);
+
+ if (!info.options.is_empty()) {
+ if (stencil_modes.has(temp) && stencil_modes[temp] < info.options.size()) {
+ if (!stencil_mode.is_empty()) {
+ stencil_mode += ", ";
+ }
+ stencil_mode += temp + "_" + info.options[stencil_modes[temp]];
+ }
+ } else if (stencil_flags.has(temp)) {
+ flag_names.push_back(temp);
+ }
+ }
+
+ // Add flags afterward.
+ for (const String &flag_name : flag_names) {
+ if (!stencil_mode.is_empty()) {
+ stencil_mode += ", ";
+ }
+ stencil_mode += flag_name;
+ }
+
+ // Add reference value.
+ if (!stencil_mode.is_empty()) {
+ stencil_mode += ", ";
+ }
+ stencil_mode += itos(stencil_reference);
+
+ global_code += "stencil_mode " + stencil_mode + ";\n\n";
+ }
+
static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
String global_expressions;
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 7d7b0996c1..4a24e147cf 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -138,6 +138,11 @@ private:
HashMap modes;
HashSet flags;
+ bool stencil_enabled = false;
+ HashMap stencil_modes;
+ HashSet stencil_flags;
+ int stencil_reference = 1;
+
HashMap varyings;
#ifdef TOOLS_ENABLED
HashMap preview_params;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 7d841f34dd..456661d712 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 0626cef6d3..b5c92ec1fe 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -408,6 +408,7 @@ private:
bool used_depth_texture = false;
bool used_sss = false;
bool used_lightmap = false;
+ bool used_opaque_stencil = false;
struct ShadowPass {
uint32_t element_from;
@@ -485,6 +486,7 @@ private:
FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768,
FLAG_USES_PARTICLE_TRAILS = 65536,
FLAG_USES_MOTION_VECTOR = 131072,
+ FLAG_USES_STENCIL = 262144,
};
union {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index e75302f345..3a4abf4169 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -84,6 +84,12 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
int depth_drawi = DEPTH_DRAW_OPAQUE;
+ int stencil_readi = 0;
+ int stencil_writei = 0;
+ int stencil_write_depth_faili = 0;
+ int stencil_comparei = STENCIL_COMPARE_ALWAYS;
+ int stencil_referencei = -1;
+
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
@@ -145,6 +151,20 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.write_flag_pointers["POSITION"] = &uses_position;
actions.write_flag_pointers["Z_CLIP_SCALE"] = &uses_z_clip_scale;
+ actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ);
+ actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE);
+ actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
+
+ actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS);
+ actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL);
+ actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
+ actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER);
+ actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
+ actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
+ actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS);
+
+ actions.stencil_reference = &stencil_referencei;
+
actions.uniforms = &uniforms;
Error err = OK;
@@ -185,6 +205,11 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
uses_tangent |= uses_normal_map;
uses_tangent |= uses_bent_normal_map;
+ stencil_enabled = stencil_referencei != -1;
+ stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili;
+ stencil_compare = StencilCompare(stencil_comparei);
+ stencil_reference = stencil_referencei;
+
#if 0
print_line("**compiling shader:");
print_line("**defines:\n");
@@ -333,6 +358,48 @@ void SceneShaderForwardClustered::ShaderData::_create_pipeline(PipelineKey p_pip
depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS;
}
}
+
+ depth_stencil_state.enable_stencil = stencil_enabled;
+ if (stencil_enabled) {
+ static const RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = {
+ RD::COMPARE_OP_LESS,
+ RD::COMPARE_OP_EQUAL,
+ RD::COMPARE_OP_LESS_OR_EQUAL,
+ RD::COMPARE_OP_GREATER,
+ RD::COMPARE_OP_NOT_EQUAL,
+ RD::COMPARE_OP_GREATER_OR_EQUAL,
+ RD::COMPARE_OP_ALWAYS,
+ };
+
+ uint32_t stencil_mask = 255;
+
+ RD::PipelineDepthStencilState::StencilOperationState op;
+ op.fail = RD::STENCIL_OP_KEEP;
+ op.pass = RD::STENCIL_OP_KEEP;
+ op.depth_fail = RD::STENCIL_OP_KEEP;
+ op.compare = stencil_compare_rd_table[stencil_compare];
+ op.compare_mask = 0;
+ op.write_mask = 0;
+ op.reference = stencil_reference;
+
+ if (stencil_flags & STENCIL_FLAG_READ) {
+ op.compare_mask = stencil_mask;
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE) {
+ op.pass = RD::STENCIL_OP_REPLACE;
+ op.write_mask = stencil_mask;
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) {
+ op.depth_fail = RD::STENCIL_OP_REPLACE;
+ op.write_mask = stencil_mask;
+ }
+
+ depth_stencil_state.front_op = op;
+ depth_stencil_state.back_op = op;
+ }
+
bool depth_pre_pass_enabled = bool(GLOBAL_GET_CACHED(bool, "rendering/driver/depth_prepass/enable"));
RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index 5090051b56..e646916a7f 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -168,6 +168,23 @@ public:
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
+ enum StencilFlags {
+ STENCIL_FLAG_READ = 1,
+ STENCIL_FLAG_WRITE = 2,
+ STENCIL_FLAG_WRITE_DEPTH_FAIL = 4,
+ };
+
+ enum StencilCompare {
+ STENCIL_COMPARE_LESS,
+ STENCIL_COMPARE_EQUAL,
+ STENCIL_COMPARE_LESS_OR_EQUAL,
+ STENCIL_COMPARE_GREATER,
+ STENCIL_COMPARE_NOT_EQUAL,
+ STENCIL_COMPARE_GREATER_OR_EQUAL,
+ STENCIL_COMPARE_ALWAYS,
+ STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators.
+ };
+
struct PipelineKey {
RD::VertexFormatID vertex_format_id;
RD::FramebufferFormatID framebuffer_format_id;
@@ -250,6 +267,11 @@ public:
bool uses_z_clip_scale = false;
RS::CullMode cull_mode = RS::CULL_MODE_DISABLED;
+ bool stencil_enabled = false;
+ uint32_t stencil_flags = 0;
+ StencilCompare stencil_compare = STENCIL_COMPARE_LESS;
+ uint32_t stencil_reference = 0;
+
uint64_t last_pass = 0;
uint32_t index = 0;
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index c9d0d7b63c..8881b423e1 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 223cfd181c..f38f223971 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -84,6 +84,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
int depth_drawi = DEPTH_DRAW_OPAQUE;
+ int stencil_readi = 0;
+ int stencil_writei = 0;
+ int stencil_write_depth_faili = 0;
+ int stencil_comparei = STENCIL_COMPARE_ALWAYS;
+ int stencil_referencei = -1;
+
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
@@ -143,6 +149,20 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection;
actions.write_flag_pointers["VERTEX"] = &uses_vertex;
+ actions.stencil_mode_values["read"] = Pair(&stencil_readi, STENCIL_FLAG_READ);
+ actions.stencil_mode_values["write"] = Pair(&stencil_writei, STENCIL_FLAG_WRITE);
+ actions.stencil_mode_values["write_depth_fail"] = Pair(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
+
+ actions.stencil_mode_values["compare_less"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS);
+ actions.stencil_mode_values["compare_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_EQUAL);
+ actions.stencil_mode_values["compare_less_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
+ actions.stencil_mode_values["compare_greater"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER);
+ actions.stencil_mode_values["compare_not_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
+ actions.stencil_mode_values["compare_greater_or_equal"] = Pair(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
+ actions.stencil_mode_values["compare_always"] = Pair(&stencil_comparei, STENCIL_COMPARE_ALWAYS);
+
+ actions.stencil_reference = &stencil_referencei;
+
actions.uniforms = &uniforms;
MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
@@ -179,6 +199,11 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
uses_tangent |= uses_normal_map;
uses_tangent |= uses_bent_normal_map;
+ stencil_enabled = stencil_referencei != -1;
+ stencil_flags = stencil_readi | stencil_writei | stencil_write_depth_faili;
+ stencil_compare = StencilCompare(stencil_comparei);
+ stencil_reference = stencil_referencei;
+
#ifdef DEBUG_ENABLED
if (uses_sss) {
WARN_PRINT_ONCE_ED("Subsurface scattering is only available when using the Forward+ renderer.");
@@ -300,6 +325,47 @@ void SceneShaderForwardMobile::ShaderData::_create_pipeline(PipelineKey p_pipeli
RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
};
+ depth_stencil_state.enable_stencil = stencil_enabled;
+ if (stencil_enabled) {
+ static const RD::CompareOperator stencil_compare_rd_table[STENCIL_COMPARE_MAX] = {
+ RD::COMPARE_OP_LESS,
+ RD::COMPARE_OP_EQUAL,
+ RD::COMPARE_OP_LESS_OR_EQUAL,
+ RD::COMPARE_OP_GREATER,
+ RD::COMPARE_OP_NOT_EQUAL,
+ RD::COMPARE_OP_GREATER_OR_EQUAL,
+ RD::COMPARE_OP_ALWAYS,
+ };
+
+ uint32_t stencil_mask = 255;
+
+ RD::PipelineDepthStencilState::StencilOperationState op;
+ op.fail = RD::STENCIL_OP_KEEP;
+ op.pass = RD::STENCIL_OP_KEEP;
+ op.depth_fail = RD::STENCIL_OP_KEEP;
+ op.compare = stencil_compare_rd_table[stencil_compare];
+ op.compare_mask = 0;
+ op.write_mask = 0;
+ op.reference = stencil_reference;
+
+ if (stencil_flags & STENCIL_FLAG_READ) {
+ op.compare_mask = stencil_mask;
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE) {
+ op.pass = RD::STENCIL_OP_REPLACE;
+ op.write_mask = stencil_mask;
+ }
+
+ if (stencil_flags & STENCIL_FLAG_WRITE_DEPTH_FAIL) {
+ op.depth_fail = RD::STENCIL_OP_REPLACE;
+ op.write_mask = stencil_mask;
+ }
+
+ depth_stencil_state.front_op = op;
+ depth_stencil_state.back_op = op;
+ }
+
RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[p_pipeline_key.primitive_type];
RD::PipelineRasterizationState raster_state;
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
index 18af561e23..7b3194822e 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
@@ -166,6 +166,23 @@ public:
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
+ enum StencilFlags {
+ STENCIL_FLAG_READ = 1,
+ STENCIL_FLAG_WRITE = 2,
+ STENCIL_FLAG_WRITE_DEPTH_FAIL = 4,
+ };
+
+ enum StencilCompare {
+ STENCIL_COMPARE_LESS,
+ STENCIL_COMPARE_EQUAL,
+ STENCIL_COMPARE_LESS_OR_EQUAL,
+ STENCIL_COMPARE_GREATER,
+ STENCIL_COMPARE_NOT_EQUAL,
+ STENCIL_COMPARE_GREATER_OR_EQUAL,
+ STENCIL_COMPARE_ALWAYS,
+ STENCIL_COMPARE_MAX // Not an actual operator, just the amount of operators.
+ };
+
struct PipelineKey {
RD::VertexFormatID vertex_format_id;
RD::FramebufferFormatID framebuffer_format_id;
@@ -246,6 +263,11 @@ public:
bool writes_modelview_or_projection = false;
bool uses_world_coordinates = false;
+ bool stencil_enabled = false;
+ uint32_t stencil_flags = 0;
+ StencilCompare stencil_compare = STENCIL_COMPARE_LESS;
+ uint32_t stencil_reference = 0;
+
uint64_t last_pass = 0;
uint32_t index = 0;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 397da80b9f..c23ba8ee01 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 7fb873a65f..16c1a034bf 100644
--- a/servers/rendering/shader_compiler.h
+++ b/servers/rendering/shader_compiler.h
@@ -50,6 +50,8 @@ public:
HashMap render_mode_flags;
HashMap usage_flag_pointers;
HashMap write_flag_pointers;
+ HashMap> stencil_mode_values;
+ int *stencil_reference = nullptr;
HashMap *uniforms = nullptr;
};
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index c428d92e6c..884f402292 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -339,6 +339,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} },
{ TK_SHADER_TYPE, "shader_type", CF_SHADER_TYPE, {}, {} },
{ TK_RENDER_MODE, "render_mode", CF_GLOBAL_SPACE, {}, {} },
+ { TK_STENCIL_MODE, "stencil_mode", CF_GLOBAL_SPACE, {}, {} },
// uniform qualifiers
@@ -4128,7 +4129,7 @@ bool ShaderLanguage::is_token_operator_assign(TokenType p_type) {
}
bool ShaderLanguage::is_token_hint(TokenType p_type) {
- return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE);
+ return int(p_type) > int(TK_STENCIL_MODE) && int(p_type) < int(TK_SHADER_TYPE);
}
bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) {
@@ -9110,7 +9111,7 @@ bool ShaderLanguage::_parse_numeric_constant_expression(const FunctionInfo &p_fu
return true;
}
-Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types) {
+Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types) {
Token tk;
TkPos prev_pos;
Token next;
@@ -9173,7 +9174,8 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f
const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo();
- HashMap defined_modes;
+ HashMap defined_render_modes;
+ HashMap defined_stencil_modes;
while (tk.type != TK_EOF) {
switch (tk.type) {
@@ -9182,83 +9184,64 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f
keyword_completion_context = CF_UNSPECIFIED;
#endif // DEBUG_ENABLED
while (true) {
- StringName mode;
- _get_completable_identifier(nullptr, COMPLETION_RENDER_MODE, mode);
-
- if (mode == StringName()) {
- _set_error(RTR("Expected an identifier for render mode."));
- return ERR_PARSE_ERROR;
+ Error error = _parse_shader_mode(false, p_render_modes, defined_render_modes);
+ if (error != OK) {
+ return error;
}
- const String smode = String(mode);
-
- if (shader->render_modes.has(mode)) {
- _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode));
- return ERR_PARSE_ERROR;
- }
-
- bool found = false;
-
- if (is_shader_inc) {
- for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
- const Vector modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
-
- for (int j = 0; j < modes.size(); j++) {
- const ModeInfo &info = modes[j];
- const String name = String(info.name);
-
- if (smode.begins_with(name)) {
- if (!info.options.is_empty()) {
- if (info.options.has(smode.substr(name.length() + 1))) {
- found = true;
-
- if (defined_modes.has(name)) {
- _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name]));
- return ERR_PARSE_ERROR;
- }
- defined_modes.insert(name, smode);
- break;
- }
- } else {
- found = true;
- break;
- }
- }
- }
- }
- } else {
- for (int i = 0; i < p_render_modes.size(); i++) {
- const ModeInfo &info = p_render_modes[i];
- const String name = String(info.name);
-
- if (smode.begins_with(name)) {
- if (!info.options.is_empty()) {
- if (info.options.has(smode.substr(name.length() + 1))) {
- found = true;
-
- if (defined_modes.has(name)) {
- _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name]));
- return ERR_PARSE_ERROR;
- }
- defined_modes.insert(name, smode);
- break;
- }
- } else {
- found = true;
- break;
- }
- }
- }
- }
-
- if (!found) {
- _set_error(vformat(RTR("Invalid render mode: '%s'."), smode));
- return ERR_PARSE_ERROR;
- }
-
- shader->render_modes.push_back(mode);
-
tk = _get_token();
+
+ if (tk.type == TK_COMMA) {
+ // All good, do nothing.
+ } else if (tk.type == TK_SEMICOLON) {
+ break; // Done.
+ } else {
+ _set_error(vformat(RTR("Unexpected token: '%s'."), get_token_text(tk)));
+ return ERR_PARSE_ERROR;
+ }
+ }
+#ifdef DEBUG_ENABLED
+ keyword_completion_context = CF_GLOBAL_SPACE;
+#endif // DEBUG_ENABLED
+ } break;
+ case TK_STENCIL_MODE: {
+#ifdef DEBUG_ENABLED
+ keyword_completion_context = CF_UNSPECIFIED;
+#endif // DEBUG_ENABLED
+ while (true) {
+ TkPos pos = _get_tkpos();
+ tk = _get_token();
+
+ if (tk.is_integer_constant()) {
+ const int reference_value = tk.constant;
+
+ if (shader->stencil_reference != -1) {
+ _set_error(vformat(RTR("Duplicated stencil mode reference value: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
+ }
+
+ if (reference_value < 0) {
+ _set_error(vformat(RTR("Stencil mode reference value cannot be negative: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
+ }
+
+ if (reference_value > 255) {
+ _set_error(vformat(RTR("Stencil mode reference value cannot be greater than 255: '%s'."), reference_value));
+ return ERR_PARSE_ERROR;
+ }
+
+ shader->stencil_reference = reference_value;
+ } else {
+ _set_tkpos(pos);
+
+ Error error = _parse_shader_mode(true, p_stencil_modes, defined_stencil_modes);
+ if (error != OK) {
+ return error;
+ }
+ }
+
+ tk = _get_token();
+
if (tk.type == TK_COMMA) {
//all good, do nothing
} else if (tk.type == TK_SEMICOLON) {
@@ -11076,6 +11059,110 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat
return FAILED;
}
+Error ShaderLanguage::_parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes) {
+ StringName mode;
+ _get_completable_identifier(nullptr, p_is_stencil ? COMPLETION_STENCIL_MODE : COMPLETION_RENDER_MODE, mode);
+
+ if (mode == StringName()) {
+ if (p_is_stencil) {
+ _set_error(RTR("Expected an identifier for stencil mode."));
+ } else {
+ _set_error(RTR("Expected an identifier for render mode."));
+ }
+ return ERR_PARSE_ERROR;
+ }
+
+ const String smode = String(mode);
+
+ Vector ¤t_modes = p_is_stencil ? shader->stencil_modes : shader->render_modes;
+
+ if (current_modes.has(mode)) {
+ if (p_is_stencil) {
+ _set_error(vformat(RTR("Duplicated stencil mode: '%s'."), smode));
+ } else {
+ _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode));
+ }
+ return ERR_PARSE_ERROR;
+ }
+
+ bool found = false;
+
+ if (is_shader_inc) {
+ for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
+ const Vector modes = p_is_stencil ? ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i)) : ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
+
+ for (const ModeInfo &info : modes) {
+ const String name = String(info.name);
+
+ if (smode.begins_with(name)) {
+ if (!info.options.is_empty()) {
+ if (info.options.has(smode.substr(name.length() + 1))) {
+ found = true;
+
+ if (r_defined_modes.has(name)) {
+ if (p_is_stencil) {
+ _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name]));
+ } else {
+ _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name]));
+ }
+ return ERR_PARSE_ERROR;
+ }
+ r_defined_modes.insert(name, smode);
+ break;
+ }
+ } else {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ for (const ModeInfo &info : p_modes) {
+ const String name = String(info.name);
+
+ if (smode.begins_with(name)) {
+ if (!info.options.is_empty()) {
+ if (info.options.has(smode.substr(name.length() + 1))) {
+ found = true;
+
+ if (r_defined_modes.has(name)) {
+ if (p_is_stencil) {
+ _set_error(vformat(RTR("Redefinition of stencil mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name]));
+ } else {
+ _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, r_defined_modes[name]));
+ }
+ return ERR_PARSE_ERROR;
+ }
+ r_defined_modes.insert(name, smode);
+ break;
+ }
+ } else {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ if (p_is_stencil) {
+ _set_error(vformat(RTR("Invalid stencil mode: '%s'."), smode));
+ } else {
+ _set_error(vformat(RTR("Invalid render mode: '%s'."), smode));
+ }
+ return ERR_PARSE_ERROR;
+ }
+
+ if (p_is_stencil) {
+ shader->stencil_modes.push_back(mode);
+ } else {
+ shader->render_modes.push_back(mode);
+ }
+
+ return OK;
+}
+
// skips over whitespace and /* */ and // comments
static int _get_first_ident_pos(const String &p_code) {
int idx = 0;
@@ -11226,7 +11313,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i
nodes = nullptr;
shader = alloc_node();
- Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types);
+ Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types);
#ifdef DEBUG_ENABLED
if (check_warnings) {
@@ -11251,7 +11338,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
global_shader_uniform_get_type_func = p_info.global_shader_uniform_type_func;
shader = alloc_node();
- _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types);
+ _parse_shader(p_info.functions, p_info.render_modes, p_info.stencil_modes, p_info.shader_types);
#ifdef DEBUG_ENABLED
// Adds context keywords.
@@ -11349,6 +11436,67 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
return OK;
} break;
+ case COMPLETION_STENCIL_MODE: {
+ if (is_shader_inc) {
+ for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
+ const Vector modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i));
+
+ for (const ModeInfo &info : modes) {
+ if (!info.options.is_empty()) {
+ bool found = false;
+
+ for (const StringName &option : info.options) {
+ if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ for (const StringName &option : info.options) {
+ ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ r_options->push_back(completion_option);
+ }
+ }
+ } else {
+ const String name = String(info.name);
+
+ if (!shader->stencil_modes.has(name)) {
+ ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ r_options->push_back(option);
+ }
+ }
+ }
+ }
+ } else {
+ for (const ModeInfo &info : p_info.stencil_modes) {
+ if (!info.options.is_empty()) {
+ bool found = false;
+
+ for (const StringName &option : info.options) {
+ if (shader->stencil_modes.has(String(info.name) + "_" + String(option))) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ for (const StringName &option : info.options) {
+ ScriptLanguage::CodeCompletionOption completion_option(String(info.name) + "_" + String(option), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ r_options->push_back(completion_option);
+ }
+ }
+ } else {
+ const String name = String(info.name);
+
+ if (!shader->stencil_modes.has(name)) {
+ ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ r_options->push_back(option);
+ }
+ }
+ }
+ }
+
+ return OK;
+ } break;
case COMPLETION_STRUCT: {
if (shader->structs.has(completion_struct)) {
StructNode *node = shader->structs[completion_struct].shader_struct;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index bee2f5bdc4..fed8550ab9 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -164,6 +164,7 @@ public:
TK_ARG_OUT,
TK_ARG_INOUT,
TK_RENDER_MODE,
+ TK_STENCIL_MODE,
TK_HINT_DEFAULT_WHITE_TEXTURE,
TK_HINT_DEFAULT_BLACK_TEXTURE,
TK_HINT_DEFAULT_TRANSPARENT_TEXTURE,
@@ -723,6 +724,8 @@ public:
HashMap structs;
HashMap functions;
Vector render_modes;
+ Vector stencil_modes;
+ int stencil_reference = -1;
Vector vfunctions;
Vector vconstants;
@@ -799,6 +802,7 @@ public:
COMPLETION_NONE,
COMPLETION_SHADER_TYPE,
COMPLETION_RENDER_MODE,
+ COMPLETION_STENCIL_MODE,
COMPLETION_MAIN_FUNCTION,
COMPLETION_IDENTIFIER,
COMPLETION_FUNCTION_CALL,
@@ -1222,11 +1226,13 @@ private:
String _get_qualifier_str(ArgumentQualifier p_qualifier) const;
bool _parse_numeric_constant_expression(const FunctionInfo &p_function_info, float &r_constant);
- Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types);
+ Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const Vector &p_stencil_modes, const HashSet &p_shader_types);
Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op);
Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op);
+ Error _parse_shader_mode(bool p_is_stencil, const Vector &p_modes, HashMap &r_defined_modes);
+
public:
#ifdef DEBUG_ENABLED
List::Element *get_warnings_ptr();
@@ -1248,6 +1254,7 @@ public:
struct ShaderCompileInfo {
HashMap functions;
Vector render_modes;
+ Vector stencil_modes;
VaryingFunctionNames varying_function_names;
HashSet shader_types;
GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr;
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index c64df961ba..5efa66b4e6 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -38,6 +38,10 @@ const Vector &ShaderTypes::get_modes(RS::ShaderMode p_
return shader_modes[p_mode].modes;
}
+const Vector &ShaderTypes::get_stencil_modes(RS::ShaderMode p_mode) const {
+ return shader_modes[p_mode].stencil_modes;
+}
+
const HashSet &ShaderTypes::get_types() const {
return shader_types;
}
@@ -246,6 +250,10 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("debug_shadow_splits") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("fog_disabled") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("specular_occlusion_disabled") });
+ shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("read") });
+ shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write") });
+ shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("write_depth_fail") });
+ shader_modes[RS::SHADER_SPATIAL].stencil_modes.push_back({ PNAME("compare"), { "always", "less", "equal", "less_or_equal", "greater", "not_equal", "greater_or_equal" } });
}
/************ CANVAS ITEM **************************/
diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h
index 3d94342948..cf42549d01 100644
--- a/servers/rendering/shader_types.h
+++ b/servers/rendering/shader_types.h
@@ -37,6 +37,7 @@ class ShaderTypes {
struct Type {
HashMap functions;
Vector modes;
+ Vector stencil_modes;
};
HashMap shader_modes;
@@ -51,6 +52,7 @@ public:
const HashMap &get_functions(RS::ShaderMode p_mode) const;
const Vector &get_modes(RS::ShaderMode p_mode) const;
+ const Vector &get_stencil_modes(RS::ShaderMode p_mode) const;
const HashSet &get_types() const;
const List &get_types_list() const;