Browse Source

Implement vertex shading

This adds support in all backends, but the Compatibility renderer works the best.
Mobile and Forward+ can only support one directional light shader (the first in the tree)
While the Compatibility renderer supports any number of shadows.

Co-authored-by: Clay John <[email protected]>
ywmaa 1 year ago
parent
commit
0a9ad8f9de

+ 0 - 5
doc/classes/ProjectSettings.xml

@@ -2863,11 +2863,6 @@
 		</member>
 		</member>
 		<member name="rendering/shading/overrides/force_vertex_shading" type="bool" setter="" getter="" default="false">
 		<member name="rendering/shading/overrides/force_vertex_shading" type="bool" setter="" getter="" default="false">
 			If [code]true[/code], forces vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can be used to optimize performance on low-end mobile devices.
 			If [code]true[/code], forces vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can be used to optimize performance on low-end mobile devices.
-			[b]Note:[/b] This setting currently has no effect, as vertex shading is not implemented yet.
-		</member>
-		<member name="rendering/shading/overrides/force_vertex_shading.mobile" type="bool" setter="" getter="" default="true">
-			Lower-end override for [member rendering/shading/overrides/force_vertex_shading] on mobile devices, due to performance concerns or driver support.
-			[b]Note:[/b] This setting currently has no effect, as vertex shading is not implemented yet.
 		</member>
 		</member>
 		<member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1">
 		<member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1">
 			The default texture filtering mode to use on [CanvasItem]s.
 			The default texture filtering mode to use on [CanvasItem]s.

+ 3 - 0
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -4125,6 +4125,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
 		global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 		global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 		global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
 		global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
 		global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
 		global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
+		if (config->force_vertex_shading) {
+			global_defines += "\n#define USE_VERTEX_LIGHTING\n";
+		}
 		material_storage->shaders.scene_shader.initialize(global_defines);
 		material_storage->shaders.scene_shader.initialize(global_defines);
 		scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
 		scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
 		material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
 		material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);

+ 279 - 10
drivers/gles3/shaders/scene.glsl

@@ -248,6 +248,175 @@ uniform lowp uint directional_shadow_index;
 #endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
 #endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
 #endif // USE_ADDITIVE_LIGHTING
 #endif // USE_ADDITIVE_LIGHTING
 
 
+#ifdef USE_VERTEX_LIGHTING
+
+out vec3 diffuse_light_interp;
+out vec3 specular_light_interp;
+
+#ifdef USE_ADDITIVE_LIGHTING
+out vec3 additive_diffuse_light_interp;
+out vec3 additive_specular_light_interp;
+#endif // USE_ADDITIVE_LIGHTING
+
+// Directional light data.
+#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+struct DirectionalLightData {
+	mediump vec3 direction;
+	mediump float energy;
+	mediump vec3 color;
+	mediump float size;
+	lowp uint unused;
+	lowp uint bake_mode;
+	mediump float shadow_opacity;
+	mediump float specular;
+};
+
+layout(std140) uniform DirectionalLights { // ubo:7
+	DirectionalLightData directional_lights[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
+};
+#endif // !DISABLE_LIGHT_DIRECTIONAL
+
+// Omni and spot light data.
+#if !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+struct LightData { // This structure needs to be as packed as possible.
+	highp vec3 position;
+	highp float inv_radius;
+
+	mediump vec3 direction;
+	highp float size;
+
+	mediump vec3 color;
+	mediump float attenuation;
+
+	mediump float cone_attenuation;
+	mediump float cone_angle;
+	mediump float specular_amount;
+	mediump float shadow_opacity;
+
+	lowp vec3 pad;
+	lowp uint bake_mode;
+};
+
+#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
+layout(std140) uniform OmniLightData { // ubo:5
+	LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
+};
+#ifdef BASE_PASS
+uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
+uniform uint omni_light_count;
+#endif // BASE_PASS
+#endif // DISABLE_LIGHT_OMNI
+
+#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
+layout(std140) uniform SpotLightData { // ubo:6
+	LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
+};
+#ifdef BASE_PASS
+uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
+uniform uint spot_light_count;
+#endif // BASE_PASS
+#endif // DISABLE_LIGHT_SPOT
+#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+#ifdef USE_ADDITIVE_LIGHTING
+#ifdef ADDITIVE_OMNI
+uniform lowp uint omni_light_index;
+#endif
+#ifdef ADDITIVE_SPOT
+uniform lowp uint spot_light_index;
+#endif
+#endif // USE_ADDITIVE_LIGHTING
+
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
+// Eyeballed approximation of `exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25`.
+// Uses slightly more FMA instructions (2x rate) to avoid special instructions (0.25x rate).
+// Range is reduced to [0.64,4977] from [068,2,221,528] which makes mediump feasible for the rest of the shader.
+mediump float roughness_to_shininess(mediump float roughness) {
+	mediump float r = 1.2 - roughness;
+	mediump float r2 = r * r;
+	return r * r2 * r2 * 2000.0;
+}
+
+void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, bool is_directional, float roughness,
+		inout vec3 diffuse_light, inout vec3 specular_light) {
+	float NdotL = min(dot(N, L), 1.0);
+	float cNdotL = max(NdotL, 0.0); // clamped NdotL
+
+#if defined(DIFFUSE_LAMBERT_WRAP)
+	// Energy conserving lambert wrap shader.
+	// https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+	float diffuse_brdf_NL = max(0.0, (cNdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
+#else
+	// lambert
+	float diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#endif
+
+	diffuse_light += light_color * diffuse_brdf_NL;
+
+#if !defined(SPECULAR_DISABLED)
+	float specular_brdf_NL = 0.0;
+	// Normalized blinn always unless disabled.
+	vec3 H = normalize(V + L);
+	float cNdotH = clamp(dot(N, H), 0.0, 1.0);
+	float shininess = roughness_to_shininess(roughness);
+	float blinn = pow(cNdotH, shininess);
+	blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)) * cNdotL;
+	specular_brdf_NL = blinn;
+	specular_light += specular_brdf_NL * light_color;
+#endif
+}
+
+float get_omni_spot_attenuation(float distance, float inv_range, float decay) {
+	float nd = distance * inv_range;
+	nd *= nd;
+	nd *= nd; // nd^4
+	nd = max(1.0 - nd, 0.0);
+	nd *= nd; // nd^2
+	return nd * pow(max(distance, 0.0001), -decay);
+}
+
+#if !defined(DISABLE_LIGHT_OMNI) || (defined(ADDITIVE_OMNI) && defined(USE_ADDITIVE_LIGHTING))
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+		inout vec3 diffuse_light, inout vec3 specular_light) {
+	vec3 light_rel_vec = omni_lights[idx].position - vertex;
+	float light_length = length(light_rel_vec);
+	float omni_attenuation = get_omni_spot_attenuation(light_length, omni_lights[idx].inv_radius, omni_lights[idx].attenuation);
+	vec3 color = omni_lights[idx].color * omni_attenuation; // No light shaders here, so combine.
+
+	light_compute(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+			diffuse_light,
+			specular_light);
+}
+#endif // !defined(DISABLE_LIGHT_OMNI) || (defined(ADDITIVE_OMNI) && defined(USE_ADDITIVE_LIGHTING))
+
+#if !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+		inout vec3 diffuse_light,
+		inout vec3 specular_light) {
+	vec3 light_rel_vec = spot_lights[idx].position - vertex;
+	float light_length = length(light_rel_vec);
+	float spot_attenuation = get_omni_spot_attenuation(light_length, spot_lights[idx].inv_radius, spot_lights[idx].attenuation);
+	vec3 spot_dir = spot_lights[idx].direction;
+	float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_lights[idx].cone_angle);
+	float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_lights[idx].cone_angle));
+
+	mediump float cone_attenuation = spot_lights[idx].cone_attenuation;
+	spot_attenuation *= 1.0 - pow(spot_rim, cone_attenuation);
+
+	vec3 color = spot_lights[idx].color * spot_attenuation;
+
+	light_compute(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+			diffuse_light, specular_light);
+}
+#endif // !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
+#endif // USE_VERTEX_LIGHTING
+
 #ifdef USE_MULTIVIEW
 #ifdef USE_MULTIVIEW
 layout(std140) uniform MultiviewData { // ubo:8
 layout(std140) uniform MultiviewData { // ubo:8
 	highp mat4 projection_matrix_view[MAX_VIEWS];
 	highp mat4 projection_matrix_view[MAX_VIEWS];
@@ -540,8 +709,65 @@ void main() {
 	gl_Position.z = 0.00001;
 	gl_Position.z = 0.00001;
 	gl_Position.w = 1.0;
 	gl_Position.w = 1.0;
 #endif
 #endif
-}
 
 
+#ifdef USE_VERTEX_LIGHTING
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifdef USE_MULTIVIEW
+	vec3 view = -normalize(vertex_interp - eye_offset);
+#else
+	vec3 view = -normalize(vertex_interp);
+#endif
+	diffuse_light_interp = vec3(0.0);
+	specular_light_interp = vec3(0.0);
+#ifdef BASE_PASS
+#ifndef DISABLE_LIGHT_DIRECTIONAL
+	for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
+		light_compute(normal_interp, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].color * directional_lights[i].energy, true, roughness,
+				diffuse_light_interp.rgb,
+				specular_light_interp.rgb);
+	}
+#endif // !DISABLE_LIGHT_DIRECTIONAL
+
+#ifndef DISABLE_LIGHT_OMNI
+	for (uint i = 0u; i < omni_light_count; i++) {
+		light_process_omni(omni_light_indices[i], vertex_interp, view, normal_interp, roughness,
+				diffuse_light_interp.rgb, specular_light_interp.rgb);
+	}
+#endif // !DISABLE_LIGHT_OMNI
+
+#ifndef DISABLE_LIGHT_SPOT
+	for (uint i = 0u; i < spot_light_count; i++) {
+		light_process_spot(spot_light_indices[i], vertex_interp, view, normal_interp, roughness,
+				diffuse_light_interp.rgb, specular_light_interp.rgb);
+	}
+#endif // !DISABLE_LIGHT_SPOT
+#endif // BASE_PASS
+
+/* ADDITIVE LIGHTING PASS */
+#ifdef USE_ADDITIVE_LIGHTING
+	additive_diffuse_light_interp = vec3(0.0);
+	additive_specular_light_interp = vec3(0.0);
+#if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
+
+	light_compute(normal_interp, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, roughness,
+			additive_diffuse_light_interp.rgb,
+			additive_specular_light_interp.rgb);
+#endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
+
+#ifdef ADDITIVE_OMNI
+	light_process_omni(omni_light_index, vertex_interp, view, normal_interp, roughness,
+			additive_diffuse_light_interp.rgb, additive_specular_light_interp.rgb);
+#endif // ADDITIVE_OMNI
+
+#ifdef ADDITIVE_SPOT
+	light_process_spot(spot_light_index, vertex_interp, view, normal_interp, roughness,
+			additive_diffuse_light_interp.rgb, additive_specular_light_interp.rgb);
+#endif // ADDITIVE_SPOT
+
+#endif // USE_ADDITIVE_LIGHTING
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#endif // USE_VERTEX_LIGHTING
+}
 /* clang-format off */
 /* clang-format off */
 #[fragment]
 #[fragment]
 
 
