Quellcode durchsuchen

Merge pull request #89988 from LunaCapra/specular-occlusion

Add support for bent normal maps for specular occlusion and indirect lighting
Thaddeus Crews vor 3 Monaten
Ursprung
Commit
d0693d6867

+ 17 - 2
doc/classes/BaseMaterial3D.xml

@@ -120,6 +120,15 @@
 		<member name="backlight_texture" type="Texture2D" setter="set_texture" getter="get_texture">
 		<member name="backlight_texture" type="Texture2D" setter="set_texture" getter="get_texture">
 			Texture used to control the backlight effect per-pixel. Added to [member backlight].
 			Texture used to control the backlight effect per-pixel. Added to [member backlight].
 		</member>
 		</member>
+		<member name="bent_normal_enabled" type="bool" setter="set_feature" getter="get_feature" default="false">
+			If [code]true[/code], the bent normal map is enabled. This allows for more accurate indirect lighting and specular occlusion.
+		</member>
+		<member name="bent_normal_texture" type="Texture2D" setter="set_texture" getter="get_texture">
+			Texture that specifies the average direction of incoming ambient light at a given pixel. The [member bent_normal_texture] only uses the red and green channels; the blue and alpha channels are ignored. The normal read from [member bent_normal_texture] is oriented around the surface normal provided by the [Mesh].
+			[b]Note:[/b] A bent normal map is different from a regular normal map. When baking a bent normal map make sure to use [b]a cosine distribution[/b] for the bent normal map to work correctly.
+			[b]Note:[/b] The mesh must have both normals and tangents defined in its vertex data. Otherwise, the shading produced by the bent normal map will not look correct. If creating geometry with [SurfaceTool], you can use [method SurfaceTool.generate_normals] and [method SurfaceTool.generate_tangents] to automatically generate normals and tangents respectively.
+			[b]Note:[/b] Godot expects the bent normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines.
+		</member>
 		<member name="billboard_keep_scale" type="bool" setter="set_flag" getter="get_flag" default="false">
 		<member name="billboard_keep_scale" type="bool" setter="set_flag" getter="get_flag" default="false">
 			If [code]true[/code], the shader will keep the scale set for the mesh. Otherwise, the scale is lost when billboarding. Only applies when [member billboard_mode] is not [constant BILLBOARD_DISABLED].
 			If [code]true[/code], the shader will keep the scale set for the mesh. Otherwise, the scale is lost when billboarding. Only applies when [member billboard_mode] is not [constant BILLBOARD_DISABLED].
 		</member>
 		</member>
@@ -461,6 +470,9 @@
 		<constant name="TEXTURE_NORMAL" value="4" enum="TextureParam">
 		<constant name="TEXTURE_NORMAL" value="4" enum="TextureParam">
 			Texture specifying per-pixel normal vector.
 			Texture specifying per-pixel normal vector.
 		</constant>
 		</constant>
+		<constant name="TEXTURE_BENT_NORMAL" value="18" enum="TextureParam">
+			Texture specifying per-pixel bent normal vector.
+		</constant>
 		<constant name="TEXTURE_RIM" value="5" enum="TextureParam">
 		<constant name="TEXTURE_RIM" value="5" enum="TextureParam">
 			Texture specifying per-pixel rim value.
 			Texture specifying per-pixel rim value.
 		</constant>
 		</constant>
@@ -500,7 +512,7 @@
 		<constant name="TEXTURE_ORM" value="17" enum="TextureParam">
 		<constant name="TEXTURE_ORM" value="17" enum="TextureParam">
 			Texture holding ambient occlusion, roughness, and metallic.
 			Texture holding ambient occlusion, roughness, and metallic.
 		</constant>
 		</constant>
-		<constant name="TEXTURE_MAX" value="18" enum="TextureParam">
+		<constant name="TEXTURE_MAX" value="19" enum="TextureParam">
 			Represents the size of the [enum TextureParam] enum.
 			Represents the size of the [enum TextureParam] enum.
 		</constant>
 		</constant>
 		<constant name="TEXTURE_FILTER_NEAREST" value="0" enum="TextureFilter">
 		<constant name="TEXTURE_FILTER_NEAREST" value="0" enum="TextureFilter">
@@ -596,7 +608,10 @@
 		<constant name="FEATURE_DETAIL" value="11" enum="Feature">
 		<constant name="FEATURE_DETAIL" value="11" enum="Feature">
 			Constant for setting [member detail_enabled].
 			Constant for setting [member detail_enabled].
 		</constant>
 		</constant>
-		<constant name="FEATURE_MAX" value="12" enum="Feature">
+		<constant name="FEATURE_BENT_NORMAL_MAPPING" value="12" enum="Feature">
+			Constant for setting [member bent_normal_enabled].
+		</constant>
+		<constant name="FEATURE_MAX" value="13" enum="Feature">
 			Represents the size of the [enum Feature] enum.
 			Represents the size of the [enum Feature] enum.
 		</constant>
 		</constant>
 		<constant name="BLEND_MODE_MIX" value="0" enum="BlendMode">
 		<constant name="BLEND_MODE_MIX" value="0" enum="BlendMode">

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

@@ -1842,6 +1842,10 @@ void main() {
 	vec3 normal_map = vec3(0.5);
 	vec3 normal_map = vec3(0.5);
 #endif
 #endif
 
 
+#if defined(BENT_NORMAL_MAP_USED)
+	vec3 bent_normal_map = vec3(0.5);
+#endif
+
 	float normal_map_depth = 1.0;
 	float normal_map_depth = 1.0;
 
 
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;

+ 4 - 0
drivers/gles3/storage/material_storage.cpp

@@ -1253,6 +1253,7 @@ MaterialStorage::MaterialStorage() {
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
+		actions.renames["BENT_NORMAL_MAP"] = "bent_normal_map";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
@@ -1330,6 +1331,7 @@ MaterialStorage::MaterialStorage() {
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
+		actions.usage_defines["BENT_NORMAL_MAP"] = "#define BENT_NORMAL_MAP_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
@@ -2904,6 +2906,7 @@ void SceneShaderData::set_code(const String &p_code) {
 	uses_screen_texture_mipmaps = false;
 	uses_screen_texture_mipmaps = false;
 	uses_depth_texture = false;
 	uses_depth_texture = false;
 	uses_normal_texture = false;
 	uses_normal_texture = false;
+	uses_bent_normal_texture = false;
 	uses_time = false;
 	uses_time = false;
 	uses_vertex_time = false;
 	uses_vertex_time = false;
 	uses_fragment_time = false;
 	uses_fragment_time = false;
@@ -2977,6 +2980,7 @@ void SceneShaderData::set_code(const String &p_code) {
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal;
+	actions.usage_flag_pointers["BENT_NORMAL_MAP"] = &uses_bent_normal_texture;
 
 
 	actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;

+ 1 - 0
drivers/gles3/storage/material_storage.h

@@ -305,6 +305,7 @@ struct SceneShaderData : public ShaderData {
 	bool uses_screen_texture_mipmaps;
 	bool uses_screen_texture_mipmaps;
 	bool uses_depth_texture;
 	bool uses_depth_texture;
 	bool uses_normal_texture;
 	bool uses_normal_texture;
+	bool uses_bent_normal_texture;
 	bool uses_time;
 	bool uses_time;
 	bool uses_vertex_time;
 	bool uses_vertex_time;
 	bool uses_fragment_time;
 	bool uses_fragment_time;

+ 25 - 0
scene/resources/material.cpp

@@ -646,6 +646,7 @@ void BaseMaterial3D::init_shaders() {
 	shader_names->texture_names[TEXTURE_ROUGHNESS] = "texture_roughness";
 	shader_names->texture_names[TEXTURE_ROUGHNESS] = "texture_roughness";
 	shader_names->texture_names[TEXTURE_EMISSION] = "texture_emission";
 	shader_names->texture_names[TEXTURE_EMISSION] = "texture_emission";
 	shader_names->texture_names[TEXTURE_NORMAL] = "texture_normal";
 	shader_names->texture_names[TEXTURE_NORMAL] = "texture_normal";
+	shader_names->texture_names[TEXTURE_BENT_NORMAL] = "texture_bent_normal";
 	shader_names->texture_names[TEXTURE_RIM] = "texture_rim";
 	shader_names->texture_names[TEXTURE_RIM] = "texture_rim";
 	shader_names->texture_names[TEXTURE_CLEARCOAT] = "texture_clearcoat";
 	shader_names->texture_names[TEXTURE_CLEARCOAT] = "texture_clearcoat";
 	shader_names->texture_names[TEXTURE_FLOWMAP] = "texture_flowmap";
 	shader_names->texture_names[TEXTURE_FLOWMAP] = "texture_flowmap";
@@ -1022,6 +1023,12 @@ uniform vec4 refraction_texture_channel;
 		code += vformat(R"(
 		code += vformat(R"(
 uniform sampler2D texture_normal : hint_roughness_normal, %s;
 uniform sampler2D texture_normal : hint_roughness_normal, %s;
 uniform float normal_scale : hint_range(-16.0, 16.0);
 uniform float normal_scale : hint_range(-16.0, 16.0);
+)",
+				texfilter_str);
+	}
+	if (features[FEATURE_BENT_NORMAL_MAPPING]) {
+		code += vformat(R"(
+uniform sampler2D texture_bent_normal : hint_roughness_normal, %s;
 )",
 )",
 				texfilter_str);
 				texfilter_str);
 	}
 	}
@@ -1641,6 +1648,13 @@ void fragment() {)";
 		code += "	NORMAL_MAP_DEPTH = normal_scale;\n";
 		code += "	NORMAL_MAP_DEPTH = normal_scale;\n";
 	}
 	}
 
 
