浏览代码

Add transparency support for LightmapGI

Co-authored-by: Guerro323 <[email protected]>
Hendrik Brucker 8 月之前
父节点
当前提交
a3525bc015
共有 26 个文件被更改,包括 441 次插入94 次删除
  1. 3 0
      doc/classes/ProjectSettings.xml
  2. 16 12
      drivers/gles3/rasterizer_scene_gles3.cpp
  3. 6 6
      drivers/gles3/rasterizer_scene_gles3.h
  4. 16 0
      drivers/gles3/shaders/scene.glsl
  5. 18 5
      drivers/gles3/storage/material_storage.cpp
  6. 2 7
      drivers/gles3/storage/material_storage.h
  7. 14 1
      modules/lightmapper_rd/lightmapper_rd.cpp
  8. 4 3
      modules/lightmapper_rd/lightmapper_rd.h
  9. 6 3
      modules/lightmapper_rd/lm_common_inc.glsl
  10. 225 30
      modules/lightmapper_rd/lm_compute.glsl
  11. 1 0
      modules/lightmapper_rd/register_types.cpp
  12. 7 1
      scene/3d/lightmap_gi.cpp
  13. 1 0
      scene/3d/lightmapper.h
  14. 2 0
      servers/rendering/dummy/storage/material_storage.h
  15. 4 0
      servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
  16. 5 5
      servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
  17. 2 8
      servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
  18. 4 0
      servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
  19. 4 4
      servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
  20. 1 7
      servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
  21. 36 1
      servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
  22. 36 1
      servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
  23. 20 0
      servers/rendering/renderer_rd/storage_rd/material_storage.cpp
  24. 1 0
      servers/rendering/renderer_rd/storage_rd/material_storage.h
  25. 1 0
      servers/rendering/storage/material_storage.h
  26. 6 0
      servers/rendering_server.h

+ 3 - 0
doc/classes/ProjectSettings.xml

@@ -2730,6 +2730,9 @@
 		<member name="rendering/lightmapping/bake_performance/max_rays_per_probe_pass" type="int" setter="" getter="" default="64">
 		<member name="rendering/lightmapping/bake_performance/max_rays_per_probe_pass" type="int" setter="" getter="" default="64">
 			The maximum number of rays that can be thrown per pass when baking dynamic object lighting in [LightmapProbe]s with [LightmapGI]. Depending on the scene, adjusting this value may result in higher GPU utilization when baking lightmaps, leading to faster bake times.
 			The maximum number of rays that can be thrown per pass when baking dynamic object lighting in [LightmapProbe]s with [LightmapGI]. Depending on the scene, adjusting this value may result in higher GPU utilization when baking lightmaps, leading to faster bake times.
 		</member>
 		</member>
+		<member name="rendering/lightmapping/bake_performance/max_transparency_rays" type="int" setter="" getter="" default="8">
+			The maximum number of retry rays that can be thrown per pass when hitting a transparent surface when baking lightmaps with [LightmapGI]. Depending on the scene, reducing this value may lead to faster bake times.
+		</member>
 		<member name="rendering/lightmapping/bake_performance/region_size" type="int" setter="" getter="" default="512">
 		<member name="rendering/lightmapping/bake_performance/region_size" type="int" setter="" getter="" default="512">
 			The region size to use when baking lightmaps with [LightmapGI].
 			The region size to use when baking lightmaps with [LightmapGI].
 		</member>
 		</member>

+ 16 - 12
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -977,7 +977,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
 		glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer);
 		glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer);
 
 
 		scene_state.reset_gl_state();
 		scene_state.reset_gl_state();
-		scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
+		scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
 		scene_state.enable_gl_blend(false);
 		scene_state.enable_gl_blend(false);
 
 
 		for (int i = 0; i < 6; i++) {
 		for (int i = 0; i < 6; i++) {
@@ -1000,7 +1000,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
 	} else {
 	} else {
 		if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
 		if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
 			scene_state.reset_gl_state();
 			scene_state.reset_gl_state();
-			scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
+			scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
 			scene_state.enable_gl_blend(false);
 			scene_state.enable_gl_blend(false);
 
 
 			cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, sky->processing_layer);
 			cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, sky->processing_layer);
@@ -1433,6 +1433,10 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
 				if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) {
 				if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) {
 					rl->add_element(surf);
 					rl->add_element(surf);
 				}
 				}
+			} else if (p_pass_mode == PASS_MODE_MATERIAL) {
+				if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE | GeometryInstanceSurface::FLAG_PASS_ALPHA)) {
+					rl->add_element(surf);
+				}
 			} else {
 			} else {
 				if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
 				if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE)) {
 					rl->add_element(surf);
 					rl->add_element(surf);
@@ -2210,7 +2214,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
 	scene_state.enable_gl_depth_test(false);
 	scene_state.enable_gl_depth_test(false);
 	scene_state.enable_gl_depth_draw(true);
 	scene_state.enable_gl_depth_draw(true);
 	glDisable(GL_CULL_FACE);
 	glDisable(GL_CULL_FACE);
-	scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
+	scene_state.cull_mode = RS::CULL_MODE_DISABLED;
 	glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
 	glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
 }
 }
 
 
@@ -2587,7 +2591,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		scene_state.enable_gl_depth_draw(false);
 		scene_state.enable_gl_depth_draw(false);
 		scene_state.enable_gl_depth_test(false);
 		scene_state.enable_gl_depth_test(false);
 		scene_state.enable_gl_blend(false);
 		scene_state.enable_gl_blend(false);
-		scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+		scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK);
 
 
 		Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
 		Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
 
 
@@ -2615,7 +2619,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 
 
 		scene_state.enable_gl_depth_test(true);
 		scene_state.enable_gl_depth_test(true);
 		scene_state.enable_gl_blend(false);
 		scene_state.enable_gl_blend(false);
-		scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+		scene_state.set_gl_cull_mode(RS::CULL_MODE_BACK);
 
 
 		Transform3D transform = render_data.cam_transform;
 		Transform3D transform = render_data.cam_transform;
 		Projection projection = render_data.cam_projection;
 		Projection projection = render_data.cam_projection;
@@ -3099,19 +3103,19 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 			}
 			}
 
 
 			// Find cull variant.
 			// Find cull variant.
-			GLES3::SceneShaderData::Cull cull_mode = shader->cull_mode;
+			RS::CullMode cull_mode = shader->cull_mode;
 
 
 			if (p_pass_mode == PASS_MODE_MATERIAL || (surf->flags & GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS)) {
 			if (p_pass_mode == PASS_MODE_MATERIAL || (surf->flags & GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS)) {
-				cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
+				cull_mode = RS::CULL_MODE_DISABLED;
 			} else {
 			} else {
 				bool mirror = inst->mirror;
 				bool mirror = inst->mirror;
 				if (p_params->reverse_cull) {
 				if (p_params->reverse_cull) {
 					mirror = !mirror;
 					mirror = !mirror;
 				}
 				}
-				if (cull_mode == GLES3::SceneShaderData::CULL_FRONT && mirror) {
-					cull_mode = GLES3::SceneShaderData::CULL_BACK;
-				} else if (cull_mode == GLES3::SceneShaderData::CULL_BACK && mirror) {
-					cull_mode = GLES3::SceneShaderData::CULL_FRONT;
+				if (cull_mode == RS::CULL_MODE_FRONT && mirror) {
+					cull_mode = RS::CULL_MODE_BACK;
+				} else if (cull_mode == RS::CULL_MODE_BACK && mirror) {
+					cull_mode = RS::CULL_MODE_FRONT;
 				}
 				}
 			}
 			}
 
 