@@ -758,6 +984,16 @@ multiview_data;
 #define LIGHT_BAKE_DYNAMIC 2u
 #define LIGHT_BAKE_DYNAMIC 2u
 
 
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
+#ifdef USE_VERTEX_LIGHTING
+in vec3 diffuse_light_interp;
+in vec3 specular_light_interp;
+
+#ifdef USE_ADDITIVE_LIGHTING
+in vec3 additive_diffuse_light_interp;
+in vec3 additive_specular_light_interp;
+#endif // USE_ADDITIVE_LIGHTING
+#endif // USE_VERTEX_LIGHTING
+
 // Directional light data.
 // Directional light data.
 #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
 #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
 
 
@@ -809,22 +1045,22 @@ struct LightData { // This structure needs to be as packed as possible.
 layout(std140) uniform OmniLightData { // ubo:5
 layout(std140) uniform OmniLightData { // ubo:5
 	LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
 	LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
 };
 };
-#ifdef BASE_PASS
+#if defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
 uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
 uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
 uniform uint omni_light_count;
 uniform uint omni_light_count;
-#endif // BASE_PASS
-#endif // DISABLE_LIGHT_OMNI
+#endif // defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
+#endif // !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
 
 
 #if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
 #if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
 layout(std140) uniform SpotLightData { // ubo:6
 layout(std140) uniform SpotLightData { // ubo:6
 	LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
 	LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
 };
 };
-#ifdef BASE_PASS
+#if defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
 uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
 uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
 uniform uint spot_light_count;
 uniform uint spot_light_count;
-#endif // BASE_PASS
-#endif // DISABLE_LIGHT_SPOT
-#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
+#endif // defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
+#endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
+#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
 
 
 #ifdef USE_ADDITIVE_LIGHTING
 #ifdef USE_ADDITIVE_LIGHTING
 #ifdef ADDITIVE_OMNI
 #ifdef ADDITIVE_OMNI
@@ -985,6 +1221,8 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
 	return mix(vec3(dielectric), albedo, vec3(metallic));
 	return mix(vec3(dielectric), albedo, vec3(metallic));
 }
 }
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
+
+#ifndef USE_VERTEX_LIGHTING
 #if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(USE_ADDITIVE_LIGHTING)
 #if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(USE_ADDITIVE_LIGHTING)
 
 
 float D_GGX(float cos_theta_m, float alpha) {
 float D_GGX(float cos_theta_m, float alpha) {
@@ -1284,6 +1522,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
 #endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
 #endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
 
 
 #endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
 #endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
+#endif // !USE_VERTEX_LIGHTING
 
 
 vec4 fog_process(vec3 vertex) {
 vec4 fog_process(vec3 vertex) {
 	vec3 fog_color = scene_data.fog_light_color;
 	vec3 fog_color = scene_data.fog_light_color;
@@ -1859,9 +2098,13 @@ void main() {
 		specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
 		specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
 #endif
 #endif
 	}
 	}
-
 #endif // !AMBIENT_LIGHT_DISABLED
 #endif // !AMBIENT_LIGHT_DISABLED
 
 
+#ifdef USE_VERTEX_LIGHTING
+	specular_light += specular_light_interp * f0;
+	diffuse_light += diffuse_light_interp;
+#else
+
 #ifndef DISABLE_LIGHT_DIRECTIONAL
 #ifndef DISABLE_LIGHT_DIRECTIONAL
 	for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
 	for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
 #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
 #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
@@ -1944,6 +2187,7 @@ void main() {
 				diffuse_light, specular_light);
 				diffuse_light, specular_light);
 	}
 	}
 #endif // !DISABLE_LIGHT_SPOT
 #endif // !DISABLE_LIGHT_SPOT
