EEVEE: Lights: Add Volume and diffuse light power slider

This adds 2 new sliders for light objects that modulates the diffuse
light and the volume light intensities.

This also changes the way volume light is computed using point lamp
representation. We use "Point Light Attenuation Without Singularity"
from Cem Yuksel instead of the usual inverse square law.
This commit is contained in:
Clément Foucault 2021-03-19 12:07:23 +01:00
parent fb3e5b7f98
commit 884f934a85
Notes: blender-bot 2024-01-16 18:05:25 +01:00
Referenced by issue #87182, EEVEE: Volumetric shadows disappear when facing sun light
12 changed files with 100 additions and 42 deletions

View File

@ -97,7 +97,12 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
col = layout.column()
col.prop(light, "color")
col.prop(light, "energy")
col.separator()
col.prop(light, "diffuse_factor", text="Diffuse")
col.prop(light, "specular_factor", text="Specular")
col.prop(light, "volume_factor", text="Volume")
col.separator()

View File

@ -36,6 +36,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@ -1915,5 +1916,12 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "diff_fac")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->diff_fac = 1.0f;
light->volume_fac = 1.0f;
}
}
}
}

View File

@ -50,7 +50,7 @@ static float light_attenuation_radius_get(const Light *la, float light_threshold
/* Compute max light power. */
float power = max_fff(la->r, la->g, la->b);
power *= fabsf(la->energy / 100.0f);
power *= max_ff(1.0f, la->spec_fac);
power *= max_fff(la->diff_fac, la->spec_fac, la->volume_fac);
/* Compute the distance (using the inverse square law)
* at which the light power reaches the light_threshold. */
float distance = sqrtf(max_ff(1e-16, power / max_ff(1e-16, light_threshold)));
@ -75,6 +75,8 @@ static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const
else {
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));
}
else if (la->type == LA_SUN) {
evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
@ -103,8 +105,8 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
/* for point lights (a.k.a radius == 0.0) */
// power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */
}
else {
power = 1.0f / (evli->radius * evli->radius * M_PI); /* 1/(r²*Pi) */
else { /* LA_SUN */
power = 1.0f / (evli->radius * evli->radius * M_PI);
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we
* cannot reproduce so we account for that by scaling the light power. This function is the
* result of a rough manual fitting. */
@ -113,11 +115,30 @@ 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)
{
/* 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;
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
/* Match cycles. Empirical fit... must correspond to some constant. */
power *= 0.0792f;
}
else { /* LA_SUN */
/* Nothing to do. */
}
return power;
}
/* Update buffer with light data */
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
{
Light *la = (Light *)ob->data;
float mat[4][4], scale[3], power, att_radius;
const Light *la = (Light *)ob->data;
float mat[4][4], scale[3], att_radius;
const DRWContextState *draw_ctx = DRW_context_state_get();
const float light_threshold = draw_ctx->scene->eevee.light_threshold;
@ -128,7 +149,9 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
/* Color */
copy_v3_v3(evli->color, &la->r);
evli->diff = la->diff_fac;
evli->spec = la->spec_fac;
evli->volume = la->volume_fac;
/* Influence Radius */
att_radius = light_attenuation_radius_get(la, light_threshold);
@ -163,8 +186,10 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
evli->light_type = LAMPTYPE_AREA_ELLIPSE;
}
power = light_shape_power_get(la, evli);
mul_v3_fl(evli->color, power * la->energy);
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);
/* No shadow by default */
evli->shadow_id = -1.0f;

View File

@ -484,11 +484,12 @@ typedef struct EEVEE_RenderPassData {
/* ************ LIGHT UBO ************* */
typedef struct EEVEE_Light {
float position[3], invsqrdist;
float color[3], spec;
float color[3], invsqrdist_volume;
float spotsize, spotblend, radius, shadow_id;
float rightvec[3], sizex;
float upvec[3], sizey;
float forwardvec[3], light_type;
float diff, spec, volume, _pad0[1];
} EEVEE_Light;
/* Special type for elliptic area lights, matches lamps_lib.glsl */

View File

@ -45,7 +45,8 @@ void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
/* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion
* factor here. */
cl_out.radiance += light.data.l_color * (light.vis * light.contact_shadow * radiance);
cl_out.radiance += light.data.l_color *
(light.data.l_diff * light.vis * light.contact_shadow * radiance);
}
void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in,

View File

@ -32,7 +32,7 @@ void closure_Translucent_light_eval(ClosureInputTranslucent cl_in,
inout ClosureOutputTranslucent cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
cl_out.radiance += light.data.l_color * (light.vis * radiance);
cl_out.radiance += light.data.l_color * (light.data.l_diff * light.vis * radiance);
}
void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in,

View File