@@ -3832,7 +3836,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
 			glActiveTexture(GL_TEXTURE0);
 			glActiveTexture(GL_TEXTURE0);
 			scene_state.enable_gl_depth_draw(true);
 			scene_state.enable_gl_depth_draw(true);
 			glDepthFunc(GL_ALWAYS);
 			glDepthFunc(GL_ALWAYS);
-			scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
+			scene_state.set_gl_cull_mode(RS::CULL_MODE_DISABLED);
 
 
 			// Loop through quadrants and copy shadows over.
 			// Loop through quadrants and copy shadows over.
 			for (int quadrant = 0; quadrant < 4; quadrant++) {
 			for (int quadrant = 0; quadrant < 4; quadrant++) {

+ 6 - 6
drivers/gles3/rasterizer_scene_gles3.h

@@ -461,7 +461,7 @@ private:
 		bool used_depth_prepass = false;
 		bool used_depth_prepass = false;
 
 
 		GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
 		GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
-		GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK;
+		RS::CullMode cull_mode = RS::CULL_MODE_BACK;
 
 
 		bool current_blend_enabled = false;
 		bool current_blend_enabled = false;
 		bool current_depth_draw_enabled = false;
 		bool current_depth_draw_enabled = false;
@@ -477,7 +477,7 @@ private:
 
 
 			glCullFace(GL_BACK);
 			glCullFace(GL_BACK);
 			glEnable(GL_CULL_FACE);
 			glEnable(GL_CULL_FACE);
-			cull_mode = GLES3::SceneShaderData::CULL_BACK;
+			cull_mode = RS::CULL_MODE_BACK;
 
 
 			glDepthMask(GL_FALSE);
 			glDepthMask(GL_FALSE);
 			current_depth_draw_enabled = false;
 			current_depth_draw_enabled = false;
@@ -485,16 +485,16 @@ private:
 			current_depth_test_enabled = false;
 			current_depth_test_enabled = false;
 		}
 		}
 
 
-		void set_gl_cull_mode(GLES3::SceneShaderData::Cull p_mode) {
+		void set_gl_cull_mode(RS::CullMode p_mode) {
 			if (cull_mode != p_mode) {
 			if (cull_mode != p_mode) {
-				if (p_mode == GLES3::SceneShaderData::CULL_DISABLED) {
+				if (p_mode == RS::CULL_MODE_DISABLED) {
 					glDisable(GL_CULL_FACE);
 					glDisable(GL_CULL_FACE);
 				} else {
 				} else {
-					if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) {
+					if (cull_mode == RS::CULL_MODE_DISABLED) {
 						// Last time was disabled, so enable and set proper face.
 						// Last time was disabled, so enable and set proper face.
 						glEnable(GL_CULL_FACE);
 						glEnable(GL_CULL_FACE);
 					}
 					}
-					glCullFace(p_mode == GLES3::SceneShaderData::CULL_FRONT ? GL_FRONT : GL_BACK);
+					glCullFace(p_mode == RS::CULL_MODE_FRONT ? GL_FRONT : GL_BACK);
 				}
 				}
 				cull_mode = p_mode;
 				cull_mode = p_mode;
 			}
 			}

+ 16 - 0
drivers/gles3/shaders/scene.glsl

@@ -1882,10 +1882,18 @@ void main() {
 #ifndef USE_SHADOW_TO_OPACITY
 #ifndef USE_SHADOW_TO_OPACITY
 
 
 #if defined(ALPHA_SCISSOR_USED)
 #if defined(ALPHA_SCISSOR_USED)
+#ifdef RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
 	alpha = 1.0;
 	alpha = 1.0;
+#endif // RENDER_MATERIAL
 #else
 #else
 #ifdef MODE_RENDER_DEPTH
 #ifdef MODE_RENDER_DEPTH
 #ifdef USE_OPAQUE_PREPASS
 #ifdef USE_OPAQUE_PREPASS
@@ -2215,9 +2223,17 @@ void main() {
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 
 
 #if defined(ALPHA_SCISSOR_USED)
 #if defined(ALPHA_SCISSOR_USED)
+#ifdef RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
+#endif // RENDER_MATERIAL
 #endif // !ALPHA_SCISSOR_USED
 #endif // !ALPHA_SCISSOR_USED
 
 
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH

+ 18 - 5
drivers/gles3/storage/material_storage.cpp

@@ -2505,6 +2505,19 @@ bool MaterialStorage::material_casts_shadows(RID p_material) {
 	return true; //by default everything casts shadows
 	return true; //by default everything casts shadows
 }
 }
 
 
+RS::CullMode MaterialStorage::material_get_cull_mode(RID p_material) const {
+	const GLES3::Material *material = material_owner.get_or_null(p_material);
+	ERR_FAIL_NULL_V(material, RS::CULL_MODE_DISABLED);
+	ERR_FAIL_NULL_V(material->shader, RS::CULL_MODE_DISABLED);
+	if (material->shader->data) {
+		SceneShaderData *data = dynamic_cast<SceneShaderData *>(material->shader->data);
+		if (data) {
+			return (RS::CullMode)data->cull_mode;
+		}
+	}
+	return RS::CULL_MODE_DISABLED;
+}
+
 void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
 void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
 	GLES3::Material *material = material_owner.get_or_null(p_material);
 	GLES3::Material *material = material_owner.get_or_null(p_material);
 	ERR_FAIL_NULL(material);
 	ERR_FAIL_NULL(material);
@@ -2907,7 +2920,7 @@ void SceneShaderData::set_code(const String &p_code) {
 	int blend_modei = BLEND_MODE_MIX;
 	int blend_modei = BLEND_MODE_MIX;
 	int depth_testi = DEPTH_TEST_ENABLED;
 	int depth_testi = DEPTH_TEST_ENABLED;
 	int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF;
 	int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF;
-	int cull_modei = CULL_BACK;
+	int cull_modei = RS::CULL_MODE_BACK;
 	int depth_drawi = DEPTH_DRAW_OPAQUE;
 	int depth_drawi = DEPTH_DRAW_OPAQUE;
 
 
 	ShaderCompiler::IdentifierActions actions;
 	ShaderCompiler::IdentifierActions actions;
@@ -2930,9 +2943,9 @@ void SceneShaderData::set_code(const String &p_code) {
 
 
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 
 
-	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
-	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
-	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
+	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_DISABLED);
+	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_FRONT);
+	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_BACK);
 
 
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["wireframe"] = &wireframe;
 	actions.render_mode_flags["wireframe"] = &wireframe;
@@ -2990,7 +3003,7 @@ void SceneShaderData::set_code(const String &p_code) {
 	alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
 	alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
 	depth_draw = DepthDraw(depth_drawi);
 	depth_draw = DepthDraw(depth_drawi);
 	depth_test = DepthTest(depth_testi);
 	depth_test = DepthTest(depth_testi);
-	cull_mode = Cull(cull_modei);
+	cull_mode = RS::CullMode(cull_modei);
 
 
 	vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals.
 	vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals.
 	vertex_input_mask |= uses_tangent << RS::ARRAY_TANGENT;
 	vertex_input_mask |= uses_tangent << RS::ARRAY_TANGENT;

+ 2 - 7
drivers/gles3/storage/material_storage.h

@@ -263,12 +263,6 @@ struct SceneShaderData : public ShaderData {
 		DEPTH_TEST_ENABLED
 		DEPTH_TEST_ENABLED
 	};
 	};
 
 
-	enum Cull {
-		CULL_DISABLED,
-		CULL_FRONT,
-		CULL_BACK
-	};
-
 	enum AlphaAntiAliasing {
 	enum AlphaAntiAliasing {
 		ALPHA_ANTIALIASING_OFF,
 		ALPHA_ANTIALIASING_OFF,
 		ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
 		ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
@@ -292,7 +286,7 @@ struct SceneShaderData : public ShaderData {
 	AlphaAntiAliasing alpha_antialiasing_mode;
 	AlphaAntiAliasing alpha_antialiasing_mode;
 	DepthDraw depth_draw;
 	DepthDraw depth_draw;
 	DepthTest depth_test;
 	DepthTest depth_test;
-	Cull cull_mode;
+	RS::CullMode cull_mode;
 
 
 	bool uses_point_size;
 	bool uses_point_size;
 	bool uses_alpha;
 	bool uses_alpha;
@@ -618,6 +612,7 @@ public:
 
 
 	virtual bool material_is_animated(RID p_material) override;
 	virtual bool material_is_animated(RID p_material) override;
 	virtual bool material_casts_shadows(RID p_material) override;
 	virtual bool material_casts_shadows(RID p_material) override;
+	virtual RS::CullMode material_get_cull_mode(RID p_material) const override;
 
 
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
 
 

+ 14 - 1
modules/lightmapper_rd/lightmapper_rd.cpp

@@ -30,6 +30,7 @@
 
 
 #include "lightmapper_rd.h"
 #include "lightmapper_rd.h"
 
 
+#include "core/string/print_string.h"
 #include "lm_blendseams.glsl.gen.h"
 #include "lm_blendseams.glsl.gen.h"
 #include "lm_compute.glsl.gen.h"
 #include "lm_compute.glsl.gen.h"
 #include "lm_raster.glsl.gen.h"
 #include "lm_raster.glsl.gen.h"
@@ -40,6 +41,7 @@
 #include "editor/editor_paths.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "servers/rendering/rendering_device_binds.h"
 #include "servers/rendering/rendering_device_binds.h"
+#include "servers/rendering/rendering_server_globals.h"
 
 
 #if defined(VULKAN_ENABLED)
 #if defined(VULKAN_ENABLED)
 #include "drivers/vulkan/rendering_context_driver_vulkan.h"
 #include "drivers/vulkan/rendering_context_driver_vulkan.h"
@@ -477,7 +479,16 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
 			t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
 			t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
 			t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
 			t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
 			t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
 			t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
-			t.pad0 = t.pad1 = 0; //make valgrind not complain
+
+			t.cull_mode = RS::CULL_MODE_BACK;
+
+			RID material = mi.data.material[i];
+			if (material.is_valid()) {
+				t.cull_mode = RSG::material_storage->material_get_cull_mode(material);
+			} else {
+				print_line("No material for mesh with vertex count ", mi.data.points.size());
+			}
+			t.pad1 = 0; //make valgrind not complain
 			triangles.push_back(t);
 			triangles.push_back(t);
 			slice_triangle_count.write[t.slice]++;
 			slice_triangle_count.write[t.slice]++;
 		}
 		}
@@ -1319,6 +1330,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
 	bake_parameters.bounces = p_bounces;
 	bake_parameters.bounces = p_bounces;
 	bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
 	bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy;
 	bake_parameters.shadowmask_light_idx = shadowmask_light_idx;
 	bake_parameters.shadowmask_light_idx = shadowmask_light_idx;
+	// Same number of rays for transparency regardless of quality (it's more of a retry rather than shooting new ones).
+	bake_parameters.transparency_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_transparency_rays");
 
 
 	bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
 	bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
 	rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);
 	rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);

