Add stencil support for spatial materials

This commit is contained in:
Apples 2023-02-17 15:20:26 -06:00
parent 7574a5dbb3
commit d674c9e289
29 changed files with 1335 additions and 141 deletions

View file

@ -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.
</member>
<member name="stencil_color" type="Color" setter="set_stencil_effect_color" getter="get_stencil_effect_color" default="Color(0, 0, 0, 1)" experimental="May be affected by future rendering pipeline changes.">
The primary color of the stencil effect.
</member>
<member name="stencil_compare" type="int" setter="set_stencil_compare" getter="get_stencil_compare" enum="BaseMaterial3D.StencilCompare" default="0" experimental="May be affected by future rendering pipeline changes.">
The comparison operator to use for stencil masking operations. See [enum StencilCompare].
</member>
<member name="stencil_flags" type="int" setter="set_stencil_flags" getter="get_stencil_flags" default="0" experimental="May be affected by future rendering pipeline changes.">
The flags dictating how the stencil operation behaves. See [enum StencilFlags].
</member>
<member name="stencil_mode" type="int" setter="set_stencil_mode" getter="get_stencil_mode" enum="BaseMaterial3D.StencilMode" default="0" experimental="May be affected by future rendering pipeline changes.">
The stencil effect mode. See [enum StencilMode].
</member>
<member name="stencil_outline_thickness" type="float" setter="set_stencil_effect_outline_thickness" getter="get_stencil_effect_outline_thickness" default="0.01" experimental="May be affected by future rendering pipeline changes.">
The outline thickness for [constant STENCIL_MODE_OUTLINE].
</member>
<member name="stencil_reference" type="int" setter="set_stencil_reference" getter="get_stencil_reference" default="1" experimental="May be affected by future rendering pipeline changes.">
The stencil reference value (0-255). Typically a power of 2.
</member>
<member name="subsurf_scatter_enabled" type="bool" setter="set_feature" getter="get_feature" default="false">
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].
</member>
@ -828,5 +846,49 @@
<constant name="DISTANCE_FADE_OBJECT_DITHER" value="3" enum="DistanceFadeMode">
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].
</constant>
<constant name="STENCIL_MODE_DISABLED" value="0" enum="StencilMode">
Disables stencil operations.
</constant>
<constant name="STENCIL_MODE_OUTLINE" value="1" enum="StencilMode">
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.
</constant>
<constant name="STENCIL_MODE_XRAY" value="2" enum="StencilMode">
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.
</constant>
<constant name="STENCIL_MODE_CUSTOM" value="3" enum="StencilMode">
Enables stencil operations without a preset.
</constant>
<constant name="STENCIL_FLAG_READ" value="1" enum="StencilFlags">
The material will only be rendered where it passes a stencil comparison with existing stencil buffer values. See [enum StencilCompare].
</constant>
<constant name="STENCIL_FLAG_WRITE" value="2" enum="StencilFlags">
The material will write the reference value to the stencil buffer where it passes the depth test.
</constant>
<constant name="STENCIL_FLAG_WRITE_DEPTH_FAIL" value="4" enum="StencilFlags">
The material will write the reference value to the stencil buffer where it fails the depth test.
</constant>
<constant name="STENCIL_COMPARE_ALWAYS" value="0" enum="StencilCompare">
Always passes the stencil test.
</constant>
<constant name="STENCIL_COMPARE_LESS" value="1" enum="StencilCompare">
Passes the stencil test when the reference value is less than the existing stencil value.
</constant>
<constant name="STENCIL_COMPARE_EQUAL" value="2" enum="StencilCompare">
Passes the stencil test when the reference value is equal to the existing stencil value.
</constant>
<constant name="STENCIL_COMPARE_LESS_OR_EQUAL" value="3" enum="StencilCompare">
Passes the stencil test when the reference value is less than or equal to the existing stencil value.
</constant>
<constant name="STENCIL_COMPARE_GREATER" value="4" enum="StencilCompare">
Passes the stencil test when the reference value is greater than the existing stencil value.
</constant>
<constant name="STENCIL_COMPARE_NOT_EQUAL" value="5" enum="StencilCompare">
Passes the stencil test when the reference value is not equal to the existing stencil value.
</constant>
<constant name="STENCIL_COMPARE_GREATER_OR_EQUAL" value="6" enum="StencilCompare">
Passes the stencil test when the reference value is greater than or equal to the existing stencil value.
</constant>
</constants>
</class>

View file

@ -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.

View file

@ -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;

View file