+	if (features[FEATURE_BENT_NORMAL_MAPPING]) {
+		code += R"(
+	// Bent Normal Map: Enabled
+	BENT_NORMAL_MAP = texture(texture_bent_normal, base_uv).rgb;
+)";
+	}
+
 	if (features[FEATURE_EMISSION]) {
 	if (features[FEATURE_EMISSION]) {
 		code += R"(
 		code += R"(
 	// Emission: Enabled
 	// Emission: Enabled
@@ -2440,6 +2454,7 @@ void BaseMaterial3D::_validate_feature(const String &text, Feature feature, Prop
 
 
 void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
 void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
 	_validate_feature("normal", FEATURE_NORMAL_MAPPING, p_property);
 	_validate_feature("normal", FEATURE_NORMAL_MAPPING, p_property);
+	_validate_feature("bent_normal", FEATURE_BENT_NORMAL_MAPPING, p_property);
 	_validate_feature("emission", FEATURE_EMISSION, p_property);
 	_validate_feature("emission", FEATURE_EMISSION, p_property);
 	_validate_feature("rim", FEATURE_RIM, p_property);
 	_validate_feature("rim", FEATURE_RIM, p_property);
 	_validate_feature("clearcoat", FEATURE_CLEARCOAT, p_property);
 	_validate_feature("clearcoat", FEATURE_CLEARCOAT, p_property);
@@ -2585,6 +2600,10 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
 			p_property.usage = PROPERTY_USAGE_NONE;
 			p_property.usage = PROPERTY_USAGE_NONE;
 		}
 		}
 
 
+		if (p_property.name.begins_with("bent_normal")) {
+			p_property.usage = PROPERTY_USAGE_NONE;
+		}
+
 		if (p_property.name.begins_with("backlight")) {
 		if (p_property.name.begins_with("backlight")) {
 			p_property.usage = PROPERTY_USAGE_NONE;
 			p_property.usage = PROPERTY_USAGE_NONE;
 		}
 		}
@@ -3251,6 +3270,10 @@ void BaseMaterial3D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_scale", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_normal_scale", "get_normal_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_scale", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_normal_scale", "get_normal_scale");
 	ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_NORMAL);
 	ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_NORMAL);
 
 
+	ADD_GROUP("Bent Normal Map", "bent_normal_");
+	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "bent_normal_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_feature", "get_feature", FEATURE_BENT_NORMAL_MAPPING);
+	ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "bent_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_BENT_NORMAL);
+
 	ADD_GROUP("Rim", "rim_");
 	ADD_GROUP("Rim", "rim_");
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "rim_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_feature", "get_feature", FEATURE_RIM);
 	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "rim_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_feature", "get_feature", FEATURE_RIM);
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rim", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_rim", "get_rim");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rim", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_rim", "get_rim");
@@ -3377,6 +3400,7 @@ void BaseMaterial3D::_bind_methods() {
 	BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS);
 	BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS);
 	BIND_ENUM_CONSTANT(TEXTURE_EMISSION);
 	BIND_ENUM_CONSTANT(TEXTURE_EMISSION);
 	BIND_ENUM_CONSTANT(TEXTURE_NORMAL);
 	BIND_ENUM_CONSTANT(TEXTURE_NORMAL);
+	BIND_ENUM_CONSTANT(TEXTURE_BENT_NORMAL);
 	BIND_ENUM_CONSTANT(TEXTURE_RIM);
 	BIND_ENUM_CONSTANT(TEXTURE_RIM);
 	BIND_ENUM_CONSTANT(TEXTURE_CLEARCOAT);
 	BIND_ENUM_CONSTANT(TEXTURE_CLEARCOAT);
 	BIND_ENUM_CONSTANT(TEXTURE_FLOWMAP);
 	BIND_ENUM_CONSTANT(TEXTURE_FLOWMAP);
@@ -3427,6 +3451,7 @@ void BaseMaterial3D::_bind_methods() {
 	BIND_ENUM_CONSTANT(FEATURE_BACKLIGHT);
 	BIND_ENUM_CONSTANT(FEATURE_BACKLIGHT);
 	BIND_ENUM_CONSTANT(FEATURE_REFRACTION);
 	BIND_ENUM_CONSTANT(FEATURE_REFRACTION);
 	BIND_ENUM_CONSTANT(FEATURE_DETAIL);
 	BIND_ENUM_CONSTANT(FEATURE_DETAIL);
+	BIND_ENUM_CONSTANT(FEATURE_BENT_NORMAL_MAPPING);
 	BIND_ENUM_CONSTANT(FEATURE_MAX);
 	BIND_ENUM_CONSTANT(FEATURE_MAX);
 
 
 	BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
 	BIND_ENUM_CONSTANT(BLEND_MODE_MIX);

+ 2 - 1
scene/resources/material.h

@@ -162,8 +162,8 @@ public:
 		TEXTURE_DETAIL_ALBEDO,
 		TEXTURE_DETAIL_ALBEDO,
 		TEXTURE_DETAIL_NORMAL,
 		TEXTURE_DETAIL_NORMAL,
 		TEXTURE_ORM,
 		TEXTURE_ORM,
+		TEXTURE_BENT_NORMAL,
 		TEXTURE_MAX
 		TEXTURE_MAX
-
 	};
 	};
 
 
 	enum TextureFilter {
 	enum TextureFilter {
@@ -218,6 +218,7 @@ public:
 		FEATURE_BACKLIGHT,
 		FEATURE_BACKLIGHT,
 		FEATURE_REFRACTION,
 		FEATURE_REFRACTION,
 		FEATURE_DETAIL,
 		FEATURE_DETAIL,
+		FEATURE_BENT_NORMAL_MAPPING,
 		FEATURE_MAX
 		FEATURE_MAX
 	};
 	};
 
 

+ 2 - 0
scene/resources/visual_shader.cpp

@@ -3977,6 +3977,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
 
 
+	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Bent Normal Map", "BENT_NORMAL_MAP" },
+
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" },
 	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" },

+ 6 - 0
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp

@@ -67,6 +67,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 	uses_normal = false;
 	uses_normal = false;
 	uses_tangent = false;
 	uses_tangent = false;
 	uses_normal_map = false;
 	uses_normal_map = false;