+ 4 - 3
modules/lightmapper_rd/lightmapper_rd.h

@@ -57,8 +57,9 @@ class LightmapperRD : public Lightmapper {
 		uint32_t bounces = 0;
 		uint32_t bounces = 0;
 
 
 		float bounce_indirect_energy = 0.0f;
 		float bounce_indirect_energy = 0.0f;
-		int shadowmask_light_idx = 0;
-		uint32_t pad[2] = {};
+		uint32_t shadowmask_light_idx = 0;
+		uint32_t transparency_rays = 0;
+		uint32_t pad[1] = {};
 	};
 	};
 
 
 	struct MeshInstance {
 	struct MeshInstance {
@@ -185,7 +186,7 @@ class LightmapperRD : public Lightmapper {
 		uint32_t indices[3] = {};
 		uint32_t indices[3] = {};
 		uint32_t slice = 0;
 		uint32_t slice = 0;
 		float min_bounds[3] = {};
 		float min_bounds[3] = {};
-		float pad0 = 0.0;
+		uint32_t cull_mode = 0;
 		float max_bounds[3] = {};
 		float max_bounds[3] = {};
 		float pad1 = 0.0;
 		float pad1 = 0.0;
 		bool operator<(const Triangle &p_triangle) const {
 		bool operator<(const Triangle &p_triangle) const {

+ 6 - 3
modules/lightmapper_rd/lm_common_inc.glsl

@@ -1,4 +1,3 @@
-/* SET 0, static data that does not change between any call */
 
 
 layout(set = 0, binding = 0) uniform BakeParameters {
 layout(set = 0, binding = 0) uniform BakeParameters {
 	vec3 world_size;
 	vec3 world_size;
@@ -18,8 +17,8 @@ layout(set = 0, binding = 0) uniform BakeParameters {
 
 
 	float bounce_indirect_energy;
 	float bounce_indirect_energy;
 	int shadowmask_light_idx;
 	int shadowmask_light_idx;
+	uint transparency_rays;
 	uint pad0;
 	uint pad0;
-	uint pad1;
 }
 }
 bake_params;
 bake_params;
 
 
@@ -35,11 +34,15 @@ layout(set = 0, binding = 1, std430) restrict readonly buffer Vertices {
 }
 }
 vertices;
 vertices;
 
 
+#define CULL_DISABLED 0
+#define CULL_FRONT 1
+#define CULL_BACK 2
+
 struct Triangle {
 struct Triangle {
 	uvec3 indices;
 	uvec3 indices;
 	uint slice;
 	uint slice;
 	vec3 min_bounds;
 	vec3 min_bounds;
-	uint pad0;
+	uint cull_mode;
 	vec3 max_bounds;
 	vec3 max_bounds;
 	uint pad1;
 	uint pad1;
 };
 };

+ 225 - 30
modules/lightmapper_rd/lm_compute.glsl

@@ -154,7 +154,8 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
 	vec3 dir_cell = normalize(rel_cell);
 	vec3 dir_cell = normalize(rel_cell);
 	vec3 delta = min(abs(1.0 / dir_cell), bake_params.grid_size); // Use bake_params.grid_size as max to prevent infinity values.
 	vec3 delta = min(abs(1.0 / dir_cell), bake_params.grid_size); // Use bake_params.grid_size as max to prevent infinity values.
 	ivec3 step = ivec3(sign(rel_cell));
 	ivec3 step = ivec3(sign(rel_cell));
-	vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
+	const vec3 init_next_cell = vec3(icell) + max(vec3(0), sign(step));
+	vec3 t_max = mix(vec3(0), (init_next_cell - from_cell) / dir_cell, notEqual(step, vec3(0))); // Distance to next boundary.
 
 
 	uint iters = 0;
 	uint iters = 0;
 	while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) {
 	while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) {
@@ -225,7 +226,6 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
 									// Return early if any hit was requested.
 									// Return early if any hit was requested.
 									return RAY_ANY;
 									return RAY_ANY;
 								}
 								}
-
 								vec3 position = p_from + dir * distance;
 								vec3 position = p_from + dir * distance;
 								vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
 								vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size;
 								if (icell != ivec3(hit_cell)) {
 								if (icell != ivec3(hit_cell)) {
@@ -242,6 +242,17 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
 								}
 								}
 
 
 								if (distance < best_distance) {
 								if (distance < best_distance) {
+									switch (triangle.cull_mode) {
+										case CULL_DISABLED:
+											backface = false;
+											break;
+										case CULL_FRONT:
+											backface = !backface;
+											break;
+										case CULL_BACK: // Default behavior.
+											break;
+									}
+
 									hit = backface ? RAY_BACK : RAY_FRONT;
 									hit = backface ? RAY_BACK : RAY_FRONT;
 									best_distance = distance;
 									best_distance = distance;
 									r_distance = distance;
 									r_distance = distance;
@@ -271,17 +282,16 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out
 		}
 		}
 
 
 		// There should be only one axis updated at a time for DDA to work properly.
 		// There should be only one axis updated at a time for DDA to work properly.
-		bvec3 mask = bvec3(true, false, false);
-		float m = side.x;
-		if (side.y < m) {
-			m = side.y;
-			mask = bvec3(false, true, false);
-		}
-		if (side.z < m) {
-			mask = bvec3(false, false, true);
+		if (t_max.x < t_max.y && t_max.x < t_max.z) {
+			icell.x += step.x;
+			t_max.x += delta.x;
+		} else if (t_max.y < t_max.z) {
+			icell.y += step.y;
+			t_max.y += delta.y;
+		} else {
+			icell.z += step.z;
+			t_max.z += delta.z;
 		}
 		}
-		side += vec3(mask) * delta;
-		icell += ivec3(vec3(mask)) * step;
 		iters++;
 		iters++;
 	}
 	}
 
 
@@ -294,6 +304,27 @@ uint trace_ray_closest_hit_triangle(vec3 p_from, vec3 p_to, out uint r_triangle,
 	return trace_ray(p_from, p_to, false, distance, normal, r_triangle, r_barycentric);
 	return trace_ray(p_from, p_to, false, distance, normal, r_triangle, r_barycentric);
 }
 }
 
 