@ -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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &p_render_
_render_list_template<PASS_MODE_COLOR>(&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<RenderSceneBuffers> &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<RenderSceneBuffers> &p_render_
_render_list_template<PASS_MODE_COLOR_TRANSPARENT>(&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.

View file

@ -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;

View file

@ -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<int *, int>(&stencil_readi, STENCIL_FLAG_READ);
actions.stencil_mode_values["write"] = Pair<int *, int>(&stencil_writei, STENCIL_FLAG_WRITE);
actions.stencil_mode_values["write_depth_fail"] = Pair<int *, int>(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
actions.stencil_mode_values["compare_less"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS);
actions.stencil_mode_values["compare_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_EQUAL);
actions.stencil_mode_values["compare_less_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
actions.stencil_mode_values["compare_greater"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER);
actions.stencil_mode_values["compare_not_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
actions.stencil_mode_values["compare_greater_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
actions.stencil_mode_values["compare_always"] = Pair<int *, int>(&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.");

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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()) {

View file

@ -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<GLuint> allocated_textures;
bool depth_has_stencil;
};
RBMap<uint32_t, FBOCacheEntry> fbo_cache;
} overridden;

View file

@ -269,20 +269,34 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
for (int j = 0; j < modes.size(); j++) {
const ShaderLanguage::ModeInfo &mode_info = modes[j];
{
const Vector<ShaderLanguage::ModeInfo> &render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
for (const ShaderLanguage::ModeInfo &mode_info : render_modes) {
if (!mode_info.options.is_empty()) {
for (int k = 0; k < mode_info.options.size(); k++) {
built_ins.push_back(String(mode_info.name) + "_" + String(mode_info.options[k]));
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<ShaderLanguage::ModeInfo> &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 if (shader.is_valid()) {
for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
@ -290,14 +304,13 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
const Vector<ShaderLanguage::ModeInfo> &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];
{
const Vector<ShaderLanguage::ModeInfo> &shader_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
for (const ShaderLanguage::ModeInfo &mode_info : shader_modes) {
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 StringName &option : mode_info.options) {
built_ins.push_back(String(mode_info.name) + "_" + String(option));
}
} else {
built_ins.push_back(String(mode_info.name));
@ -305,6 +318,21 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
{
const Vector<ShaderLanguage::ModeInfo> &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));
}
}
}
}
const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
for (const String &E : built_ins) {
@ -437,6 +465,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLa
_check_shader_mode();
comp_info.functions = ShaderTypes::get_singleton()->get_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();
}

View file

@ -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;

View file

@ -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<Material> 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<BaseMaterial3D> stencil_next_pass;
if (current_next_pass.is_null() || !current_next_pass->has_meta("_stencil_owned")) {
stencil_next_pass = Ref<BaseMaterial3D>(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> BaseMaterial3D::_get_stencil_next_pass() const {
const Ref<Material> current_next_pass = get_next_pass();
Ref<BaseMaterial3D> 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<BaseMaterial3D> 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<BaseMaterial3D> 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<BaseMaterial3D> 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<BaseMaterial3D *>(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;

View file

@ -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<Texture2D> textures[TEXTURE_MAX];
void _prepare_stencil_effect();
Ref<BaseMaterial3D> _get_stencil_next_pass() const;
static HashMap<uint64_t, Ref<StandardMaterial3D>> 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)

View file

@ -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<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E)));
}
const Vector<ShaderLanguage::ModeInfo> &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<String, String> stencil_enums;
HashSet<String> 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<String, String> &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<String, Varying> &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<ShaderLanguage::ModeInfo> &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<String> 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;

View file

@ -138,6 +138,11 @@ private:
HashMap<String, int> modes;
HashSet<StringName> flags;
bool stencil_enabled = false;
HashMap<String, int> stencil_modes;
HashSet<StringName> stencil_flags;
int stencil_reference = 1;
HashMap<String, Varying> varyings;
#ifdef TOOLS_ENABLED
HashMap<String, Variant> preview_params;

View file

@ -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()) {

View file

@ -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 {

View file

@ -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<int *, int>(&stencil_readi, STENCIL_FLAG_READ);
actions.stencil_mode_values["write"] = Pair<int *, int>(&stencil_writei, STENCIL_FLAG_WRITE);
actions.stencil_mode_values["write_depth_fail"] = Pair<int *, int>(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
actions.stencil_mode_values["compare_less"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS);
actions.stencil_mode_values["compare_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_EQUAL);
actions.stencil_mode_values["compare_less_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
actions.stencil_mode_values["compare_greater"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER);
actions.stencil_mode_values["compare_not_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
actions.stencil_mode_values["compare_greater_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
actions.stencil_mode_values["compare_always"] = Pair<int *, int>(&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] = {

View file

@ -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;

View file

@ -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()) {

View file

@ -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<int *, int>(&stencil_readi, STENCIL_FLAG_READ);
actions.stencil_mode_values["write"] = Pair<int *, int>(&stencil_writei, STENCIL_FLAG_WRITE);
actions.stencil_mode_values["write_depth_fail"] = Pair<int *, int>(&stencil_write_depth_faili, STENCIL_FLAG_WRITE_DEPTH_FAIL);
actions.stencil_mode_values["compare_less"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS);
actions.stencil_mode_values["compare_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_EQUAL);
actions.stencil_mode_values["compare_less_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_LESS_OR_EQUAL);
actions.stencil_mode_values["compare_greater"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER);
actions.stencil_mode_values["compare_not_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_NOT_EQUAL);
actions.stencil_mode_values["compare_greater_or_equal"] = Pair<int *, int>(&stencil_comparei, STENCIL_COMPARE_GREATER_OR_EQUAL);
actions.stencil_mode_values["compare_always"] = Pair<int *, int>(&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;

View file

@ -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;

View file

@ -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<int *, int> &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;

View file

@ -50,6 +50,8 @@ public:
HashMap<StringName, bool *> render_mode_flags;
HashMap<StringName, bool *> usage_flag_pointers;
HashMap<StringName, bool *> write_flag_pointers;
HashMap<StringName, Pair<int *, int>> stencil_mode_values;
int *stencil_reference = nullptr;
HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms = nullptr;
};

View file

@ -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<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types) {
Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const Vector<ModeInfo> &p_stencil_modes, const HashSet<String> &p_shader_types) {
Token tk;
TkPos prev_pos;
Token next;
@ -9173,7 +9174,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo();
HashMap<String, String> defined_modes;
HashMap<String, String> defined_render_modes;
HashMap<String, String> defined_stencil_modes;
while (tk.type != TK_EOF) {
switch (tk.type) {
@ -9182,83 +9184,64 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &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<ModeInfo> 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<ModeInfo> &p_modes, HashMap<String, String> &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<StringName> &current_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<ModeInfo> 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<ShaderNode>();
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<ShaderNode>();
_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<ModeInfo> 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;

View file

@ -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<StringName, Struct> structs;
HashMap<StringName, Function> functions;
Vector<StringName> render_modes;
Vector<StringName> stencil_modes;
int stencil_reference = -1;
Vector<Function> vfunctions;
Vector<Constant> 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<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types);
Error _parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const Vector<ModeInfo> &p_stencil_modes, const HashSet<String> &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<ModeInfo> &p_modes, HashMap<String, String> &r_defined_modes);
public:
#ifdef DEBUG_ENABLED
List<ShaderWarning>::Element *get_warnings_ptr();
@ -1248,6 +1254,7 @@ public:
struct ShaderCompileInfo {
HashMap<StringName, FunctionInfo> functions;
Vector<ModeInfo> render_modes;
Vector<ModeInfo> stencil_modes;
VaryingFunctionNames varying_function_names;
HashSet<String> shader_types;
GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr;

View file

@ -38,6 +38,10 @@ const Vector<ShaderLanguage::ModeInfo> &ShaderTypes::get_modes(RS::ShaderMode p_
return shader_modes[p_mode].modes;
}
const Vector<ShaderLanguage::ModeInfo> &ShaderTypes::get_stencil_modes(RS::ShaderMode p_mode) const {
return shader_modes[p_mode].stencil_modes;
}
const HashSet<String> &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 **************************/

View file

@ -37,6 +37,7 @@ class ShaderTypes {
struct Type {
HashMap<StringName, ShaderLanguage::FunctionInfo> functions;
Vector<ShaderLanguage::ModeInfo> modes;
Vector<ShaderLanguage::ModeInfo> stencil_modes;
};
HashMap<RS::ShaderMode, Type> shader_modes;
@ -51,6 +52,7 @@ public:
const HashMap<StringName, ShaderLanguage::FunctionInfo> &get_functions(RS::ShaderMode p_mode) const;
const Vector<ShaderLanguage::ModeInfo> &get_modes(RS::ShaderMode p_mode) const;
const Vector<ShaderLanguage::ModeInfo> &get_stencil_modes(RS::ShaderMode p_mode) const;
const HashSet<String> &get_types() const;
const List<String> &get_types_list() const;