+	uses_bent_normal_map = false;
 	wireframe = false;
 	wireframe = false;
 
 
 	unshaded = false;
 	unshaded = false;
@@ -125,6 +126,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
+	actions.usage_flag_pointers["BENT_NORMAL_MAP"] = &uses_bent_normal_map;
 
 
 	actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
 	actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
@@ -169,7 +171,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
 	uses_vertex_time = gen_code.uses_vertex_time;
 	uses_vertex_time = gen_code.uses_vertex_time;
 	uses_fragment_time = gen_code.uses_fragment_time;
 	uses_fragment_time = gen_code.uses_fragment_time;
 	uses_normal |= uses_normal_map;
 	uses_normal |= uses_normal_map;
+	uses_normal |= uses_bent_normal_map;
 	uses_tangent |= uses_normal_map;
 	uses_tangent |= uses_normal_map;
+	uses_tangent |= uses_bent_normal_map;
 
 
 #if 0
 #if 0
 	print_line("**compiling shader:");
 	print_line("**compiling shader:");
@@ -630,6 +634,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
+		actions.renames["BENT_NORMAL_MAP"] = "bent_normal_map";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
@@ -707,6 +712,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
+		actions.usage_defines["BENT_NORMAL_MAP"] = "#define BENT_NORMAL_MAP_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";

+ 1 - 0
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h

@@ -228,6 +228,7 @@ public:
 		bool uses_tangent = false;
 		bool uses_tangent = false;
 		bool uses_particle_trails = false;
 		bool uses_particle_trails = false;
 		bool uses_normal_map = false;
 		bool uses_normal_map = false;
+		bool uses_bent_normal_map = false;
 		bool wireframe = false;
 		bool wireframe = false;
 
 
 		bool unshaded = false;
 		bool unshaded = false;

+ 6 - 0
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp

@@ -69,6 +69,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
 	uses_normal = false;
 	uses_normal = false;
 	uses_tangent = false;
 	uses_tangent = false;
 	uses_normal_map = false;
 	uses_normal_map = false;
+	uses_bent_normal_map = false;
 	wireframe = false;
 	wireframe = false;
 
 
 	unshaded = false;
 	unshaded = false;
@@ -126,6 +127,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL"] = &uses_normal;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
 	actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
+	actions.usage_flag_pointers["BENT_NORMAL_MAP"] = &uses_bent_normal_map;
 
 
 	actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
 	actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
 	actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
 	actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
@@ -165,7 +167,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
 	uses_depth_texture = gen_code.uses_depth_texture;
 	uses_depth_texture = gen_code.uses_depth_texture;
 	uses_normal_texture = gen_code.uses_normal_roughness_texture;
 	uses_normal_texture = gen_code.uses_normal_roughness_texture;
 	uses_normal |= uses_normal_map;
 	uses_normal |= uses_normal_map;
+	uses_normal |= uses_bent_normal_map;
 	uses_tangent |= uses_normal_map;
 	uses_tangent |= uses_normal_map;