@ -14,18 +14,22 @@
struct LightData {
vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
vec4 color_spec; /* w : Spec Intensity */
vec4 color_influence_volume; /* w : InfluenceRadius but for Volume power */
vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */
vec4 diff_spec_volume; /* xyz: Diffuse/Spec/Volume power, w: unused. */
};
/* convenience aliases */
#define l_color color_spec.rgb
#define l_spec color_spec.a
#define l_color color_influence_volume.rgb
#define l_diff diff_spec_volume.x
#define l_spec diff_spec_volume.y
#define l_volume diff_spec_volume.z
#define l_position position_influence.xyz
#define l_influence position_influence.w
#define l_influence_volume color_influence_volume.w
#define l_sizex rightvec_sizex.w
#define l_sizey upvec_sizey.w
#define l_right rightvec_sizex.xyz

View File

@ -64,38 +64,35 @@ float phase_function(vec3 v, vec3 l, float g)
vec3 light_volume(LightData ld, vec4 l_vector)
{
float power;
/* TODO : Area lighting ? */
/* XXX : Removing Area Power. */
/* TODO : put this out of the shader. */
/* See eevee_light_setup(). */
if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
power = (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0);
if (ld.l_type == AREA_ELLIPSE) {
power *= M_PI * 0.25;
float power = 1.0;
if (ld.l_type != SUN) {
/**
* Using "Point Light Attenuation Without Singularity" from Cem Yuksel
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
* http://www.cemyuksel.com/research/pointlightattenuation/
**/
float d = l_vector.w;
float d_sqr = sqr(d);
float r_sqr = sqr(ld.l_radius);
/* Using reformulation that has better numerical percision. */
power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr));
if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(-ld.l_forward, l_vector.xyz / l_vector.w));
}
power *= 20.0 *
max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */
}
else if (ld.l_type == SUN) {
power = ld.l_radius * ld.l_radius * M_PI; /* Removing area light power*/
power /= 1.0f + (ld.l_radius * ld.l_radius * 0.5f);
power *= M_PI * 0.5; /* Matching cycles. */
return ld.l_color * ld.l_volume * power;
}
vec3 light_volume_light_vector(LightData ld, vec3 P)
{
if (ld.l_type == SUN) {
return -ld.l_forward;
}
else {
power = (4.0 * ld.l_radius * ld.l_radius) * (1.0 / 10.0);
power *= M_2PI; /* Matching cycles with point light. */
return ld.l_position - P;
}
power /= (l_vector.w * l_vector.w);
/* OPTI: find a better way than calculating this on the fly */
float lum = dot(ld.l_color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
vec3 tint = (lum > 0.0) ? ld.l_color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
lum = min(lum * power, volLightClamp);
return tint * lum;
}
#define VOLUMETRIC_SHADOW_MAX_STEP 128.0

View File

@ -44,7 +44,7 @@ void main()
LightData ld = lights_data[i];
vec4 l_vector;
l_vector.xyz = (ld.l_type == SUN) ? -ld.l_forward : ld.l_position - P;
l_vector.xyz = light_volume_light_vector(ld, P);
l_vector.w = length(l_vector.xyz);
float Vis = light_visibility(ld, P, l_vector);

View File

@ -63,7 +63,9 @@
.contact_bias = 0.03f, \
.contact_spread = 0.2f, \
.contact_thickness = 0.2f, \
.diff_fac = 1.0f, \
.spec_fac = 1.0f, \
.volume_fac = 1.0f, \
.att_dist = 40.0f, \
.sun_angle = DEG2RADF(0.526f), \
}

View File

@ -93,6 +93,7 @@ typedef struct Light {
float contact_spread; /* DEPRECATED kept for compatibility. */
float contact_thickness;
float diff_fac, volume_fac;
float spec_fac, att_dist;
/* preview */

View File

@ -150,11 +150,25 @@ static void rna_def_light(BlenderRNA *brna)
prop = RNA_def_property(srna, "specular_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "spec_fac");
RNA_def_property_range(prop, 0.0f, 9999.0f);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 2);
RNA_def_property_ui_text(prop, "Specular Factor", "Specular reflection multiplier");
RNA_def_property_update(prop, 0, "rna_Light_update");
prop = RNA_def_property(srna, "diffuse_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "diff_fac");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 2);
RNA_def_property_ui_text(prop, "Diffuse Factor", "Diffuse reflection multiplier");
RNA_def_property_update(prop, 0, "rna_Light_update");
prop = RNA_def_property(srna, "volume_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "volume_fac");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 2);
RNA_def_property_ui_text(prop, "Volume Factor", "Volume light multiplier");
RNA_def_property_update(prop, 0, "rna_Light_update");
prop = RNA_def_property(srna, "use_custom_distance", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "mode", LA_CUSTOM_ATTENUATION);
RNA_def_property_ui_text(prop,