+uint trace_ray_closest_hit_triangle_albedo_alpha(vec3 p_from, vec3 p_to, out vec4 albedo_alpha, out vec3 hit_position) {
+	float distance;
+	vec3 normal;
+	uint tidx;
+	vec3 barycentric;
+
+	uint ret = trace_ray(p_from, p_to, false, distance, normal, tidx, barycentric);
+	if (ret != RAY_MISS) {
+		Vertex vert0 = vertices.data[triangles.data[tidx].indices.x];
+		Vertex vert1 = vertices.data[triangles.data[tidx].indices.y];
+		Vertex vert2 = vertices.data[triangles.data[tidx].indices.z];
+
+		vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
+
+		albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0);
+		hit_position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
+	}
+
+	return ret;
+}
+
 uint trace_ray_closest_hit_distance(vec3 p_from, vec3 p_to, out float r_distance, out vec3 r_normal) {
 uint trace_ray_closest_hit_distance(vec3 p_from, vec3 p_to, out float r_distance, out vec3 r_normal) {
 	uint triangle;
 	uint triangle;
 	vec3 barycentric;
 	vec3 barycentric;
@@ -392,6 +423,8 @@ vec2 get_vogel_disk(float p_i, float p_rotation, float p_sample_count_sqrt) {
 }
 }
 
 
 void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool p_soft_shadowing, out vec3 r_light, out vec3 r_light_dir, inout uint r_noise, float p_texel_size, out float r_shadow) {
 void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool p_soft_shadowing, out vec3 r_light, out vec3 r_light_dir, inout uint r_noise, float p_texel_size, out float r_shadow) {
+	const float EPSILON = 0.00001;
+
 	r_light = vec3(0.0f);
 	r_light = vec3(0.0f);
 	r_shadow = 0.0f;
 	r_shadow = 0.0f;
 
 
@@ -460,6 +493,7 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
 		vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
 		vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
 
 
 		uint hits = 0;
 		uint hits = 0;
+		float aa_power = 0.0;
 		for (uint i = 0; i < ray_count; i++) {
 		for (uint i = 0; i < ray_count; i++) {
 			// Create a random sample within the texel.
 			// Create a random sample within the texel.
 			vec2 disk_sample = (halton_map[i] - vec2(0.5)) * p_texel_size * light_data.shadow_blur;
 			vec2 disk_sample = (halton_map[i] - vec2(0.5)) * p_texel_size * light_data.shadow_blur;
@@ -468,9 +502,13 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
 			vec3 origin = p_position - disk_aligned;
 			vec3 origin = p_position - disk_aligned;
 			vec3 light_dir = normalize(light_pos - origin);
 			vec3 light_dir = normalize(light_pos - origin);
 
 
+			float power = 0.0;
+			uint power_accm = 0;
+			vec3 prev_pos = origin;
 			if (use_soft_shadows) {
 			if (use_soft_shadows) {
 				uint soft_shadow_hits = 0;
 				uint soft_shadow_hits = 0;
 				for (uint j = 0; j < shadowing_ray_count; j++) {
 				for (uint j = 0; j < shadowing_ray_count; j++) {
+					origin = prev_pos;
 					// Optimization:
 					// Optimization:
 					// Once already traced an important proportion of rays, if all are hits or misses,
 					// Once already traced an important proportion of rays, if all are hits or misses,
 					// assume we're not in the penumbra so we can infer the rest would have the same result.
 					// assume we're not in the penumbra so we can infer the rest would have the same result.
@@ -490,24 +528,116 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
 					float vogel_index = float(total_ray_count - 1 - (i * shadowing_ray_count + j)); // Start from (total_ray_count - 1) so we check the outer points first.
 					float vogel_index = float(total_ray_count - 1 - (i * shadowing_ray_count + j)); // Start from (total_ray_count - 1) so we check the outer points first.
 					vec2 light_disk_sample = get_vogel_disk(vogel_index, a, shadowing_ray_count_sqrt) * soft_shadowing_disk_size * light_data.shadow_blur;
 					vec2 light_disk_sample = get_vogel_disk(vogel_index, a, shadowing_ray_count_sqrt) * soft_shadowing_disk_size * light_data.shadow_blur;
 					vec3 light_disk_to_point = normalize(light_to_point + light_disk_sample.x * light_to_point_tan + light_disk_sample.y * light_to_point_bitan);
 					vec3 light_disk_to_point = normalize(light_to_point + light_disk_sample.x * light_to_point_tan + light_disk_sample.y * light_to_point_bitan);
-					// Offset the ray origin for AA, offset the light position for soft shadows.
-					if (trace_ray_any_hit(origin - light_disk_to_point * (bake_params.bias + length(disk_sample)), p_position - light_disk_to_point * dist) == RAY_MISS) {
-						soft_shadow_hits++;
+					float sample_penumbra = 0.0;
+					bool sample_did_hit = false;
+
+					for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
+						vec4 hit_albedo = vec4(1.0);
+						vec3 hit_position;
+						// Offset the ray origin for AA, offset the light position for soft shadows.
+						uint ret = trace_ray_closest_hit_triangle_albedo_alpha(origin - light_disk_to_point * (bake_params.bias + length(disk_sample)), p_position - light_disk_to_point * dist, hit_albedo, hit_position);
+						if (ret == RAY_MISS) {
+							if (!sample_did_hit) {
+								sample_penumbra = 1.0;
+							}
+							soft_shadow_hits += 1;
+							break;
+						} else if (ret == RAY_FRONT || ret == RAY_BACK) {
+							bool contribute = ret == RAY_FRONT || !sample_did_hit;
+							if (!sample_did_hit) {
+								sample_penumbra = 1.0;
+								sample_did_hit = true;
+							}
+
+							soft_shadow_hits += 1;
+
+							if (contribute) {
+								sample_penumbra = max(sample_penumbra - hit_albedo.a - EPSILON, 0.0);
+							}
+							origin = hit_position + r_light_dir * bake_params.bias;
+
+							if (sample_penumbra - EPSILON <= 0) {
+								break;
+							}
+						}
 					}
 					}
+
+					power += sample_penumbra;
+					power_accm++;
 				}
 				}
+
 				hits += soft_shadow_hits;
 				hits += soft_shadow_hits;
-			} else {
-				// Offset the ray origin based on the disk. Also increase the bias for further samples to avoid bleeding.
-				if (trace_ray_any_hit(origin + light_dir * (bake_params.bias + length(disk_sample)), light_pos) == RAY_MISS) {
-					hits++;
+			} else { // No soft shadows.
+				float sample_penumbra = 0.0;
+				bool sample_did_hit = false;
+				for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
+					vec4 hit_albedo = vec4(1.0);
+					vec3 hit_position;
+					// Offset the ray origin for AA, offset the light position for soft shadows.
+					uint ret = trace_ray_closest_hit_triangle_albedo_alpha(origin + light_dir * (bake_params.bias + length(disk_sample)), light_pos, hit_albedo, hit_position);
+					if (ret == RAY_MISS) {
+						if (!sample_did_hit) {
+							sample_penumbra = 1.0;
+						}
+						hits++;
+						break;
+					} else if (ret == RAY_FRONT || ret == RAY_BACK) {
+						bool contribute = ret == RAY_FRONT || !sample_did_hit;
+						if (!sample_did_hit) {
+							sample_penumbra = 1.0;
+							sample_did_hit = true;
+						}
+
+						hits++;
+
+						if (contribute) {
+							sample_penumbra = max(sample_penumbra - hit_albedo.a - EPSILON, 0.0);
+						}
+						origin = hit_position + r_light_dir * bake_params.bias;
+
+						if (sample_penumbra - EPSILON <= 0) {
+							break;
+						}
+					}
 				}
 				}
+				power += sample_penumbra;
+				power_accm = 1;
 			}
 			}
+			aa_power = power / float(power_accm);
 		}
 		}