+	uses_tangent |= uses_bent_normal_map;
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	if (uses_sss) {
 	if (uses_sss) {
@@ -563,6 +567,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["FRONT_FACING"] = "gl_FrontFacing";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP"] = "normal_map";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
 		actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
+		actions.renames["BENT_NORMAL_MAP"] = "bent_normal_map";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALBEDO"] = "albedo";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["ALPHA"] = "alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
 		actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
@@ -640,6 +645,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
 		actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
+		actions.usage_defines["BENT_NORMAL_MAP"] = "#define BENT_NORMAL_MAP_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
 		actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";

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

@@ -217,6 +217,7 @@ public:
 		bool uses_tangent = false;
 		bool uses_tangent = false;
 		bool uses_particle_trails = false;
 		bool uses_particle_trails = false;
 		bool uses_normal_map = false;
 		bool uses_normal_map = false;
+		bool uses_bent_normal_map = false;
 		bool wireframe = false;
 		bool wireframe = false;
 
 
 		bool unshaded = false;
 		bool unshaded = false;

+ 97 - 43
servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl

@@ -1201,6 +1201,11 @@ void fragment_shader(in SceneData scene_data) {
 	vec3 normal_map = vec3(0.5);
 	vec3 normal_map = vec3(0.5);
 #endif
 #endif
 
 
+#if defined(BENT_NORMAL_MAP_USED)
+	vec3 bent_normal_vector = vec3(0.5);
+	vec3 bent_normal_map = vec3(0.5);
+#endif
+
 	float normal_map_depth = 1.0;
 	float normal_map_depth = 1.0;
 
 
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
@@ -1331,6 +1336,13 @@ void fragment_shader(in SceneData scene_data) {
 	normal = geo_normal;
 	normal = geo_normal;
 #endif // NORMAL_MAP_USED
 #endif // NORMAL_MAP_USED
 
 
+#ifdef BENT_NORMAL_MAP_USED
+	bent_normal_map.xy = bent_normal_map.xy * 2.0 - 1.0;
+	bent_normal_map.z = sqrt(max(0.0, 1.0 - dot(bent_normal_map.xy, bent_normal_map.xy)));
+
+	bent_normal_vector = normalize(tangent * bent_normal_map.x + binormal * bent_normal_map.y + normal * bent_normal_map.z);
+#endif
+
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
 
 
 	if (anisotropy > 0.01) {
 	if (anisotropy > 0.01) {
@@ -1543,7 +1555,8 @@ void fragment_shader(in SceneData scene_data) {
 #endif
 #endif
 	//apply energy conservation
 	//apply energy conservation
 
 
-	vec3 specular_light = vec3(0.0, 0.0, 0.0);
+	vec3 direct_specular_light = vec3(0.0, 0.0, 0.0);
+	vec3 indirect_specular_light = vec3(0.0, 0.0, 0.0);
 	vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
 	vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
 	vec3 ambient_light = vec3(0.0, 0.0, 0.0);
 	vec3 ambient_light = vec3(0.0, 0.0, 0.0);
 #ifndef MODE_UNSHADED
 #ifndef MODE_UNSHADED
@@ -1554,6 +1567,12 @@ void fragment_shader(in SceneData scene_data) {
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 
 #ifndef AMBIENT_LIGHT_DISABLED
 #ifndef AMBIENT_LIGHT_DISABLED
+// Use bent normal for indirect lighting where possible
+#ifdef BENT_NORMAL_MAP_USED
+	vec3 indirect_normal = bent_normal_vector;
+#else
+	vec3 indirect_normal = normal;
+#endif
 
 
 	if (scene_data.use_reflection_cubemap) {
 	if (scene_data.use_reflection_cubemap) {
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
@@ -1571,25 +1590,26 @@ void fragment_shader(in SceneData scene_data) {
 
 
 		float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
 		float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
+
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 
 
 		float lod, blend;
 		float lod, blend;
 
 
 		blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
 		blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
-		specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
-		specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
+		indirect_specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+		indirect_specular_light = mix(indirect_specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
 
 
 #else
 #else
-		specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
+		indirect_specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
 
 
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
-		specular_light *= scene_data.IBL_exposure_normalization;
-		specular_light *= horizon * horizon;
-		specular_light *= scene_data.ambient_light_color_energy.a;
+		indirect_specular_light *= scene_data.IBL_exposure_normalization;
+		indirect_specular_light *= horizon * horizon;
+		indirect_specular_light *= scene_data.ambient_light_color_energy.a;
 	}
 	}
 
 
 #if defined(CUSTOM_RADIANCE_USED)
 #if defined(CUSTOM_RADIANCE_USED)
-	specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
+	indirect_specular_light = mix(indirect_specular_light, custom_radiance.rgb, custom_radiance.a);
 #endif
 #endif
 
 
 #ifndef USE_LIGHTMAP
 #ifndef USE_LIGHTMAP
@@ -1598,7 +1618,7 @@ void fragment_shader(in SceneData scene_data) {
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
 
 
 		if (scene_data.use_ambient_cubemap) {
 		if (scene_data.use_ambient_cubemap) {
-			vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
+			vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 			vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
 			vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
 #else
 #else
@@ -1623,9 +1643,9 @@ void fragment_shader(in SceneData scene_data) {
 		float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV));
 		float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV));
 		float attenuation = 1.0 - Fc;
 		float attenuation = 1.0 - Fc;
 		ambient_light *= attenuation;
 		ambient_light *= attenuation;
-		specular_light *= attenuation;
+		indirect_specular_light *= attenuation;
 
 
-		float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
+		float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
 		float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
@@ -1639,7 +1659,7 @@ void fragment_shader(in SceneData scene_data) {
 		vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
 		vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
 
 
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
-		specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
+		indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
 	}
 	}
 #endif // LIGHT_CLEARCOAT_USED
 #endif // LIGHT_CLEARCOAT_USED
 #endif // !AMBIENT_LIGHT_DISABLED
 #endif // !AMBIENT_LIGHT_DISABLED
@@ -1657,7 +1677,7 @@ void fragment_shader(in SceneData scene_data) {
 		uint index = instances.data[instance_index].gi_offset;
 		uint index = instances.data[instance_index].gi_offset;
 
 
 		// The world normal.
 		// The world normal.
-		vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal;
+		vec3 wnormal = mat3(scene_data.inv_view_matrix) * indirect_normal;
 
 
 		// The SH coefficients used for evaluating diffuse data from SH probes.
 		// The SH coefficients used for evaluating diffuse data from SH probes.
 		const float c[5] = float[](
 		const float c[5] = float[](
@@ -1706,7 +1726,7 @@ void fragment_shader(in SceneData scene_data) {
 				lm_light_l1p1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0;
 				lm_light_l1p1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0;
 			}
 			}
 
 
-			vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal);
+			vec3 n = normalize(lightmaps.data[ofs].normal_xform * indirect_normal);
 			float en = lightmaps.data[ofs].exposure_normalization;
 			float en = lightmaps.data[ofs].exposure_normalization;
 
 
 			ambient_light += lm_light_l0 * en;
 			ambient_light += lm_light_l0 * en;
@@ -1728,8 +1748,8 @@ void fragment_shader(in SceneData scene_data) {
 
 
 		//make vertex orientation the world one, but still align to camera
 		//make vertex orientation the world one, but still align to camera
 		vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
 		vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
-		vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normal;
-		vec3 cam_reflection = mat3(scene_data.inv_view_matrix) * reflect(-view, normal);
+		vec3 cam_normal = mat3(scene_data.inv_view_matrix) * indirect_normal;
+		vec3 cam_reflection = mat3(scene_data.inv_view_matrix) * reflect(-view, indirect_normal);
 
 
 		//apply y-mult
 		//apply y-mult
 		cam_pos.y *= sdfgi.y_mult;
 		cam_pos.y *= sdfgi.y_mult;
@@ -1775,7 +1795,7 @@ void fragment_shader(in SceneData scene_data) {
 				if (cascade == sdfgi.max_cascades - 1) {
 				if (cascade == sdfgi.max_cascades - 1) {
 					diffuse = mix(diffuse, ambient_light, blend);
 					diffuse = mix(diffuse, ambient_light, blend);
 					if (use_specular) {
 					if (use_specular) {
-						specular = mix(specular, specular_light, blend);
+						indirect_specular_light = mix(specular, indirect_specular_light, blend);
 					}
 					}
 				} else {
 				} else {
 					vec3 diffuse2, specular2;
 					vec3 diffuse2, specular2;
@@ -1791,7 +1811,7 @@ void fragment_shader(in SceneData scene_data) {
 
 
 			ambient_light = diffuse;
 			ambient_light = diffuse;
 			if (use_specular) {
 			if (use_specular) {
-				specular_light = specular;
+				indirect_specular_light = specular;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1800,8 +1820,8 @@ void fragment_shader(in SceneData scene_data) {
 		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
 		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
 		// Make vertex orientation the world one, but still align to camera.
 		// Make vertex orientation the world one, but still align to camera.
 		vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
 		vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
-		vec3 cam_normal = mat3(scene_data.inv_view_matrix) * normal;
-		vec3 ref_vec = mat3(scene_data.inv_view_matrix) * normalize(reflect(-view, normal));
+		vec3 cam_normal = mat3(scene_data.inv_view_matrix) * indirect_normal;
+		vec3 ref_vec = mat3(scene_data.inv_view_matrix) * normalize(reflect(-view, indirect_normal));
 
 
 		//find arbitrary tangent and bitangent, then build a matrix
 		//find arbitrary tangent and bitangent, then build a matrix
 		vec3 v0 = abs(cam_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
 		vec3 v0 = abs(cam_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
@@ -1811,12 +1831,12 @@ void fragment_shader(in SceneData scene_data) {
 
 
 		vec4 amb_accum = vec4(0.0);
 		vec4 amb_accum = vec4(0.0);
 		vec4 spec_accum = vec4(0.0);
 		vec4 spec_accum = vec4(0.0);
-		voxel_gi_compute(index1, cam_pos, cam_normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
+		voxel_gi_compute(index1, cam_pos, cam_normal, ref_vec, normal_mat, roughness * roughness, ambient_light, indirect_specular_light, spec_accum, amb_accum);
 
 
 		uint index2 = instances.data[instance_index].gi_offset >> 16;
 		uint index2 = instances.data[instance_index].gi_offset >> 16;
 
 
 		if (index2 != 0xFFFF) {
 		if (index2 != 0xFFFF) {
-			voxel_gi_compute(index2, cam_pos, cam_normal, ref_vec, normal_mat, roughness * roughness, ambient_light, specular_light, spec_accum, amb_accum);
+			voxel_gi_compute(index2, cam_pos, cam_normal, ref_vec, normal_mat, roughness * roughness, ambient_light, indirect_specular_light, spec_accum, amb_accum);
 		}
 		}
 
 
 		if (amb_accum.a > 0.0) {
 		if (amb_accum.a > 0.0) {
@@ -1827,7 +1847,7 @@ void fragment_shader(in SceneData scene_data) {
 			spec_accum.rgb /= spec_accum.a;
 			spec_accum.rgb /= spec_accum.a;
 		}
 		}
 
 
-		specular_light = spec_accum.rgb;
+		indirect_specular_light = spec_accum.rgb;
 		ambient_light = amb_accum.rgb;
 		ambient_light = amb_accum.rgb;
 	}
 	}
 
 
@@ -1839,18 +1859,18 @@ void fragment_shader(in SceneData scene_data) {
 			vec2 base_coord = screen_uv;
 			vec2 base_coord = screen_uv;
 			vec2 closest_coord = base_coord;
 			vec2 closest_coord = base_coord;
 #ifdef USE_MULTIVIEW
 #ifdef USE_MULTIVIEW
-			float closest_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0));
+			float closest_ang = dot(indirect_normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0));
 #else // USE_MULTIVIEW
 #else // USE_MULTIVIEW
-			float closest_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0));
+			float closest_ang = dot(indirect_normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0));
 #endif // USE_MULTIVIEW
 #endif // USE_MULTIVIEW
 
 
 			for (int i = 0; i < 4; i++) {
 			for (int i = 0; i < 4; i++) {
 				const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1));
 				const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1));
 				vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size;
 				vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size;
 #ifdef USE_MULTIVIEW
 #ifdef USE_MULTIVIEW
-				float neighbour_ang = dot(normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0));
+				float neighbour_ang = dot(indirect_normal, normalize(textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0));
 #else // USE_MULTIVIEW
 #else // USE_MULTIVIEW
-				float neighbour_ang = dot(normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0));
+				float neighbour_ang = dot(indirect_normal, normalize(textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0));
 #endif // USE_MULTIVIEW
 #endif // USE_MULTIVIEW
 				if (neighbour_ang > closest_ang) {
 				if (neighbour_ang > closest_ang) {
 					closest_ang = neighbour_ang;
 					closest_ang = neighbour_ang;
@@ -1873,7 +1893,7 @@ void fragment_shader(in SceneData scene_data) {
 #endif // USE_MULTIVIEW
 #endif // USE_MULTIVIEW
 
 
 		ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
 		ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
-		specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a);
+		indirect_specular_light = mix(indirect_specular_light, buffer_reflection.rgb, buffer_reflection.a);
 	}
 	}
 #endif // !USE_LIGHTMAP
 #endif // !USE_LIGHTMAP
 
 
@@ -1946,7 +1966,7 @@ void fragment_shader(in SceneData scene_data) {
 					break;
 					break;
 				}
 				}
 
 
-				reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
+				reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum);
 			}
 			}
 		}
 		}
 
 
@@ -1955,11 +1975,11 @@ void fragment_shader(in SceneData scene_data) {
 		}
 		}
 
 
 		if (reflection_accum.a < 1.0) {
 		if (reflection_accum.a < 1.0) {
-			reflection_accum.rgb = specular_light * (1.0 - reflection_accum.a) + reflection_accum.rgb;
+			reflection_accum.rgb = indirect_specular_light * (1.0 - reflection_accum.a) + reflection_accum.rgb;
 		}
 		}
 
 
 		if (reflection_accum.a > 0.0) {
 		if (reflection_accum.a > 0.0) {
-			specular_light = reflection_accum.rgb;
+			indirect_specular_light = reflection_accum.rgb;
 		}
 		}
 
 
 #if !defined(USE_LIGHTMAP)
 #if !defined(USE_LIGHTMAP)
@@ -1973,6 +1993,38 @@ void fragment_shader(in SceneData scene_data) {
 	{
 	{
 		ambient_light *= ao;
 		ambient_light *= ao;
 #ifndef SPECULAR_OCCLUSION_DISABLED
 #ifndef SPECULAR_OCCLUSION_DISABLED
+#ifdef BENT_NORMAL_MAP_USED
+		// Apply cone to cone intersection with cosine weighted assumption:
+		// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf
+		float cos_a_v = sqrt(1.0 - ao);
+		float limited_roughness = max(roughness, 0.01); // Avoid artifacts at really low roughness.
+		float cos_a_s = exp2((-log(10.0) / log(2.0)) * limited_roughness * limited_roughness);
+		float cos_b = dot(bent_normal_vector, reflect(-view, normal));
+
+		// Intersection between the spherical caps of the visibility and specular cone.
+		// Based on Christopher Oat and Pedro V. Sander's "Ambient aperture lighting":
+		// https://advances.realtimerendering.com/s2006/Chapter8-Ambient_Aperture_Lighting.pdf
+		float r1 = acos(cos_a_v);
+		float r2 = acos(cos_a_s);
+		float d = acos(cos_b);
+		float area = 0.0;
+
+		if (d <= max(r1, r2) - min(r1, r2)) {
+			// One cap is enclosed in the other.
+			area = M_TAU - M_TAU * max(cos_a_v, cos_a_s);
+		} else if (d >= r1 + r2) {
+			// No intersection.
+			area = 0.0;
+		} else {
+			float delta = abs(r1 - r2);
+			float x = 1.0 - clamp((d - delta) / (r1 + r2 - delta), 0.0, 1.0);
+			area = smoothstep(0.0, 1.0, x);
+			area *= M_TAU - M_TAU * max(cos_a_v, cos_a_s);
+		}
+
+		float specular_occlusion = area / (M_TAU * (1.0 - cos_a_s));
+		indirect_specular_light *= specular_occlusion;
+#else // BENT_NORMAL_MAP_USED
 		float specular_occlusion = (ambient_light.r * 0.3 + ambient_light.g * 0.59 + ambient_light.b * 0.11) * 2.0; // Luminance of ambient light.
 		float specular_occlusion = (ambient_light.r * 0.3 + ambient_light.g * 0.59 + ambient_light.b * 0.11) * 2.0; // Luminance of ambient light.
 		specular_occlusion = min(specular_occlusion * 4.0, 1.0); // This multiplication preserves speculars on bright areas.
 		specular_occlusion = min(specular_occlusion * 4.0, 1.0); // This multiplication preserves speculars on bright areas.
 
 
@@ -1980,7 +2032,8 @@ void fragment_shader(in SceneData scene_data) {
 		// 10.0 is a magic number, it gives the intended effect in most scenarios.
 		// 10.0 is a magic number, it gives the intended effect in most scenarios.
 		// Low enough for occlusion, high enough for reaction to lights and shadows.
 		// Low enough for occlusion, high enough for reaction to lights and shadows.
 		specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion);
 		specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion);
-		specular_light *= specular_occlusion;
+		indirect_specular_light *= specular_occlusion;
+#endif // BENT_NORMAL_MAP_USED
 #endif // SPECULAR_OCCLUSION_DISABLED
 #endif // SPECULAR_OCCLUSION_DISABLED
 		ambient_light *= albedo.rgb;
 		ambient_light *= albedo.rgb;
 
 
@@ -1995,6 +2048,7 @@ void fragment_shader(in SceneData scene_data) {
 		}
 		}
 	}
 	}
 #endif // AMBIENT_LIGHT_DISABLED
 #endif // AMBIENT_LIGHT_DISABLED
+
 	// convert ao to direct light ao
 	// convert ao to direct light ao
 	ao = mix(1.0, ao, ao_light_affect);
 	ao = mix(1.0, ao, ao_light_affect);
 
 
@@ -2004,7 +2058,7 @@ void fragment_shader(in SceneData scene_data) {
 	{
 	{
 #if defined(DIFFUSE_TOON)
 #if defined(DIFFUSE_TOON)
 		//simplify for toon, as
 		//simplify for toon, as
-		specular_light *= specular * metallic * albedo * 2.0;
+		indirect_specular_light *= specular * metallic * albedo * 2.0;
 #else
 #else
 		// Base Layer
 		// Base Layer
 		float NdotV = clamp(dot(normal, view), 0.0001, 1.0);
 		float NdotV = clamp(dot(normal, view), 0.0001, 1.0);
@@ -2014,7 +2068,7 @@ void fragment_shader(in SceneData scene_data) {
 
 
 		// cheap luminance approximation
 		// cheap luminance approximation
 		float f90 = clamp(50.0 * f0.g, metallic, 1.0);
 		float f90 = clamp(50.0 * f0.g, metallic, 1.0);
-		specular_light *= energy_compensation * (f90 * envBRDF.x + f0 * envBRDF.y);
+		indirect_specular_light *= energy_compensation * (f90 * envBRDF.x + f0 * envBRDF.y);
 #endif
 #endif
 	}
 	}
 
 
@@ -2031,7 +2085,7 @@ void fragment_shader(in SceneData scene_data) {
 
 
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 	diffuse_light += diffuse_light_interp.rgb;
 	diffuse_light += diffuse_light_interp.rgb;
-	specular_light += specular_light_interp.rgb * f0;
+	direct_specular_light += specular_light_interp.rgb * f0;
 #endif
 #endif
 
 
 	{ // Directional light.
 	{ // Directional light.
@@ -2284,7 +2338,7 @@ void fragment_shader(in SceneData scene_data) {
 
 
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 					diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
 					diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
-					specular_light *= mix(1.0, shadow, specular_light_interp.a);
+					direct_specular_light *= mix(1.0, shadow, specular_light_interp.a);
 #endif
 #endif
 
 
 #undef BIAS_FUNC
 #undef BIAS_FUNC
@@ -2302,7 +2356,7 @@ void fragment_shader(in SceneData scene_data) {
 
 
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 			diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a);
 			diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a);
-			specular_light *= mix(1.0, shadowmask, specular_light_interp.a);
+			direct_specular_light *= mix(1.0, shadowmask, specular_light_interp.a);
 #endif
 #endif
 
 
 			shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0));
 			shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0));
@@ -2434,7 +2488,7 @@ void fragment_shader(in SceneData scene_data) {
 					tangent, anisotropy,
 					tangent, anisotropy,
 #endif
 #endif
 					diffuse_light,
 					diffuse_light,
-					specular_light);
+					direct_specular_light);
 		}
 		}
 #endif // USE_VERTEX_LIGHTING
 #endif // USE_VERTEX_LIGHTING
 	}
 	}
