diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index e76bb5dc9c7..16c378dcedd 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -729,6 +729,8 @@
Default specular blob.
+ [b]Note:[/b] Forward+ uses multiscattering for more accurate reflections, although the impact of multiscattering is more noticeable on rough metallic surfaces than on smooth, non-metallic surfaces.
+ [b]Note:[/b] Mobile and Compatibility don't perform multiscattering for performance reasons. Instead, they perform single scattering, which means rough metallic surfaces may look slightly darker than intended.
Toon blob which changes size based on roughness.
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index ff1e6b42b48..dfb352f91c6 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -3205,6 +3205,14 @@ void RenderForwardClustered::_update_render_base_uniform_set() {
uniforms.push_back(u);
}
+ {
+ RD::Uniform u;
+ u.binding = 16;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.append_id(dfg_lut.texture);
+ uniforms.push_back(u);
+ }
+
render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET);
}
}
@@ -4951,6 +4959,45 @@ RenderForwardClustered::RenderForwardClustered() {
best_fit_normal.shader.version_free(best_fit_normal.shader_version);
}
+ /* DFG LUT */
+ {
+ Vector modes;
+ modes.push_back("\n");
+ dfg_lut.shader.initialize(modes);
+ dfg_lut.shader_version = dfg_lut.shader.version_create();
+ dfg_lut.pipeline = RD::get_singleton()->compute_pipeline_create(dfg_lut.shader.version_get_shader(dfg_lut.shader_version, 0));
+
+ RD::TextureFormat tformat;
+ tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tformat.width = 128;
+ tformat.height = 128;
+ tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ tformat.texture_type = RD::TEXTURE_TYPE_2D;
+ dfg_lut.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView());
+
+ RID shader = dfg_lut.shader.version_get_shader(dfg_lut.shader_version, 0);
+ ERR_FAIL_COND(shader.is_null());
+
+ Vector uniforms;
+
+ {
+ RD::Uniform u;
+ u.binding = 0;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.append_id(dfg_lut.texture);
+ uniforms.push_back(u);
+ }
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader, 0);
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, dfg_lut.pipeline);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0);
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, tformat.width, tformat.height, 1);
+ RD::get_singleton()->compute_list_end();
+
+ dfg_lut.shader.version_free(dfg_lut.shader_version);
+ }
+
_update_shader_quality_settings();
_update_global_pipeline_data_requirements_from_project();
@@ -5000,6 +5047,7 @@ RenderForwardClustered::~RenderForwardClustered() {
RD::get_singleton()->free(shadow_sampler);
RSG::light_storage->directional_shadow_atlas_set_size(0);
RD::get_singleton()->free(best_fit_normal.texture);
+ RD::get_singleton()->free(dfg_lut.texture);
{
for (const RID &rid : scene_state.uniform_buffers) {
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 745e9aef119..0ee21ad2252 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -43,6 +43,7 @@
#include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
#include "servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/forward_clustered/integrate_dfg.glsl.gen.h"
#define RB_SCOPE_FORWARD_CLUSTERED SNAME("forward_clustered")
@@ -180,6 +181,13 @@ private:
RID texture;
} best_fit_normal;
+ struct IntegrateDFG {
+ IntegrateDfgShaderRD shader;
+ RID shader_version;
+ RID pipeline;
+ RID texture;
+ } dfg_lut;
+
enum PassMode {
PASS_MODE_COLOR,
PASS_MODE_SHADOW,
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/integrate_dfg.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/integrate_dfg.glsl
new file mode 100644
index 00000000000..4434f96f243
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/integrate_dfg.glsl
@@ -0,0 +1,156 @@
+#[compute]
+#version 450
+
+// References:
+// https://www.gamedevs.org/uploads/real-shading-in-unreal-engine-4.pdf
+// https://google.github.io/filament/Filament.html
+// https://learnopengl.com/PBR/IBL/Specular-IBL
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D current_image;
+
+#define M_PI 3.14159265359
+#define SAMPLE_COUNT 1024
+#define SIZE 128
+
+#define saturate(x) clamp(x, 0, 1)
+
+// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+// efficient VanDerCorpus calculation
+float radical_inverse_vdc(uint bits) {
+ bits = (bits << 16u) | (bits >> 16u);
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+ return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+}
+
+vec2 hammersley(uint i, float n) {
+ return vec2(float(i) / n, radical_inverse_vdc(i));
+}
+
+vec3 importance_sample_ggx(vec2 Xi, vec3 N, float roughness) {
+ float a = roughness * roughness;
+
+ float phi = 2.0 * M_PI * Xi.x;
+ float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+
+ // from spherical coordinates to cartesian coordinates - halfway vector
+ vec3 H;
+ H.x = cos(phi) * sinTheta;
+ H.y = sin(phi) * sinTheta;
+ H.z = cosTheta;
+
+ // from tangent-space H vector to world-space sample vector
+ vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ vec3 tangent = normalize(cross(up, N));
+ vec3 bitangent = cross(N, tangent);
+
+ vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+ return normalize(sampleVec);
+}
+
+float geometry_schlick_ggx(float NdotV, float roughness) {
+ // note that we use a different k for IBL
+ float a = roughness;
+ float k = (a * a) / 2.0;
+
+ float nom = NdotV;
+ float denom = NdotV * (1.0 - k) + k;
+
+ return nom / denom;
+}
+
+float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) {
+ float NdotV = saturate(dot(N, V));
+ float NdotL = saturate(dot(N, L));
+ float ggx2 = geometry_schlick_ggx(NdotV, roughness);
+ float ggx1 = geometry_schlick_ggx(NdotL, roughness);
+
+ return ggx1 * ggx2;
+}
+
+vec3 importance_uniform_sample(vec2 u) {
+ float phi = 2.0f * M_PI * u.x;
+ float cosTheta = 1 - u.y;
+ float sinTheta = sqrt(1 - cosTheta * cosTheta);
+ return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+}
+
+float distribution_charlie(float NoH, float roughness) {
+ // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
+ float a = roughness * roughness;
+ float invAlpha = 1 / a;
+ float cos2h = NoH * NoH;
+ float sin2h = 1 - cos2h;
+ return (2.0f + invAlpha) * pow(sin2h, invAlpha * 0.5f) / (2.0f * M_PI);
+}
+
+float visibility_ashikhmin(float NoV, float NoL) {
+ // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
+ return 1 / (4 * (NoL + NoV - NoL * NoV));
+}
+
+void integrate_brdfs(float n_dot_v, float roughness, out vec2 brdf, out float cloth_brdf) {
+ vec3 v = vec3(sqrt(1.0 - n_dot_v * n_dot_v), 0, n_dot_v);
+ vec3 n = vec3(0.0f, 0.0f, 1.0f);
+ float A = 0.0f;
+ float B = 0.0f;
+ float C = 0.0f;
+
+ for (uint i = 0; i < SAMPLE_COUNT; ++i) {
+ vec2 Xi = hammersley(i, SAMPLE_COUNT);
+ vec3 h = importance_sample_ggx(Xi, n, roughness);
+ vec3 l = normalize(2.0 * dot(v, h) * h - v);
+
+ float n_dot_l = saturate(l.z);
+ float n_dot_h = saturate(h.z);
+ float v_dot_h = saturate(dot(v, h));
+
+ if (n_dot_l > 0.0) {
+ float G = geometry_smith(n, v, l, roughness);
+ float G_Vis = (G * v_dot_h) / (n_dot_h * n_dot_v);
+ float Fc = pow(1.0 - v_dot_h, 5.0);
+
+ // LDFG term for multiscattering
+ // https://google.github.io/filament/Filament.html#toc5.3.4.7
+ A += Fc * G_Vis;
+ B += G_Vis;
+ }
+
+ // Cloth BRDF calculations
+ // https://github.com/google/filament/blob/main/libs/ibl/src/CubemapIBL.cpp#L856-L874
+ vec3 h_cloth = importance_uniform_sample(Xi);
+ vec3 l_cloth = normalize(2.0 * dot(v, h_cloth) * h_cloth - v);
+ float n_dot_l_cloth = saturate(l_cloth.z);
+ float n_dot_h_cloth = saturate(h_cloth.z);
+ float v_dot_h_cloth = saturate(dot(v, h_cloth));
+
+ if (n_dot_l_cloth > 0.0) {
+ float v_cloth = visibility_ashikhmin(n_dot_v, n_dot_l_cloth);
+ float d_cloth = distribution_charlie(n_dot_h_cloth, roughness);
+ C += v_cloth * d_cloth * n_dot_l_cloth * v_dot_h_cloth;
+ }
+ }
+
+ A /= float(SAMPLE_COUNT);
+ B /= float(SAMPLE_COUNT);
+ C *= (4.0 * 2.0 * M_PI / SAMPLE_COUNT);
+
+ brdf = vec2(A, B);
+ cloth_brdf = C;
+}
+
+void main() {
+ ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+ float roughness = float(pos.y + 0.5f) / SIZE;
+ float NdotV = float(pos.x + 0.5f) / SIZE;
+ vec2 brdf;
+ float cloth_brdf;
+ integrate_brdfs(NdotV, roughness, brdf, cloth_brdf);
+ ivec2 out_pos = ivec2(pos.x, (SIZE - 1) - pos.y);
+ imageStore(current_image, out_pos, vec4(brdf, cloth_brdf, 1.0));
+}
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 b1812d72f8a..dc762e43d9e 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
@@ -1151,6 +1151,7 @@ void fragment_shader(in SceneData scene_data) {
float clearcoat_roughness = 0.0;
float anisotropy = 0.0;
vec2 anisotropy_flow = vec2(1.0, 0.0);
+ vec3 energy_compensation = vec3(1.0);
#ifndef FOG_DISABLED
vec4 fog = vec4(0.0);
#endif // !FOG_DISABLED
@@ -2004,19 +2005,15 @@ void fragment_shader(in SceneData scene_data) {
//simplify for toon, as
specular_light *= specular * metallic * albedo * 2.0;
#else
+ // Base Layer
+ float NdotV = clamp(dot(normal, view), 0.0001, 1.0);
+ vec2 envBRDF = prefiltered_dfg(roughness, NdotV).xy;
+ // Multiscattering
+ energy_compensation = get_energy_compensation(f0, envBRDF.y);
- // scales the specular reflections, needs to be computed before lighting happens,
- // but after environment, GI, and reflection probes are added
- // Environment brdf approximation (Lazarov 2013)
- // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
- const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
- const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
- vec4 r = roughness * c0 + c1;
- float ndotv = clamp(dot(normal, view), 0.0, 1.0);
- float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
- vec2 env = vec2(-1.04, 1.04) * a004 + r.zw;
-
- specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
+ // cheap luminance approximation
+ float f90 = clamp(50.0 * f0.g, metallic, 1.0);
+ specular_light *= energy_compensation * (f90 * envBRDF.x + f0 * envBRDF.y);
#endif
}
@@ -2415,7 +2412,7 @@ void fragment_shader(in SceneData scene_data) {
#else
directional_lights.data[i].color * directional_lights.data[i].energy * tint,
#endif
- true, shadow, f0, orms, directional_lights.data[i].specular, albedo, alpha, screen_uv,
+ true, shadow, f0, orms, directional_lights.data[i].specular, albedo, alpha, screen_uv, energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2485,7 +2482,7 @@ void fragment_shader(in SceneData scene_data) {
continue; // Statically baked light and object uses lightmap, skip
}
- light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv,
+ light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv, energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2553,7 +2550,7 @@ void fragment_shader(in SceneData scene_data) {
continue; // Statically baked light and object uses lightmap, skip
}
- light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv,
+ light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv, energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index 3257a0d0563..32838e0a78c 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -280,6 +280,8 @@ layout(set = 0, binding = 14) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAP
layout(set = 0, binding = 15) uniform texture2D best_fit_normal_texture;
+layout(set = 0, binding = 16) uniform texture2D dfg;
+
/* Set 1: Render Pass (changes per render pass) */
layout(set = 1, binding = 0, std140) uniform SceneDataBlock {
@@ -454,6 +456,18 @@ vec4 normal_roughness_compatibility(vec4 p_normal_roughness) {
return vec4(normalize(p_normal_roughness.xyz * 2.0 - 1.0) * 0.5 + 0.5, roughness);
}
+// https://google.github.io/filament/Filament.html#toc5.3.4.7
+// Note: The roughness value is inverted
+vec3 prefiltered_dfg(float lod, float NoV) {
+ return textureLod(sampler2D(dfg, SAMPLER_LINEAR_CLAMP), vec2(NoV, 1.0 - lod), 0.0).rgb;
+}
+
+// Compute multiscatter compensation
+// https://google.github.io/filament/Filament.html#listing_energycompensationimpl
+vec3 get_energy_compensation(vec3 f0, float env) {
+ return 1.0 + f0 * (1.0 / env - 1.0);
+}
+
/* Set 2 Skeleton & Instancing (can change per item) */
layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
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 83fd4bff89a..eba80930663 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
@@ -1714,7 +1714,8 @@ void main() {
light_compute(normal, directional_lights.data[i].direction, view, size_A,
directional_lights.data[i].color * directional_lights.data[i].energy * tint,
- true, shadow, f0, orms, directional_lights.data[i].specular, albedo, alpha, screen_uv,
+ true, shadow, f0, orms, directional_lights.data[i].specular, albedo, alpha,
+ screen_uv, vec3(1.0),
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -1745,7 +1746,7 @@ void main() {
uvec2 omni_indices = instances.data[draw_call.instance_index].omni_lights;
for (uint i = 0; i < sc_omni_lights(); i++) {
uint light_index = (i > 3) ? ((omni_indices.y >> ((i - 4) * 8)) & 0xFF) : ((omni_indices.x >> (i * 8)) & 0xFF);
- light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv,
+ light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv, vec3(1.0),
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -1773,7 +1774,7 @@ void main() {
uvec2 spot_indices = instances.data[draw_call.instance_index].spot_lights;
for (uint i = 0; i < sc_spot_lights(); i++) {
uint light_index = (i > 3) ? ((spot_indices.y >> ((i - 4) * 8)) & 0xFF) : ((spot_indices.x >> (i * 8)) & 0xFF);
- light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv,
+ light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, scene_data.taa_frame_count, albedo, alpha, screen_uv, vec3(1.0),
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 48d68525676..586098e88e9 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -52,7 +52,7 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
return mix(vec3(dielectric), albedo, vec3(metallic));
}
-void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, uint orms, float specular_amount, vec3 albedo, inout float alpha, vec2 screen_uv,
+void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, uint orms, float specular_amount, vec3 albedo, inout float alpha, vec2 screen_uv, vec3 energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -230,7 +230,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
// https://google.github.io/filament/Filament.html#lighting/occlusion/specularocclusion
float f90 = clamp(dot(f0, vec3(50.0 * 0.33)), metallic, 1.0);
vec3 F = f0 + (f90 - f0) * cLdotH5;
- vec3 specular_brdf_NL = cNdotL * D * F * G;
+ vec3 specular_brdf_NL = energy_compensation * (cNdotL * D * F * G);
specular_light += specular_brdf_NL * light_color * attenuation * specular_amount;
#endif
}
@@ -405,7 +405,7 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
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, vec3 f0, uint orms, float taa_frame_count, vec3 albedo, inout float alpha, vec2 screen_uv,
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 f0, uint orms, float taa_frame_count, vec3 albedo, inout float alpha, vec2 screen_uv, vec3 energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -667,7 +667,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
}
vec3 light_rel_vec_norm = light_rel_vec / light_length;
- light_compute(normal, light_rel_vec_norm, eye_vec, size, color, false, omni_attenuation * shadow, f0, orms, omni_lights.data[idx].specular_amount, albedo, alpha, screen_uv,
+ light_compute(normal, light_rel_vec_norm, eye_vec, size, color, false, omni_attenuation * shadow, f0, orms, omni_lights.data[idx].specular_amount, albedo, alpha, screen_uv, energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -702,7 +702,7 @@ vec2 normal_to_panorama(vec3 n) {
return panorama_coords;
}
-void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 f0, uint orms, float taa_frame_count, vec3 albedo, inout float alpha, vec2 screen_uv,
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 f0, uint orms, float taa_frame_count, vec3 albedo, inout float alpha, vec2 screen_uv, vec3 energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -871,7 +871,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
}
}
- light_compute(normal, light_rel_vec_norm, eye_vec, size, color, false, spot_attenuation * shadow, f0, orms, spot_lights.data[idx].specular_amount, albedo, alpha, screen_uv,
+ light_compute(normal, light_rel_vec_norm, eye_vec, size, color, false, spot_attenuation * shadow, f0, orms, spot_lights.data[idx].specular_amount, albedo, alpha, screen_uv, energy_compensation,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif