EEVEE-Next: Jittered Soft Shadows #119753
|
@ -162,6 +162,17 @@ class DATA_PT_EEVEE_light_shadow(DataButtonsPanel, Panel):
|
|||
col.prop(light, "shadow_filter_radius", text="Filter")
|
||||
col.prop(light, "shadow_resolution_scale", text="Resolution")
|
||||
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=False, heading="Jitter")
|
||||
row = col.row(align=True)
|
||||
sub = row.row(align=True)
|
||||
sub.prop(light, "use_shadow_jittering", text="")
|
||||
sub = sub.row(align=True)
|
||||
sub.active = light.use_shadow_jittering
|
||||
sub.prop(light, "shadow_jitter_overblur_percentage", text="Overblur")
|
||||
|
||||
|
||||
|
||||
class DATA_PT_EEVEE_light_influence(DataButtonsPanel, Panel):
|
||||
bl_label = "Influence"
|
||||
|
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 19
|
||||
#define BLENDER_FILE_SUBVERSION 20
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -3184,6 +3184,12 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 20)) {
|
||||
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||
light->shadow_jitter_overblur_percentage = 10.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -399,6 +399,8 @@ void Instance::render_sample()
|
|||
|
||||
sampling.step();
|
||||
|
||||
lights.sync_jitter();
|
||||
|
||||
capture_view.render_world();
|
||||
capture_view.render_probes();
|
||||
|
||||
|
|
|
@ -93,32 +93,10 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
|||
if (la->mode & LA_SHADOW) {
|
||||
shadow_ensure(shadows);
|
||||
if (is_sun_light(this->type)) {
|
||||
this->directional->sync(this->object_mat,
|
||||
1.0f,
|
||||
la->sun_angle * la->shadow_softness_factor,
|
||||
la->shadow_trace_distance);
|
||||
this->directional->sync(la, this->object_mat, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* Reuse shape radius as near clip plane. */
|
||||
/* This assumes `shape_parameters_set` has already set `radius_squared`. */
|
||||
float radius = math::sqrt(this->local.radius_squared);
|
||||
float shadow_radius = la->shadow_softness_factor * radius;
|
||||
if (ELEM(la->type, LA_LOCAL, LA_SPOT)) {
|
||||
/* `shape_parameters_set` can increase the radius of point and spot lights to ensure a
|
||||
* minimum radius/energy ratio.
|
||||
* But we don't want to take that into account for computing the shadow-map projection,
|
||||
* since non-zero radius introduces padding (required for soft-shadows tracing), reducing
|
||||
* the effective resolution of shadow-maps.
|
||||
* So we use the original light radius instead. */
|
||||
shadow_radius = la->shadow_softness_factor * la->radius;
|
||||
}
|
||||
this->punctual->sync(this->type,
|
||||
this->object_mat,
|
||||
la->spotsize,
|
||||
radius,
|
||||
this->local.influence_radius_max,
|
||||
la->shadow_softness_factor,
|
||||
shadow_radius);
|
||||
this->punctual->sync(la, this->type, this->object_mat, this->local.influence_radius_max);
|
||||
pragma37 marked this conversation as resolved
Outdated
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -346,10 +324,16 @@ void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
|||
|
||||
Light &light = light_map_.lookup_or_add_default(handle.object_key);
|
||||
light.used = true;
|
||||
if (handle.recalc != 0 || !light.initialized) {
|
||||
|
||||
::Light *bl_light = static_cast<::Light *>(ob->data);
|
||||
bool do_jittering = inst_.shadows.do_jittering() && bl_light->mode & LA_SHADOW_JITTER;
|
||||
bool jittering_changed = assign_if_different(light.do_jittering, do_jittering);
|
||||
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
unused var unused var
Miguel Pozo
commented
It was at some point, but not since I added the toggle on viewport playback/navigation. It was at some point, but not since I added the toggle on viewport playback/navigation.
|
||||
if (handle.recalc != 0 || !light.initialized || do_jittering || jittering_changed) {
|
||||
light.initialized = true;
|
||||
light.sync(inst_.shadows, ob, light_threshold_);
|
||||
}
|
||||
|
||||
sun_lights_len_ += int(is_sun_light(light.type));
|
||||
local_lights_len_ += int(!is_sun_light(light.type));
|
||||
}
|
||||
|
@ -505,6 +489,31 @@ void LightModule::debug_pass_sync()
|
|||
}
|
||||
}
|
||||
|
||||
void LightModule::sync_jitter()
|
||||
{
|
||||
if (!inst_.shadows.do_jittering()) {
|
||||
return;
|
||||
}
|
||||
/* Indices inside GPU data array. */
|
||||
int sun_lights_idx = 0;
|
||||
int local_lights_idx = sun_lights_len_;
|
||||
for (Light &light : light_map_.values()) {
|
||||
int dst_idx = is_sun_light(light.type) ? sun_lights_idx++ : local_lights_idx++;
|
||||
if (!light.do_jittering) {
|
||||
continue;
|
||||
}
|
||||
if (light.directional != nullptr) {
|
||||
light.directional->end_sync(light, inst_.camera, true);
|
||||
}
|
||||
else if (light.punctual != nullptr) {
|
||||
light.punctual->end_sync(light, true);
|
||||
}
|
||||
light_buf_[dst_idx] = light;
|
||||
}
|
||||
light_buf_.push_update();
|
||||
inst_.shadows.tilemap_pool.tilemaps_data.push_update();
|
||||
}
|
||||
|
||||
void LightModule::set_view(View &view, const int2 extent)
|
||||
{
|
||||
float far_z = view.far_clip();
|
||||
|
|
|
@ -44,6 +44,7 @@ struct Light : public LightData, NonCopyable {
|
|||
public:
|
||||
bool initialized = false;
|
||||
bool used = false;
|
||||
bool do_jittering = false;
|
||||
|
||||
/** Pointers to source Shadow. Type depends on `LightData::type`. */
|
||||
ShadowDirectional *directional = nullptr;
|
||||
|
@ -63,6 +64,7 @@ struct Light : public LightData, NonCopyable {
|
|||
*static_cast<LightData *>(this) = other;
|
||||
this->initialized = other.initialized;
|
||||
this->used = other.used;
|
||||
this->do_jittering = other.do_jittering;
|
||||
this->directional = other.directional;
|
||||
this->punctual = other.punctual;
|
||||
other.directional = nullptr;
|
||||
|
@ -158,6 +160,8 @@ class LightModule {
|
|||
void sync_light(const Object *ob, ObjectHandle &handle);
|
||||
void end_sync();
|
||||
|
||||
void sync_jitter();
|
||||
|
||||
/**
|
||||
* Update acceleration structure for the given view.
|
||||
*/
|
||||
|
|
|
@ -767,23 +767,20 @@ static inline bool is_local_light(eLightType type)
|
|||
float influence_radius_invsqr_surface; \
|
||||
float influence_radius_invsqr_volume; \
|
||||
/** --- Shadow Data --- */ \
|
||||
/** Shift to apply to the light origin to get the shadow projection origin. */ \
|
||||
packed_float3 shadow_projection_shift; \
|
||||
/** Other parts of the perspective matrix. Assumes symmetric frustum. */ \
|
||||
float clip_side; \
|
||||
/** Number of allocated tilemap for this local light. */ \
|
||||
int tilemaps_count; \
|
||||
/** Scaling factor to the light shape for shadow ray casting. */ \
|
||||
float shadow_scale; \
|
||||
/** Shift to apply to the light origin to get the shadow projection origin. */ \
|
||||
float shadow_projection_shift;
|
||||
float shadow_scale;
|
||||
|
||||
/* Untyped local light data. Gets reinterpreted to LightSpotData and LightAreaData.
|
||||
* Allow access to local light common data without casting. */
|
||||
struct LightLocalData {
|
||||
LOCAL_LIGHT_COMMON
|
||||
|
||||
/** Padding reserved for when shadow_projection_shift will become a vec3. */
|
||||
float _pad0_reserved;
|
||||
float _pad1_reserved;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
|
||||
|
@ -797,9 +794,6 @@ BLI_STATIC_ASSERT_ALIGN(LightLocalData, 16)
|
|||
struct LightSpotData {
|
||||
LOCAL_LIGHT_COMMON
|
||||
|
||||
/** Padding reserved for when shadow_projection_shift will become a vec3. */
|
||||
float _pad0_reserved;
|
||||
float _pad1_reserved;
|
||||
/** Sphere light radius. */
|
||||
float radius;
|
||||
/** Scale and bias to spot equation parameter. Used for adjusting the falloff. */
|
||||
|
@ -816,9 +810,6 @@ BLI_STATIC_ASSERT(sizeof(LightSpotData) == sizeof(LightLocalData), "Data size mu
|
|||
struct LightAreaData {
|
||||
LOCAL_LIGHT_COMMON
|
||||
|
||||
/** Padding reserved for when shadow_projection_shift will become a vec3. */
|
||||
float _pad0_reserved;
|
||||
float _pad1_reserved;
|
||||
float _pad2;
|
||||
float _pad3;
|
||||
|
||||
|
@ -831,21 +822,18 @@ BLI_STATIC_ASSERT(sizeof(LightAreaData) == sizeof(LightLocalData), "Data size mu
|
|||
|
||||
struct LightSunData {
|
||||
float radius;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
|
||||
float _pad3;
|
||||
float _pad4;
|
||||
/** --- Shadow Data --- */
|
||||
/** Offset of the LOD min in LOD min tile units. Split positive and negative for bit-shift. */
|
||||
int2 clipmap_base_offset_neg;
|
||||
|
||||
int2 clipmap_base_offset_pos;
|
||||
/** Angle covered by the light shape for shadow ray casting. */
|
||||
float shadow_angle;
|
||||
/** Trace distance around the shading point. */
|
||||
float shadow_trace_distance;
|
||||
float _pad0;
|
||||
|
||||
/** --- Shadow Data --- */
|
||||
/** Offset of the LOD min in LOD min tile units. Split positive and negative for bit-shift. */
|
||||
int2 clipmap_base_offset_neg;
|
||||
int2 clipmap_base_offset_pos;
|
||||
|
||||
float4 shadow_projection_rotation;
|
||||
|
||||
/** Offset to convert from world units to tile space of the clipmap_lod_max. */
|
||||
float2 clipmap_origin;
|
||||
|
@ -925,10 +913,12 @@ BLI_STATIC_ASSERT_ALIGN(LightData, 16)
|
|||
#ifdef GPU_SHADER
|
||||
# define CHECK_TYPE_PAIR(a, b)
|
||||
# define CHECK_TYPE(a, b)
|
||||
# define INT_AS_FLOAT intBitsToFloat
|
||||
# define FLOAT_AS_INT floatBitsToInt
|
||||
# define TYPECAST_NOOP
|
||||
|
||||
#else /* C++ */
|
||||
# define INT_AS_FLOAT int_as_float
|
||||
# define FLOAT_AS_INT float_as_int
|
||||
# define TYPECAST_NOOP
|
||||
#endif
|
||||
|
@ -985,7 +975,9 @@ BLI_STATIC_ASSERT_ALIGN(LightData, 16)
|
|||
|
||||
#define SAFE_ASSIGN_FLOAT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float, b);
|
||||
#define SAFE_ASSIGN_FLOAT2(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float2, b);
|
||||
#define SAFE_ASSIGN_FLOAT3(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, float3, b);
|
||||
#define SAFE_ASSIGN_INT(a, b) SAFE_ASSIGN(a, TYPECAST_NOOP, int, b);
|
||||
#define SAFE_ASSIGN_INT_AS_FLOAT(a, b) SAFE_ASSIGN(a, INT_AS_FLOAT, int, b);
|
||||
#define SAFE_ASSIGN_FLOAT_AS_INT(a, b) SAFE_ASSIGN(a, FLOAT_AS_INT, float, b);
|
||||
#define SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(a, b, c) \
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(a.x, b); \
|
||||
|
@ -1008,7 +1000,7 @@ static inline LightSpotData light_local_data_get(LightData light)
|
|||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_scale, shadow_scale)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT3(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
return data;
|
||||
}
|
||||
|
@ -1022,7 +1014,7 @@ static inline LightSpotData light_spot_data_get(LightData light)
|
|||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_scale, shadow_scale)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT3(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
SAFE_ASSIGN_FLOAT(radius, _pad1)
|
||||
SAFE_ASSIGN_FLOAT(spot_mul, _pad2)
|
||||
|
@ -1041,7 +1033,7 @@ static inline LightAreaData light_area_data_get(LightData light)
|
|||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_scale, shadow_scale)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT3(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
SAFE_ASSIGN_FLOAT2(size, _pad3)
|
||||
return data;
|
||||
|
@ -1051,10 +1043,15 @@ static inline LightSunData light_sun_data_get(LightData light)
|
|||
{
|
||||
SAFE_BEGIN(LightSunData, is_sun_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT(radius, radius_squared)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_neg, shadow_scale, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_pos, _pad0_reserved, _pad1_reserved)
|
||||
SAFE_ASSIGN_FLOAT(shadow_angle, _pad1)
|
||||
SAFE_ASSIGN_FLOAT(shadow_trace_distance, _pad2)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(
|
||||
clipmap_base_offset_neg, shadow_projection_shift.x, shadow_projection_shift.y)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_pos, shadow_projection_shift.z, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_angle, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT(shadow_trace_distance, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(shadow_projection_rotation.x, tilemaps_count);
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_rotation.y, shadow_scale);
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_rotation.z, _pad1);
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_rotation.w, _pad2);
|
||||
SAFE_ASSIGN_FLOAT2(clipmap_origin, _pad3)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_min, _pad4)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_max, _pad5)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
@ -44,7 +43,7 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_,
|
|||
}
|
||||
grid_offset = origin_offset;
|
||||
|
||||
if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) {
|
||||
if (object_mat != object_mat_) {
|
||||
object_mat = object_mat_;
|
||||
set_dirty();
|
||||
}
|
||||
|
@ -73,7 +72,6 @@ void ShadowTileMap::sync_cubeface(const float4x4 &object_mat_,
|
|||
float near_,
|
||||
float far_,
|
||||
float side_,
|
||||
float shift,
|
||||
eCubeFace face,
|
||||
float lod_bias_)
|
||||
{
|
||||
|
@ -94,15 +92,14 @@ void ShadowTileMap::sync_cubeface(const float4x4 &object_mat_,
|
|||
half_size = side_;
|
||||
center_offset = float2(0.0f);
|
||||
|
||||
if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) {
|
||||
if (object_mat != object_mat_) {
|
||||
object_mat = object_mat_;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
winmat = math::projection::perspective(
|
||||
-half_size, half_size, -half_size, half_size, clip_near, clip_far);
|
||||
viewmat = float4x4(shadow_face_mat[cubeface]) *
|
||||
math::from_location<float4x4>(float3(0.0f, 0.0f, -shift)) * math::invert(object_mat);
|
||||
viewmat = float4x4(shadow_face_mat[cubeface]) * math::invert(object_mat);
|
||||
|
||||
/* Update corners. */
|
||||
float4x4 viewinv = object_mat;
|
||||
|
@ -221,16 +218,15 @@ void ShadowTileMapPool::end_sync(ShadowModule &module)
|
|||
*
|
||||
* \{ */
|
||||
|
||||
void ShadowPunctual::sync(eLightType light_type,
|
||||
void ShadowPunctual::sync(const ::Light *bl_light,
|
||||
eLightType light_type,
|
||||
const float4x4 &object_mat,
|
||||
float cone_aperture,
|
||||
float light_shape_radius,
|
||||
float max_distance,
|
||||
float softness_factor,
|
||||
float shadow_radius)
|
||||
float max_distance)
|
||||
{
|
||||
light_type_ = light_type;
|
||||
|
||||
if (is_spot_light(light_type)) {
|
||||
tilemaps_needed_ = (cone_aperture > DEG2RADF(90.0f)) ? 5 : 1;
|
||||
tilemaps_needed_ = (bl_light->spotsize > DEG2RADF(90.0f)) ? 5 : 1;
|
||||
}
|
||||
else if (is_area_light(light_type)) {
|
||||
tilemaps_needed_ = 5;
|
||||
|
@ -239,14 +235,19 @@ void ShadowPunctual::sync(eLightType light_type,
|
|||
tilemaps_needed_ = 6;
|
||||
}
|
||||
|
||||
/* Clamp for near/far clip distance calculation. */
|
||||
max_distance_ = max_ff(max_distance, 4e-4f);
|
||||
light_radius_ = min_ff(light_shape_radius, max_distance_ - 1e-4f);
|
||||
light_type_ = light_type;
|
||||
position_ = object_mat.location();
|
||||
max_distance_ = max_distance;
|
||||
softness_factor_ = bl_light->shadow_softness_factor;
|
||||
jitter_overblur_ = bl_light->shadow_jitter_overblur_percentage / 100.0f;
|
||||
|
||||
position_ = float3(object_mat[3]);
|
||||
softness_factor_ = softness_factor;
|
||||
shadow_radius_ = shadow_radius;
|
||||
if (is_area_light(light_type)) {
|
||||
const bool is_irregular = ELEM(bl_light->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE);
|
||||
light_radius_ = math::length(
|
||||
float2(bl_light->area_size, is_irregular ? bl_light->area_sizey : bl_light->area_size));
|
||||
}
|
||||
else {
|
||||
light_radius_ = bl_light->radius;
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowPunctual::release_excess_tilemaps()
|
||||
|
@ -320,6 +321,8 @@ void ShadowPunctual::compute_projection_boundaries(float light_radius,
|
|||
*
|
||||
* TODO(fclem): Explain derivation.
|
||||
*/
|
||||
max_lit_distance = max_ff(max_lit_distance, 4e-4f);
|
||||
light_radius = min_ff(light_radius, max_lit_distance - 1e-4f);
|
||||
float cos_alpha = shadow_radius / max_lit_distance;
|
||||
float sin_alpha = sqrt(1.0f - math::square(cos_alpha));
|
||||
float near_shift = M_SQRT2 * shadow_radius * 0.5f * (sin_alpha - cos_alpha);
|
||||
|
@ -336,41 +339,68 @@ void ShadowPunctual::compute_projection_boundaries(float light_radius,
|
|||
}
|
||||
}
|
||||
|
||||
void ShadowPunctual::end_sync(Light &light, float lod_bias)
|
||||
void ShadowPunctual::end_sync(Light &light, bool is_render_sync)
|
||||
{
|
||||
ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool;
|
||||
|
||||
if (!is_render_sync) {
|
||||
/* Acquire missing tile-maps. */
|
||||
while (tilemaps_.size() < tilemaps_needed_) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
}
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
for (ShadowTileMap *tilemap : tilemaps_) {
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
}
|
||||
|
||||
if (light.do_jittering) {
|
||||
/* Just acquire the tilemaps, we must sync them at render time. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float shadow_radius = shadow_radius_get();
|
||||
if (light.do_jittering) {
|
||||
shadow_radius *= jitter_overblur_;
|
||||
}
|
||||
|
||||
float side, near, far;
|
||||
compute_projection_boundaries(light_radius_, shadow_radius_, max_distance_, near, far, side);
|
||||
compute_projection_boundaries(light_radius_, shadow_radius, max_distance_, near, far, side);
|
||||
|
||||
/* Shift shadow map origin for area light to avoid clipping nearby geometry. */
|
||||
float shift = is_area_light(light.type) ? near : 0.0f;
|
||||
float3 origin_shift = float3(0.0f);
|
||||
|
||||
float4x4 obmat_tmp = light.object_mat;
|
||||
|
||||
/* Clear embedded custom data. */
|
||||
obmat_tmp[0][3] = obmat_tmp[1][3] = obmat_tmp[2][3] = 0.0f;
|
||||
obmat_tmp[3][3] = 1.0f;
|
||||
|
||||
/* Acquire missing tile-maps. */
|
||||
while (tilemaps_.size() < tilemaps_needed_) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
if (is_render_sync) {
|
||||
/* Render sync is only needed for jittered shadows. */
|
||||
BLI_assert(light.do_jittering);
|
||||
Sampling &sampling = shadows_.inst_.sampling;
|
||||
/* TODO: de-correlate. */
|
||||
float3 random = sampling.rng_3d_get(SAMPLING_SHADOW_U);
|
||||
if (is_area_light(light.type)) {
|
||||
origin_shift = light.type == LIGHT_RECT ? float3(random.xy() * 2.0f - 1.0f, 0.0f) :
|
||||
float3(sampling.sample_disk(random.xy()), 0.0f);
|
||||
origin_shift.x *= light.area.size.x;
|
||||
origin_shift.y *= light.area.size.y;
|
||||
}
|
||||
else {
|
||||
origin_shift = sampling.sample_ball(random);
|
||||
origin_shift *= shadow_radius_get();
|
||||
}
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
This can be removed. And you can use This can be removed. And you can use `light.object_mat` directly as it is junk free now!.
|
||||
}
|
||||
|
||||
tilemaps_[Z_NEG]->sync_cubeface(obmat_tmp, near, far, side, shift, Z_NEG, lod_bias);
|
||||
if (tilemaps_needed_ >= 5) {
|
||||
tilemaps_[X_POS]->sync_cubeface(obmat_tmp, near, far, side, shift, X_POS, lod_bias);
|
||||
tilemaps_[X_NEG]->sync_cubeface(obmat_tmp, near, far, side, shift, X_NEG, lod_bias);
|
||||
tilemaps_[Y_POS]->sync_cubeface(obmat_tmp, near, far, side, shift, Y_POS, lod_bias);
|
||||
tilemaps_[Y_NEG]->sync_cubeface(obmat_tmp, near, far, side, shift, Y_NEG, lod_bias);
|
||||
}
|
||||
if (tilemaps_needed_ == 6) {
|
||||
tilemaps_[Z_POS]->sync_cubeface(obmat_tmp, near, far, side, shift, Z_POS, lod_bias);
|
||||
if (is_area_light(light.type)) {
|
||||
/* Shift shadow map origin, so the near plane overlaps with the area light plane. */
|
||||
origin_shift.z += near;
|
||||
}
|
||||
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
float4x4 shifted_mat = light.object_mat * math::from_location<float4x4>(origin_shift);
|
||||
|
||||
for (int i : IndexRange(tilemaps_needed_)) {
|
||||
tilemaps_[i]->sync_cubeface(shifted_mat, near, far, side, eCubeFace(i), light.lod_bias);
|
||||
tilemap_pool.tilemaps_data[light.tilemap_index + i] = *tilemaps_[i];
|
||||
tilemaps_[i]->set_updated();
|
||||
}
|
||||
|
||||
light.local.tilemaps_count = tilemaps_needed_;
|
||||
/* TODO(fclem): `as_uint()`. */
|
||||
union {
|
||||
float f;
|
||||
|
@ -381,14 +411,9 @@ void ShadowPunctual::end_sync(Light &light, float lod_bias)
|
|||
as_int.f = far;
|
||||
light.clip_far = as_int.i;
|
||||
light.local.clip_side = side;
|
||||
light.local.shadow_projection_shift = shift;
|
||||
light.local.shadow_scale = softness_factor_;
|
||||
|
||||
for (ShadowTileMap *tilemap : tilemaps_) {
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
tilemap->set_updated();
|
||||
}
|
||||
light.local.shadow_projection_shift = origin_shift;
|
||||
light.local.shadow_scale = softness_factor_get(light.do_jittering);
|
||||
light.local.tilemaps_count = tilemaps_needed_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -509,9 +534,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
|||
int2 level_offset = origin_offset +
|
||||
shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i);
|
||||
tilemap->sync_orthographic(object_mat_, level_offset, level, 0.0f, SHADOW_PROJECTION_CASCADE);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
shadows_.tilemap_pool.tilemaps_data[light.tilemap_index + i] = *tilemap;
|
||||
tilemap->set_updated();
|
||||
}
|
||||
|
||||
|
@ -559,9 +582,7 @@ IndexRange ShadowDirectional::clipmap_level_range(const Camera &camera)
|
|||
return range;
|
||||
}
|
||||
|
||||
void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
|
||||
const Camera &camera,
|
||||
float lod_bias)
|
||||
void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera &camera)
|
||||
{
|
||||
for (int lod : IndexRange(levels_range.size())) {
|
||||
ShadowTileMap *tilemap = tilemaps_[lod];
|
||||
|
@ -575,10 +596,8 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
|
|||
int2 level_offset = int2(math::round(light_space_camera_position / tile_size));
|
||||
|
||||
tilemap->sync_orthographic(
|
||||
object_mat_, level_offset, level, lod_bias, SHADOW_PROJECTION_CLIPMAP);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
object_mat_, level_offset, level, light.lod_bias, SHADOW_PROJECTION_CLIPMAP);
|
||||
shadows_.tilemap_pool.tilemaps_data[light.tilemap_index + lod] = *tilemap;
|
||||
tilemap->set_updated();
|
||||
}
|
||||
|
||||
|
@ -614,25 +633,21 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
|
|||
|
||||
light.sun.clipmap_lod_min = levels_range.first();
|
||||
light.sun.clipmap_lod_max = levels_range.last();
|
||||
|
||||
light.lod_bias = lod_bias;
|
||||
}
|
||||
|
||||
void ShadowDirectional::sync(const float4x4 &object_mat,
|
||||
float min_resolution,
|
||||
float shadow_disk_angle,
|
||||
float trace_distance)
|
||||
void ShadowDirectional::sync(const ::Light *bl_light,
|
||||
const float4x4 &object_mat,
|
||||
float min_resolution)
|
||||
{
|
||||
object_mat_ = object_mat;
|
||||
/* Clear embedded custom data. */
|
||||
object_mat_[0][3] = object_mat_[1][3] = object_mat_[2][3] = 0.0f;
|
||||
object_mat_[3][3] = 1.0f;
|
||||
/* Remove translation. */
|
||||
object_mat_.location() = float3(0.0f);
|
||||
|
||||
light_angle_ = bl_light->sun_angle;
|
||||
min_resolution_ = min_resolution;
|
||||
disk_shape_angle_ = min_ff(shadow_disk_angle, DEG2RADF(179.9f)) / 2.0f;
|
||||
trace_distance_ = trace_distance;
|
||||
trace_distance_ = bl_light->shadow_trace_distance;
|
||||
softness_factor_ = bl_light->shadow_softness_factor;
|
||||
jitter_overblur_ = bl_light->shadow_jitter_overblur_percentage / 100.0f;
|
||||
}
|
||||
|
||||
void ShadowDirectional::release_excess_tilemaps(const Camera &camera, float lod_bias)
|
||||
|
@ -657,43 +672,82 @@ void ShadowDirectional::release_excess_tilemaps(const Camera &camera, float lod_
|
|||
levels_range = isect_range;
|
||||
}
|
||||
|
||||
void ShadowDirectional::end_sync(Light &light, const Camera &camera, float lod_bias)
|
||||
void ShadowDirectional::end_sync(Light &light, const Camera &camera, bool is_render_sync)
|
||||
{
|
||||
ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool;
|
||||
IndexRange levels_new = directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE ?
|
||||
cascade_level_range(camera, lod_bias) :
|
||||
clipmap_level_range(camera);
|
||||
|
||||
if (levels_range != levels_new) {
|
||||
/* Acquire missing tile-maps. */
|
||||
IndexRange isect_range = levels_new.intersect(levels_range);
|
||||
int64_t before_range = isect_range.start() - levels_new.start();
|
||||
int64_t after_range = levels_new.one_after_last() - isect_range.one_after_last();
|
||||
|
||||
Vector<ShadowTileMap *> cached_tilemaps = tilemaps_;
|
||||
tilemaps_.clear();
|
||||
for (int64_t i = 0; i < before_range; i++) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
if (!is_render_sync) {
|
||||
IndexRange levels_new;
|
||||
if (directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE) {
|
||||
levels_new = cascade_level_range(camera, light.lod_bias);
|
||||
}
|
||||
/* Keep cached LOD's. */
|
||||
tilemaps_.extend(cached_tilemaps);
|
||||
for (int64_t i = 0; i < after_range; i++) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
else {
|
||||
levels_new = clipmap_level_range(camera);
|
||||
}
|
||||
|
||||
if (levels_range != levels_new) {
|
||||
/* Acquire missing tile-maps. */
|
||||
IndexRange isect_range = levels_new.intersect(levels_range);
|
||||
int64_t before_range = isect_range.start() - levels_new.start();
|
||||
int64_t after_range = levels_new.one_after_last() - isect_range.one_after_last();
|
||||
|
||||
Vector<ShadowTileMap *> cached_tilemaps = tilemaps_;
|
||||
tilemaps_.clear();
|
||||
for (int64_t i = 0; i < before_range; i++) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
}
|
||||
/* Keep cached LOD's. */
|
||||
tilemaps_.extend(cached_tilemaps);
|
||||
for (int64_t i = 0; i < after_range; i++) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
}
|
||||
levels_range = levels_new;
|
||||
}
|
||||
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
for (int lod : IndexRange(levels_range.size())) {
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
tilemap_pool.tilemaps_data.append(*tilemaps_[lod]);
|
||||
}
|
||||
|
||||
if (light.do_jittering) {
|
||||
/* Just acquire the tilemaps, we must sync them at render time. */
|
||||
return;
|
||||
}
|
||||
levels_range = levels_new;
|
||||
}
|
||||
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
float3x3 jitter_mat = float3x3::identity();
|
||||
|
||||
if (is_render_sync) {
|
||||
/* Render sync is only needed for jittered shadows. */
|
||||
BLI_assert(light.do_jittering);
|
||||
Sampling &sampling = shadows_.inst_.sampling;
|
||||
/* TODO: de-correlate. */
|
||||
float2 random = sampling.rng_2d_get(SAMPLING_SHADOW_U);
|
||||
float2 r_disk = sampling.sample_disk(random) * shadow_radius_get();
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
I am confused, you are jittering the light matrix that is use for shading? This is wrong as it changes the shading and doesn't even change the shadow projection. Although the shadowing looks correct. So you should be able to generate the tilemaps with this matrix and only use this matrix for shadowing (have to find trick to pack it inside the LightData, might be able to pack a quaternion in there). I am confused, you are jittering the light matrix that is use for shading? This is wrong as it changes the shading and doesn't even change the shadow projection.
Although the shadowing looks correct. So you should be able to generate the tilemaps with this matrix and only use this matrix for shadowing (have to find trick to pack it inside the LightData, might be able to pack a quaternion in there).
Miguel Pozo
commented
Yes, I mentioned it at the last week meeting, and you seemed to agree (?). I don't understand what you mean by not changing the shadow projection. The shading is jittered as well, which is not ideal, but I think the final image should converge to the same result anyway. Yes, I mentioned it at the last week meeting, and you seemed to agree (?).
Maybe I didn't explain it properly.
I don't understand what you mean by not changing the shadow projection.
The shading is jittered as well, which is not ideal, but I think the final image should converge to the same result anyway.
Clément Foucault
commented
Rotating the matrix to render and sample the shadow map is the right approach, but using it for shading is not. I thought you mentioned this because you looked up what legacy EEVEE was doing (which is effectively what I just mentioned).
I mean the
No, it doesn't converge to the same result. Make a sunlight with 20° angle and look up the difference on a smooth reflective surface (i.e: metal). > Yes, I mentioned it at the last week meeting, and you seemed to agree (?).
Rotating the matrix to render and sample the shadow map is the right approach, but using it for shading is not. I thought you mentioned this because you looked up what legacy EEVEE was doing (which is effectively what I just mentioned).
> I don't understand what you mean by not changing the shadow projection.
I mean the `tilemap.viewmat` is not changed, which is the `viewmat` the shadow is rendered with. So you effectively only jitter the tracing / projection direction (I think, not sure) but the shadow maps are still render from the initial position of the light.
> The shading is jittered as well, which is not ideal, but I think the final image should converge to the same result anyway.
No, it doesn't converge to the same result. Make a sunlight with 20° angle and look up the difference on a smooth reflective surface (i.e: metal).
Miguel Pozo
commented
It's changed. This is changing I don't understand why the position of a directional light would need to be changed. They're not supposed to have a position at all? > I mean the `tilemap.viewmat` is not changed, which is the `viewmat` the shadow is rendered with. So you effectively only jitter the tracing / projection direction (I think, not sure) but the shadow maps are still render from the initial position of the light.
It's changed. This is changing `object_mat_` just before calling the `tilemaps_distribution`, which is what those functions pass to `tilemap->sync_orthographic`.
I don't understand why the position of a directional light would need to be changed. They're not supposed to have a position at all?
Clément Foucault
commented
Ok, my bad on this.
Sorry, I mean the initial light direction. But whatever, I was wrong. > It's changed. This is changing object_mat_ just before calling the tilemaps_distribution, which is what those functions pass to tilemap->sync_orthographic.
Ok, my bad on this.
> I don't understand why the position of a directional light would need to be changed. They're not supposed to have a position at all?
Sorry, I mean the initial light direction. But whatever, I was wrong.
Miguel Pozo
commented
I've made the split of jitter rotation into its own quaternion, but it's still WIP. I've made the split of jitter rotation into its own quaternion, but it's still WIP.
|
||||
float3 jitter = object_mat_.x_axis() * r_disk.x + object_mat_.y_axis() * r_disk.y;
|
||||
|
||||
jitter_mat[2] += jitter;
|
||||
jitter_mat = math::orthogonalize(jitter_mat, math::Axis::Z);
|
||||
jitter_mat = math::normalize(jitter_mat);
|
||||
|
||||
object_mat_ = light.object_mat;
|
||||
object_mat_.location() = float3(0.0f);
|
||||
object_mat_ = object_mat_ * float4x4(jitter_mat);
|
||||
}
|
||||
|
||||
math::Quaternion q = math::to_quaternion(jitter_mat);
|
||||
light.sun.shadow_projection_rotation = {q.x, q.y, q.z, q.w};
|
||||
|
||||
light.clip_near = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
|
||||
light.clip_far = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
|
||||
light.sun.shadow_trace_distance = trace_distance_;
|
||||
light.sun.shadow_angle = disk_shape_angle_;
|
||||
light.sun.shadow_angle = shadow_angle_get(light.do_jittering);
|
||||
|
||||
if (directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE) {
|
||||
cascade_tilemaps_distribution(light, camera);
|
||||
}
|
||||
else {
|
||||
clipmap_tilemaps_distribution(light, camera, lod_bias);
|
||||
clipmap_tilemaps_distribution(light, camera);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -735,8 +789,9 @@ void ShadowModule::init()
|
|||
}
|
||||
}
|
||||
|
||||
jittered_transparency_ = !inst_.is_viewport() ||
|
||||
scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT;
|
||||
do_jittering_ = !inst_.is_viewport() ||
|
||||
((scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT) &&
|
||||
!inst_.is_navigating() && !inst_.is_playback());
|
||||
|
||||
data_.ray_count = clamp_i(inst_.scene->eevee.shadow_ray_count, 1, SHADOW_MAX_RAY);
|
||||
data_.step_count = clamp_i(inst_.scene->eevee.shadow_step_count, 1, SHADOW_MAX_STEP);
|
||||
|
@ -906,7 +961,7 @@ void ShadowModule::sync_object(const Object *ob,
|
|||
ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key);
|
||||
shadow_ob.used = true;
|
||||
const bool is_initialized = shadow_ob.resource_handle.raw != 0;
|
||||
const bool has_jittered_transparency = has_transparent_shadows && jittered_transparency_;
|
||||
const bool has_jittered_transparency = has_transparent_shadows && do_jittering_;
|
||||
if (is_shadow_caster && (handle.recalc || !is_initialized || has_jittered_transparency)) {
|
||||
if (handle.recalc && is_initialized) {
|
||||
past_casters_updated_.append(shadow_ob.resource_handle.raw);
|
||||
|
@ -952,10 +1007,10 @@ void ShadowModule::end_sync()
|
|||
light.tilemap_index = LIGHT_NO_SHADOW;
|
||||
}
|
||||
else if (light.directional != nullptr) {
|
||||
light.directional->end_sync(light, inst_.camera, light.lod_bias);
|
||||
light.directional->end_sync(light, inst_.camera);
|
||||
}
|
||||
else if (light.punctual != nullptr) {
|
||||
light.punctual->end_sync(light, light.lod_bias);
|
||||
light.punctual->end_sync(light);
|
||||
}
|
||||
else {
|
||||
light.tilemap_index = LIGHT_NO_SHADOW;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_pool.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
|
@ -17,6 +18,7 @@
|
|||
|
||||
#include "eevee_camera.hh"
|
||||
#include "eevee_material.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
#include "eevee_sync.hh"
|
||||
|
@ -107,7 +109,6 @@ struct ShadowTileMap : public ShadowTileMapData {
|
|||
float near,
|
||||
float far,
|
||||
float side,
|
||||
float shift,
|
||||
eCubeFace face,
|
||||
float lod_bias_);
|
||||
|
||||
|
@ -216,7 +217,7 @@ class ShadowModule {
|
|||
/* Used to call caster_update_ps_ only once per sync (Initialized on begin_sync). */
|
||||
bool update_casters_ = false;
|
||||
|
||||
bool jittered_transparency_ = false;
|
||||
bool do_jittering_ = false;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tile-map Management
|
||||
|
@ -371,6 +372,11 @@ class ShadowModule {
|
|||
return lod_bias_;
|
||||
}
|
||||
|
||||
bool do_jittering()
|
||||
{
|
||||
return do_jittering_;
|
||||
}
|
||||
|
||||
private:
|
||||
void remove_unused();
|
||||
void debug_page_map_call(DRWPass *pass);
|
||||
|
@ -400,19 +406,18 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
|||
Vector<ShadowTileMap *> tilemaps_;
|
||||
/** Shape type. */
|
||||
eLightType light_type_;
|
||||
/** Light position. */
|
||||
float3 position_;
|
||||
/** Used to compute near and far clip distances. */
|
||||
float max_distance_, light_radius_;
|
||||
/** Number of tile-maps needed to cover the light angular extents. */
|
||||
int tilemaps_needed_;
|
||||
/** Scaling factor to the light shape for shadow ray casting. */
|
||||
/** Light position. */
|
||||
float3 position_;
|
||||
/** Unmodified light shape radius. */
|
||||
float light_radius_;
|
||||
/** Used to compute near and far clip distances. */
|
||||
float max_distance_;
|
||||
/** Scaling factor to the light shape for shadows. */
|
||||
float softness_factor_;
|
||||
/**
|
||||
* `radius * softness_factor` (Bypasses LightModule radius modifications
|
||||
* to avoid unnecessary padding in the shadow projection).
|
||||
*/
|
||||
float shadow_radius_;
|
||||
/** Softness scale for jittered shadows. */
|
||||
float jitter_overblur_;
|
||||
|
||||
public:
|
||||
ShadowPunctual(ShadowModule &module) : shadows_(module){};
|
||||
|
@ -427,13 +432,10 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
|||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(eLightType light_type,
|
||||
void sync(const ::Light *bl_light,
|
||||
eLightType light_type,
|
||||
const float4x4 &object_mat,
|
||||
float cone_aperture,
|
||||
float light_shape_radius,
|
||||
float max_distance,
|
||||
float softness_factor,
|
||||
float shadow_radius);
|
||||
float max_distance);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
|
@ -443,9 +445,21 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
|||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
*/
|
||||
void end_sync(Light &light, float lod_bias);
|
||||
void end_sync(Light &light, bool is_render_sync = false);
|
||||
|
||||
private:
|
||||
/** Scaling factor to the light shape for shadow tracing. */
|
||||
float softness_factor_get(bool do_jittering)
|
||||
{
|
||||
return softness_factor_ * (do_jittering ? jitter_overblur_ : 1.0f);
|
||||
}
|
||||
|
||||
/** Light shape radius used for shadows. */
|
||||
float shadow_radius_get()
|
||||
{
|
||||
return light_radius_ * softness_factor_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the projection matrix inputs.
|
||||
* Make sure that the projection encompass all possible rays that can start in the projection
|
||||
|
@ -470,10 +484,14 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
|||
float4x4 object_mat_;
|
||||
/** Current range of clip-map / cascades levels covered by this shadow. */
|
||||
IndexRange levels_range;
|
||||
/** Angle of the shadowed light shape. Might be scaled compared to the shading disk. */
|
||||
float disk_shape_angle_;
|
||||
/** Unmodified light shape angle. */
|
||||
float light_angle_;
|
||||
/** Maximum distance a shadow map ray can be travel. */
|
||||
float trace_distance_;
|
||||
/** Scaling factor to the light shape for shadows. */
|
||||
float softness_factor_;
|
||||
/** Softness scale for jittered shadows. */
|
||||
float jitter_overblur_;
|
||||
|
||||
public:
|
||||
ShadowDirectional(ShadowModule &module) : shadows_(module){};
|
||||
|
@ -488,10 +506,7 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
|||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(const float4x4 &object_mat,
|
||||
float min_resolution,
|
||||
float shadow_disk_angle,
|
||||
float trace_distance);
|
||||
void sync(const ::Light *bl_light, const float4x4 &object_mat, float min_resolution);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
|
@ -501,7 +516,7 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
|||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
*/
|
||||
void end_sync(Light &light, const Camera &camera, float lod_bias);
|
||||
void end_sync(Light &light, const Camera &camera, bool is_render_sync = false);
|
||||
|
||||
/* Return coverage of the whole tile-map in world unit. */
|
||||
static float coverage_get(int lvl)
|
||||
|
@ -518,11 +533,29 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
|||
}
|
||||
|
||||
private:
|
||||
/** Scaling factor to the light shape for shadow tracing. */
|
||||
float softness_factor_get(bool do_jittering)
|
||||
{
|
||||
return softness_factor_ * (do_jittering ? jitter_overblur_ : 1.0f);
|
||||
}
|
||||
|
||||
/** Light shape angle used for shadows. */
|
||||
float shadow_angle_get(bool do_jittering)
|
||||
{
|
||||
return min_ff(light_angle_ * softness_factor_get(do_jittering), DEG2RADF(179.9f)) / 2.0f;
|
||||
}
|
||||
|
||||
/** Light shape radius used for shadows. */
|
||||
float shadow_radius_get()
|
||||
{
|
||||
return tanf(min_ff(light_angle_ * softness_factor_, DEG2RADF(179.9f)) / 2.0f);
|
||||
}
|
||||
|
||||
IndexRange clipmap_level_range(const Camera &camera);
|
||||
IndexRange cascade_level_range(const Camera &camera, float lod_bias);
|
||||
|
||||
void cascade_tilemaps_distribution(Light &light, const Camera &camera);
|
||||
void clipmap_tilemaps_distribution(Light &light, const Camera &camera, float lod_bias);
|
||||
void clipmap_tilemaps_distribution(Light &light, const Camera &camera);
|
||||
|
||||
void cascade_tilemaps_distribution_near_far_points(const Camera &camera,
|
||||
float3 &near_point,
|
||||
|
|
|
@ -70,7 +70,7 @@ void light_shadow_single(uint l_idx,
|
|||
#endif
|
||||
|
||||
ShadowEvalResult result = shadow_eval(
|
||||
light, is_directional, is_transmission, false, P, Ng, Ng, ray_count, ray_step_count);
|
||||
light, is_directional, is_transmission, false, P, Ng, Ng, 0.0, ray_count, ray_step_count);
|
||||
|
||||
shadow_bits |= shadow_pack(result.light_visibilty, ray_count, shift);
|
||||
shift += ray_count;
|
||||
|
@ -252,15 +252,14 @@ void light_eval_single(uint l_idx,
|
|||
shadow = shadow_unpack(packed_shadows, ray_count, shift);
|
||||
shift += ray_count;
|
||||
#else
|
||||
|
||||
vec3 shadow_P = (is_transmission) ? P + lv.L * stack.cl[0].shadow_offset : P;
|
||||
ShadowEvalResult result = shadow_eval(light,
|
||||
is_directional,
|
||||
is_transmission,
|
||||
is_translucent_with_thickness,
|
||||
shadow_P,
|
||||
P,
|
||||
Ng,
|
||||
lv.L,
|
||||
shadow_vector_get(light, is_directional, P),
|
||||
stack.cl[0].shadow_offset,
|
||||
ray_count,
|
||||
ray_step_count);
|
||||
pragma37 marked this conversation as resolved
Clément Foucault
commented
At this point, just pass Also prefer calling At this point, just pass `stack.cl[0].shadow_offset` to `shadow_eval`. It will be needed to fix the SSS shadow anyway.
Also prefer calling `shadow_dir` instead of `L`. `L` is reserved for direction towards the light (except in small function / sampling context where it can just refer to outgoing ray direction). But I don't think it is worth changing all existing usage of it right now.
|
||||
shadow = result.light_visibilty;
|
||||
|
|
|
@ -216,7 +216,7 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere)
|
|||
{
|
||||
/* The sine of the half-angle spanned by a sphere light is equal to the tangent of the
|
||||
* half-angle spanned by a disk light with the same radius. */
|
||||
return sphere_radius * inversesqrt(1.0 - square(sphere_radius / distance_to_sphere));
|
||||
return sphere_radius * inversesqrt(max(1e-8, 1.0 - square(sphere_radius / distance_to_sphere)));
|
||||
}
|
||||
|
||||
float light_ltc(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_rotation_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
#define EEVEE_SHADOW_LIB
|
||||
|
@ -14,6 +15,39 @@
|
|||
# define SHADOW_ATLAS_TYPE usampler2DArray
|
||||
#endif
|
||||
|
||||
vec3 shadow_vector_get(LightData light, const bool is_directional, vec3 P)
|
||||
{
|
||||
if (is_directional) {
|
||||
vec3 b = float3(0, 0, 1);
|
||||
b = rotate(as_quaternion(light_sun_data_get(light).shadow_projection_rotation), b);
|
||||
return mat3(light.object_mat) * b;
|
||||
}
|
||||
else {
|
||||
return safe_normalize((light._position + light_local_data_get(light).shadow_projection_shift) -
|
||||
P);
|
||||
}
|
||||
}
|
||||
|
||||
vec3 light_local_to_shadow_local(LightData light, vec3 lP, const bool is_directional)
|
||||
Clément Foucault
commented
Use Use `const bool is_directional` for all three functions.
Miguel Pozo
commented
Done, but why? Done, but why?
|
||||
{
|
||||
if (is_directional) {
|
||||
return rotate(invert(as_quaternion(light_sun_data_get(light).shadow_projection_rotation)), lP);
|
||||
}
|
||||
else {
|
||||
return lP - light_local_data_get(light).shadow_projection_shift;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 shadow_local_to_light_local(LightData light, vec3 lP, const bool is_directional)
|
||||
{
|
||||
if (is_directional) {
|
||||
return rotate(as_quaternion(light_sun_data_get(light).shadow_projection_rotation), lP);
|
||||
}
|
||||
else {
|
||||
return lP + light_local_data_get(light).shadow_projection_shift;
|
||||
}
|
||||
}
|
||||
|
||||
struct ShadowSampleParams {
|
||||
vec3 lP;
|
||||
vec3 uv;
|
||||
|
@ -152,12 +186,14 @@ vec3 shadow_punctual_reconstruct_position(ShadowSampleParams params,
|
|||
vec3 lP = project_point(wininv, clip_P);
|
||||
int face_id = params.tilemap_index - light.tilemap_index;
|
||||
lP = shadow_punctual_face_local_to_local_position(face_id, lP);
|
||||
lP = shadow_local_to_light_local(light, lP, false);
|
||||
return mat3(light.object_mat) * lP + light._position;
|
||||
}
|
||||
|
||||
ShadowSampleParams shadow_punctual_sample_params_get(LightData light, vec3 P)
|
||||
{
|
||||
vec3 lP = (P - light._position) * mat3(light.object_mat);
|
||||
lP = light_local_to_shadow_local(light, lP, false);
|
||||
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
/* Local Light Space > Face Local (View) Space. */
|
||||
|
@ -234,6 +270,8 @@ vec3 shadow_directional_reconstruct_position(ShadowSampleParams params, LightDat
|
|||
lP.xy = clipmap_pos + info.clipmap_origin;
|
||||
lP.z = (params.uv.z + info.clip_near) * -1.0;
|
||||
|
||||
lP = shadow_local_to_light_local(light, lP, true);
|
||||
|
||||
return mat3(light.object_mat) * lP;
|
||||
}
|
||||
|
||||
|
@ -242,6 +280,8 @@ ShadowSampleParams shadow_directional_sample_params_get(usampler2D tilemaps_tx,
|
|||
vec3 P)
|
||||
{
|
||||
vec3 lP = P * mat3(light.object_mat);
|
||||
lP = light_local_to_shadow_local(light, lP, true);
|
||||
|
||||
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, lP);
|
||||
|
||||
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
|
||||
|
|
|
@ -35,6 +35,7 @@ void shadow_tag_usage_tilemap_directional_at_level(uint l_idx, vec3 P, int level
|
|||
}
|
||||
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
lP = light_local_to_shadow_local(light, lP, true);
|
||||
|
||||
level = clamp(
|
||||
level, light_sun_data_get(light).clipmap_lod_min, light_sun_data_get(light).clipmap_lod_max);
|
||||
|
@ -52,6 +53,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
|||
}
|
||||
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
lP = light_local_to_shadow_local(light, lP, true);
|
||||
|
||||
/* TODO(Miguel Pozo): Implement lod_bias support. */
|
||||
if (radius == 0.0) {
|
||||
|
@ -61,7 +63,11 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
|||
}
|
||||
else {
|
||||
vec3 start_lP = light_world_to_local(light, P - V * radius);
|
||||
start_lP = light_local_to_shadow_local(light, start_lP, true);
|
||||
|
||||
vec3 end_lP = light_world_to_local(light, P + V * radius);
|
||||
end_lP = light_local_to_shadow_local(light, end_lP, true);
|
||||
|
||||
int min_level = shadow_directional_level(light, start_lP - light._position);
|
||||
int max_level = shadow_directional_level(light, end_lP - light._position);
|
||||
|
||||
|
@ -90,6 +96,7 @@ void shadow_tag_usage_tilemap_punctual(
|
|||
}
|
||||
|
||||
vec3 lP = light_world_to_local(light, P - light._position);
|
||||
|
||||
float dist_to_light = max(length(lP) - radius, 1e-5);
|
||||
if (dist_to_light > light_local_data_get(light).influence_radius_max) {
|
||||
return;
|
||||
|
@ -108,8 +115,7 @@ void shadow_tag_usage_tilemap_punctual(
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO(fclem): 3D shift for jittered soft shadows. */
|
||||
lP += vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift);
|
||||
lP = light_local_to_shadow_local(light, lP, false);
|
||||
|
||||
float footprint_ratio = shadow_punctual_footprint_ratio(
|
||||
light, P, drw_view_is_perspective(), dist_to_cam, tilemap_proj_ratio);
|
||||
|
|
|
@ -30,10 +30,9 @@ void set_clipmap_data(inout LightData light,
|
|||
void set_clipmap_base_offset(inout LightData light, ivec2 clipmap_base_offset)
|
||||
{
|
||||
/* WATCH: Can get out of sync with light_sun_data_get(). */
|
||||
light.do_not_access_directly.shadow_scale = intBitsToFloat(0);
|
||||
light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(0);
|
||||
light.do_not_access_directly._pad0_reserved = intBitsToFloat(clipmap_base_offset.x);
|
||||
light.do_not_access_directly._pad1_reserved = intBitsToFloat(clipmap_base_offset.y);
|
||||
light.do_not_access_directly.shadow_projection_shift.xy = vec2(intBitsToFloat(0));
|
||||
light.do_not_access_directly.shadow_projection_shift.z = intBitsToFloat(clipmap_base_offset.x);
|
||||
Clément Foucault
commented
This needs to be fixed. This needs to be fixed.
Miguel Pozo
commented
I've updated the code, but I'm not sure if it's right. I've updated the code, but I'm not sure if it's right.
The test is disabled and fails even in main if I enable it back.
|
||||
light.do_not_access_directly.clip_side = intBitsToFloat(clipmap_base_offset.y);
|
||||
}
|
||||
|
||||
void main()
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_rotation_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(draw_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
|
||||
shared int global_min;
|
||||
shared int global_max;
|
||||
|
@ -52,8 +54,10 @@ void main()
|
|||
|
||||
float local_min = FLT_MAX;
|
||||
float local_max = -FLT_MAX;
|
||||
|
||||
vec3 shadow_forward_z_axis = -shadow_vector_get(light, true, vec3(0));
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
Negate and rename Negate and rename `shadow_forward_z_axis`.
|
||||
for (int i = 0; i < 8; i++) {
|
||||
float z = dot(box.corners[i].xyz, -light._back);
|
||||
float z = dot(box.corners[i].xyz, shadow_forward_z_axis);
|
||||
local_min = min(local_min, z);
|
||||
local_max = max(local_max, z);
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ vec3 shadow_punctual_face_local_to_local_position(int face_id, vec3 fL)
|
|||
}
|
||||
}
|
||||
|
||||
/* Turns local light coordinate into shadow region index. Matches eCubeFace order.
|
||||
/* Turns local shadow coordinate into shadow region index. Matches eCubeFace order.
|
||||
* \note lL does not need to be normalized. */
|
||||
int shadow_punctual_face_index_get(vec3 lL)
|
||||
{
|
||||
|
|
|
@ -185,16 +185,16 @@ ShadowMapTraceResult shadow_map_trace_finish(ShadowMapTracingState state)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* If the ray direction `L` is below the horizon defined by N (normalized) at the shading point,
|
||||
* push it just above the horizon so that this ray will never be below it and produce
|
||||
/* If the ray direction `shadow_dir` is below the horizon defined by N (normalized) at the shading
|
||||
* point, push it just above the horizon so that this ray will never be below it and produce
|
||||
* over-shadowing (since light evaluation already clips the light shape). */
|
||||
vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N)
|
||||
vec3 shadow_ray_above_horizon_ensure(vec3 shadow_dir, vec3 N)
|
||||
{
|
||||
float distance_to_plan = dot(L, -N);
|
||||
float distance_to_plan = dot(shadow_dir, -N);
|
||||
if (distance_to_plan > 0.0) {
|
||||
L += N * (0.01 + distance_to_plan);
|
||||
shadow_dir += N * (0.01 + distance_to_plan);
|
||||
}
|
||||
return L;
|
||||
return shadow_dir;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -309,16 +309,13 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
|||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
|
||||
/* TODO(fclem): 3D shift for jittered soft shadows. */
|
||||
vec3 projection_origin = vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift);
|
||||
vec3 direction;
|
||||
if (is_area_light(light.type)) {
|
||||
random_2d *= light_area_data_get(light).size;
|
||||
|
||||
vec3 point_on_light_shape = vec3(random_2d, 0.0);
|
||||
/* Progressively blend the shape back to the projection origin. */
|
||||
point_on_light_shape = mix(
|
||||
-projection_origin, point_on_light_shape, light_local_data_get(light).shadow_scale);
|
||||
point_on_light_shape = point_on_light_shape * light_local_data_get(light).shadow_scale;
|
||||
point_on_light_shape.z -= light_local_data_get(light).shadow_projection_shift.z;
|
||||
|
||||
direction = point_on_light_shape - lP;
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
|
@ -331,14 +328,13 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
|||
}
|
||||
else {
|
||||
float dist;
|
||||
vec3 L = normalize_and_get_length(lP, dist);
|
||||
vec3 shadow_dir = normalize_and_get_length(lP, dist);
|
||||
/* Disk rotated towards light vector. */
|
||||
vec3 right, up;
|
||||
make_orthonormal_basis(L, right, up);
|
||||
make_orthonormal_basis(shadow_dir, right, up);
|
||||
|
||||
float shape_radius = light_spot_data_get(light).radius;
|
||||
if (is_sphere_light(light.type)) {
|
||||
/* FIXME(weizhen): this is not well-defined when `dist < light.spot.radius`. */
|
||||
shape_radius = light_sphere_disk_radius(shape_radius, dist);
|
||||
}
|
||||
random_2d *= shape_radius;
|
||||
|
@ -355,13 +351,13 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
|||
}
|
||||
|
||||
/* Apply shadow origin shift. */
|
||||
vec3 local_ray_start = lP + projection_origin;
|
||||
vec3 local_ray_start = lP;
|
||||
vec3 local_ray_end = local_ray_start + direction;
|
||||
|
||||
/* Use an offset in the ray direction to jitter which face is traced.
|
||||
* This helps hiding some harsh discontinuity. */
|
||||
int face_id = shadow_punctual_face_index_get(local_ray_start + direction * 0.5);
|
||||
/* Local Light Space > Face Local (View) Space. */
|
||||
/* Local Shadow Space > Face Local (View) Space. */
|
||||
vec3 view_ray_start = shadow_punctual_local_position_to_face_local(face_id, local_ray_start);
|
||||
vec3 view_ray_end = shadow_punctual_local_position_to_face_local(face_id, local_ray_end);
|
||||
|
||||
|
@ -405,15 +401,15 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
|
|||
|
||||
/* Compute the world space offset of the shading position required for
|
||||
* stochastic percentage closer filtering of shadow-maps. */
|
||||
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng, vec2 random)
|
||||
vec3 shadow_pcf_offset(
|
||||
LightData light, const bool is_directional, vec3 shadow_dir, vec3 P, vec3 Ng, vec2 random)
|
||||
{
|
||||
if (light.pcf_radius <= 0.001) {
|
||||
/* Early return. */
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
vec3 L = light_vector_get(light, is_directional, P).L;
|
||||
if (dot(L, Ng) < 0.001) {
|
||||
if (dot(shadow_dir, Ng) < 0.001) {
|
||||
/* Don't apply PCF to almost perpendicular,
|
||||
* since we can't project the offset to the surface. */
|
||||
return vec3(0.0);
|
||||
|
@ -482,22 +478,22 @@ vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3
|
|||
|
||||
#ifdef GPU_NVIDIA
|
||||
/* Workaround for a bug in the Nvidia shader compiler.
|
||||
* If we don't compute L here again, it breaks shadows on reflection probes. */
|
||||
L = light_vector_get(light, is_directional, P).L;
|
||||
* If we don't compute shadow_dir here again, it breaks shadows on reflection probes. */
|
||||
shadow_dir = shadow_vector_get(light, is_directional, P);
|
||||
#endif
|
||||
|
||||
if (abs(dot(Ng, L)) > 0.999) {
|
||||
if (abs(dot(Ng, shadow_dir)) > 0.999) {
|
||||
return ws_offset;
|
||||
}
|
||||
|
||||
offset_P = line_plane_intersect(offset_P, L, P, Ng);
|
||||
offset_P = line_plane_intersect(offset_P, shadow_dir, P, Ng);
|
||||
ws_offset = offset_P - P;
|
||||
|
||||
if (dot(ws_offset, L) < 0.0) {
|
||||
if (dot(ws_offset, shadow_dir) < 0.0) {
|
||||
/* Project the offset position into the perpendicular plane, since it's closer to the light
|
||||
* (avoids overshadowing at geometry angles). */
|
||||
vec3 perpendicular_plane_normal = cross(Ng, normalize(cross(Ng, L)));
|
||||
offset_P = line_plane_intersect(offset_P, L, P, perpendicular_plane_normal);
|
||||
vec3 perpendicular_plane_normal = cross(Ng, normalize(cross(Ng, shadow_dir)));
|
||||
offset_P = line_plane_intersect(offset_P, shadow_dir, P, perpendicular_plane_normal);
|
||||
ws_offset = offset_P - P;
|
||||
}
|
||||
|
||||
|
@ -513,10 +509,23 @@ ShadowEvalResult shadow_eval(LightData light,
|
|||
bool is_translucent_with_thickness,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 L,
|
||||
vec3 shadow_dir,
|
||||
float shadow_offset,
|
||||
int ray_count,
|
||||
int ray_step_count)
|
||||
{
|
||||
if (!is_directional && (is_point_light(light.type) || is_spot_light(light.type))) {
|
||||
bool inside_light = distance_squared(P, light._position) <
|
||||
light_spot_data_get(light).radius_squared;
|
||||
if (inside_light) {
|
||||
/* Early out. */
|
||||
ShadowEvalResult result;
|
||||
result.light_visibilty = 1.0;
|
||||
result.occluder_distance = 0.0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EEVEE_SAMPLING_DATA
|
||||
# ifdef GPU_FRAGMENT_SHADER
|
||||
vec2 pixel = floor(gl_FragCoord.xy);
|
||||
|
@ -535,13 +544,20 @@ ShadowEvalResult shadow_eval(LightData light,
|
|||
float normal_offset = 0.02;
|
||||
#endif
|
||||
|
||||
P += shadow_pcf_offset(light, is_directional, P, Ng, random_pcf_2d);
|
||||
P = is_transmission ? (P + shadow_dir * shadow_offset) : P;
|
||||
|
||||
P += shadow_pcf_offset(light, is_directional, shadow_dir, P, Ng, random_pcf_2d);
|
||||
|
||||
/* We want to bias inside the object for transmission to go through the object itself.
|
||||
* But doing so split the shadow in two different directions at the horizon. Also this
|
||||
* doesn't fix the the aliasing issue. So we reflect the normal so that it always go towards
|
||||
* the light. */
|
||||
vec3 N_bias = is_transmission ? reflect(Ng, L) : Ng;
|
||||
vec3 N_bias = is_transmission ? reflect(Ng, shadow_dir) : Ng;
|
||||
|
||||
if (dot(Ng, shadow_dir) < 0.0) {
|
||||
/* Always bias towards the light. */
|
||||
N_bias -= 2.0;
|
||||
}
|
||||
|
||||
/* Avoid self intersection. */
|
||||
P = offset_ray(P, N_bias);
|
||||
Clément Foucault
commented
This is very shady, I'm pretty sure it make things explode to jitter the shading point position (it can move it behind the light). And if doesn't, it is still super confusing. What is the reason to move this here? Also if you change it here why not in This is very shady, I'm pretty sure it make things explode to jitter the shading point position (it can move it behind the light). And if doesn't, it is still super confusing. What is the reason to move this here?
Also if you change it here why not in `shadow_sample()`?
Miguel Pozo
commented
How is this shady? This is just computing the shadow map local space.
I forgot, but that's needed regardless, isn't it? How is this shady? This is just computing the shadow map local space.
I moved it here because I found way more confusing the way it was.
Also, doesn't that mess some computations in `shadow_ray_generate_punctual`, like the `direction`?
> Also if you change it here why not in shadow_sample()?
I forgot, but that's needed regardless, isn't it?
Clément Foucault
commented
Yes. Both need to be changed.
Because
Yes you are right. The existing code is also quite confusing if not wrong. That said, it isn't a reason to move this out of the existing branch. > I forgot, but that's needed regardless, isn't it?
Yes. Both need to be changed.
> How is this shady?
Because `lP` refers to object local position which in this case, is the light.
> Also, doesn't that mess some computations in shadow_ray_generate_punctual, like the direction?
Yes you are right. The existing code is also quite confusing if not wrong. That said, it isn't a reason to move this out of the existing branch.
Clément Foucault
commented
The existing code made sense because it was only used for area light shadow shift. > The existing code is also quite confusing if not wrong
The existing code made sense because it was only used for area light shadow shift.
`projection_origin` is the point in light object space where the shadow is recorded at. So after the shadow scale mix, you need to move the point to shadow space for `local_ray_start/end`. Here the `local_` denotes the shadow space. But I agree it could be better named.
|
||||
|
@ -549,9 +565,18 @@ ShadowEvalResult shadow_eval(LightData light,
|
|||
/* TODO(fclem): Scale based on depth. */
|
||||
P += N_bias * normal_offset;
|
||||
|
||||
vec3 lP = is_directional ? light_world_to_local(light, P) :
|
||||
light_world_to_local(light, P - light._position);
|
||||
vec3 lNg = light_world_to_local(light, Ng);
|
||||
vec3 lP, lNg;
|
||||
if (is_directional) {
|
||||
lP = light_world_to_local(light, P);
|
||||
lP = light_local_to_shadow_local(light, lP, true);
|
||||
lNg = light_world_to_local(light, Ng);
|
||||
lNg = light_local_to_shadow_local(light, lNg, true);
|
||||
}
|
||||
else {
|
||||
lP = light_world_to_local(light, P - light._position);
|
||||
lP = light_local_to_shadow_local(light, lP, false);
|
||||
lNg = light_world_to_local(light, Ng);
|
||||
}
|
||||
/* Invert horizon clipping. */
|
||||
lNg = (is_transmission) ? -lNg : lNg;
|
||||
/* Don't do a any horizon clipping in this case as the closure is lit from both sides. */
|
||||
|
|
|
@ -233,7 +233,7 @@ static void test_eevee_shadow_tag_update()
|
|||
|
||||
{
|
||||
ShadowTileMap tilemap(0 * SHADOW_TILEDATA_PER_TILEMAP);
|
||||
tilemap.sync_cubeface(float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f);
|
||||
tilemap.sync_cubeface(float4x4::identity(), 0.01f, 1.0f, 0.01f, Z_NEG, 0.0f);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
{
|
||||
|
@ -1541,7 +1541,7 @@ static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap)
|
|||
|
||||
{
|
||||
ShadowTileMap tilemap(0);
|
||||
tilemap.sync_cubeface(float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f);
|
||||
tilemap.sync_cubeface(float4x4::identity(), 0.01f, 1.0f, 0.01f, Z_NEG, 0.0f);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,11 @@ vec4 as_vec4(Quaternion quat)
|
|||
return vec4(quat.x, quat.y, quat.z, quat.w);
|
||||
}
|
||||
|
||||
Quaternion as_quaternion(vec4 quat)
|
||||
{
|
||||
return Quaternion(quat.x, quat.y, quat.z, quat.w);
|
||||
}
|
||||
|
||||
Quaternion Quaternion_identity()
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
Move this function next to Move this function next to `as_vec4` as it is a conversion function.
|
||||
{
|
||||
return Quaternion(1, 0, 0, 0);
|
||||
|
@ -113,6 +118,19 @@ Quaternion interpolate(Quaternion a, Quaternion b, float t)
|
|||
return Quaternion(UNPACK4(quat));
|
||||
}
|
||||
|
||||
Quaternion invert(Quaternion quat)
|
||||
{
|
||||
return Quaternion(-quat.x, -quat.y, -quat.z, quat.w);
|
||||
}
|
||||
|
||||
vec3 rotate(Quaternion quat, vec3 vec)
|
||||
{
|
||||
/* https://fgiesen.wordpress.com/2019/02/09/rotating-a-single-vector-using-a-quaternion/ */
|
||||
vec4 q = as_vec4(quat);
|
||||
vec3 t = cross(q.xyz, vec) * 2.0;
|
||||
return vec + t * q.w + cross(q.xyz, t);
|
||||
}
|
||||
|
||||
Quaternion to_quaternion(EulerXYZ eul)
|
||||
{
|
||||
float ti = eul.x * 0.5;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
.shadow_trace_distance = 10.0f, \
|
||||
.shadow_filter_radius = 3.0f, \
|
||||
.shadow_resolution_scale = 1.0f, \
|
||||
.shadow_jitter_overblur_percentage = 10.0f, \
|
||||
.att_dist = 40.0f, \
|
||||
.sun_angle = DEG2RADF(0.526f), \
|
||||
.area_spread = DEG2RADF(180.0f), \
|
||||
|
|
|
@ -83,6 +83,9 @@ typedef struct Light {
|
|||
float shadow_trace_distance;
|
||||
float shadow_filter_radius;
|
||||
float shadow_resolution_scale;
|
||||
float shadow_jitter_overblur_percentage;
|
||||
|
||||
float _pad2;
|
||||
|
||||
/* Preview */
|
||||
struct PreviewImage *preview;
|
||||
|
@ -93,7 +96,7 @@ typedef struct Light {
|
|||
/* Deprecated. */
|
||||
struct Ipo *ipo DNA_DEPRECATED; /* Old animation system. */
|
||||
float energy_deprecated DNA_DEPRECATED;
|
||||
float _pad2;
|
||||
float _pad3;
|
||||
} Light;
|
||||
|
||||
/* **************** LIGHT ********************* */
|
||||
|
@ -145,6 +148,7 @@ enum {
|
|||
LA_SHAD_CONTACT = 1 << 19,
|
||||
LA_CUSTOM_ATTENUATION = 1 << 20,
|
||||
LA_USE_SOFT_FALLOFF = 1 << 21,
|
||||
LA_SHADOW_JITTER = 1 << 22
|
||||
};
|
||||
|
||||
/** #Light::falloff_type */
|
||||
|
|
|
@ -322,6 +322,25 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun)
|
|||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, 0, "rna_Light_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_shadow_jittering", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_SHADOW_JITTER);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Shadow Jittering",
|
||||
"Enable jittered shoft shadows (Disabled in the Viewport unless "
|
||||
"enabled in the Scene settings)");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, 0, "rna_Light_update");
|
||||
|
||||
prop = RNA_def_property(srna, "shadow_jitter_overblur_percentage", PROP_FLOAT, PROP_PERCENTAGE);
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
Should be Should be `PROP_PERCENTAGE`.
Miguel Pozo
commented
Done, but I can't say I like it. Done, but I can't say I like it.
|
||||
RNA_def_property_range(prop, 0.0f, 100.0f);
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
Nitpick: It might be copied from somewhere else, but Nitpick: It might be copied from somewhere else, but `1.0` is missing a `f`.
|
||||
RNA_def_property_ui_range(prop, 0.0f, 20.0f, 10.0f, 0);
|
||||
pragma37 marked this conversation as resolved
Outdated
Clément Foucault
commented
UI range (soft range) should be similar to the DOF overblur. UI range (soft range) should be similar to the DOF overblur. `20%` soft max sounds good.
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Shadow Jitter Overblur",
|
||||
"Apply shadow tracing to each jittered sample to reduce under-sampling artifacts");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, 0, "rna_Light_update");
|
||||
|
||||
if (sun) {
|
||||
prop = RNA_def_property(srna, "shadow_cascade_max_distance", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "cascade_max_dist");
|
||||
|
|
Why use sun.radius here? it is Clampped to a min and thus will always produce some flicker if jitter is enabled. Prefer computing the disk size again from the angle
la->sun_angle * la->shadow_softness_factor
when jittering the position (code is less hard to follow).I feel the same way as for punctual. Just pass
sun_angle
, jitter amount and tracing amount.