@@ -2502,7 +2556,7 @@ void fragment_shader(in SceneData scene_data) {
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
 						tangent, binormal, anisotropy,
 						tangent, binormal, anisotropy,
 #endif
 #endif
-						diffuse_light, specular_light);
+						diffuse_light, direct_specular_light);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2571,7 +2625,7 @@ void fragment_shader(in SceneData scene_data) {
 						tangent,
 						tangent,
 						binormal, anisotropy,
 						binormal, anisotropy,
 #endif
 #endif
-						diffuse_light, specular_light);
+						diffuse_light, direct_specular_light);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2747,8 +2801,8 @@ void fragment_shader(in SceneData scene_data) {
 
 
 	// apply direct light AO
 	// apply direct light AO
 	ao = unpackUnorm4x8(orms).x;
 	ao = unpackUnorm4x8(orms).x;
-	specular_light *= ao;
 	diffuse_light *= ao;
 	diffuse_light *= ao;
+	direct_specular_light *= ao;
 
 
 	// apply metallic
 	// apply metallic
 	metallic = unpackUnorm4x8(orms).z;
 	metallic = unpackUnorm4x8(orms).z;
@@ -2772,7 +2826,7 @@ void fragment_shader(in SceneData scene_data) {
 	sss_strength = -sss_strength;
 	sss_strength = -sss_strength;
 #endif
 #endif
 	diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
 	diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
-	specular_buffer = vec4(specular_light, metallic);
+	specular_buffer = vec4(direct_specular_light + indirect_specular_light, metallic);
 #endif
 #endif
 
 
 #ifndef FOG_DISABLED
 #ifndef FOG_DISABLED
@@ -2787,7 +2841,7 @@ void fragment_shader(in SceneData scene_data) {
 #ifdef MODE_UNSHADED
 #ifdef MODE_UNSHADED
 	frag_color = vec4(albedo, alpha);
 	frag_color = vec4(albedo, alpha);
 #else
 #else
-	frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha);
+	frag_color = vec4(emission + ambient_light + diffuse_light + direct_specular_light + indirect_specular_light, alpha);
 //frag_color = vec4(1.0);
 //frag_color = vec4(1.0);
 #endif //USE_NO_SHADING
 #endif //USE_NO_SHADING
 
 

+ 3 - 2
servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl

@@ -1,4 +1,5 @@
 #define M_PI 3.14159265359
 #define M_PI 3.14159265359
+#define M_TAU 6.28318530718
 #define ROUGHNESS_MAX_LOD 5
 #define ROUGHNESS_MAX_LOD 5
 
 
 #define MAX_VOXEL_GI_INSTANCES 8
 #define MAX_VOXEL_GI_INSTANCES 8
@@ -22,13 +23,13 @@
 #include "../decal_data_inc.glsl"
 #include "../decal_data_inc.glsl"
 #include "../scene_data_inc.glsl"
 #include "../scene_data_inc.glsl"
 
 
-#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(MODE_RENDER_SDF) || defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_VOXEL_GI) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(MODE_RENDER_SDF) || defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_VOXEL_GI) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 #ifndef NORMAL_USED
 #ifndef NORMAL_USED
 #define NORMAL_USED
 #define NORMAL_USED
 #endif
 #endif
 #endif
 #endif
 
 
-#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
+#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
 #define TANGENT_USED
 #define TANGENT_USED
 #endif
 #endif
 
 

+ 69 - 43
servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl

@@ -97,7 +97,7 @@ layout(location = 3) mediump out vec2 uv_interp;
 layout(location = 4) mediump out vec2 uv2_interp;
 layout(location = 4) mediump out vec2 uv2_interp;
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 layout(location = 5) mediump out vec3 tangent_interp;
 layout(location = 5) mediump out vec3 tangent_interp;
 layout(location = 6) mediump out vec3 binormal_interp;
 layout(location = 6) mediump out vec3 binormal_interp;
 #endif
 #endif
@@ -305,7 +305,7 @@ void main() {
 	vec3 normal = oct_to_vec3(axis_tangent_attrib.xy * 2.0 - 1.0);
 	vec3 normal = oct_to_vec3(axis_tangent_attrib.xy * 2.0 - 1.0);
 #endif
 #endif
 
 
-#if defined(NORMAL_USED) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(NORMAL_USED) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 
 
 	vec3 binormal;
 	vec3 binormal;
 	float binormal_sign;
 	float binormal_sign;
@@ -369,7 +369,7 @@ void main() {
 	normal = model_normal_matrix * normal;
 	normal = model_normal_matrix * normal;
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 
 
 	tangent = model_normal_matrix * tangent;
 	tangent = model_normal_matrix * tangent;
 	binormal = model_normal_matrix * binormal;
 	binormal = model_normal_matrix * binormal;
@@ -411,7 +411,7 @@ void main() {
 	normal = modelview_normal * normal;
 	normal = modelview_normal * normal;
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 
 
 	binormal = modelview_normal * binormal;
 	binormal = modelview_normal * binormal;
 	tangent = modelview_normal * tangent;
 	tangent = modelview_normal * tangent;
@@ -426,7 +426,7 @@ void main() {
 	normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz;
 	normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz;
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 	binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz;
 	binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz;
 	tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz;
 	tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz;
 #endif
 #endif
@@ -440,7 +440,7 @@ void main() {
 	normal_interp = normalize(normal);
 	normal_interp = normalize(normal);
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) || defined(BENT_NORMAL_MAP_USED)
 	tangent_interp = normalize(tangent);
 	tangent_interp = normalize(tangent);
 	binormal_interp = normalize(binormal);
 	binormal_interp = normalize(binormal);
 #endif
 #endif
@@ -603,7 +603,7 @@ layout(location = 3) mediump in vec2 uv_interp;
 layout(location = 4) mediump in vec2 uv2_interp;
 layout(location = 4) mediump in vec2 uv2_interp;
 #endif
 #endif
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 layout(location = 5) mediump in vec3 tangent_interp;
 layout(location = 5) mediump in vec3 tangent_interp;
 layout(location = 6) mediump in vec3 binormal_interp;
 layout(location = 6) mediump in vec3 binormal_interp;
 #endif
 #endif
@@ -891,10 +891,10 @@ void main() {
 
 
 	float alpha = 1.0;
 	float alpha = 1.0;
 
 
-#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) || defined(BENT_NORMAL_MAP_USED)
 	vec3 binormal = binormal_interp;
 	vec3 binormal = binormal_interp;
 	vec3 tangent = tangent_interp;
 	vec3 tangent = tangent_interp;
-#else // TANGENT_USED || NORMAL_MAP_USED || LIGHT_ANISOTROPY_USED
+#else // TANGENT_USED || NORMAL_MAP_USED || LIGHT_ANISOTROPY_USED || BENT_NORMAL_MAP_USED
 	vec3 binormal = vec3(0.0);
 	vec3 binormal = vec3(0.0);
 	vec3 tangent = vec3(0.0);
 	vec3 tangent = vec3(0.0);
 #endif
 #endif
@@ -925,6 +925,11 @@ void main() {
 	vec3 normal_map = vec3(0.5);
 	vec3 normal_map = vec3(0.5);
 #endif
 #endif
 
 
+#if defined(BENT_NORMAL_MAP_USED)
+	vec3 bent_normal_vector;
+	vec3 bent_normal_map = vec3(0.5);
+#endif
+
 	float normal_map_depth = 1.0;
 	float normal_map_depth = 1.0;
 
 
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
 	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
@@ -1060,6 +1065,13 @@ void main() {
 	normal = geo_normal;
 	normal = geo_normal;
 #endif // NORMAL_MAP_USED
 #endif // NORMAL_MAP_USED
 
 
+#ifdef BENT_NORMAL_MAP_USED
+	bent_normal_map.xy = bent_normal_map.xy * 2.0 - 1.0;
+	bent_normal_map.z = sqrt(max(0.0, 1.0 - dot(bent_normal_map.xy, bent_normal_map.xy)));
+
+	bent_normal_vector = normalize(tangent * bent_normal_map.x + binormal * bent_normal_map.y + normal * bent_normal_map.z);
+#endif
+
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
 
 
 	if (anisotropy > 0.01) {
 	if (anisotropy > 0.01) {
@@ -1202,7 +1214,8 @@ void main() {
 #endif // NORMAL_USED
 #endif // NORMAL_USED
 	//apply energy conservation
 	//apply energy conservation
 
 
-	vec3 specular_light = vec3(0.0, 0.0, 0.0);
+	vec3 indirect_specular_light = vec3(0.0, 0.0, 0.0);
+	vec3 direct_specular_light = vec3(0.0, 0.0, 0.0);
 	vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
 	vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
 	vec3 ambient_light = vec3(0.0, 0.0, 0.0);
 	vec3 ambient_light = vec3(0.0, 0.0, 0.0);
 
 
@@ -1214,6 +1227,11 @@ void main() {
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 
 #ifndef AMBIENT_LIGHT_DISABLED
 #ifndef AMBIENT_LIGHT_DISABLED
+#ifdef BENT_NORMAL_MAP_USED
+	vec3 indirect_normal = bent_normal_vector;
+#else
+	vec3 indirect_normal = normal;
+#endif
 
 
 	if (sc_scene_use_reflection_cubemap()) {
 	if (sc_scene_use_reflection_cubemap()) {
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
@@ -1221,34 +1239,34 @@ void main() {
 		vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent;
 		vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent;
 		vec3 anisotropic_tangent = cross(anisotropic_direction, view);
 		vec3 anisotropic_tangent = cross(anisotropic_direction, view);
 		vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction);
 		vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction);
-		vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0)));
+		vec3 bent_normal = normalize(mix(indirect_normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0)));
 		vec3 ref_vec = reflect(-view, bent_normal);
 		vec3 ref_vec = reflect(-view, bent_normal);
 		ref_vec = mix(ref_vec, bent_normal, roughness * roughness);
 		ref_vec = mix(ref_vec, bent_normal, roughness * roughness);
 #else
 #else
-		vec3 ref_vec = reflect(-view, normal);
-		ref_vec = mix(ref_vec, normal, roughness * roughness);
+		vec3 ref_vec = reflect(-view, indirect_normal);
+		ref_vec = mix(ref_vec, indirect_normal, roughness * roughness);
 #endif
 #endif
-		float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
+		float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 
 
 		float lod, blend;
 		float lod, blend;
 		blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
 		blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
-		specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
-		specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
+		indirect_specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+		indirect_specular_light = mix(indirect_specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
 
 
 #else // USE_RADIANCE_CUBEMAP_ARRAY
 #else // USE_RADIANCE_CUBEMAP_ARRAY
-		specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
+		indirect_specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
 
 
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
-		specular_light *= sc_luminance_multiplier();
-		specular_light *= scene_data.IBL_exposure_normalization;
-		specular_light *= horizon * horizon;
-		specular_light *= scene_data.ambient_light_color_energy.a;
+		indirect_specular_light *= sc_luminance_multiplier();
+		indirect_specular_light *= scene_data.IBL_exposure_normalization;
+		indirect_specular_light *= horizon * horizon;
+		indirect_specular_light *= scene_data.ambient_light_color_energy.a;
 	}
 	}
 
 
 #if defined(CUSTOM_RADIANCE_USED)
 #if defined(CUSTOM_RADIANCE_USED)
-	specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
+	indirect_specular_light = mix(indirect_specular_light, custom_radiance.rgb, custom_radiance.a);
 #endif // CUSTOM_RADIANCE_USED
 #endif // CUSTOM_RADIANCE_USED
 
 
 #ifndef USE_LIGHTMAP
 #ifndef USE_LIGHTMAP
@@ -1257,7 +1275,7 @@ void main() {
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
 
 
 		if (sc_scene_use_ambient_cubemap()) {
 		if (sc_scene_use_ambient_cubemap()) {
-			vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
+			vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 			vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
 			vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
 #else
 #else
@@ -1283,9 +1301,9 @@ void main() {
 		float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV));
 		float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV));
 		float attenuation = 1.0 - Fc;
 		float attenuation = 1.0 - Fc;
 		ambient_light *= attenuation;
 		ambient_light *= attenuation;
-		specular_light *= attenuation;
+		indirect_specular_light *= attenuation;
 
 
-		float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
+		float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
 		float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
 		float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
@@ -1299,7 +1317,7 @@ void main() {
 		vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
 		vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
 
 
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
-		specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
+		indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
 	}
 	}
 #endif // LIGHT_CLEARCOAT_USED
 #endif // LIGHT_CLEARCOAT_USED
 #endif // !AMBIENT_LIGHT_DISABLED
 #endif // !AMBIENT_LIGHT_DISABLED
@@ -1316,7 +1334,7 @@ void main() {
 		uint index = instances.data[draw_call.instance_index].gi_offset;
 		uint index = instances.data[draw_call.instance_index].gi_offset;
 
 
 		// The world normal.
 		// The world normal.
-		vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal;
+		vec3 wnormal = mat3(scene_data.inv_view_matrix) * indirect_normal;
 
 
 		// The SH coefficients used for evaluating diffuse data from SH probes.
 		// The SH coefficients used for evaluating diffuse data from SH probes.
 		const float c[5] = float[](
 		const float c[5] = float[](
@@ -1365,7 +1383,7 @@ void main() {
 				lm_light_l1p1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0;
 				lm_light_l1p1 = (textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb - vec3(0.5)) * 2.0;
 			}
 			}
 
 
-			vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal);
+			vec3 n = normalize(lightmaps.data[ofs].normal_xform * indirect_normal);
 			float exposure_normalization = lightmaps.data[ofs].exposure_normalization;
 			float exposure_normalization = lightmaps.data[ofs].exposure_normalization;
 
 
 			ambient_light += lm_light_l0 * exposure_normalization;
 			ambient_light += lm_light_l0 * exposure_normalization;
@@ -1412,7 +1430,7 @@ void main() {
 				break;
 				break;
 			}
 			}
 
 
-			reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
+			reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum);
 		}
 		}
 
 
 		if (ambient_accum.a < 1.0) {
 		if (ambient_accum.a < 1.0) {
@@ -1420,11 +1438,11 @@ void main() {
 		}
 		}
 
 
 		if (reflection_accum.a < 1.0) {
 		if (reflection_accum.a < 1.0) {
-			reflection_accum.rgb = specular_light * (1.0 - reflection_accum.a) + reflection_accum.rgb;
+			reflection_accum.rgb = indirect_specular_light * (1.0 - reflection_accum.a) + reflection_accum.rgb;
 		}
 		}
 
 
 		if (reflection_accum.a > 0.0) {
 		if (reflection_accum.a > 0.0) {
-			specular_light = reflection_accum.rgb;
+			indirect_specular_light = reflection_accum.rgb;
 		}
 		}
 
 
 #if !defined(USE_LIGHTMAP)
 #if !defined(USE_LIGHTMAP)
@@ -1437,6 +1455,13 @@ void main() {
 	// finalize ambient light here
 	// finalize ambient light here
 	ambient_light *= ao;
 	ambient_light *= ao;
 #ifndef SPECULAR_OCCLUSION_DISABLED
 #ifndef SPECULAR_OCCLUSION_DISABLED
+#ifdef BENT_NORMAL_MAP_USED
+	// Simplified bent normal occlusion.
+	float cos_b = max(dot(reflect(-view, normal), bent_normal_vector), 0.0);
+	float specular_occlusion = clamp((ao - (1.0 - cos_b)) / roughness, 0.0, 1.0);
+	specular_occlusion = mix(specular_occlusion, cos_b * (1.0 - ao), roughness);
+	indirect_specular_light *= specular_occlusion;
+#else // BENT_NORMAL_MAP_USED
 	float specular_occlusion = (ambient_light.r * 0.3 + ambient_light.g * 0.59 + ambient_light.b * 0.11) * 2.0; // Luminance of ambient light.
 	float specular_occlusion = (ambient_light.r * 0.3 + ambient_light.g * 0.59 + ambient_light.b * 0.11) * 2.0; // Luminance of ambient light.
 	specular_occlusion = min(specular_occlusion * 4.0, 1.0); // This multiplication preserves speculars on bright areas.
 	specular_occlusion = min(specular_occlusion * 4.0, 1.0); // This multiplication preserves speculars on bright areas.
 
 
@@ -1444,7 +1469,8 @@ void main() {
 	// 10.0 is a magic number, it gives the intended effect in most scenarios.
 	// 10.0 is a magic number, it gives the intended effect in most scenarios.
 	// Low enough for occlusion, high enough for reaction to lights and shadows.
 	// Low enough for occlusion, high enough for reaction to lights and shadows.
 	specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion);
 	specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion);
-	specular_light *= specular_occlusion;
+	indirect_specular_light *= specular_occlusion;
+#endif // BENT_NORMAL_MAP_USED
 #endif // USE_SPECULAR_OCCLUSION
 #endif // USE_SPECULAR_OCCLUSION
 	ambient_light *= albedo.rgb;
 	ambient_light *= albedo.rgb;
 
 
@@ -1460,7 +1486,7 @@ void main() {
 	{
 	{
 #if defined(DIFFUSE_TOON)
 #if defined(DIFFUSE_TOON)
 		//simplify for toon, as
 		//simplify for toon, as
-		specular_light *= specular * metallic * albedo * 2.0;
+		indirect_specular_light *= specular * metallic * albedo * 2.0;
 #else
 #else
 
 
 		// scales the specular reflections, needs to be computed before lighting happens,
 		// scales the specular reflections, needs to be computed before lighting happens,
@@ -1474,7 +1500,7 @@ void main() {
 		float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
 		float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
 		vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
 		vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
 
 
-		specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
+		indirect_specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
 #endif
 #endif
 	}
 	}
 
 
@@ -1490,7 +1516,7 @@ void main() {
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 	diffuse_light += diffuse_light_interp.rgb;
 	diffuse_light += diffuse_light_interp.rgb;
-	specular_light += specular_light_interp.rgb * f0;
+	direct_specular_light += specular_light_interp.rgb * f0;
 #endif
 #endif
 
 
 	if (sc_directional_lights() > 0) {
 	if (sc_directional_lights() > 0) {
@@ -1643,7 +1669,7 @@ void main() {
 
 
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 					diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
 					diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
-					specular_light *= mix(1.0, shadow, specular_light_interp.a);
+					direct_specular_light *= mix(1.0, shadow, specular_light_interp.a);
 #endif
 #endif
 #undef BIAS_FUNC
 #undef BIAS_FUNC
 				}
 				}
@@ -1660,7 +1686,7 @@ void main() {
 
 
 #ifdef USE_VERTEX_LIGHTING
 #ifdef USE_VERTEX_LIGHTING
 			diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a);
 			diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a);
-			specular_light *= mix(1.0, shadowmask, specular_light_interp.a);
+			direct_specular_light *= mix(1.0, shadowmask, specular_light_interp.a);
 #endif
 #endif
 
 
 			shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0));
 			shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0));
@@ -1735,7 +1761,7 @@ void main() {
 					binormal, tangent, anisotropy,
 					binormal, tangent, anisotropy,
 #endif
 #endif
 					diffuse_light,
 					diffuse_light,
-					specular_light);
+					direct_specular_light);
 		}
 		}
 #endif // USE_VERTEX_LIGHTING
 #endif // USE_VERTEX_LIGHTING
 	} //directional light
 	} //directional light