-		penumbra = float(hits) / float(total_ray_count);
-	} else {
-		if (trace_ray_any_hit(p_position + r_light_dir * bake_params.bias, light_pos) == RAY_MISS) {
-			penumbra = 1.0;
+		penumbra = aa_power;
+	} else { // No soft shadows.
+		bool did_hit = false;
+		penumbra = 0.0;
+		for (uint iter = 0; iter < bake_params.transparency_rays; iter++) {
+			vec4 hit_albedo = vec4(1.0);
+			vec3 hit_position;
+			uint ret = trace_ray_closest_hit_triangle_albedo_alpha(p_position + r_light_dir * bake_params.bias, light_pos, hit_albedo, hit_position);
+			if (ret == RAY_MISS) {
+				if (!did_hit) {
+					penumbra = 1.0;
+				}
+				break;
+			} else if (ret == RAY_FRONT || ret == RAY_BACK) {
+				bool contribute = (ret == RAY_FRONT || !did_hit);
+				if (!did_hit) {
+					penumbra = 1.0;
+					did_hit = true;
+				}
+
+				if (contribute) {
+					penumbra = max(penumbra - hit_albedo.a - EPSILON, 0.0);
+				}
+
+				p_position = hit_position + r_light_dir * bake_params.bias;
+
+				if (penumbra - EPSILON <= 0) {
+					break;
+				}
+			}
 		}
 		}
+
+		penumbra = clamp(penumbra, 0.0, 1.0);
 	}
 	}
 
 
 	r_shadow = penumbra;
 	r_shadow = penumbra;
@@ -533,6 +663,7 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, f
 	vec3 position = p_position;
 	vec3 position = p_position;
 	vec3 ray_dir = p_ray_dir;
 	vec3 ray_dir = p_ray_dir;
 	uint max_depth = max(bake_params.bounces, 1);
 	uint max_depth = max(bake_params.bounces, 1);
+	uint transparency_rays_left = bake_params.transparency_rays;
 	vec3 throughput = vec3(1.0);
 	vec3 throughput = vec3(1.0);
 	vec3 light = vec3(0.0);
 	vec3 light = vec3(0.0);
 	for (uint depth = 0; depth < max_depth; depth++) {
 	for (uint depth = 0; depth < max_depth; depth++) {
@@ -546,6 +677,8 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, f
 			vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
 			vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
 			position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
 			position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
 
 
+			vec3 prev_normal = ray_dir;
+
 			vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
 			vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
 			vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
 			vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
 			vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
 			vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
@@ -568,13 +701,29 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, f
 			direct_light *= bake_params.exposure_normalization;
 			direct_light *= bake_params.exposure_normalization;
 #endif
 #endif
 
 
-			vec3 albedo = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgb;
+			vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
 			vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
 			vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
 			emissive *= bake_params.exposure_normalization;
 			emissive *= bake_params.exposure_normalization;
 
 
-			light += throughput * emissive;
-			throughput *= albedo;
-			light += throughput * direct_light * bake_params.bounce_indirect_energy;
+			light += throughput * emissive * albedo_alpha.a;
+			throughput = mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a);
+			light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
+
+			if (albedo_alpha.a < 1.0) {
+				transparency_rays_left -= 1;
+				depth -= 1;
+				if (transparency_rays_left <= 0) {
+					break;
+				}
+
+				// Either bounce off the transparent surface or keep going forward.
+				float pa = albedo_alpha.a * albedo_alpha.a;
+				if (randomize(r_noise) > pa) {
+					normal = prev_normal;
+				}
+
+				position += normal * bake_params.bias;
+			}
 
 
 			// Use Russian Roulette to determine a probability to terminate the bounce earlier as an optimization.
 			// Use Russian Roulette to determine a probability to terminate the bounce earlier as an optimization.
 			// <https://computergraphics.stackexchange.com/questions/2316/is-russian-roulette-really-the-answer>
 			// <https://computergraphics.stackexchange.com/questions/2316/is-russian-roulette-really-the-answer>
@@ -592,9 +741,55 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, f
 			// Look for the environment color and stop bouncing.
 			// Look for the environment color and stop bouncing.
 			light += throughput * trace_environment_color(ray_dir);
 			light += throughput * trace_environment_color(ray_dir);
 			break;
 			break;
-		} else {
-			// Ignore any other trace results.
-			break;
+		} else if (trace_result == RAY_BACK) {
+			Vertex vert0 = vertices.data[triangles.data[tidx].indices.x];
+			Vertex vert1 = vertices.data[triangles.data[tidx].indices.y];
+			Vertex vert2 = vertices.data[triangles.data[tidx].indices.z];
+			vec3 uvw = vec3(barycentric.x * vert0.uv + barycentric.y * vert1.uv + barycentric.z * vert2.uv, float(triangles.data[tidx].slice));
+			position = barycentric.x * vert0.position + barycentric.y * vert1.position + barycentric.z * vert2.position;
+
+			vec4 albedo_alpha = textureLod(sampler2DArray(albedo_tex, linear_sampler), uvw, 0).rgba;
+
+			if (albedo_alpha.a > 1.0) {
+				break;
+			}
+
+			transparency_rays_left -= 1;
+			depth -= 1;
+			if (transparency_rays_left <= 0) {
+				break;
+			}
+
+			vec3 norm0 = vec3(vert0.normal_xy, vert0.normal_z);
+			vec3 norm1 = vec3(vert1.normal_xy, vert1.normal_z);
+			vec3 norm2 = vec3(vert2.normal_xy, vert2.normal_z);
+			vec3 normal = barycentric.x * norm0 + barycentric.y * norm1 + barycentric.z * norm2;
+
+			vec3 direct_light = vec3(0.0f);
+#ifdef USE_LIGHT_TEXTURE_FOR_BOUNCES
+			direct_light += textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
+#else
+			// Trace the lights directly. Significantly more expensive but more accurate in scenarios
+			// where the lightmap texture isn't reliable.
+			for (uint i = 0; i < bake_params.light_count; i++) {
+				vec3 light;
+				vec3 light_dir;
+				float shadow;
+				trace_direct_light(position, normal, i, false, light, light_dir, r_noise, p_texel_size, shadow);
+				direct_light += light * lights.data[i].indirect_energy;
+			}
+
+			direct_light *= bake_params.exposure_normalization;
+#endif
+
+			vec3 emissive = textureLod(sampler2DArray(emission_tex, linear_sampler), uvw, 0).rgb;
+			emissive *= bake_params.exposure_normalization;
+
+			light += throughput * emissive * albedo_alpha.a;
+			throughput = mix(mix(throughput, throughput * albedo_alpha.rgb, albedo_alpha.a), vec3(0.0), albedo_alpha.a);
+			light += throughput * direct_light * bake_params.bounce_indirect_energy * albedo_alpha.a;
+
+			position += ray_dir * bake_params.bias;
 		}
 		}
 	}
 	}
 
 