+#endif // !USE_VERTEX_LIGHTING
 #endif // BASE_PASS
 #endif // BASE_PASS
 #endif // !MODE_UNSHADED
 #endif // !MODE_UNSHADED
 
 
@@ -1993,7 +2237,6 @@ void main() {
 #else
 #else
 
 
 	diffuse_light *= albedo;
 	diffuse_light *= albedo;
-
 	diffuse_light *= 1.0 - metallic;
 	diffuse_light *= 1.0 - metallic;
 	ambient_light *= 1.0 - metallic;
 	ambient_light *= 1.0 - metallic;
 
 
@@ -2024,6 +2267,11 @@ void main() {
 	diffuse_light = vec3(0.0);
 	diffuse_light = vec3(0.0);
 	specular_light = vec3(0.0);
 	specular_light = vec3(0.0);
 
 
+#ifdef USE_VERTEX_LIGHTING
+	diffuse_light = additive_diffuse_light_interp;
+	specular_light = additive_specular_light_interp * f0;
+#endif // USE_VERTEX_LIGHTING
+
 #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
 #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
 
 
 #ifndef SHADOWS_DISABLED
 #ifndef SHADOWS_DISABLED
@@ -2137,6 +2385,8 @@ void main() {
 #else
 #else
 	float directional_shadow = 1.0f;
 	float directional_shadow = 1.0f;
 #endif // SHADOWS_DISABLED
 #endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
 	light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha,
 	light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 			backlight,
 			backlight,
@@ -2153,6 +2403,11 @@ void main() {
 #endif
 #endif
 			diffuse_light,
 			diffuse_light,
 			specular_light);
 			specular_light);
+#else
+	// Just apply shadows to vertex lighting.
+	diffuse_light *= directional_shadow;
+	specular_light *= directional_shadow;
+#endif // !USE_VERTEX_LIGHTING
 #endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
 #endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
 
 
 #ifdef ADDITIVE_OMNI
 #ifdef ADDITIVE_OMNI
@@ -2162,6 +2417,8 @@ void main() {
 	omni_shadow = texture(omni_shadow_texture, vec4(light_ray, 1.0 - length(light_ray) * omni_lights[omni_light_index].inv_radius));
 	omni_shadow = texture(omni_shadow_texture, vec4(light_ray, 1.0 - length(light_ray) * omni_lights[omni_light_index].inv_radius));
 	omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
 	omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
 #endif // SHADOWS_DISABLED
 #endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
 	light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
 	light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 			backlight,
 			backlight,
@@ -2177,6 +2434,11 @@ void main() {
 			binormal, tangent, anisotropy,
 			binormal, tangent, anisotropy,
 #endif
 #endif
 			diffuse_light, specular_light);
 			diffuse_light, specular_light);
+#else
+	// Just apply shadows to vertex lighting.
+	diffuse_light *= omni_shadow;
+	specular_light *= omni_shadow;
+#endif // !USE_VERTEX_LIGHTING
 #endif // ADDITIVE_OMNI
 #endif // ADDITIVE_OMNI
 
 
 #ifdef ADDITIVE_SPOT
 #ifdef ADDITIVE_SPOT
@@ -2185,6 +2447,8 @@ void main() {
 	spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
 	spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
 	spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity);
 	spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity);
 #endif // SHADOWS_DISABLED
 #endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
 	light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha,
 	light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 			backlight,
 			backlight,
@@ -2201,6 +2465,11 @@ void main() {
 			binormal, anisotropy,
 			binormal, anisotropy,
 #endif
 #endif
 			diffuse_light, specular_light);
 			diffuse_light, specular_light);
+#else
+	// Just apply shadows to vertex lighting.
+	diffuse_light *= spot_shadow;
+	specular_light *= spot_shadow;
+#endif // !USE_VERTEX_LIGHTING
 
 
 #endif // ADDITIVE_SPOT
 #endif // ADDITIVE_SPOT
 
 

+ 1 - 1
drivers/gles3/storage/config.cpp

@@ -178,7 +178,7 @@ Config::Config() {
 	}
 	}
 #endif
 #endif
 
 
-	force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
+	force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
 	use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
 	use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
 
 
 	use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
 	use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));

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

