diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b20b374382e9..ef9e1ec9adaa 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2833,11 +2833,6 @@ 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. - - - 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. The default texture filtering mode to use on [CanvasItem]s. diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 6dd04af6b602..98a2c03d451e 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -246,6 +246,193 @@ uniform lowp uint directional_shadow_index; #endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)) #endif // USE_ADDITIVE_LIGHTING +#ifdef USE_VERTEX_LIGHTING + +out vec4 diffuse_light_interp; +out vec4 specular_light_interp; + +// Directional light data. +#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) + +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]; +}; + +#if defined(USE_ADDITIVE_LIGHTING) && (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) +// Directional shadows can be in the base pass or in the additive passes +uniform highp sampler2DShadow directional_shadow_atlas; // texunit:-3 +#endif // defined(USE_ADDITIVE_LIGHTING) && (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) + +#endif // !DISABLE_LIGHT_DIRECTIONAL + +// Omni and spot light data. +#if !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) + +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) + +#ifdef USE_ADDITIVE_LIGHTING +#ifdef ADDITIVE_OMNI +uniform highp samplerCubeShadow omni_shadow_texture; // texunit:-3 +uniform lowp uint omni_light_index; +#endif +#ifdef ADDITIVE_SPOT +uniform highp sampler2DShadow spot_shadow_texture; // texunit:-3 +uniform lowp uint spot_light_index; +#endif + +#endif // USE_ADDITIVE_LIGHTING + +#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(USE_ADDITIVE_LIGHTING) + +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, float roughness, + inout vec3 diffuse_light, inout vec3 specular_light) { + float NdotL = min(A + dot(N, L), 1.0); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance + +#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/ + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI); +#else + // Lambert + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + diffuse_light += light_color * diffuse_brdf_NL * attenuation; + if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely + + // D + float specular_brdf_NL = 0.0; + +#if !defined(SPECULAR_DISABLED) + //normalized blinn always unless disabled + vec3 H = normalize(V + L); + float cNdotH = clamp(A + dot(N, H), 0.0, 1.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = blinn; +#endif + specular_light += specular_brdf_NL * light_color * attenuation; + } +} + +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) +void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, float shadow, + 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; + float size_A = 0.0; + + if (omni_lights[idx].size > 0.0) { + float t = omni_lights[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); + } + + omni_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, omni_attenuation, roughness, + diffuse_light, + specular_light); +} +#endif // !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI) + +#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT) +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, float shadow, + 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; + + float size_A = 0.0; + + if (spot_lights[idx].size > 0.0) { + float t = spot_lights[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); + } + + spot_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, spot_attenuation, roughness, + diffuse_light, specular_light); +} +#endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT) + +#endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) + +#endif // USE_VERTEX_LIGHTING + #ifdef USE_MULTIVIEW layout(std140) uniform MultiviewData { // ubo:8 highp mat4 projection_matrix_view[MAX_VIEWS]; @@ -538,8 +725,92 @@ void main() { gl_Position.z = 0.00001; gl_Position.w = 1.0; #endif -} +#ifdef USE_VERTEX_LIGHTING +#ifndef MODE_RENDER_DEPTH +#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); +#ifdef BASE_PASS +#ifndef DISABLE_LIGHT_DIRECTIONAL + vec3 directional_diffuse = vec3(0.0); + vec3 directional_specular = vec3(0.0); + 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].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, roughness, + directional_diffuse, + directional_specular); + } + 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 // !DISABLE_LIGHT_DIRECTIONAL + +#ifndef DISABLE_LIGHT_OMNI + for (uint i = 0u; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= omni_light_count) { + break; + } + light_process_omni(omni_light_indices[i], vertex_interp, view, normal_interp, roughness, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + } +#endif // !DISABLE_LIGHT_OMNI + +#ifndef DISABLE_LIGHT_SPOT + for (uint i = 0u; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= spot_light_count) { + break; + } + light_process_spot(spot_light_indices[i], vertex_interp, view, normal_interp, roughness, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + } +#endif // !DISABLE_LIGHT_SPOT +#endif // BASE_PASS + +/* ADDITIVE LIGHTING PASS */ +#ifdef USE_ADDITIVE_LIGHTING + +#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].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, 1.0, roughness, + diffuse_light_interp.rgb, + 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, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); +#endif // ADDITIVE_OMNI + +#ifdef ADDITIVE_SPOT + light_process_spot(spot_light_index, vertex_interp, view, normal_interp, roughness, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + +#endif // ADDITIVE_SPOT + +#endif // USE_ADDITIVE_LIGHTING +#endif //!MODE_RENDER_DEPTH +#endif // USE_VERTEX_LIGHTING +} /* clang-format off */ #[fragment] @@ -753,6 +1024,11 @@ multiview_data; #define LIGHT_BAKE_DYNAMIC 2u #ifndef MODE_RENDER_DEPTH +#ifdef USE_VERTEX_LIGHTING +in vec4 diffuse_light_interp; +in vec4 specular_light_interp; +#endif // USE_VERTEX_LIGHTING + // Directional light data. #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) @@ -975,6 +1251,8 @@ vec3 F0(float metallic, float specular, vec3 albedo) { return mix(vec3(dielectric), albedo, vec3(metallic)); } #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) float D_GGX(float cos_theta_m, float alpha) { @@ -1274,6 +1552,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_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) +#endif // !USE_VERTEX_LIGHTING vec4 fog_process(vec3 vertex) { vec3 fog_color = scene_data.fog_light_color; @@ -1605,7 +1884,10 @@ void main() { vec3 specular_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); - +#ifdef USE_VERTEX_LIGHTING + specular_light = specular_light_interp.rgb; + diffuse_light = diffuse_light_interp.rgb; +#endif #ifdef BASE_PASS /////////////////////// LIGHTING ////////////////////////////// @@ -1778,7 +2060,7 @@ void main() { specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0); #endif } - +#ifndef USE_VERTEX_LIGHTING #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) @@ -1861,6 +2143,7 @@ void main() { diffuse_light, specular_light); } #endif // !DISABLE_LIGHT_SPOT +#endif // !USE_VERTEX_LIGHTING #endif // BASE_PASS #endif // !MODE_UNSHADED @@ -2054,6 +2337,7 @@ void main() { #else float directional_shadow = 1.0f; #endif // SHADOWS_DISABLED +#if !defined(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, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -2070,6 +2354,10 @@ void main() { #endif diffuse_light, specular_light); +#else // Pass Vertex Lighting data to fragment colors with fragment shadows + specular_light += specular_light_interp.rgb * directional_shadow; + diffuse_light += diffuse_light_interp.rgb * directional_shadow; +#endif // !defined(USE_VERTEX_LIGHTING) #endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) #ifdef ADDITIVE_OMNI @@ -2079,6 +2367,7 @@ 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 = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity); #endif // SHADOWS_DISABLED +#if !defined(USE_VERTEX_LIGHTING) light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -2094,6 +2383,10 @@ void main() { binormal, tangent, anisotropy, #endif diffuse_light, specular_light); +#else // Pass Vertex Lighting data to fragment colors with fragment shadows + specular_light += specular_light_interp.rgb * omni_shadow; + diffuse_light += diffuse_light_interp.rgb * omni_shadow; +#endif // !defined(USE_VERTEX_LIGHTING) #endif // ADDITIVE_OMNI #ifdef ADDITIVE_SPOT @@ -2102,6 +2395,7 @@ void main() { 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); #endif // SHADOWS_DISABLED +#if !defined(USE_VERTEX_LIGHTING) light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -2118,6 +2412,10 @@ void main() { binormal, anisotropy, #endif diffuse_light, specular_light); +#else // Pass Vertex Lighting data to fragment colors with fragment shadows + specular_light += specular_light_interp.rgb * spot_shadow; + diffuse_light += diffuse_light_interp.rgb * spot_shadow; +#endif // !defined(USE_VERTEX_LIGHTING) #endif // ADDITIVE_SPOT diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index a28b050bf801..6a3696e00dd3 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -168,7 +168,7 @@ Config::Config() { } #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_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")); diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 41c23fc3ec03..0567339ea748 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1367,6 +1367,7 @@ MaterialStorage::MaterialStorage() { 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["unshaded"] = "#define MODE_UNSHADED\n"; + actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n"; actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n"; actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; @@ -2862,6 +2863,7 @@ void SceneShaderData::set_code(const String &p_code) { wireframe = false; unshaded = false; + vertex_lighting = false; uses_vertex = false; uses_position = false; uses_sss = false; @@ -2924,6 +2926,11 @@ void SceneShaderData::set_code(const String &p_code) { actions.render_mode_values["cull_back"] = Pair(&cull_modei, CULL_BACK); actions.render_mode_flags["unshaded"] = &unshaded; + actions.render_mode_flags["vertex_lighting"] = &vertex_lighting; + bool force_vertex_lighting = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading"); + if (force_vertex_lighting) { + actions.render_mode_flags["vertex_lighting"] = &force_vertex_lighting; + } actions.render_mode_flags["wireframe"] = &wireframe; actions.render_mode_flags["particle_trails"] = &uses_particle_trails; actions.render_mode_flags["world_vertex_coords"] = &uses_world_coordinates; diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 392ebcc570d9..2c7ae9201d9d 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -306,6 +306,7 @@ struct SceneShaderData : public ShaderData { bool wireframe; bool unshaded; + bool vertex_lighting; bool uses_vertex; bool uses_position; bool uses_sss; diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp index 8eab3fbea968..357290dfe76f 100644 --- a/editor/renames_map_3_to_4.cpp +++ b/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.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.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_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" }, @@ -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.mobile", "shading/overrides/force_lambert_over_burley.mobile" }, { "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_1_subdiv", "lights_and_shadows/shadow_atlas/quadrant_1_subdiv" }, { "quality/shadow_atlas/quadrant_2_subdiv", "lights_and_shadows/shadow_atlas/quadrant_2_subdiv" }, diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 7d121c9d87fb..6a4121e42418 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -782,7 +782,7 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_PARTICLE_TRAILS_MODE]) { code += ", particle_trails"; } - if (shading_mode == SHADING_MODE_PER_VERTEX) { + if (shading_mode == SHADING_MODE_PER_VERTEX || force_vertex_shading) { code += ", vertex_lighting"; } if (flags[FLAG_DONT_RECEIVE_SHADOWS]) { @@ -3426,6 +3426,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : current_key.invalid_key = 1; _mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change), callable_mp(this, &BaseMaterial3D::_update_shader)); + + force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading"); } BaseMaterial3D::~BaseMaterial3D() { diff --git a/scene/resources/material.h b/scene/resources/material.h index 50a774e961f8..d477ee322643 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -554,6 +554,8 @@ class BaseMaterial3D : public Material { Ref textures[TEXTURE_MAX]; + bool force_vertex_shading = false; + _FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const; static HashMap> materials_for_2d; //used by Sprite3D, Label3D and other stuff diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 42e1f7b6dc4f..42e24c2950d8 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -729,6 +729,7 @@ void SceneShaderForwardClustered::init(const String p_defines) { 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["unshaded"] = "#define MODE_UNSHADED\n"; + 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["fog_disabled"] = "#define FOG_DISABLED\n"; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index cf661bb8f4bf..ed7c7beca05e 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -632,6 +632,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { 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["unshaded"] = "#define MODE_UNSHADED\n"; + 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["fog_disabled"] = "#define FOG_DISABLED\n"; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 5c68fb82b1fd..7d537790208c 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -2624,6 +2624,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; + actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n"; actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n"; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 4a630b0b0a0e..1b35ff619274 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -67,6 +67,125 @@ layout(location = 13) in vec4 previous_normal_attrib; #endif // MOTION_VECTORS +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) +/* Specialization Constants (Toggles) */ + +layout(constant_id = 2) const bool sc_use_light_soft_shadows = false; + +/* Specialization Constants (Values) */ + +layout(constant_id = 8) const uint sc_directional_soft_shadow_samples = 4; +layout(constant_id = 11) const bool sc_projector_use_mipmaps = true; + +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, uint orms, + inout vec3 diffuse_light, inout vec3 specular_light) { + vec4 orms_unpacked = unpackUnorm4x8(orms); + + float roughness = orms_unpacked.y; + float metallic = orms_unpacked.z; + + float NdotL = min(A + dot(N, L), 1.0); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + + if (metallic < 1.0) { + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance + +#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/ + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI); +#else + // lambert + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + diffuse_light += light_color * diffuse_brdf_NL * attenuation; + } + + if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely + + // D + + float specular_brdf_NL = 0.0; + +#if !defined(SPECULAR_DISABLED) + //normalized blinn always unless disabled + vec3 H = normalize(V + L); + float cNdotH = clamp(A + dot(N, H), 0.0, 1.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = blinn; +#endif + specular_light += specular_brdf_NL * light_color * attenuation; + } +} + +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(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, uint orms, float shadow, + 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); + float light_attenuation = omni_attenuation; + vec3 color = omni_lights.data[idx].color; + + float size_A = 0.0; + + if (sc_use_light_soft_shadows && omni_lights.data[idx].size > 0.0) { + float t = omni_lights.data[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + } + + light_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, light_attenuation, orms, + diffuse_light, + specular_light); +} + +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, uint orms, float shadow, + 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); + float light_attenuation = spot_attenuation; + vec3 color = spot_lights.data[idx].color; + float specular_amount = spot_lights.data[idx].specular_amount; + + float size_A = 0.0; + + if (sc_use_light_soft_shadows && spot_lights.data[idx].size > 0.0) { + float t = spot_lights.data[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + } + + light_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, light_attenuation, orms, + diffuse_light, specular_light); +} + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) + vec3 oct_to_vec3(vec2 e) { vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); float t = max(-v.z, 0.0); @@ -143,7 +262,6 @@ vec3 multiview_uv(vec2 uv) { ivec3 multiview_uv(ivec2 uv) { return ivec3(uv, int(ViewIndex)); } -layout(location = 11) out vec4 combined_projected; #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 @@ -154,7 +272,11 @@ ivec2 multiview_uv(ivec2 uv) { return uv; } #endif //USE_MULTIVIEW - +layout(location = 11) out vec4 combined_projected; +#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; +#endif invariant gl_Position; #GLOBALS @@ -186,6 +308,23 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec } #endif +#ifdef USE_VERTEX_LIGHTING +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 // USE_VERTEX_LIGHTING + void vertex_shader(vec3 vertex_input, #ifdef NORMAL_USED in vec3 normal_input, @@ -354,9 +493,8 @@ void vertex_shader(vec3 vertex_input, #ifdef OVERRIDE_POSITION vec4 position = vec4(1.0); #endif - -#ifdef USE_MULTIVIEW mat4 combined_projection = scene_data.projection_matrix; +#ifdef USE_MULTIVIEW mat4 projection_matrix = scene_data.projection_matrix_view[ViewIndex]; mat4 inv_projection_matrix = scene_data.inv_projection_matrix_view[ViewIndex]; vec3 eye_offset = scene_data.eye_offset[ViewIndex].xyz; @@ -477,13 +615,184 @@ void vertex_shader(vec3 vertex_input, gl_Position = projection_matrix * vec4(vertex_interp, 1.0); #endif -#ifdef USE_MULTIVIEW combined_projected = combined_projection * vec4(vertex_interp, 1.0); -#endif #ifdef MOTION_VECTORS screen_pos = gl_Position; #endif +// VERTEX LIGHTING +#ifdef USE_VERTEX_LIGHTING + float alpha = 1.0; +#endif // USE_VERTEX_LIGHTING + +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) + diffuse_light_interp = vec4(0.0); + specular_light_interp = vec4(0.0); + + // UV in our combined frustum space is used for certain screen uv processes where it's + // overkill to render separate left and right eye views + vec2 combined_uv = (combined_projected.xy / combined_projected.w) * 0.5 + 0.5; + + uvec2 cluster_pos = uvec2(combined_uv.xy / scene_data.screen_pixel_size) >> implementation_data.cluster_shift; + vec3 view = -normalize(vertex_interp - eye_offset); + 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.z / scene_data.z_far) * 32.0, 0.0, 31.0)); + //used for interpolating anything cluster related + vec3 vertex_ddx = vec3(0); // Fake variable + vec3 vertex_ddy = vec3(0); // Fake variable + +#if !defined(MODE_RENDER_DEPTH) + //this saves some VGPRs + uint orms = packUnorm4x8(vec4(0.0, roughness, 0.0, 0.0)); +#endif + + { // Directional light. + + for (uint i = 0; i < 8; i++) { + if (i >= scene_data.directional_light_count) { + break; + } + + if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) { + continue; //not masked + } + + if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { + continue; // Statically baked light and object uses lightmap, skip + } + + float shadow = 1.0; + +#ifdef DEBUG_DRAW_PSSM_SPLITS + vec3 tint = vec3(1.0); + if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) { + tint = vec3(1.0, 0.0, 0.0); + } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) { + tint = vec3(0.0, 1.0, 0.0); + } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.z) { + tint = vec3(0.0, 0.0, 1.0); + } else { + tint = vec3(1.0, 1.0, 0.0); + } + tint = mix(tint, vec3(1.0), shadow); + shadow = 1.0; +#endif + + float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0; + + light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A, +#ifndef DEBUG_DRAW_PSSM_SPLITS + directional_lights.data[i].color * directional_lights.data[i].energy, +#else + directional_lights.data[i].color * directional_lights.data[i].energy * tint, +#endif + true, 1.0, orms, + diffuse_light_interp.rgb, + specular_light_interp.rgb); + } + } + + { //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); + +#ifdef USE_SUBGROUPS + item_from = subgroupBroadcastFirst(subgroupMin(item_from)); + item_to = subgroupBroadcastFirst(subgroupMax(item_to)); +#endif + + 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); +#ifdef USE_SUBGROUPS + uint merged_mask = subgroupBroadcastFirst(subgroupOr(mask)); +#else + uint merged_mask = mask; +#endif + + while (merged_mask != 0) { + uint bit = findMSB(merged_mask); + merged_mask &= ~(1u << bit); +#ifdef USE_SUBGROUPS + if (((1u << bit) & mask) == 0) { //do not process if not originally here + continue; + } +#endif + 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(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, orms, 1.0, + 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); + +#ifdef USE_SUBGROUPS + item_from = subgroupBroadcastFirst(subgroupMin(item_from)); + item_to = subgroupBroadcastFirst(subgroupMax(item_to)); +#endif + + 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); +#ifdef USE_SUBGROUPS + uint merged_mask = subgroupBroadcastFirst(subgroupOr(mask)); +#else + uint merged_mask = mask; +#endif + + while (merged_mask != 0) { + uint bit = findMSB(merged_mask); + merged_mask &= ~(1u << bit); +#ifdef USE_SUBGROUPS + if (((1u << bit) & mask) == 0) { //do not process if not originally here + continue; + } +#endif + + 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(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, orms, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + } + } + } + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) #ifdef MODE_RENDER_DEPTH if (scene_data.pancake_shadows) { @@ -725,7 +1034,10 @@ ivec2 multiview_uv(ivec2 uv) { return uv; } #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 #ifdef USE_MULTIVIEW @@ -1302,7 +1614,6 @@ void fragment_shader(in SceneData scene_data) { vec3 specular_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); - #ifndef MODE_UNSHADED // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. emission *= scene_data.emissive_exposure_normalization; @@ -2044,7 +2355,12 @@ void fragment_shader(in SceneData scene_data) { #endif blur_shadow(shadow); - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + diffuse_light += diffuse_light_interp.rgb * shadow; + specular_light += specular_light_interp.rgb * shadow; +#else // USE_VERTEX_LIGHTING +// Fragment lighting #ifdef DEBUG_DRAW_PSSM_SPLITS vec3 tint = vec3(1.0); if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) { @@ -2090,6 +2406,7 @@ void fragment_shader(in SceneData scene_data) { #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } @@ -2139,7 +2456,18 @@ void fragment_shader(in SceneData scene_data) { float shadow = light_process_omni_shadow(light_index, vertex, normal); shadow = blur_shadow(shadow); - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + vec3 light_rel_vec = omni_lights.data[light_index].position - vertex; + float light_length = length(light_rel_vec); + float omni_attenuation = get_omni_attenuation(light_length, omni_lights.data[light_index].inv_radius, omni_lights.data[light_index].attenuation); + + float light_attenuation = omni_attenuation; + light_attenuation *= shadow; + diffuse_light += diffuse_light_interp.rgb * light_attenuation; + specular_light += specular_light_interp.rgb * light_attenuation; +#else // USE_VERTEX_LIGHTING + // Fragment lighting light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -2160,6 +2488,7 @@ void fragment_shader(in SceneData scene_data) { tangent, binormal, anisotropy, #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } } @@ -2211,7 +2540,26 @@ void fragment_shader(in SceneData scene_data) { float shadow = light_process_spot_shadow(light_index, vertex, normal); shadow = blur_shadow(shadow); - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + vec3 light_rel_vec = spot_lights.data[light_index].position - vertex; + float light_length = length(light_rel_vec); + float spot_attenuation = get_omni_attenuation(light_length, spot_lights.data[light_index].inv_radius, spot_lights.data[light_index].attenuation); + vec3 spot_dir = spot_lights.data[light_index].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[light_index].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[light_index].cone_attenuation); + float light_attenuation = spot_attenuation; + light_attenuation *= shadow; + diffuse_light += diffuse_light_interp.rgb * light_attenuation; + specular_light += specular_light_interp.rgb * light_attenuation; +#else // USE_VERTEX_LIGHTING + // Fragment lighting light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -2233,10 +2581,13 @@ void fragment_shader(in SceneData scene_data) { binormal, anisotropy, #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } } +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + #ifdef USE_SHADOW_TO_OPACITY #ifndef MODE_RENDER_DEPTH alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); @@ -2250,8 +2601,6 @@ void fragment_shader(in SceneData scene_data) { #endif // !MODE_RENDER_DEPTH #endif // USE_SHADOW_TO_OPACITY -#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - #ifdef MODE_RENDER_DEPTH #ifdef MODE_RENDER_SDF diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 530a7a37db87..ee16494279fb 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -59,6 +59,125 @@ layout(location = 10) in uvec4 bone_attrib; layout(location = 11) in vec4 weight_attrib; #endif +#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) + +layout(constant_id = 0) const bool sc_use_light_projector = false; +layout(constant_id = 1) const bool sc_use_light_soft_shadows = false; +layout(constant_id = 8) const bool sc_projector_use_mipmaps = true; + +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; + +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, uint orms, + inout vec3 diffuse_light, inout vec3 specular_light) { + vec4 orms_unpacked = unpackUnorm4x8(orms); + + float roughness = orms_unpacked.y; + float metallic = orms_unpacked.z; + + float NdotL = min(A + dot(N, L), 1.0); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + + if (metallic < 1.0) { + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance + +#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/ + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI); +#else + // lambert + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + diffuse_light += light_color * diffuse_brdf_NL * attenuation; + } + + if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely + + // D + + float specular_brdf_NL = 0.0; + +#if !defined(SPECULAR_DISABLED) + //normalized blinn always unless disabled + vec3 H = normalize(V + L); + float cNdotH = clamp(A + dot(N, H), 0.0, 1.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)); + specular_brdf_NL = blinn; +#endif + specular_light += specular_brdf_NL * light_color * attenuation; + } +} + +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(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, uint orms, float shadow, + 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); + float light_attenuation = omni_attenuation; + vec3 color = omni_lights.data[idx].color; + + float size_A = 0.0; + + if (sc_use_light_soft_shadows && omni_lights.data[idx].size > 0.0) { + float t = omni_lights.data[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + } + + light_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, light_attenuation, orms, + diffuse_light, + specular_light); +} + +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, uint orms, float shadow, + 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); + float light_attenuation = spot_attenuation; + vec3 color = spot_lights.data[idx].color; + float specular_amount = spot_lights.data[idx].specular_amount; + + float size_A = 0.0; + + if (sc_use_light_soft_shadows && spot_lights.data[idx].size > 0.0) { + float t = spot_lights.data[idx].size / max(0.001, light_length); + size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + } + + light_attenuation *= shadow; + + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, light_attenuation, orms, + diffuse_light, specular_light); +} + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) + vec3 oct_to_vec3(vec2 e) { vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); float t = max(-v.z, 0.0); @@ -104,7 +223,10 @@ layout(location = 4) mediump out vec2 uv2_interp; layout(location = 5) mediump out vec3 tangent_interp; layout(location = 6) mediump out vec3 binormal_interp; #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; +#endif #ifdef MATERIAL_UNIFORMS_USED layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{ @@ -184,6 +306,10 @@ void main() { mat4 model_matrix = instances.data[draw_call.instance_index].transform; mat4 inv_view_matrix = scene_data.inv_view_matrix; +#ifdef USE_VERTEX_LIGHTING + float alpha = 1.0; +#endif // USE_VERTEX_LIGHTING + #ifdef USE_DOUBLE_PRECISION vec3 model_precision = vec3(model_matrix[0][3], model_matrix[1][3], model_matrix[2][3]); model_matrix[0][3] = 0.0; @@ -447,6 +573,115 @@ void main() { binormal_interp = binormal; #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 +#ifndef MODE_RENDER_DEPTH + + vec3 vertex_ddx = vec3(0); // Fake variable + vec3 vertex_ddy = vec3(0); // Fake variable +#endif //!MODE_RENDER_DEPTH + + diffuse_light_interp = vec4(0.0); + specular_light_interp = vec4(0.0); + +#if !defined(MODE_RENDER_DEPTH) + //this saves some VGPRs + uint orms = packUnorm4x8(vec4(0.0, roughness, 0.0, 0.0)); +#endif + + if (!sc_disable_directional_lights) { //directional light + for (uint i = 0; i < 8; i++) { + if (i >= scene_data.directional_light_count) { + break; + } + + if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) { + continue; //not masked + } + + 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 + } + // We're not doing light transmittence + + float shadow = 1.0; + +#ifdef DEBUG_DRAW_PSSM_SPLITS + vec3 tint = vec3(1.0); + if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) { + tint = vec3(1.0, 0.0, 0.0); + } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) { + tint = vec3(0.0, 1.0, 0.0); + } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.z) { + tint = vec3(0.0, 0.0, 1.0); + } else { + tint = vec3(1.0, 1.0, 0.0); + } + tint = mix(tint, vec3(1.0), shadow); + shadow = 1.0; +#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 + directional_lights.data[i].color * directional_lights.data[i].energy * tint, +#endif + true, 1.0, orms, +#ifdef USE_SOFT_SHADOW + directional_lights.data[i].size, +#endif + diffuse_light_interp.rgb, + specular_light_interp.rgb); + } + } //directional light + + if (!sc_disable_omni_lights) { //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(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, orms, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + } + } //omni lights + + if (!sc_disable_spot_lights) { //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(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, orms, 1.0, + diffuse_light_interp.rgb, specular_light_interp.rgb); + } + } //spot lights + +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING) + #ifdef MODE_RENDER_DEPTH #ifdef MODE_DUAL_PARABOLOID @@ -561,6 +796,11 @@ layout(location = 5) mediump in vec3 tangent_interp; layout(location = 6) mediump in vec3 binormal_interp; #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 layout(location = 9) highp in float dp_clip; @@ -645,7 +885,7 @@ layout(location = 0) out mediump vec4 frag_color; #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. #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON) @@ -654,7 +894,7 @@ layout(location = 0) out mediump vec4 frag_color; #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 @@ -1063,7 +1303,6 @@ void main() { vec3 specular_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); - #ifndef MODE_UNSHADED // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. emission *= scene_data.emissive_exposure_normalization; @@ -1319,7 +1558,6 @@ void main() { // LIGHTING #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - if (!sc_disable_directional_lights) { //directional light #ifndef SHADOWS_DISABLED // Do shadow and lighting in two passes to reduce register pressure @@ -1635,7 +1873,12 @@ void main() { tint = mix(tint, vec3(1.0), shadow); shadow = 1.0; #endif - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + diffuse_light += diffuse_light_interp.rgb * shadow; + specular_light += specular_light_interp.rgb * shadow; +#else // USE_VERTEX_LIGHTIN + // Fragment lighting 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, @@ -1668,6 +1911,7 @@ void main() { #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } //directional light @@ -1688,7 +1932,19 @@ void main() { float shadow = light_process_omni_shadow(light_index, vertex, normal); shadow = blur_shadow(shadow); - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + vec3 light_rel_vec = omni_lights.data[light_index].position - vertex; + float light_length = length(light_rel_vec); + float omni_attenuation = get_omni_attenuation(light_length, omni_lights.data[light_index].inv_radius, omni_lights.data[light_index].attenuation); + + float light_attenuation = omni_attenuation; + light_attenuation *= shadow; + + diffuse_light += diffuse_light_interp.rgb * light_attenuation; + specular_light += specular_light_interp.rgb * light_attenuation; +#else // USE_VERTEX_LIGHTING + // Fragment lighting light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1712,6 +1968,7 @@ void main() { binormal, anisotropy, #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } //omni lights @@ -1733,7 +1990,27 @@ void main() { float shadow = light_process_spot_shadow(light_index, vertex, normal); shadow = blur_shadow(shadow); - +#ifdef USE_VERTEX_LIGHTING + // Apply Fragment Shadows on vertex lighting + vec3 light_rel_vec = spot_lights.data[light_index].position - vertex; + float light_length = length(light_rel_vec); + float spot_attenuation = get_omni_attenuation(light_length, spot_lights.data[light_index].inv_radius, spot_lights.data[light_index].attenuation); + vec3 spot_dir = spot_lights.data[light_index].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[light_index].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[light_index].cone_attenuation); + float light_attenuation = spot_attenuation; + light_attenuation *= shadow; + + diffuse_light += diffuse_light_interp.rgb * light_attenuation; + specular_light += specular_light_interp.rgb * light_attenuation; +#else // USE_VERTEX_LIGHTING + // Fragment lighting light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1757,9 +2034,12 @@ void main() { binormal, anisotropy, #endif diffuse_light, specular_light); +#endif // USE_VERTEX_LIGHTING } } //spot lights +#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + #ifdef USE_SHADOW_TO_OPACITY #ifndef MODE_RENDER_DEPTH alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); @@ -1773,8 +2053,6 @@ void main() { #endif // !MODE_RENDER_DEPTH #endif // USE_SHADOW_TO_OPACITY -#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - #ifdef MODE_RENDER_DEPTH #ifdef MODE_RENDER_MATERIAL diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 70b585d68342..712ab2978f5f 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3560,8 +3560,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("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.mobile", true);