Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize ProceduralSkyMaterial by removing uses of acos and simplifying logic #101973

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 74 additions & 56 deletions scene/resources/3d/sky_material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -65,7 +67,8 @@ float ProceduralSkyMaterial::get_sky_curve() const {

void ProceduralSkyMaterial::set_sky_energy_multiplier(float p_multiplier) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setter must also update "sky_cover_modulate" material param

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);
}

float ProceduralSkyMaterial::get_sky_energy_multiplier() const {
Expand All @@ -74,11 +77,16 @@ float ProceduralSkyMaterial::get_sky_energy_multiplier() const {

void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &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<Texture2D> ProceduralSkyMaterial::get_sky_cover() const {
Expand All @@ -87,7 +95,7 @@ Ref<Texture2D> 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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sky energy multiplier is multiplying alpha, not the entire thing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's no issue, see how vec4 sky_cover_texture was previously calculated in the shader vs. how it is done now. It appears equivalent to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you say so, just stood out to me at a glance

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could remove one more multiply by pre multiplying sky color modulate by its own alpha and the energy multiplier

}

Color ProceduralSkyMaterial::get_sky_cover_modulate() const {
Expand All @@ -96,7 +104,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 {
Expand All @@ -105,7 +113,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 {
Expand All @@ -114,7 +122,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 {
Expand All @@ -123,7 +133,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 {
Expand All @@ -132,7 +143,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 {
Expand All @@ -141,7 +152,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 {
Expand All @@ -153,7 +166,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());
}
}

Expand All @@ -174,19 +187,24 @@ 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();
}

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 {
Expand Down Expand Up @@ -265,13 +283,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).
Expand All @@ -283,75 +303,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;" : ""));
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion scene/resources/3d/sky_material.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ class ProceduralSkyMaterial : public Material {
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;
Expand Down