@@ -1368,6 +1368,10 @@ MaterialStorage::MaterialStorage() {
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+		if (!GLES3::Config::get_singleton()->force_vertex_shading) {
+			// If forcing vertex shading, this will be defined already.
+			actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+		}
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 
 
 		actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
 		actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;

+ 0 - 2
editor/renames_map_3_to_4.cpp

@@ -1354,7 +1354,6 @@ const char *RenamesMap3To4::project_settings_renames[][2] = {
 	{ "rendering/quality/shading/force_lambert_over_burley", "rendering/shading/overrides/force_lambert_over_burley" },
 	{ "rendering/quality/shading/force_lambert_over_burley", "rendering/shading/overrides/force_lambert_over_burley" },
 	{ "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" },
 	{ "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" },
 	{ "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" },
 	{ "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" },
-	{ "rendering/quality/shading/force_vertex_shading.mobile", "rendering/shading/overrides/force_vertex_shading.mobile" },
 	{ "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
 	{ "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
 	{ "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
 	{ "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
 	{ "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },
 	{ "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },
@@ -1400,7 +1399,6 @@ const char *RenamesMap3To4::project_godot_renames[][2] = {
 	{ "quality/shading/force_lambert_over_burley", "shading/overrides/force_lambert_over_burley" },
 	{ "quality/shading/force_lambert_over_burley", "shading/overrides/force_lambert_over_burley" },
 	{ "quality/shading/force_lambert_over_burley.mobile", "shading/overrides/force_lambert_over_burley.mobile" },
 	{ "quality/shading/force_lambert_over_burley.mobile", "shading/overrides/force_lambert_over_burley.mobile" },
 	{ "quality/shading/force_vertex_shading", "shading/overrides/force_vertex_shading" },
 	{ "quality/shading/force_vertex_shading", "shading/overrides/force_vertex_shading" },
-	{ "quality/shading/force_vertex_shading.mobile", "shading/overrides/force_vertex_shading.mobile" },
 	{ "quality/shadow_atlas/quadrant_0_subdiv", "lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
 	{ "quality/shadow_atlas/quadrant_0_subdiv", "lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
 	{ "quality/shadow_atlas/quadrant_1_subdiv", "lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
 	{ "quality/shadow_atlas/quadrant_1_subdiv", "lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
 	{ "quality/shadow_atlas/quadrant_2_subdiv", "lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },
 	{ "quality/shadow_atlas/quadrant_2_subdiv", "lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },

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

@@ -4261,6 +4261,11 @@ RenderForwardClustered::RenderForwardClustered() {
 		defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
 		defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
 		defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 		defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 
 
+		bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+		if (force_vertex_shading) {
+			defines += "\n#define USE_VERTEX_LIGHTING\n";
+		}
+
 		{
 		{
 			//lightmaps
 			//lightmaps
 			scene_state.max_lightmaps = MAX_LIGHTMAPS;
 			scene_state.max_lightmaps = MAX_LIGHTMAPS;

+ 8 - 1
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp

@@ -730,13 +730,20 @@ void SceneShaderForwardClustered::init(const String p_defines) {
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+
+		bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+		if (!force_vertex_shading) {
+			// If forcing vertex shading, this will be defined already.
+			actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+		}
+
 		actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
 		actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 
 
 		actions.base_texture_binding_index = 1;
 		actions.base_texture_binding_index = 1;
 		actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
 		actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
 		actions.base_uniform_string = "material.";
 		actions.base_uniform_string = "material.";
-		actions.base_varying_index = 12;
+		actions.base_varying_index = 14;
 
 
 		actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
 		actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
 		actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
 		actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;

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

@@ -2800,6 +2800,11 @@ RenderForwardMobile::RenderForwardMobile() {
 	// defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
 	// defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
 	defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 	defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 
 
+	bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+	if (force_vertex_shading) {
+		defines += "\n#define USE_VERTEX_LIGHTING\n";
+	}
+
 	{
 	{
 		//lightmaps
 		//lightmaps
 		scene_state.max_lightmaps = 2;
 		scene_state.max_lightmaps = 2;

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

@@ -633,6 +633,13 @@ void SceneShaderForwardMobile::init(const String p_defines) {
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
 		actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+
+		bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+		if (!force_vertex_shading) {
+			// If forcing vertex shading, this will be defined already.
+			actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+		}
+
 		actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
 		actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 		actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
 
 

+ 186 - 4
servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl

@@ -156,8 +156,30 @@ vec2 multiview_uv(vec2 uv) {
 ivec2 multiview_uv(ivec2 uv) {
 ivec2 multiview_uv(ivec2 uv) {
 	return uv;
 	return uv;
 }
 }
+
 #endif //USE_MULTIVIEW
 #endif //USE_MULTIVIEW
 
 
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 12) highp out vec4 diffuse_light_interp;
+layout(location = 13) highp out vec4 specular_light_interp;
+
+#include "../scene_forward_vertex_lights_inc.glsl"
+
+void cluster_get_item_range(uint p_offset, out uint item_min, out uint item_max, out uint item_from, out uint item_to) {
+	uint item_min_max = cluster_buffer.data[p_offset];
+	item_min = item_min_max & 0xFFFFu;
+	item_max = item_min_max >> 16;
+
+	item_from = item_min >> 5;
+	item_to = (item_max == 0) ? 0 : ((item_max - 1) >> 5) + 1; //side effect of how it is stored, as item_max 0 means no elements
+}
+
+uint cluster_get_range_clip_mask(uint i, uint z_min, uint z_max) {
+	int local_min = clamp(int(z_min) - int(i) * 32, 0, 31);
+	int mask_width = min(int(z_max) - int(z_min), 32 - local_min);
+	return bitfieldInsert(uint(0), uint(0xFFFFFFFF), local_min, mask_width);
+}
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
 invariant gl_Position;
 invariant gl_Position;
 
 
 #GLOBALS
 #GLOBALS
@@ -488,6 +510,145 @@ void vertex_shader(vec3 vertex_input,
 	screen_pos = gl_Position;
 	screen_pos = gl_Position;
 #endif
 #endif
 
 
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+	diffuse_light_interp = vec4(0.0);
+	specular_light_interp = vec4(0.0);
+
+#ifdef USE_MULTIVIEW
+	vec3 view = -normalize(vertex_interp - eye_offset);
+	vec2 clip_pos = clamp((combined_projected.xy / combined_projected.w) * 0.5 + 0.5, 0.0, 1.0);
+#else
+	vec3 view = -normalize(vertex_interp);
+	vec2 clip_pos = clamp((gl_Position.xy / gl_Position.w) * 0.5 + 0.5, 0.0, 1.0);
+#endif
+
+	uvec2 cluster_pos = uvec2(clip_pos / scene_data.screen_pixel_size) >> implementation_data.cluster_shift;
+	uint cluster_offset = (implementation_data.cluster_width * cluster_pos.y + cluster_pos.x) * (implementation_data.max_cluster_element_count_div_32 + 32);
+	uint cluster_z = uint(clamp((-vertex_interp.z / scene_data.z_far) * 32.0, 0.0, 31.0));
+
+	{ //omni lights
+
+		uint cluster_omni_offset = cluster_offset;
+
+		uint item_min;
+		uint item_max;
+		uint item_from;
+		uint item_to;
+
+		cluster_get_item_range(cluster_omni_offset + implementation_data.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
+
+		for (uint i = item_from; i < item_to; i++) {
+			uint mask = cluster_buffer.data[cluster_omni_offset + i];
+			mask &= cluster_get_range_clip_mask(i, item_min, item_max);
+			uint merged_mask = mask;
+
+			while (merged_mask != 0) {
+				uint bit = findMSB(merged_mask);
+				merged_mask &= ~(1u << bit);
+				uint light_index = 32 * i + bit;
+
+				if (!bool(omni_lights.data[light_index].mask & instances.data[instance_index].layer_mask)) {
+					continue; //not masked
+				}
+
+				if (omni_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+					continue; // Statically baked light and object uses lightmap, skip
+				}
+
+				light_process_omni_vertex(light_index, vertex, view, normal, roughness,
+						diffuse_light_interp.rgb, specular_light_interp.rgb);
+			}
+		}
+	}
+
+	{ //spot lights
+		uint cluster_spot_offset = cluster_offset + implementation_data.cluster_type_size;
+
+		uint item_min;
+		uint item_max;
+		uint item_from;
+		uint item_to;
+
+		cluster_get_item_range(cluster_spot_offset + implementation_data.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
+
+		for (uint i = item_from; i < item_to; i++) {
+			uint mask = cluster_buffer.data[cluster_spot_offset + i];
+			mask &= cluster_get_range_clip_mask(i, item_min, item_max);
+			uint merged_mask = mask;
+
+			while (merged_mask != 0) {
+				uint bit = findMSB(merged_mask);
+				merged_mask &= ~(1u << bit);
+
+				uint light_index = 32 * i + bit;
+
+				if (!bool(spot_lights.data[light_index].mask & instances.data[instance_index].layer_mask)) {
+					continue; //not masked
+				}
+
+				if (spot_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+					continue; // Statically baked light and object uses lightmap, skip
+				}
+
+				light_process_spot_vertex(light_index, vertex, view, normal, roughness,
+						diffuse_light_interp.rgb, specular_light_interp.rgb);
+			}
+		}
+	}
+
+	{ // Directional light.
+
+		// We process the first directional light separately as it may have shadows.
+		vec3 directional_diffuse = vec3(0.0);
+		vec3 directional_specular = vec3(0.0);
+
+		for (uint i = 0; i < scene_data.directional_light_count; i++) {
+			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
+				continue; // Not masked, skip.
+			}
+
+			if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+				continue; // Statically baked light and object uses lightmap, skip.
+			}
+			if (i == 0) {
+				light_compute_vertex(normal, directional_lights.data[0].direction, view,
+						directional_lights.data[0].color * directional_lights.data[0].energy,
+						true, roughness,
+						directional_diffuse,
+						directional_specular);
+			} else {
+				light_compute_vertex(normal, directional_lights.data[i].direction, view,
+						directional_lights.data[i].color * directional_lights.data[i].energy,
+						true, roughness,
+						diffuse_light_interp.rgb,
+						specular_light_interp.rgb);
+			}
+		}
+
+		// Calculate the contribution from the shadowed light so we can scale the shadows accordingly.
+		float diff_avg = dot(diffuse_light_interp.rgb, vec3(0.33333));
+		float diff_dir_avg = dot(directional_diffuse, vec3(0.33333));
+		if (diff_avg > 0.0) {
+			diffuse_light_interp.a = diff_dir_avg / (diff_avg + diff_dir_avg);
+		} else {
+			diffuse_light_interp.a = 1.0;
+		}
+
+		diffuse_light_interp.rgb += directional_diffuse;
+
+		float spec_avg = dot(specular_light_interp.rgb, vec3(0.33333));
+		float spec_dir_avg = dot(directional_specular, vec3(0.33333));
+		if (spec_avg > 0.0) {
+			specular_light_interp.a = spec_dir_avg / (spec_avg + spec_dir_avg);
+		} else {
+			specular_light_interp.a = 1.0;
+		}
+
+		specular_light_interp.rgb += directional_specular;
+	}
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
 #ifdef MODE_RENDER_DEPTH
 #ifdef MODE_RENDER_DEPTH
 	if (scene_data.pancake_shadows) {
 	if (scene_data.pancake_shadows) {
 		if (gl_Position.z >= 0.9999) {
 		if (gl_Position.z >= 0.9999) {
@@ -791,7 +952,10 @@ ivec2 multiview_uv(ivec2 uv) {
 	return uv;
 	return uv;
 }
 }
 #endif //USE_MULTIVIEW
 #endif //USE_MULTIVIEW
-
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 12) highp in vec4 diffuse_light_interp;
+layout(location = 13) highp in vec4 specular_light_interp;
+#endif
 //defines to keep compatibility with vertex
 //defines to keep compatibility with vertex
 
 
 #ifdef USE_MULTIVIEW
 #ifdef USE_MULTIVIEW
@@ -1375,7 +1539,6 @@ void fragment_shader(in SceneData scene_data) {
 	vec3 specular_light = vec3(0.0, 0.0, 0.0);
 	vec3 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
 	// Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI.
 	// Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI.
 	emission *= scene_data.emissive_exposure_normalization;
 	emission *= scene_data.emissive_exposure_normalization;
@@ -1836,6 +1999,11 @@ void fragment_shader(in SceneData scene_data) {
 // LIGHTING
 // LIGHTING
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 
+#ifdef USE_VERTEX_LIGHTING
+	diffuse_light += diffuse_light_interp.rgb;
+	specular_light += specular_light_interp.rgb * f0;
+#endif
+
 	{ // Directional light.
 	{ // Directional light.
 
 
 		// Do shadow and lighting in two passes to reduce register pressure.
 		// Do shadow and lighting in two passes to reduce register pressure.
@@ -1843,10 +2011,15 @@ void fragment_shader(in SceneData scene_data) {
 		uint shadow0 = 0;
 		uint shadow0 = 0;
 		uint shadow1 = 0;
 		uint shadow1 = 0;
 
 
+#ifdef USE_VERTEX_LIGHTING
+		// Only process the first light's shadow for vertex lighting.
+		for (uint i = 0; i < 1; i++) {
+#else
 		for (uint i = 0; i < 8; i++) {
 		for (uint i = 0; i < 8; i++) {
 			if (i >= scene_data.directional_light_count) {
 			if (i >= scene_data.directional_light_count) {
 				break;
 				break;
 			}
 			}
+#endif
 
 
 			if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) {
 			if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) {
 				continue; //not masked
 				continue; //not masked
@@ -2044,6 +2217,11 @@ void fragment_shader(in SceneData scene_data) {
 
 
 				shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
 				shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
 
 
+#ifdef USE_VERTEX_LIGHTING
+				diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
+				specular_light *= mix(1.0, shadow, specular_light_interp.a);
+#endif
+
 #undef BIAS_FUNC
 #undef BIAS_FUNC
 			} // shadows
 			} // shadows
 
 
@@ -2055,6 +2233,8 @@ void fragment_shader(in SceneData scene_data) {
 		}
 		}
 #endif // SHADOWS_DISABLED
 #endif // SHADOWS_DISABLED
 
 
+#ifndef USE_VERTEX_LIGHTING
+
 		for (uint i = 0; i < 8; i++) {
 		for (uint i = 0; i < 8; i++) {
 			if (i >= scene_data.directional_light_count) {
 			if (i >= scene_data.directional_light_count) {
 				break;
 				break;
@@ -2175,8 +2355,10 @@ void fragment_shader(in SceneData scene_data) {
 					diffuse_light,
 					diffuse_light,
 					specular_light);
 					specular_light);
 		}
 		}
+#endif // USE_VERTEX_LIGHTING
 	}
 	}
 
 
+#ifndef USE_VERTEX_LIGHTING
 	{ //omni lights
 	{ //omni lights
 
 
 		uint cluster_omni_offset = cluster_offset;
 		uint cluster_omni_offset = cluster_offset;
@@ -2320,6 +2502,8 @@ void fragment_shader(in SceneData scene_data) {
 			}
 			}
 		}
 		}
 	}
 	}
+#endif // !USE_VERTEX_LIGHTING
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 
 #ifdef USE_SHADOW_TO_OPACITY
 #ifdef USE_SHADOW_TO_OPACITY
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
@@ -2334,8 +2518,6 @@ void fragment_shader(in SceneData scene_data) {
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH
 #endif // USE_SHADOW_TO_OPACITY
 #endif // USE_SHADOW_TO_OPACITY
 
 
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
 #ifdef MODE_RENDER_DEPTH
 #ifdef MODE_RENDER_DEPTH
 
 
 #ifdef MODE_RENDER_SDF
 #ifdef MODE_RENDER_SDF

+ 144 - 182
servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl

@@ -105,7 +105,16 @@ layout(location = 4) mediump out vec2 uv2_interp;
 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
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 7) highp out vec4 diffuse_light_interp;
+layout(location = 8) highp out vec4 specular_light_interp;
 
 
+layout(constant_id = 9) const bool sc_disable_omni_lights = false;
+layout(constant_id = 10) const bool sc_disable_spot_lights = false;
+layout(constant_id = 12) const bool sc_disable_directional_lights = false;
+
+#include "../scene_forward_vertex_lights_inc.glsl"
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
 #ifdef MATERIAL_UNIFORMS_USED
 #ifdef MATERIAL_UNIFORMS_USED
 /* clang-format off */
 /* clang-format off */
 layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms {
 layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms {
@@ -185,6 +194,7 @@ void main() {
 
 
 	mat4 model_matrix = instances.data[draw_call.instance_index].transform;
 	mat4 model_matrix = instances.data[draw_call.instance_index].transform;
 	mat4 inv_view_matrix = scene_data.inv_view_matrix;
 	mat4 inv_view_matrix = scene_data.inv_view_matrix;
+
 #ifdef USE_DOUBLE_PRECISION
 #ifdef USE_DOUBLE_PRECISION
 	vec3 model_precision = vec3(model_matrix[0][3], model_matrix[1][3], model_matrix[2][3]);
 	vec3 model_precision = vec3(model_matrix[0][3], model_matrix[1][3], model_matrix[2][3]);
 	model_matrix[0][3] = 0.0;
 	model_matrix[0][3] = 0.0;
@@ -448,6 +458,107 @@ void main() {
 	binormal_interp = binormal;
 	binormal_interp = binormal;
 #endif
 #endif
 
 
+// VERTEX LIGHTING
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+#ifdef USE_MULTIVIEW
+	vec3 view = -normalize(vertex_interp - eye_offset);
+#else
+	vec3 view = -normalize(vertex_interp);
+#endif
+
+	diffuse_light_interp = vec4(0.0);
+	specular_light_interp = vec4(0.0);
+
+	if (!sc_disable_omni_lights) {
+		uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
+		for (uint i = 0; i < 8; i++) {
+			uint light_index = light_indices & 0xFF;
+			if (i == 3) {
+				light_indices = instances.data[draw_call.instance_index].omni_lights.y;
+			} else {
+				light_indices = light_indices >> 8;
+			}
+
+			if (light_index == 0xFF) {
+				break;
+			}
+
+			light_process_omni_vertex(light_index, vertex, view, normal, roughness,
+					diffuse_light_interp.rgb, specular_light_interp.rgb);
+		}
+	}
+
+	if (!sc_disable_spot_lights) {
+		uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
+		for (uint i = 0; i < 8; i++) {
+			uint light_index = light_indices & 0xFF;
+			if (i == 3) {
+				light_indices = instances.data[draw_call.instance_index].spot_lights.y;
+			} else {
+				light_indices = light_indices >> 8;
+			}
+
+			if (light_index == 0xFF) {
+				break;
+			}
+
+			light_process_spot_vertex(light_index, vertex, view, normal, roughness,
+					diffuse_light_interp.rgb, specular_light_interp.rgb);
+		}
+	}
+
+	if (!sc_disable_directional_lights) {
+		// We process the first directional light separately as it may have shadows.
+		vec3 directional_diffuse = vec3(0.0);
+		vec3 directional_specular = vec3(0.0);
+
+		for (uint i = 0; i < scene_data.directional_light_count; i++) {
+			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
+				continue; // Not masked, skip.
+			}
+
+			if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+				continue; // Statically baked light and object uses lightmap, skip.
+			}
+			if (i == 0) {
+				light_compute_vertex(normal, directional_lights.data[0].direction, view,
+						directional_lights.data[0].color * directional_lights.data[0].energy,
+						true, roughness,
+						directional_diffuse,
+						directional_specular);
+			} else {
+				light_compute_vertex(normal, directional_lights.data[i].direction, view,
+						directional_lights.data[i].color * directional_lights.data[i].energy,
+						true, roughness,
+						diffuse_light_interp.rgb,
+						specular_light_interp.rgb);
+			}
+		}
+
+		// Calculate the contribution from the shadowed light so we can scale the shadows accordingly.
+		float diff_avg = dot(diffuse_light_interp.rgb, vec3(0.33333));
+		float diff_dir_avg = dot(directional_diffuse, vec3(0.33333));
+		if (diff_avg > 0.0) {
+			diffuse_light_interp.a = diff_dir_avg / (diff_avg + diff_dir_avg);
+		} else {
+			diffuse_light_interp.a = 1.0;
+		}
+
+		diffuse_light_interp.rgb += directional_diffuse;
+
+		float spec_avg = dot(specular_light_interp.rgb, vec3(0.33333));
+		float spec_dir_avg = dot(directional_specular, vec3(0.33333));
+		if (spec_avg > 0.0) {
+			specular_light_interp.a = spec_dir_avg / (spec_avg + spec_dir_avg);
+		} else {
+			specular_light_interp.a = 1.0;
+		}
+
+		specular_light_interp.rgb += directional_specular;
+	}
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
 #ifdef MODE_RENDER_DEPTH
 #ifdef MODE_RENDER_DEPTH
 
 
 #ifdef MODE_DUAL_PARABOLOID
 #ifdef MODE_DUAL_PARABOLOID
@@ -564,6 +675,11 @@ 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
 
 
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 7) highp in vec4 diffuse_light_interp;
+layout(location = 8) highp in vec4 specular_light_interp;
+#endif
+
 #ifdef MODE_DUAL_PARABOLOID
 #ifdef MODE_DUAL_PARABOLOID
 
 
 layout(location = 9) highp in float dp_clip;
 layout(location = 9) highp in float dp_clip;
@@ -709,7 +825,7 @@ layout(location = 0) out mediump vec4 frag_color;
 
 
 #include "../scene_forward_aa_inc.glsl"
 #include "../scene_forward_aa_inc.glsl"
 
 
-#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) // && !defined(USE_VERTEX_LIGHTING)
 
 
 // Default to SPECULAR_SCHLICK_GGX.
 // Default to SPECULAR_SCHLICK_GGX.
 #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
 #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
@@ -718,7 +834,7 @@ layout(location = 0) out mediump vec4 frag_color;
 
 
 #include "../scene_forward_lights_inc.glsl"
 #include "../scene_forward_lights_inc.glsl"
 
 
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && !defined(USE_VERTEX_LIGHTING)
 
 
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
 
 
@@ -1401,6 +1517,10 @@ void main() {
 
 
 // LIGHTING
 // LIGHTING
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifdef USE_VERTEX_LIGHTING
+	diffuse_light += diffuse_light_interp.rgb;
+	specular_light += specular_light_interp.rgb * f0;
+#endif
 
 
 	if (!sc_disable_directional_lights) { //directional light
 	if (!sc_disable_directional_lights) { //directional light
 #ifndef SHADOWS_DISABLED
 #ifndef SHADOWS_DISABLED
@@ -1408,10 +1528,12 @@ void main() {
 		uint shadow0 = 0;
 		uint shadow0 = 0;
 		uint shadow1 = 0;
 		uint shadow1 = 0;
 
 
-		for (uint i = 0; i < 8; i++) {
-			if (i >= scene_data.directional_light_count) {
-				break;
-			}
+#ifdef USE_VERTEX_LIGHTING
+		// Only process the first light's shadow for vertex lighting.
+		for (uint i = 0; i < 1; i++) {
+#else
+		for (uint i = 0; i < scene_data.directional_light_count; i++) {
+#endif
 
 
 			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
 			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
 				continue; //not masked
 				continue; //not masked
@@ -1419,164 +1541,6 @@ void main() {
 
 
 			float shadow = 1.0;
 			float shadow = 1.0;
 
 
-			// Directional light shadow code is basically the same as forward clustered at this point in time minus `LIGHT_TRANSMITTANCE_USED` support.
-			// Not sure if there is a reason to change this seeing directional lights are part of our global data
-			// Should think about whether we may want to move this code into an include file or function??
-
-#ifdef USE_SOFT_SHADOWS
-			//version with soft shadows, more expensive
-			if (directional_lights.data[i].shadow_opacity > 0.001) {
-				float depth_z = -vertex.z;
-
-				vec4 pssm_coord;
-				vec3 light_dir = directional_lights.data[i].direction;
-
-#define BIAS_FUNC(m_var, m_idx)                                                                                                                                       \
-	m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx];                                                                                           \
-	vec3 normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))) * directional_lights.data[i].shadow_normal_bias[m_idx]; \
-	normal_bias -= light_dir * dot(light_dir, normal_bias);                                                                                                           \
-	m_var.xyz += normal_bias;
-
-				if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
-					vec4 v = vec4(vertex, 1.0);
-
-					BIAS_FUNC(v, 0)
-
-					pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
-					pssm_coord /= pssm_coord.w;
-
-					if (directional_lights.data[i].softshadow_angle > 0) {
-						float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-						float range_begin = directional_lights.data[i].shadow_range_begin.x;
-						float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-						vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius;
-						shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-					} else {
-						shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-					}
-				} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
-					vec4 v = vec4(vertex, 1.0);
-
-					BIAS_FUNC(v, 1)
-
-					pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
-					pssm_coord /= pssm_coord.w;
-
-					if (directional_lights.data[i].softshadow_angle > 0) {
-						float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-						float range_begin = directional_lights.data[i].shadow_range_begin.y;
-						float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-						vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
-						shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-					} else {
-						shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-					}
-				} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
-					vec4 v = vec4(vertex, 1.0);
-
-					BIAS_FUNC(v, 2)
-
-					pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
-					pssm_coord /= pssm_coord.w;
-
-					if (directional_lights.data[i].softshadow_angle > 0) {
-						float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-						float range_begin = directional_lights.data[i].shadow_range_begin.z;
-						float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-						vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
-						shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-					} else {
-						shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-					}
-				} else {
-					vec4 v = vec4(vertex, 1.0);
-
-					BIAS_FUNC(v, 3)
-
-					pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
-					pssm_coord /= pssm_coord.w;
-
-					if (directional_lights.data[i].softshadow_angle > 0) {
-						float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-						float range_begin = directional_lights.data[i].shadow_range_begin.w;
-						float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-						vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
-						shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-					} else {
-						shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-					}
-				}
-
-				if (directional_lights.data[i].blend_splits) {
-					float pssm_blend;
-					float shadow2;
-
-					if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
-						vec4 v = vec4(vertex, 1.0);
-						BIAS_FUNC(v, 1)
-						pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
-						pssm_coord /= pssm_coord.w;
-
-						if (directional_lights.data[i].softshadow_angle > 0) {
-							float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-							float range_begin = directional_lights.data[i].shadow_range_begin.y;
-							float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-							vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
-							shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-						} else {
-							shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-						}
-
-						pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z);
-					} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
-						vec4 v = vec4(vertex, 1.0);
-						BIAS_FUNC(v, 2)
-						pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
-						pssm_coord /= pssm_coord.w;
-
-						if (directional_lights.data[i].softshadow_angle > 0) {
-							float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-							float range_begin = directional_lights.data[i].shadow_range_begin.z;
-							float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-							vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
-							shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-						} else {
-							shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-						}
-
-						pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z);
-					} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
-						vec4 v = vec4(vertex, 1.0);
-						BIAS_FUNC(v, 3)
-						pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
-						pssm_coord /= pssm_coord.w;
-						if (directional_lights.data[i].softshadow_angle > 0) {
-							float range_pos = dot(directional_lights.data[i].direction, v.xyz);
-							float range_begin = directional_lights.data[i].shadow_range_begin.w;
-							float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
-							vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
-							shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
-						} else {
-							shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
-						}
-
-						pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z);
-					} else {
-						pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached)
-					}
-
-					pssm_blend = sqrt(pssm_blend);
-
-					shadow = mix(shadow, shadow2, pssm_blend);
-				}
-
-				shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
-
-#undef BIAS_FUNC
-			}
-#else
-			// Soft shadow disabled version
-
 			if (directional_lights.data[i].shadow_opacity > 0.001) {
 			if (directional_lights.data[i].shadow_opacity > 0.001) {
 				float depth_z = -vertex.z;
 				float depth_z = -vertex.z;
 
 
@@ -1667,6 +1631,10 @@ void main() {
 
 
 				shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
 				shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
 
 
+#ifdef USE_VERTEX_LIGHTING
+				diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
+				specular_light *= mix(1.0, shadow, specular_light_interp.a);
+#endif
 #undef BIAS_FUNC
 #undef BIAS_FUNC
 			}
 			}
 #endif
 #endif
@@ -1678,13 +1646,8 @@ void main() {
 			}
 			}
 		}
 		}
 
 
-#endif // SHADOWS_DISABLED
-
-		for (uint i = 0; i < 8; i++) {
-			if (i >= scene_data.directional_light_count) {
-				break;
-			}
-
+#ifndef USE_VERTEX_LIGHTING
+		for (uint i = 0; i < scene_data.directional_light_count; i++) {
 			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
 			if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
 				continue; //not masked
 				continue; //not masked
 			}
 			}
@@ -1703,8 +1666,8 @@ void main() {
 #endif
 #endif
 			blur_shadow(shadow);
 			blur_shadow(shadow);
 
 
-#ifdef DEBUG_DRAW_PSSM_SPLITS
 			vec3 tint = vec3(1.0);
 			vec3 tint = vec3(1.0);
+#ifdef DEBUG_DRAW_PSSM_SPLITS
 			if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) {
 			if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) {
 				tint = vec3(1.0, 0.0, 0.0);
 				tint = vec3(1.0, 0.0, 0.0);
 			} else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) {
 			} else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) {
@@ -1718,12 +1681,10 @@ void main() {
 			shadow = 1.0;
 			shadow = 1.0;
 #endif
 #endif
 
 
-			light_compute(normal, directional_lights.data[i].direction, normalize(view), 0.0,
-#ifndef DEBUG_DRAW_PSSM_SPLITS
-					directional_lights.data[i].color * directional_lights.data[i].energy,
-#else
+			float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0;
+
+			light_compute(normal, directional_lights.data[i].direction, view, size_A,
 					directional_lights.data[i].color * directional_lights.data[i].energy * tint,
 					directional_lights.data[i].color * directional_lights.data[i].energy * tint,
-#endif
 					true, shadow, f0, orms, 1.0, albedo, alpha,
 					true, shadow, f0, orms, 1.0, albedo, alpha,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 					backlight,
 					backlight,
@@ -1744,15 +1705,14 @@ void main() {
 #endif
 #endif
 #ifdef LIGHT_ANISOTROPY_USED
 #ifdef LIGHT_ANISOTROPY_USED
 					binormal, tangent, anisotropy,
 					binormal, tangent, anisotropy,
-#endif
-#ifdef USE_SOFT_SHADOW
-					directional_lights.data[i].size,
 #endif
 #endif
 					diffuse_light,
 					diffuse_light,
 					specular_light);
 					specular_light);
 		}
 		}
+#endif // USE_VERTEX_LIGHTING
 	} //directional light
 	} //directional light
 
 
+#ifndef USE_VERTEX_LIGHTING
 	if (!sc_disable_omni_lights) { //omni lights
 	if (!sc_disable_omni_lights) { //omni lights
 		uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
 		uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
 		for (uint i = 0; i < 8; i++) {
 		for (uint i = 0; i < 8; i++) {
@@ -1771,6 +1731,7 @@ void main() {
 
 
 			shadow = blur_shadow(shadow);
 			shadow = blur_shadow(shadow);
 
 
+			// Fragment lighting
 			light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
 			light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 					backlight,
 					backlight,
@@ -1841,6 +1802,9 @@ void main() {
 					diffuse_light, specular_light);
 					diffuse_light, specular_light);
 		}
 		}
 	} //spot lights
 	} //spot lights
+#endif // !VERTEX_LIGHTING
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
 
 #ifdef USE_SHADOW_TO_OPACITY
 #ifdef USE_SHADOW_TO_OPACITY
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
@@ -1855,8 +1819,6 @@ void main() {
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH
 #endif // USE_SHADOW_TO_OPACITY
 #endif // USE_SHADOW_TO_OPACITY
 
 
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
 #ifdef MODE_RENDER_DEPTH
 #ifdef MODE_RENDER_DEPTH
 
 
 #ifdef MODE_RENDER_MATERIAL
 #ifdef MODE_RENDER_MATERIAL

+ 82 - 0
servers/rendering/renderer_rd/shaders/scene_forward_vertex_lights_inc.glsl

@@ -0,0 +1,82 @@
+// Simplified versions of light functions intended for the vertex shader.
+
+// Eyeballed approximation of `exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25`.
+// Uses slightly more FMA instructions (2x rate) to avoid special instructions (0.25x rate).
+// Range is reduced to [0.64,4977] from [068,2,221,528] which makes mediump feasible for the rest of the shader.
+mediump float roughness_to_shininess(mediump float roughness) {
+	mediump float r = 1.2 - roughness;
+	mediump float r2 = r * r;
+	return r * r2 * r2 * 2000.0;
+}
+
+void light_compute_vertex(vec3 N, vec3 L, vec3 V, vec3 light_color, bool is_directional, float roughness,
+		inout vec3 diffuse_light, inout vec3 specular_light) {
+	float NdotL = min(dot(N, L), 1.0);
+	float cNdotL = max(NdotL, 0.0); // clamped NdotL
+
+#if defined(DIFFUSE_LAMBERT_WRAP)
+	// Energy conserving lambert wrap shader.
+	// https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+	float diffuse_brdf_NL = max(0.0, (cNdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
+#else
+	// lambert
+	float diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#endif
+
+	diffuse_light += light_color * diffuse_brdf_NL;
+
+#if !defined(SPECULAR_DISABLED)
+	float specular_brdf_NL = 0.0;
+	// Normalized blinn always unless disabled.
+	vec3 H = normalize(V + L);
+	float cNdotH = clamp(dot(N, H), 0.0, 1.0);
+	float shininess = roughness_to_shininess(roughness);
+	float blinn = pow(cNdotH, shininess);
+	blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)) * cNdotL;
+	specular_brdf_NL = blinn;
+	specular_light += specular_brdf_NL * light_color;
+#endif
+}
+
+float get_omni_attenuation(float distance, float inv_range, float decay) {
+	float nd = distance * inv_range;
+	nd *= nd;
+	nd *= nd; // nd^4
+	nd = max(1.0 - nd, 0.0);
+	nd *= nd; // nd^2
+	return nd * pow(max(distance, 0.0001), -decay);
+}
+
+void light_process_omni_vertex(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+		inout vec3 diffuse_light, inout vec3 specular_light) {
+	vec3 light_rel_vec = omni_lights.data[idx].position - vertex;
+	float light_length = length(light_rel_vec);
+	float omni_attenuation = get_omni_attenuation(light_length, omni_lights.data[idx].inv_radius, omni_lights.data[idx].attenuation);
+	vec3 color = omni_lights.data[idx].color * omni_attenuation;
+
+	light_compute_vertex(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+			diffuse_light,
+			specular_light);
+}
+
+void light_process_spot_vertex(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+		inout vec3 diffuse_light,
+		inout vec3 specular_light) {
+	vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
+	float light_length = length(light_rel_vec);
+	float spot_attenuation = get_omni_attenuation(light_length, spot_lights.data[idx].inv_radius, spot_lights.data[idx].attenuation);
+	vec3 spot_dir = spot_lights.data[idx].direction;
+
+	// This conversion to a highp float is crucial to prevent light leaking
+	// due to precision errors in the following calculations (cone angle is mediump).
+	highp float cone_angle = spot_lights.data[idx].cone_angle;
+	float scos = max(dot(-normalize(light_rel_vec), spot_dir), cone_angle);
+	float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cone_angle));
+
+	spot_attenuation *= 1.0 - pow(spot_rim, spot_lights.data[idx].cone_attenuation);
+	vec3 color = spot_lights.data[idx].color * spot_attenuation;
+	float specular_amount = spot_lights.data[idx].specular_amount;
+
+	light_compute_vertex(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+			diffuse_light, specular_light);
+}

+ 1 - 2
servers/rendering_server.cpp

@@ -3580,8 +3580,7 @@ void RenderingServer::init() {
 
 
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/voxel_gi/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"), 0);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/voxel_gi/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"), 0);
 
 
-	GLOBAL_DEF("rendering/shading/overrides/force_vertex_shading", false);
-	GLOBAL_DEF("rendering/shading/overrides/force_vertex_shading.mobile", true);
+	GLOBAL_DEF_RST("rendering/shading/overrides/force_vertex_shading", false);
 	GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley", false);
 	GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley", false);
 	GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley.mobile", true);
 	GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley.mobile", true);