From 355f884b2f0932e0e1d50e9506d4c0e3bf6e2289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 19 Mar 2021 17:28:37 +0100 Subject: EEVEE: Volumetrics: Add Area light shape support Previously area lights were just considered as point lights. We now use a "most representative point" technique that make the light shape appearant and gives more homogenous result. This technique is quite cheap but it is not physically correct. So I came up with a power function to have almost the same intensity output as cycles in the general case. --- source/blender/draw/engines/eevee/eevee_lights.c | 16 ++++++++++++---- .../draw/engines/eevee/shaders/volumetric_lib.glsl | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'source') diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index 63d1cb7adc8..dff69bcdd52 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -72,7 +72,7 @@ static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const evli->sizey = max_ff(0.003f, la->area_size * scale[1] * 0.5f); } /* For volume point lighting. */ - evli->radius = max_ff(0.001f, hypotf(evli->sizex, evli->sizey)); + evli->radius = max_ff(0.001f, hypotf(evli->sizex, evli->sizey) * 0.5f); } else if (la->type == LA_SUN) { evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f)); @@ -111,14 +111,22 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) return power; } -static float light_shape_power_volume_get(const Light *la, float area_power) +static float light_shape_power_volume_get(const Light *la, + const EEVEE_Light *evli, + float area_power) { /* Volume light is evaluated as point lights. Remove the shape power. */ float power = 1.0f / area_power; - /* Make illumination power constant */ + if (la->type == LA_AREA) { /* Match cycles. Empirical fit... must correspond to some constant. */ power *= 0.0792f * M_PI; + /* This corrects for area light most representative point trick. The fit was found by + * reducing the average error compared to cycles. */ + float area = evli->sizex * evli->sizey; + float tmp = M_PI_2 / (M_PI_2 + sqrtf(area)); + /* Lerp between 1.0 and the limit (1 / pi). */ + power *= tmp + (1.0f - tmp) * M_1_PI; } else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { /* Match cycles. Empirical fit... must correspond to some constant. */ @@ -191,7 +199,7 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli) float shape_power = light_shape_power_get(la, evli); mul_v3_fl(evli->color, shape_power * la->energy); - evli->volume *= light_shape_power_volume_get(la, shape_power); + evli->volume *= light_shape_power_volume_get(la, evli, shape_power); /* No shadow by default */ evli->shadow_id = -1.0f; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl index 8c2d1f8a5cb..6d7ddd4e56e 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl @@ -90,6 +90,23 @@ vec3 light_volume_light_vector(LightData ld, vec3 P) if (ld.l_type == SUN) { return -ld.l_forward; } + else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) { + vec3 L = P - ld.l_position; + vec2 closest_point = vec2(dot(ld.l_right, L), dot(ld.l_up, L)); + vec2 max_pos = vec2(ld.l_sizex, ld.l_sizey); + closest_point /= max_pos; + + if (ld.l_type == AREA_ELLIPSE) { + closest_point /= max(1.0, length(closest_point)); + } + else { + closest_point = clamp(closest_point, -1.0, 1.0); + } + closest_point *= max_pos; + + vec3 L_prime = ld.l_right * closest_point.x + ld.l_up * closest_point.y; + return L_prime - L; + } else { return ld.l_position - P; } -- cgit v1.2.3