+ 1 - 0
modules/lightmapper_rd/register_types.cpp

@@ -52,6 +52,7 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/ultra_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 2048);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/ultra_quality_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 2048);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 32);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_rays_per_pass", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 32);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/region_size", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 512);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/region_size", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 512);
+	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_performance/max_transparency_rays", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 8);
 
 
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/low_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 64);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/low_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 64);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/medium_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 256);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lightmapping/bake_quality/medium_quality_probe_ray_count", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 256);

+ 7 - 1
scene/3d/lightmap_gi.cpp

@@ -981,7 +981,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
 					w_albedo[i + 0] = uint8_t(CLAMP(float(r_aa[i + 0]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
 					w_albedo[i + 0] = uint8_t(CLAMP(float(r_aa[i + 0]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
 					w_albedo[i + 1] = uint8_t(CLAMP(float(r_aa[i + 1]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
 					w_albedo[i + 1] = uint8_t(CLAMP(float(r_aa[i + 1]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
 					w_albedo[i + 2] = uint8_t(CLAMP(float(r_aa[i + 2]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
 					w_albedo[i + 2] = uint8_t(CLAMP(float(r_aa[i + 2]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
-					w_albedo[i + 3] = 255;
+					w_albedo[i + 3] = r_aa[i + 3];
 				}
 				}
 
 
 				md.albedo_on_uv2.instantiate();
 				md.albedo_on_uv2.instantiate();
@@ -1002,6 +1002,11 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
 					continue;
 					continue;
 				}
 				}
 				Array a = mf.mesh->surface_get_arrays(i);
 				Array a = mf.mesh->surface_get_arrays(i);
+				Ref<Material> mat = mf.mesh->surface_get_material(i);
+				RID mat_rid;
+				if (mat.is_valid()) {
+					mat_rid = mat->get_rid();
+				}
 
 
 				Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
 				Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
 				const Vector3 *vr = vertices.ptr();
 				const Vector3 *vr = vertices.ptr();
@@ -1051,6 +1056,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
 
 
 						md.uv2.push_back(uvr[vidx[k]]);
 						md.uv2.push_back(uvr[vidx[k]]);
 						md.normal.push_back(normal_xform.xform(nr[vidx[k]]).normalized());
 						md.normal.push_back(normal_xform.xform(nr[vidx[k]]).normalized());
+						md.material.push_back(mat_rid);
 					}
 					}
 				}
 				}
 			}
 			}

+ 1 - 0
scene/3d/lightmapper.h

@@ -171,6 +171,7 @@ public:
 		Vector<Vector3> points;
 		Vector<Vector3> points;
 		Vector<Vector2> uv2;
 		Vector<Vector2> uv2;
 		Vector<Vector3> normal;
 		Vector<Vector3> normal;
+		Vector<RID> material;
 		Ref<Image> albedo_on_uv2;
 		Ref<Image> albedo_on_uv2;
 		Ref<Image> emission_on_uv2;
 		Ref<Image> emission_on_uv2;
 		Variant userdata;
 		Variant userdata;

+ 2 - 0
servers/rendering/dummy/storage/material_storage.h

@@ -109,6 +109,8 @@ public:
 
 
 	virtual bool material_is_animated(RID p_material) override { return false; }
 	virtual bool material_is_animated(RID p_material) override { return false; }
 	virtual bool material_casts_shadows(RID p_material) override { return false; }
 	virtual bool material_casts_shadows(RID p_material) override { return false; }
+	virtual RS::CullMode material_get_cull_mode(RID p_material) const override { return RS::CULL_MODE_DISABLED; }
+
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {}
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {}
 	virtual void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {}
 	virtual void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {}
 };
 };

+ 4 - 0
servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

@@ -1120,6 +1120,10 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
 				if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) {
 				if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) {
 					rl->add_element(surf);
 					rl->add_element(surf);
 				}
 				}
+			} else if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL) {
+				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) {
+					rl->add_element(surf);
+				}
 			} else {
 			} else {
 				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) {
 				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) {
 					rl->add_element(surf);
 					rl->add_element(surf);

+ 5 - 5
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp

@@ -54,7 +54,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 	blend_mode = BLEND_MODE_MIX;
 	blend_mode = BLEND_MODE_MIX;
 	depth_testi = DEPTH_TEST_ENABLED;
 	depth_testi = DEPTH_TEST_ENABLED;
 	alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
 	alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
-	int cull_modei = CULL_BACK;
+	int cull_modei = RS::CULL_MODE_BACK;
 
 
 	uses_point_size = false;
 	uses_point_size = false;
 	uses_alpha = false;
 	uses_alpha = false;
@@ -101,9 +101,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 
 
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 
 
-	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
-	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
-	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
+	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_DISABLED);
+	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_FRONT);
+	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, RS::CULL_MODE_BACK);
 
 
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["wireframe"] = &wireframe;
 	actions.render_mode_flags["wireframe"] = &wireframe;
@@ -158,7 +158,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 
 
 	depth_draw = DepthDraw(depth_drawi);
 	depth_draw = DepthDraw(depth_drawi);
 	depth_test = DepthTest(depth_testi);
 	depth_test = DepthTest(depth_testi);
-	cull_mode = Cull(cull_modei);
+	cull_mode = RS::CullMode(cull_modei);
 	uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
 	uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
 	uses_screen_texture = gen_code.uses_screen_texture;
 	uses_screen_texture = gen_code.uses_screen_texture;
 	uses_depth_texture = gen_code.uses_depth_texture;
 	uses_depth_texture = gen_code.uses_depth_texture;

