From 6fede0b95196a35bc0c1ce434ba14a01f278aa9e Mon Sep 17 00:00:00 2001 From: clayjohn Date: Thu, 23 Jan 2025 20:03:19 -0800 Subject: [PATCH] Optimize SkyMaterials by removing uses of acos and simplifying logic The results looks almost the same and run much faster. --- drivers/gles3/shaders/sky.glsl | 28 ++- scene/resources/3d/sky_material.cpp | 168 +++++++++++------- scene/resources/3d/sky_material.h | 8 +- .../renderer_rd/shaders/environment/sky.glsl | 28 ++- 4 files changed, 159 insertions(+), 73 deletions(-) diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 043023aee0d..727eaa6575d 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -151,6 +151,32 @@ vec4 fog_process(vec3 view, vec3 sky_color) { } #endif // !DISABLE_FOG +// Eberly approximations from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/. +// input [-1, 1] and output [0, PI] +float acos_approx(float p_x) { + float x = abs(p_x); + float res = -0.156583f * x + (M_PI / 2.0); + res *= sqrt(1.0f - x); + return (p_x >= 0) ? res : M_PI - res; +} + +// Based on https://math.stackexchange.com/questions/1098487/atan2-faster-approximation +// but using the Eberly coefficients from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/. +float atan2_approx(float y, float x) { + float a = min(abs(x), abs(y)) / max(abs(x), abs(y)); + float s = a * a; + float poly = 0.0872929f; + poly = -0.301895f + poly * s; + poly = 1.0f + poly * s; + poly = poly * a; + + float r = abs(y) > abs(x) ? (M_PI / 2.0) - poly : poly; + r = x < 0.0 ? M_PI - r : r; + r = y < 0.0 ? -r : r; + + return r; +} + void main() { vec3 cube_normal; #ifdef USE_MULTIVIEW @@ -171,7 +197,7 @@ void main() { vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5; - vec2 panorama_coords = vec2(atan(cube_normal.x, -cube_normal.z), acos(cube_normal.y)); + vec2 panorama_coords = vec2(atan2_approx(cube_normal.x, -cube_normal.z), acos_approx(cube_normal.y)); if (panorama_coords.x < 0.0) { panorama_coords.x += M_PI * 2.0; diff --git a/scene/resources/3d/sky_material.cpp b/scene/resources/3d/sky_material.cpp index ce56ee28790..09904bec4ba 100644 --- a/scene/resources/3d/sky_material.cpp +++ b/scene/resources/3d/sky_material.cpp @@ -34,11 +34,11 @@ #include "core/version.h" Mutex ProceduralSkyMaterial::shader_mutex; -RID ProceduralSkyMaterial::shader_cache[2]; +RID ProceduralSkyMaterial::shader_cache[4]; void ProceduralSkyMaterial::set_sky_top_color(const Color &p_sky_top) { sky_top_color = p_sky_top; - RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color); + RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color * sky_energy_multiplier); } Color ProceduralSkyMaterial::get_sky_top_color() const { @@ -47,7 +47,7 @@ Color ProceduralSkyMaterial::get_sky_top_color() const { void ProceduralSkyMaterial::set_sky_horizon_color(const Color &p_sky_horizon) { sky_horizon_color = p_sky_horizon; - RS::get_singleton()->material_set_param(_get_material(), "sky_horizon_color", sky_horizon_color); + RS::get_singleton()->material_set_param(_get_material(), "sky_horizon_color", sky_horizon_color * sky_energy_multiplier); } Color ProceduralSkyMaterial::get_sky_horizon_color() const { @@ -56,7 +56,9 @@ Color ProceduralSkyMaterial::get_sky_horizon_color() const { void ProceduralSkyMaterial::set_sky_curve(float p_curve) { sky_curve = p_curve; - RS::get_singleton()->material_set_param(_get_material(), "sky_curve", sky_curve); + // Actual curve passed to shader includes an ad hoc adjustment because the curve used to be + // in calculated in angles and now uses cosines. + RS::get_singleton()->material_set_param(_get_material(), "inv_sky_curve", 0.6 / sky_curve); } float ProceduralSkyMaterial::get_sky_curve() const { @@ -65,7 +67,9 @@ float ProceduralSkyMaterial::get_sky_curve() const { void ProceduralSkyMaterial::set_sky_energy_multiplier(float p_multiplier) { sky_energy_multiplier = p_multiplier; - RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy_multiplier); + RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color * sky_energy_multiplier); + RS::get_singleton()->material_set_param(_get_material(), "sky_horizon_color", sky_horizon_color * sky_energy_multiplier); + RS::get_singleton()->material_set_param(_get_material(), "sky_cover_modulate", Color(sky_cover_modulate.r, sky_cover_modulate.g, sky_cover_modulate.b, sky_cover_modulate.a * sky_energy_multiplier)); } float ProceduralSkyMaterial::get_sky_energy_multiplier() const { @@ -74,11 +78,16 @@ float ProceduralSkyMaterial::get_sky_energy_multiplier() const { void ProceduralSkyMaterial::set_sky_cover(const Ref &p_sky_cover) { sky_cover = p_sky_cover; + if (p_sky_cover.is_valid()) { RS::get_singleton()->material_set_param(_get_material(), "sky_cover", p_sky_cover->get_rid()); } else { RS::get_singleton()->material_set_param(_get_material(), "sky_cover", Variant()); } + + if (shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); + } } Ref ProceduralSkyMaterial::get_sky_cover() const { @@ -87,7 +96,7 @@ Ref ProceduralSkyMaterial::get_sky_cover() const { void ProceduralSkyMaterial::set_sky_cover_modulate(const Color &p_sky_cover_modulate) { sky_cover_modulate = p_sky_cover_modulate; - RS::get_singleton()->material_set_param(_get_material(), "sky_cover_modulate", sky_cover_modulate); + RS::get_singleton()->material_set_param(_get_material(), "sky_cover_modulate", Color(sky_cover_modulate.r, sky_cover_modulate.g, sky_cover_modulate.b, sky_cover_modulate.a * sky_energy_multiplier)); } Color ProceduralSkyMaterial::get_sky_cover_modulate() const { @@ -96,7 +105,7 @@ Color ProceduralSkyMaterial::get_sky_cover_modulate() const { void ProceduralSkyMaterial::set_ground_bottom_color(const Color &p_ground_bottom) { ground_bottom_color = p_ground_bottom; - RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color); + RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color * ground_energy_multiplier); } Color ProceduralSkyMaterial::get_ground_bottom_color() const { @@ -105,7 +114,7 @@ Color ProceduralSkyMaterial::get_ground_bottom_color() const { void ProceduralSkyMaterial::set_ground_horizon_color(const Color &p_ground_horizon) { ground_horizon_color = p_ground_horizon; - RS::get_singleton()->material_set_param(_get_material(), "ground_horizon_color", ground_horizon_color); + RS::get_singleton()->material_set_param(_get_material(), "ground_horizon_color", ground_horizon_color * ground_energy_multiplier); } Color ProceduralSkyMaterial::get_ground_horizon_color() const { @@ -114,7 +123,9 @@ Color ProceduralSkyMaterial::get_ground_horizon_color() const { void ProceduralSkyMaterial::set_ground_curve(float p_curve) { ground_curve = p_curve; - RS::get_singleton()->material_set_param(_get_material(), "ground_curve", ground_curve); + // Actual curve passed to shader includes an ad hoc adjustment because the curve used to be + // in calculated in angles and now uses cosines. + RS::get_singleton()->material_set_param(_get_material(), "inv_ground_curve", 0.6 / ground_curve); } float ProceduralSkyMaterial::get_ground_curve() const { @@ -123,7 +134,8 @@ float ProceduralSkyMaterial::get_ground_curve() const { void ProceduralSkyMaterial::set_ground_energy_multiplier(float p_multiplier) { ground_energy_multiplier = p_multiplier; - RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy_multiplier); + RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color * ground_energy_multiplier); + RS::get_singleton()->material_set_param(_get_material(), "ground_horizon_color", ground_horizon_color * ground_energy_multiplier); } float ProceduralSkyMaterial::get_ground_energy_multiplier() const { @@ -132,7 +144,7 @@ float ProceduralSkyMaterial::get_ground_energy_multiplier() const { void ProceduralSkyMaterial::set_sun_angle_max(float p_angle) { sun_angle_max = p_angle; - RS::get_singleton()->material_set_param(_get_material(), "sun_angle_max", Math::deg_to_rad(sun_angle_max)); + RS::get_singleton()->material_set_param(_get_material(), "sun_angle_max", Math::cos(Math::deg_to_rad(sun_angle_max))); } float ProceduralSkyMaterial::get_sun_angle_max() const { @@ -141,7 +153,9 @@ float ProceduralSkyMaterial::get_sun_angle_max() const { void ProceduralSkyMaterial::set_sun_curve(float p_curve) { sun_curve = p_curve; - RS::get_singleton()->material_set_param(_get_material(), "sun_curve", sun_curve); + // Actual curve passed to shader includes an ad hoc adjustment because the curve used to be + // in calculated in angles and now uses cosines. + RS::get_singleton()->material_set_param(_get_material(), "inv_sun_curve", 1.6f / Math::pow(sun_curve, 1.4f)); } float ProceduralSkyMaterial::get_sun_curve() const { @@ -153,7 +167,7 @@ void ProceduralSkyMaterial::set_use_debanding(bool p_use_debanding) { _update_shader(); // Only set if shader already compiled if (shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); } } @@ -174,11 +188,16 @@ Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } +// Internal function to grab the current shader RID. +// Must only be called if the shader is initialized. +RID ProceduralSkyMaterial::get_shader_cache() const { + return shader_cache[int(use_debanding) + (sky_cover.is_valid() ? 2 : 0)]; +} + RID ProceduralSkyMaterial::get_rid() const { _update_shader(); if (!shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(use_debanding)]); - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); shader_set = true; } return _get_material(); @@ -186,7 +205,7 @@ RID ProceduralSkyMaterial::get_rid() const { RID ProceduralSkyMaterial::get_shader_rid() const { _update_shader(); - return shader_cache[int(use_debanding)]; + return get_shader_cache(); } void ProceduralSkyMaterial::_validate_property(PropertyInfo &p_property) const { @@ -265,13 +284,15 @@ void ProceduralSkyMaterial::cleanup_shader() { if (shader_cache[0].is_valid()) { RS::get_singleton()->free(shader_cache[0]); RS::get_singleton()->free(shader_cache[1]); + RS::get_singleton()->free(shader_cache[2]); + RS::get_singleton()->free(shader_cache[3]); } } void ProceduralSkyMaterial::_update_shader() { MutexLock shader_lock(shader_mutex); if (shader_cache[0].is_null()) { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 4; i++) { shader_cache[i] = RS::get_singleton()->shader_create(); // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). @@ -283,75 +304,73 @@ shader_type sky; uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); -uniform float sky_curve : hint_range(0, 1) = 0.15; -uniform float sky_energy = 1.0; // In Lux. -uniform sampler2D sky_cover : filter_linear, source_color, hint_default_black; -uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform float inv_sky_curve : hint_range(1, 100) = 4.0; uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); uniform vec4 ground_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); -uniform float ground_curve : hint_range(0, 1) = 0.02; -uniform float ground_energy = 1.0; -uniform float sun_angle_max = 30.0; -uniform float sun_curve : hint_range(0, 1) = 0.15; +uniform float inv_ground_curve : hint_range(1, 100) = 30.0; +uniform float sun_angle_max = 0.877; +uniform float inv_sun_curve : hint_range(1, 100) = 22.78; uniform float exposure : hint_range(0, 128) = 1.0; +uniform sampler2D sky_cover : filter_linear, source_color, hint_default_black; +uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); + void sky() { - float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); - float c = (1.0 - v_angle / (PI * 0.5)); - vec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0)); - sky *= sky_energy; + float v_angle = clamp(EYEDIR.y, -1.0, 1.0); + vec3 sky = mix(sky_top_color.rgb, sky_horizon_color.rgb, clamp(pow(1.0 - v_angle, inv_sky_curve), 0.0, 1.0)); if (LIGHT0_ENABLED) { - float sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR)); - if (sun_angle < LIGHT0_SIZE) { + float sun_angle = dot(LIGHT0_DIRECTION, EYEDIR); + float sun_size = cos(LIGHT0_SIZE); + if (sun_angle > sun_size) { sky = LIGHT0_COLOR * LIGHT0_ENERGY; - } else if (sun_angle < sun_angle_max) { - float c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE); - sky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } else if (sun_angle > sun_angle_max) { + float c2 = (sun_size - sun_angle) / (sun_size - sun_angle_max); + sky = mix(sky, LIGHT0_COLOR * LIGHT0_ENERGY, clamp(pow(1.0 - c2, inv_sun_curve), 0.0, 1.0)); } } if (LIGHT1_ENABLED) { - float sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR)); - if (sun_angle < LIGHT1_SIZE) { + float sun_angle = dot(LIGHT1_DIRECTION, EYEDIR); + float sun_size = cos(LIGHT1_SIZE); + if (sun_angle > sun_size) { sky = LIGHT1_COLOR * LIGHT1_ENERGY; - } else if (sun_angle < sun_angle_max) { - float c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE); - sky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } else if (sun_angle > sun_angle_max) { + float c2 = (sun_size - sun_angle) / (sun_size - sun_angle_max); + sky = mix(sky, LIGHT1_COLOR * LIGHT1_ENERGY, clamp(pow(1.0 - c2, inv_sun_curve), 0.0, 1.0)); } } if (LIGHT2_ENABLED) { - float sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR)); - if (sun_angle < LIGHT2_SIZE) { + float sun_angle = dot(LIGHT2_DIRECTION, EYEDIR); + float sun_size = cos(LIGHT2_SIZE); + if (sun_angle > sun_size) { sky = LIGHT2_COLOR * LIGHT2_ENERGY; - } else if (sun_angle < sun_angle_max) { - float c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE); - sky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } else if (sun_angle > sun_angle_max) { + float c2 = (sun_size - sun_angle) / (sun_size - sun_angle_max); + sky = mix(sky, LIGHT2_COLOR * LIGHT2_ENERGY, clamp(pow(1.0 - c2, inv_sun_curve), 0.0, 1.0)); } } if (LIGHT3_ENABLED) { - float sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR)); - if (sun_angle < LIGHT3_SIZE) { + float sun_angle = dot(LIGHT3_DIRECTION, EYEDIR); + float sun_size = cos(LIGHT3_SIZE); + if (sun_angle > sun_size) { sky = LIGHT3_COLOR * LIGHT3_ENERGY; - } else if (sun_angle < sun_angle_max) { - float c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE); - sky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } else if (sun_angle > sun_angle_max) { + float c2 = (sun_size - sun_angle) / (sun_size - sun_angle_max); + sky = mix(sky, LIGHT3_COLOR * LIGHT3_ENERGY, clamp(pow(1.0 - c2, inv_sun_curve), 0.0, 1.0)); } } - vec4 sky_cover_texture = texture(sky_cover, SKY_COORDS); - sky += (sky_cover_texture.rgb * sky_cover_modulate.rgb) * sky_cover_texture.a * sky_cover_modulate.a * sky_energy; - - c = (v_angle - (PI * 0.5)) / (PI * 0.5); - vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0)); - ground *= ground_energy; + %s + %s + vec3 ground = mix(ground_bottom_color.rgb, ground_horizon_color.rgb, clamp(pow(1.0 + v_angle, inv_ground_curve), 0.0, 1.0)); COLOR = mix(ground, sky, step(0.0, EYEDIR.y)) * exposure; } )", - i ? "render_mode use_debanding;" : "")); + (i % 2) ? "render_mode use_debanding;" : "", i > 1 ? "vec4 sky_cover_texture = texture(sky_cover, SKY_COORDS);" : "", i > 1 ? "sky += (sky_cover_texture.rgb * sky_cover_modulate.rgb) * sky_cover_texture.a * sky_cover_modulate.a;" : "")); } } } @@ -583,7 +602,7 @@ void PhysicalSkyMaterial::set_use_debanding(bool p_use_debanding) { _update_shader(); // Only set if shader already compiled if (shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); } } @@ -598,6 +617,10 @@ void PhysicalSkyMaterial::set_night_sky(const Ref &p_night_sky) { } else { RS::get_singleton()->material_set_param(_get_material(), "night_sky", Variant()); } + + if (shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); + } } Ref PhysicalSkyMaterial::get_night_sky() const { @@ -608,11 +631,16 @@ Shader::Mode PhysicalSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } +// Internal function to grab the current shader RID. +// Must only be called if the shader is initialized. +RID PhysicalSkyMaterial::get_shader_cache() const { + return shader_cache[int(use_debanding) + (night_sky.is_valid() ? 2 : 0)]; +} + RID PhysicalSkyMaterial::get_rid() const { _update_shader(); if (!shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(use_debanding)]); - RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), get_shader_cache()); shader_set = true; } return _get_material(); @@ -620,7 +648,7 @@ RID PhysicalSkyMaterial::get_rid() const { RID PhysicalSkyMaterial::get_shader_rid() const { _update_shader(); - return shader_cache[int(use_debanding)]; + return get_shader_cache(); } void PhysicalSkyMaterial::_validate_property(PropertyInfo &p_property) const { @@ -630,7 +658,7 @@ void PhysicalSkyMaterial::_validate_property(PropertyInfo &p_property) const { } Mutex PhysicalSkyMaterial::shader_mutex; -RID PhysicalSkyMaterial::shader_cache[2]; +RID PhysicalSkyMaterial::shader_cache[4]; void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rayleigh_coefficient", "rayleigh"), &PhysicalSkyMaterial::set_rayleigh_coefficient); @@ -687,13 +715,15 @@ void PhysicalSkyMaterial::cleanup_shader() { if (shader_cache[0].is_valid()) { RS::get_singleton()->free(shader_cache[0]); RS::get_singleton()->free(shader_cache[1]); + RS::get_singleton()->free(shader_cache[2]); + RS::get_singleton()->free(shader_cache[3]); } } void PhysicalSkyMaterial::_update_shader() { MutexLock shader_lock(shader_mutex); if (shader_cache[0].is_null()) { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 4; i++) { shader_cache[i] = RS::get_singleton()->shader_create(); // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). @@ -730,7 +760,7 @@ float henyey_greenstein(float cos_theta, float g) { void sky() { if (LIGHT0_ENABLED) { float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 ); - float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * LIGHT0_ENERGY; + float sun_energy = max(0.0, 0.757 * zenith_angle) * LIGHT0_ENERGY; float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0); // Rayleigh coefficients. @@ -740,8 +770,8 @@ void sky() { vec3 mie_beta = turbidity * mie * mie_color.rgb * 0.000434; // Optical length. - float zenith = acos(max(0.0, dot(UP, EYEDIR))); - float optical_mass = 1.0 / (cos(zenith) + 0.15 * pow(93.885 - degrees(zenith), -1.253)); + float zenith = max(0.0, dot(UP, EYEDIR)); + float optical_mass = 1.0 / (zenith + 0.15 * pow(3.885 + 54.5 * zenith, -1.253)); float rayleigh_scatter = rayleigh_zenith_size * optical_mass; float mie_scatter = mie_zenith_size * optical_mass; @@ -766,22 +796,22 @@ void sky() { // Solar disk and out-scattering. float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale); - float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5); + float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale * 0.5); float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta); vec3 L0 = (sun_energy * extinction) * sundisk * LIGHT0_COLOR; - L0 += texture(night_sky, SKY_COORDS).xyz * extinction; + %s vec3 color = Lin + L0; COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade)))); COLOR *= exposure; } else { // There is no sun, so display night_sky and nothing else. - COLOR = texture(night_sky, SKY_COORDS).xyz; + %s COLOR *= exposure; } } )", - i ? "render_mode use_debanding;" : "")); + (i % 2) ? "render_mode use_debanding;" : "", i > 1 ? "L0 += texture(night_sky, SKY_COORDS).xyz * extinction;" : "", i > 1 ? "COLOR = texture(night_sky, SKY_COORDS).xyz;" : "")); } } } diff --git a/scene/resources/3d/sky_material.h b/scene/resources/3d/sky_material.h index b452f3d59af..0a6516994ce 100644 --- a/scene/resources/3d/sky_material.h +++ b/scene/resources/3d/sky_material.h @@ -55,10 +55,12 @@ private: float global_energy_multiplier = 1.0f; static Mutex shader_mutex; - static RID shader_cache[2]; + static RID shader_cache[4]; static void _update_shader(); mutable bool shader_set = false; + RID get_shader_cache() const; + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const; @@ -164,7 +166,9 @@ class PhysicalSkyMaterial : public Material { private: static Mutex shader_mutex; - static RID shader_cache[2]; + static RID shader_cache[4]; + + RID get_shader_cache() const; float rayleigh = 0.0f; Color rayleigh_color; diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index 105a42d7083..4b57648eb34 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -187,6 +187,32 @@ vec4 fog_process(vec3 view, vec3 sky_color) { return vec4(fog_color, 1.0); } +// Eberly approximation from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/. +// input [-1, 1] and output [0, PI] +float acos_approx(float p_x) { + float x = abs(p_x); + float res = -0.156583f * x + (M_PI / 2.0); + res *= sqrt(1.0f - x); + return (p_x >= 0) ? res : M_PI - res; +} + +// Based on https://math.stackexchange.com/questions/1098487/atan2-faster-approximation +// but using the Eberly coefficients from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/. +float atan2_approx(float y, float x) { + float a = min(abs(x), abs(y)) / max(abs(x), abs(y)); + float s = a * a; + float poly = 0.0872929f; + poly = -0.301895f + poly * s; + poly = 1.0f + poly * s; + poly = poly * a; + + float r = abs(y) > abs(x) ? (M_PI / 2.0) - poly : poly; + r = x < 0.0 ? M_PI - r : r; + r = y < 0.0 ? -r : r; + + return r; +} + void main() { vec3 cube_normal; #ifdef USE_MULTIVIEW @@ -207,7 +233,7 @@ void main() { vec2 uv = uv_interp * 0.5 + 0.5; - vec2 panorama_coords = vec2(atan(cube_normal.x, -cube_normal.z), acos(cube_normal.y)); + vec2 panorama_coords = vec2(atan2_approx(cube_normal.x, -cube_normal.z), acos_approx(cube_normal.y)); if (panorama_coords.x < 0.0) { panorama_coords.x += M_PI * 2.0;