Eevee: Add Light Threshold value
This is an important change. Starting from now, all lights have a finite influence radius (similar to the old sphere option for BI). In order to avoid costly setup time, this distance is first computed automatically based on a light threshold. The distance is computed at the light origin and using the inverse square falloff. The setting can be found inside the render settings panel > shadow tab. This light threshold does not take the light shape into account an may not suit every case. That's why we provide a per lamp override where you can just set the cutt off distance (Light Properties Panel > Light > Custom Distance). The influence distance is also used as shadow far clip distance. This influence distance does not concerns sun lights that still have a far clip distance. --- This change is important because it makes it possible to cull lights an improve performance drastically in the future.
This commit is contained in:
parent
d082b18d87
commit
516e000aa9
|
@ -105,6 +105,36 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
|
|||
sub.prop(light, "size_y", text="Y")
|
||||
|
||||
|
||||
class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel):
|
||||
bl_label = "Custom Distance"
|
||||
bl_parent_id = "DATA_PT_EEVEE_light"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
light = context.light
|
||||
engine = context.engine
|
||||
|
||||
return (light and light.type != 'SUN') and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw_header(self, context):
|
||||
light = context.light
|
||||
|
||||
layout = self.layout
|
||||
layout.active = light.use_shadow
|
||||
layout.prop(light, "use_custom_distance", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
light = context.light
|
||||
layout.use_property_split = True
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.prop(light, "cutoff_distance", text="Distance")
|
||||
|
||||
|
||||
class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
|
||||
bl_label = "Shadow"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
@ -131,7 +161,8 @@ class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
|
|||
col = layout.column()
|
||||
sub = col.column(align=True)
|
||||
sub.prop(light, "shadow_buffer_clip_start", text="Clip Start")
|
||||
sub.prop(light, "shadow_buffer_clip_end", text="End")
|
||||
if light.type == 'SUN':
|
||||
sub.prop(light, "shadow_buffer_clip_end", text="End")
|
||||
|
||||
col.prop(light, "shadow_buffer_soft", text="Softness")
|
||||
|
||||
|
@ -281,6 +312,7 @@ classes = (
|
|||
DATA_PT_preview,
|
||||
DATA_PT_light,
|
||||
DATA_PT_EEVEE_light,
|
||||
DATA_PT_EEVEE_light_distance,
|
||||
DATA_PT_EEVEE_shadow,
|
||||
DATA_PT_EEVEE_shadow_contact,
|
||||
DATA_PT_EEVEE_shadow_cascaded_shadow_map,
|
||||
|
|
|
@ -390,6 +390,7 @@ class RENDER_PT_eevee_shadows(RenderButtonsPanel, Panel):
|
|||
col.prop(props, "shadow_cascade_size", text="Cascade Size")
|
||||
col.prop(props, "use_shadow_high_bitdepth")
|
||||
col.prop(props, "use_soft_shadows")
|
||||
col.prop(props, "light_threshold")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
|
||||
|
|
|
@ -89,6 +89,7 @@ void BKE_lamp_init(Lamp *la)
|
|||
la->contact_spread = 0.2f;
|
||||
la->contact_thickness = 0.2f;
|
||||
la->spec_fac = 1.0f;
|
||||
la->att_dist = 40.0f;
|
||||
|
||||
curvemapping_initialize(la->curfalloff);
|
||||
}
|
||||
|
|
|
@ -915,6 +915,7 @@ void BKE_scene_init(Scene *sce)
|
|||
sce->eevee.shadow_cascade_size = 1024;
|
||||
|
||||
sce->eevee.light_cache = NULL;
|
||||
sce->eevee.light_threshold = 0.01f;
|
||||
|
||||
sce->eevee.overscan = 3.0f;
|
||||
|
||||
|
|
|
@ -2265,6 +2265,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "light_threshold")) {
|
||||
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
|
||||
scene->eevee.light_threshold = 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "att_dist")) {
|
||||
for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
|
||||
la->att_dist = la->clipend;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "Brush", "char", "weightpaint_tool")) {
|
||||
/* Magic defines from old files (2.7x) */
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ extern char datatoc_shadow_store_frag_glsl[];
|
|||
extern char datatoc_shadow_copy_frag_glsl[];
|
||||
extern char datatoc_concentric_samples_lib_glsl[];
|
||||
|
||||
/* Prototype */
|
||||
/* Prototypes */
|
||||
static void eevee_light_setup(Object *ob, EEVEE_Light *evli);
|
||||
static float light_attenuation_radius_get(Lamp *la, float light_threshold);
|
||||
|
||||
/* *********** LIGHT BITS *********** */
|
||||
static void lightbits_set_single(EEVEE_LightBits *bitf, uint idx, bool val)
|
||||
|
@ -337,6 +338,9 @@ void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
|
|||
{
|
||||
EEVEE_LampsInfo *linfo = sldata->lamps;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const float threshold = draw_ctx->scene->eevee.light_threshold;
|
||||
|
||||
/* Step 1 find all lamps in the scene and setup them */
|
||||
if (linfo->num_light >= MAX_LIGHT) {
|
||||
printf("Too many lights in the scene !!!\n");
|
||||
|
@ -399,7 +403,7 @@ void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
|
|||
/* Saving lamp bounds for later. */
|
||||
BLI_assert(linfo->cpu_cube_len >= 0 && linfo->cpu_cube_len < MAX_LIGHT);
|
||||
copy_v3_v3(linfo->shadow_bounds[linfo->cpu_cube_len].center, ob->obmat[3]);
|
||||
linfo->shadow_bounds[linfo->cpu_cube_len].radius = la->clipend;
|
||||
linfo->shadow_bounds[linfo->cpu_cube_len].radius = light_attenuation_radius_get(la, threshold);
|
||||
|
||||
EEVEE_ShadowCubeData *data = &led->data.scd;
|
||||
/* Store indices. */
|
||||
|
@ -590,37 +594,25 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
|||
EEVEE_lights_update(sldata, vedata);
|
||||
}
|
||||
|
||||
/* Update buffer with lamp data */
|
||||
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
||||
float light_attenuation_radius_get(Lamp *la, float light_threshold)
|
||||
{
|
||||
Lamp *la = (Lamp *)ob->data;
|
||||
float mat[4][4], scale[3], power;
|
||||
if (la->mode & LA_CUSTOM_ATTENUATION)
|
||||
return la->att_dist;
|
||||
|
||||
/* Position */
|
||||
copy_v3_v3(evli->position, ob->obmat[3]);
|
||||
/* Compute max light power. */
|
||||
float power = max_fff(la->r, la->g, la->b);
|
||||
power *= fabsf(la->energy);
|
||||
power *= max_ff(1.0f, la->spec_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)));
|
||||
return distance;
|
||||
}
|
||||
|
||||
/* Color */
|
||||
copy_v3_v3(evli->color, &la->r);
|
||||
|
||||
evli->spec = la->spec_fac;
|
||||
|
||||
/* Influence Radius */
|
||||
evli->dist = la->dist;
|
||||
|
||||
/* Vectors */
|
||||
normalize_m4_m4_ex(mat, ob->obmat, scale);
|
||||
copy_v3_v3(evli->forwardvec, mat[2]);
|
||||
normalize_v3(evli->forwardvec);
|
||||
negate_v3(evli->forwardvec);
|
||||
|
||||
copy_v3_v3(evli->rightvec, mat[0]);
|
||||
normalize_v3(evli->rightvec);
|
||||
|
||||
copy_v3_v3(evli->upvec, mat[1]);
|
||||
normalize_v3(evli->upvec);
|
||||
|
||||
/* Spot size & blend */
|
||||
static void light_shape_parameters_set(EEVEE_Light *evli, const Lamp *la, float scale[3])
|
||||
{
|
||||
if (la->type == LA_SPOT) {
|
||||
/* Spot size & blend */
|
||||
evli->sizex = scale[0] / scale[2];
|
||||
evli->sizey = scale[1] / scale[2];
|
||||
evli->spotsize = cosf(la->spotsize * 0.5f);
|
||||
|
@ -639,16 +631,16 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
|||
else {
|
||||
evli->radius = max_ff(0.001f, la->area_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lamp Type */
|
||||
evli->lamptype = (float)la->type;
|
||||
|
||||
static float light_shape_power_get(const Lamp *la, const EEVEE_Light *evli)
|
||||
{
|
||||
float power;
|
||||
/* Make illumination power constant */
|
||||
if (la->type == LA_AREA) {
|
||||
power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
|
||||
80.0f; /* XXX : Empirical, Fit cycles power */
|
||||
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
|
||||
evli->lamptype = LAMPTYPE_AREA_ELLIPSE;
|
||||
/* Scale power to account for the lower area of the ellipse compared to the surrounding rectangle. */
|
||||
power *= 4.0f / M_PI;
|
||||
}
|
||||
|
@ -664,8 +656,54 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
|||
power = 1.0f / (evli->radius * evli->radius * M_PI); /* 1/(r²*Pi) */
|
||||
/* Make illumation 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. */
|
||||
power *= 1.0f + evli->radius * evli->radius * 0.5f;
|
||||
power += 1.0f / (2.0f * M_PI); /* power *= 1 + r²/2 */
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
/* Update buffer with lamp data */
|
||||
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
|
||||
{
|
||||
Lamp *la = (Lamp *)ob->data;
|
||||
float mat[4][4], scale[3], power, att_radius;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const float light_threshold = draw_ctx->scene->eevee.light_threshold;
|
||||
|
||||
/* Position */
|
||||
copy_v3_v3(evli->position, ob->obmat[3]);
|
||||
|
||||
/* Color */
|
||||
copy_v3_v3(evli->color, &la->r);
|
||||
|
||||
evli->spec = la->spec_fac;
|
||||
|
||||
/* Influence Radius */
|
||||
att_radius = light_attenuation_radius_get(la, light_threshold);
|
||||
/* Take the inverse square of this distance. */
|
||||
evli->invsqrdist = 1.0 / max_ff(1e-4f, att_radius * att_radius);
|
||||
|
||||
/* Vectors */
|
||||
normalize_m4_m4_ex(mat, ob->obmat, scale);
|
||||
copy_v3_v3(evli->forwardvec, mat[2]);
|
||||
normalize_v3(evli->forwardvec);
|
||||
negate_v3(evli->forwardvec);
|
||||
|
||||
copy_v3_v3(evli->rightvec, mat[0]);
|
||||
normalize_v3(evli->rightvec);
|
||||
|
||||
copy_v3_v3(evli->upvec, mat[1]);
|
||||
normalize_v3(evli->upvec);
|
||||
|
||||
light_shape_parameters_set(evli, la, scale);
|
||||
|
||||
/* Lamp Type */
|
||||
evli->lamptype = (float)la->type;
|
||||
if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
|
||||
evli->lamptype = LAMPTYPE_AREA_ELLIPSE;
|
||||
}
|
||||
|
||||
power = light_shape_power_get(la, evli);
|
||||
mul_v3_fl(evli->color, power * la->energy);
|
||||
|
||||
/* No shadow by default */
|
||||
|
@ -789,7 +827,7 @@ static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_La
|
|||
|
||||
ubo_data->bias = 0.05f * la->bias;
|
||||
ubo_data->near = la->clipsta;
|
||||
ubo_data->far = la->clipend;
|
||||
ubo_data->far = 1.0f / (evli->invsqrdist * evli->invsqrdist);
|
||||
ubo_data->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
|
||||
|
||||
evli->shadowid = (float)(sh_data->shadow_id);
|
||||
|
@ -1180,6 +1218,8 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
|||
EEVEE_StorageList *stl = vedata->stl;
|
||||
EEVEE_EffectsInfo *effects = stl->effects;
|
||||
EEVEE_LampsInfo *linfo = sldata->lamps;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const float light_threshold = draw_ctx->scene->eevee.light_threshold;
|
||||
Object *ob;
|
||||
int i;
|
||||
|
||||
|
@ -1196,7 +1236,7 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
|||
Lamp *la = (Lamp *)ob->data;
|
||||
BoundSphere bsphere = {
|
||||
.center = {ob->obmat[3][0], ob->obmat[3][1], ob->obmat[3][2]},
|
||||
.radius = la->clipend
|
||||
.radius = light_attenuation_radius_get(la, light_threshold)
|
||||
};
|
||||
cube_visible[i] = DRW_culling_sphere_test(&bsphere);
|
||||
}
|
||||
|
@ -1236,14 +1276,13 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
|
|||
EEVEE_ShadowCubeData *evscd = &led->data.scd;
|
||||
EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + evscd->cube_id;
|
||||
|
||||
perspective_m4(winmat, -la->clipsta, la->clipsta, -la->clipsta, la->clipsta, la->clipsta, la->clipend);
|
||||
|
||||
srd->clip_near = la->clipsta;
|
||||
srd->clip_far = la->clipend;
|
||||
copy_v3_v3(srd->position, cube_data->position);
|
||||
|
||||
srd->clip_far = light_attenuation_radius_get(la, light_threshold);
|
||||
srd->stored_texel_size = 1.0 / (float)linfo->shadow_cube_store_size;
|
||||
srd->exponent = la->bleedexp;
|
||||
copy_v3_v3(srd->position, cube_data->position);
|
||||
|
||||
perspective_m4(winmat, -srd->clip_near, srd->clip_near, -srd->clip_near, srd->clip_near, srd->clip_near, srd->clip_far);
|
||||
|
||||
DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
|
||||
|
||||
|
|
|
@ -349,7 +349,7 @@ typedef struct EEVEE_StorageList {
|
|||
|
||||
/* ************ LIGHT UBO ************* */
|
||||
typedef struct EEVEE_Light {
|
||||
float position[3], dist;
|
||||
float position[3], invsqrdist;
|
||||
float color[3], spec;
|
||||
float spotsize, spotblend, radius, shadowid;
|
||||
float rightvec[3], sizex;
|
||||
|
|
|
@ -24,7 +24,7 @@ uniform sampler2DArray planarDepth;
|
|||
|
||||
/* ------ Lights ----- */
|
||||
struct LightData {
|
||||
vec4 position_influence; /* w : InfluenceRadius */
|
||||
vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
|
||||
vec4 color_spec; /* w : Spec Intensity */
|
||||
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 */
|
||||
|
|
|
@ -157,6 +157,15 @@ float shadow_cascade(ShadowData sd, int scd_id, float texid, vec3 W)
|
|||
/* --------------------- Light Functions --------------------- */
|
||||
/* ----------------------------------------------------------- */
|
||||
#define MAX_MULTI_SHADOW 4
|
||||
/* From Frostbite PBR Course
|
||||
* Distance based attenuation
|
||||
* http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
|
||||
float distance_attenuation(float dist_sqr, float inv_sqr_influence)
|
||||
{
|
||||
float factor = dist_sqr * inv_sqr_influence;
|
||||
float fac = saturate(1.0 - factor * factor);
|
||||
return fac * fac;
|
||||
}
|
||||
|
||||
float light_visibility(LightData ld, vec3 W,
|
||||
#ifndef VOLUMETRICS
|
||||
|
@ -183,6 +192,9 @@ float light_visibility(LightData ld, vec3 W,
|
|||
else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
|
||||
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
|
||||
}
|
||||
if (ld.l_type != SUN) {
|
||||
vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence);
|
||||
}
|
||||
|
||||
#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
|
||||
/* shadowing */
|
||||
|
@ -343,6 +355,10 @@ vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
|
|||
#else
|
||||
vec3 vis = vec3(1.0);
|
||||
|
||||
if (ld.l_type != SUN) {
|
||||
vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence);
|
||||
}
|
||||
|
||||
/* Only shadowed light can produce translucency */
|
||||
if (ld.l_shadowid >= 0.0) {
|
||||
ShadowData data = shadows_data[int(ld.l_shadowid)];
|
||||
|
|
|
@ -86,7 +86,7 @@ typedef struct Lamp {
|
|||
|
||||
float contact_dist, contact_bias, contact_spread, contact_thickness;
|
||||
|
||||
float spec_fac, pad;
|
||||
float spec_fac, att_dist;
|
||||
|
||||
/* preview */
|
||||
struct PreviewImage *preview;
|
||||
|
@ -134,6 +134,7 @@ typedef struct Lamp {
|
|||
#define LA_SHOW_CONE (1 << 17)
|
||||
/* #define LA_SHOW_SHADOW_BOX (1 << 18) */
|
||||
#define LA_SHAD_CONTACT (1 << 19)
|
||||
#define LA_CUSTOM_ATTENUATION (1 << 20)
|
||||
|
||||
/* falloff_type */
|
||||
#define LA_FALLOFF_CONSTANT 0
|
||||
|
|
|
@ -1508,7 +1508,7 @@ typedef struct SceneEEVEE {
|
|||
char light_cache_info[64];
|
||||
|
||||
float overscan;
|
||||
float pad;
|
||||
float light_threshold;
|
||||
} SceneEEVEE;
|
||||
|
||||
/* *************************************************************** */
|
||||
|
|
|
@ -164,6 +164,19 @@ static void rna_def_light(BlenderRNA *brna)
|
|||
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, "use_custom_distance", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "mode", LA_CUSTOM_ATTENUATION);
|
||||
RNA_def_property_ui_text(prop, "Custom Attenuation", "Use custom attenuation distance instead of global light threshold");
|
||||
RNA_def_property_update(prop, 0, "rna_Light_update");
|
||||
|
||||
prop = RNA_def_property(srna, "cutoff_distance", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "att_dist");
|
||||
RNA_def_property_float_default(prop, 1.0f);
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.01f, 100.0f, 1.0, 2);
|
||||
RNA_def_property_ui_text(prop, "Cutoff Distance", "Distance at which the light influence will be set to 0");
|
||||
RNA_def_property_update(prop, 0, "rna_Light_update");
|
||||
|
||||
/* nodes */
|
||||
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
|
||||
|
|
|
@ -6035,6 +6035,13 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Soft Shadows", "Randomize shadowmaps origin to create soft shadows");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
|
||||
|
||||
prop = RNA_def_property(srna, "light_threshold", PROP_FLOAT, PROP_UNSIGNED);
|
||||
RNA_def_property_float_default(prop, 0.01f);
|
||||
RNA_def_property_ui_text(prop, "Light Threshold", "Minimum light intensity for a light to contribute to the lighting");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
|
||||
|
||||
/* Overscan */
|
||||
prop = RNA_def_property(srna, "use_overscan", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_OVERSCAN);
|
||||
|
|
Loading…
Reference in New Issue