+ 2 - 8
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h

@@ -153,12 +153,6 @@ public:
 			DEPTH_TEST_ENABLED
 			DEPTH_TEST_ENABLED
 		};
 		};
 
 
-		enum Cull {
-			CULL_DISABLED,
-			CULL_FRONT,
-			CULL_BACK
-		};
-
 		enum CullVariant {
 		enum CullVariant {
 			CULL_VARIANT_NORMAL,
 			CULL_VARIANT_NORMAL,
 			CULL_VARIANT_REVERSED,
 			CULL_VARIANT_REVERSED,
@@ -250,7 +244,7 @@ public:
 		bool writes_modelview_or_projection = false;
 		bool writes_modelview_or_projection = false;
 		bool uses_world_coordinates = false;
 		bool uses_world_coordinates = false;
 		bool uses_screen_texture_mipmaps = false;
 		bool uses_screen_texture_mipmaps = false;
-		Cull cull_mode = CULL_DISABLED;
+		RS::CullMode cull_mode = RS::CULL_MODE_DISABLED;
 
 
 		uint64_t last_pass = 0;
 		uint64_t last_pass = 0;
 		uint32_t index = 0;
 		uint32_t index = 0;
@@ -272,7 +266,7 @@ public:
 		}
 		}
 
 
 		_FORCE_INLINE_ bool uses_shared_shadow_material() const {
 		_FORCE_INLINE_ bool uses_shared_shadow_material() const {
-			bool backface_culling = cull_mode == CULL_BACK;
+			bool backface_culling = cull_mode == RS::CULL_MODE_BACK;
 			return !uses_particle_trails && !writes_modelview_or_projection && !uses_vertex && !uses_position && !uses_discard && !uses_depth_prepass_alpha && !uses_alpha_clip && !uses_alpha_antialiasing && backface_culling && !uses_point_size && !uses_world_coordinates && !wireframe;
 			return !uses_particle_trails && !writes_modelview_or_projection && !uses_vertex && !uses_position && !uses_discard && !uses_depth_prepass_alpha && !uses_alpha_clip && !uses_alpha_antialiasing && backface_culling && !uses_point_size && !uses_world_coordinates && !wireframe;
 		}
 		}
 
 

+ 4 - 0
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

@@ -2061,6 +2061,10 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
 				if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) {
 				if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) {
 					rl->add_element(surf);
 					rl->add_element(surf);
 				}
 				}
+			} else if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL) {
+				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA)) {
+					rl->add_element(surf);
+				}
 			} else {
 			} else {
 				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) {
 				if (surf->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) {
 					rl->add_element(surf);
 					rl->add_element(surf);

+ 4 - 4
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp

@@ -56,7 +56,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
 	blend_mode = BLEND_MODE_MIX;
 	blend_mode = BLEND_MODE_MIX;
 	depth_testi = DEPTH_TEST_ENABLED;
 	depth_testi = DEPTH_TEST_ENABLED;
 	alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
 	alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
-	cull_mode = CULL_BACK;
+	cull_mode = RS::CULL_MODE_BACK;
 
 
 	uses_point_size = false;
 	uses_point_size = false;
 	uses_alpha = false;
 	uses_alpha = false;
@@ -102,9 +102,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
 
 
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 	actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
 
 
-	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_mode, CULL_DISABLED);
-	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_mode, CULL_FRONT);
-	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_mode, CULL_BACK);
+	actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_mode, RS::CULL_MODE_DISABLED);
+	actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_mode, RS::CULL_MODE_FRONT);
+	actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_mode, RS::CULL_MODE_BACK);
 
 
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["unshaded"] = &unshaded;
 	actions.render_mode_flags["wireframe"] = &wireframe;
 	actions.render_mode_flags["wireframe"] = &wireframe;

+ 1 - 7
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h

@@ -141,12 +141,6 @@ public:
 			DEPTH_TEST_ENABLED
 			DEPTH_TEST_ENABLED
 		};
 		};
 
 