@@ -1766,7 +1792,7 @@ void main() {
 				tangent,
 				tangent,
 				binormal, anisotropy,
 				binormal, anisotropy,
 #endif
 #endif
-				diffuse_light, specular_light);
+				diffuse_light, direct_specular_light);
 	}
 	}
 
 
 	uvec2 spot_indices = instances.data[draw_call.instance_index].spot_lights;
 	uvec2 spot_indices = instances.data[draw_call.instance_index].spot_lights;
@@ -1794,7 +1820,7 @@ void main() {
 				tangent,
 				tangent,
 				binormal, anisotropy,
 				binormal, anisotropy,
 #endif
 #endif
-				diffuse_light, specular_light);
+				diffuse_light, direct_specular_light);
 	}
 	}
 #endif // !VERTEX_LIGHTING
 #endif // !VERTEX_LIGHTING
 
 
@@ -1848,8 +1874,8 @@ void main() {
 
 
 	// apply direct light AO
 	// apply direct light AO
 	ao = unpackUnorm4x8(orms).x;
 	ao = unpackUnorm4x8(orms).x;
-	specular_light *= ao;
 	diffuse_light *= ao;
 	diffuse_light *= ao;
+	direct_specular_light *= ao;
 
 
 	// apply metallic
 	// apply metallic
 	metallic = unpackUnorm4x8(orms).z;
 	metallic = unpackUnorm4x8(orms).z;
@@ -1873,7 +1899,7 @@ void main() {
 	sss_strength = -sss_strength;
 	sss_strength = -sss_strength;
 #endif // SSS_MODE_SKIN
 #endif // SSS_MODE_SKIN
 	diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
 	diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
-	specular_buffer = vec4(specular_light, metallic);
+	specular_buffer = vec4(direct_specular_light + indirect_specular_light, metallic);
 #endif // MODE_UNSHADED
 #endif // MODE_UNSHADED
 
 
 #ifndef FOG_DISABLED
 #ifndef FOG_DISABLED
@@ -1886,7 +1912,7 @@ void main() {
 #ifdef MODE_UNSHADED
 #ifdef MODE_UNSHADED
 	frag_color = vec4(albedo, alpha);
 	frag_color = vec4(albedo, alpha);
 #else // MODE_UNSHADED
 #else // MODE_UNSHADED
-	frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha);
+	frag_color = vec4(emission + ambient_light + diffuse_light + direct_specular_light + indirect_specular_light, alpha);
 #endif // MODE_UNSHADED
 #endif // MODE_UNSHADED
 
 
 #ifndef FOG_DISABLED
 #ifndef FOG_DISABLED

+ 1 - 1
servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl

@@ -8,7 +8,7 @@
 #include "../decal_data_inc.glsl"
 #include "../decal_data_inc.glsl"
 #include "../scene_data_inc.glsl"
 #include "../scene_data_inc.glsl"
 
 
-#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
+#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
 #ifndef NORMAL_USED
 #ifndef NORMAL_USED
 #define NORMAL_USED
 #define NORMAL_USED
 #endif
 #endif

+ 1 - 0
servers/rendering/shader_types.cpp

@@ -133,6 +133,7 @@ ShaderTypes::ShaderTypes() {
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_MAP"] = ShaderLanguage::TYPE_VEC3;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_MAP"] = ShaderLanguage::TYPE_VEC3;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_MAP_DEPTH"] = ShaderLanguage::TYPE_FLOAT;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_MAP_DEPTH"] = ShaderLanguage::TYPE_FLOAT;
+	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["BENT_NORMAL_MAP"] = ShaderLanguage::TYPE_VEC3;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4);