-		enum Cull {
-			CULL_DISABLED,
-			CULL_FRONT,
-			CULL_BACK
-		};
-
 		enum CullVariant {
 		enum CullVariant {
 			CULL_VARIANT_NORMAL,
 			CULL_VARIANT_NORMAL,
 			CULL_VARIANT_REVERSED,
 			CULL_VARIANT_REVERSED,
@@ -209,7 +203,7 @@ public:
 		int blend_mode = BLEND_MODE_MIX;
 		int blend_mode = BLEND_MODE_MIX;
 		int depth_testi = DEPTH_TEST_ENABLED;
 		int depth_testi = DEPTH_TEST_ENABLED;
 		int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
 		int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
-		int cull_mode = CULL_BACK;
+		int cull_mode = RS::CULL_MODE_BACK;
 
 
 		bool uses_point_size = false;
 		bool uses_point_size = false;
 		bool uses_alpha = false;
 		bool uses_alpha = false;

+ 36 - 1
servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl

@@ -1256,21 +1256,37 @@ void fragment_shader(in SceneData scene_data) {
 #ifndef USE_SHADOW_TO_OPACITY
 #ifndef USE_SHADOW_TO_OPACITY
 
 
 #ifdef ALPHA_SCISSOR_USED
 #ifdef ALPHA_SCISSOR_USED
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // ALPHA_SCISSOR_USED
 #endif // ALPHA_SCISSOR_USED
 
 
 // alpha hash can be used in unison with alpha antialiasing
 // alpha hash can be used in unison with alpha antialiasing
 #ifdef ALPHA_HASH_USED
 #ifdef ALPHA_HASH_USED
 	vec3 object_pos = (inverse(read_model_matrix) * inv_view_matrix * vec4(vertex, 1.0)).xyz;
 	vec3 object_pos = (inverse(read_model_matrix) * inv_view_matrix * vec4(vertex, 1.0)).xyz;
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
 	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // ALPHA_HASH_USED
 #endif // ALPHA_HASH_USED
 
 
 // If we are not edge antialiasing, we need to remove the output alpha channel from scissor and hash
 // If we are not edge antialiasing, we need to remove the output alpha channel from scissor and hash
-#if (defined(ALPHA_SCISSOR_USED) || defined(ALPHA_HASH_USED)) && !defined(ALPHA_ANTIALIASING_EDGE_USED)
+#if (defined(ALPHA_SCISSOR_USED) || defined(ALPHA_HASH_USED)) && !defined(ALPHA_ANTIALIASING_EDGE_USED) && !defined(MODE_RENDER_MATERIAL)
 	alpha = 1.0;
 	alpha = 1.0;
 #endif
 #endif
 
 
@@ -1314,10 +1330,21 @@ void fragment_shader(in SceneData scene_data) {
 #endif
 #endif
 
 
 #ifdef ENABLE_CLIP_ALPHA
 #ifdef ENABLE_CLIP_ALPHA
+#ifdef MODE_RENDER_MATERIAL
+	if (albedo.a < 0.99) {
+		// Used for doublepass and shadowmapping.
+		albedo.a = 0.0;
+		alpha = 0.0;
+	} else {
+		albedo.a = 1.0;
+		alpha = 1.0;
+	}
+#else
 	if (albedo.a < 0.99) {
 	if (albedo.a < 0.99) {
 		//used for doublepass and shadowmapping
 		//used for doublepass and shadowmapping
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif
 #endif
 
 
 	/////////////////////// FOG //////////////////////
 	/////////////////////// FOG //////////////////////
@@ -2521,9 +2548,17 @@ void fragment_shader(in SceneData scene_data) {
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 
 
 #if defined(ALPHA_SCISSOR_USED)
 #if defined(ALPHA_SCISSOR_USED)
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // ALPHA_SCISSOR_USED
 #endif // ALPHA_SCISSOR_USED
 
 
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH

+ 36 - 1
servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl

@@ -984,21 +984,37 @@ void main() {
 #ifndef USE_SHADOW_TO_OPACITY
 #ifndef USE_SHADOW_TO_OPACITY
 
 
 #ifdef ALPHA_SCISSOR_USED
 #ifdef ALPHA_SCISSOR_USED
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // ALPHA_SCISSOR_USED
 #endif // ALPHA_SCISSOR_USED
 
 
 // alpha hash can be used in unison with alpha antialiasing
 // alpha hash can be used in unison with alpha antialiasing
 #ifdef ALPHA_HASH_USED
 #ifdef ALPHA_HASH_USED
 	vec3 object_pos = (inverse(read_model_matrix) * inv_view_matrix * vec4(vertex, 1.0)).xyz;
 	vec3 object_pos = (inverse(read_model_matrix) * inv_view_matrix * vec4(vertex, 1.0)).xyz;
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
 	if (alpha < compute_alpha_hash_threshold(object_pos, alpha_hash_scale)) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // ALPHA_HASH_USED
 #endif // ALPHA_HASH_USED
 
 
 // If we are not edge antialiasing, we need to remove the output alpha channel from scissor and hash
 // If we are not edge antialiasing, we need to remove the output alpha channel from scissor and hash
-#if (defined(ALPHA_SCISSOR_USED) || defined(ALPHA_HASH_USED)) && !defined(ALPHA_ANTIALIASING_EDGE_USED)
+#if (defined(ALPHA_SCISSOR_USED) || defined(ALPHA_HASH_USED)) && !defined(ALPHA_ANTIALIASING_EDGE_USED) && !defined(MODE_RENDER_MATERIAL)
 	alpha = 1.0;
 	alpha = 1.0;
 #endif
 #endif
 
 
@@ -1042,10 +1058,21 @@ void main() {
 #endif
 #endif
 
 
 #ifdef ENABLE_CLIP_ALPHA
 #ifdef ENABLE_CLIP_ALPHA
+#ifdef MODE_RENDER_MATERIAL
+	if (albedo.a < 0.99) {
+		// Used for doublepass and shadowmapping.
+		albedo.a = 0.0;
+		alpha = 0.0;
+	} else {
+		albedo.a = 1.0;
+		alpha = 1.0;
+	}
+#else
 	if (albedo.a < 0.99) {
 	if (albedo.a < 0.99) {
 		//used for doublepass and shadowmapping
 		//used for doublepass and shadowmapping
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif
 #endif
 
 
 	/////////////////////// FOG //////////////////////
 	/////////////////////// FOG //////////////////////
@@ -1739,9 +1766,17 @@ void main() {
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 	alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
 
 
 #if defined(ALPHA_SCISSOR_USED)
 #if defined(ALPHA_SCISSOR_USED)
+#ifdef MODE_RENDER_MATERIAL
+	if (alpha < alpha_scissor_threshold) {
+		alpha = 0.0;
+	} else {
+		alpha = 1.0;
+	}
+#else
 	if (alpha < alpha_scissor_threshold) {
 	if (alpha < alpha_scissor_threshold) {
 		discard;
 		discard;
 	}
 	}
+#endif // MODE_RENDER_MATERIAL
 #endif // !ALPHA_SCISSOR_USED
 #endif // !ALPHA_SCISSOR_USED
 
 
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH

+ 20 - 0
servers/rendering/renderer_rd/storage_rd/material_storage.cpp

@@ -32,6 +32,8 @@
 #include "core/config/engine.h"
 #include "core/config/engine.h"
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
+#include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h"
+#include "servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h"
 #include "servers/rendering/storage/variant_converters.h"
 #include "servers/rendering/storage/variant_converters.h"
 #include "texture_storage.h"
 #include "texture_storage.h"
 
 
@@ -2302,6 +2304,24 @@ bool MaterialStorage::material_casts_shadows(RID p_material) {
 	return true; //by default everything casts shadows
 	return true; //by default everything casts shadows
 }
 }
 
 
+RS::CullMode RendererRD::MaterialStorage::material_get_cull_mode(RID p_material) const {
+	Material *material = material_owner.get_or_null(p_material);
+	ERR_FAIL_NULL_V(material, RS::CULL_MODE_DISABLED);
+	ERR_FAIL_NULL_V(material->shader, RS::CULL_MODE_DISABLED);
+	if (material->shader->type == ShaderType::SHADER_TYPE_3D && material->shader->data) {
+		RendererSceneRenderImplementation::SceneShaderForwardClustered::ShaderData *sd_clustered = dynamic_cast<RendererSceneRenderImplementation::SceneShaderForwardClustered::ShaderData *>(material->shader->data);
+		if (sd_clustered) {
+			return (RS::CullMode)sd_clustered->cull_mode;
+		}
+
+		RendererSceneRenderImplementation::SceneShaderForwardMobile::ShaderData *sd_mobile = dynamic_cast<RendererSceneRenderImplementation::SceneShaderForwardMobile::ShaderData *>(material->shader->data);
+		if (sd_mobile) {
+			return (RS::CullMode)sd_mobile->cull_mode;
+		}
+	}
+	return RS::CULL_MODE_DISABLED;
+}
+
 void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
 void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
 	Material *material = material_owner.get_or_null(p_material);
 	Material *material = material_owner.get_or_null(p_material);
 	ERR_FAIL_NULL(material);
 	ERR_FAIL_NULL(material);

+ 1 - 0
servers/rendering/renderer_rd/storage_rd/material_storage.h

@@ -444,6 +444,7 @@ public:
 
 
 	virtual bool material_is_animated(RID p_material) override;
 	virtual bool material_is_animated(RID p_material) override;
 	virtual bool material_casts_shadows(RID p_material) override;
 	virtual bool material_casts_shadows(RID p_material) override;
+	virtual RS::CullMode material_get_cull_mode(RID p_material) const override;
 
 
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
 	virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
 
 

+ 1 - 0
servers/rendering/storage/material_storage.h

@@ -87,6 +87,7 @@ public:
 
 
 	virtual bool material_is_animated(RID p_material) = 0;
 	virtual bool material_is_animated(RID p_material) = 0;
 	virtual bool material_casts_shadows(RID p_material) = 0;
 	virtual bool material_casts_shadows(RID p_material) = 0;
+	virtual RS::CullMode material_get_cull_mode(RID p_material) const = 0;
 
 
 	struct InstanceShaderParam {
 	struct InstanceShaderParam {
 		PropertyInfo info;
 		PropertyInfo info;

+ 6 - 0
servers/rendering_server.h

@@ -221,6 +221,12 @@ public:
 		SHADER_MAX
 		SHADER_MAX
 	};
 	};
 
 
+	enum CullMode {
+		CULL_MODE_DISABLED,
+		CULL_MODE_FRONT,
+		CULL_MODE_BACK,
+	};
+
 	virtual RID shader_create() = 0;
 	virtual RID shader_create() = 0;
 	virtual RID shader_create_from_code(const String &p_code, const String &p_path_hint = String()) = 0;
 	virtual RID shader_create_from_code(const String &p_code, const String &p_path_hint = String()) = 0;