EEVEE-Next: Jittered Soft Shadows
Jittered Soft Shadows support. Improves soft shadow quality at the cost of re-rendering shadow maps every sample. Disabled by default in the viewport unless enabled in the Scene settings. | Tracing-only | Jitter-only | Jitter+Over-blur | | --- | --- | --- | |  |  |  | Tracing-only is the method used by default in EEVEE-Next. Jitter-only is the method used by EEVEE-Legacy Soft Shadows. Jitter+Over-blur combines both. Co-authored by Miguel Pozo @pragma37 (initial patch #119753) Pull Request: https://projects.blender.org/blender/blender/pulls/121836
This commit is contained in:
committed by
Clément Foucault
parent
e7801e0d07
commit
e0b3dee35a
@@ -152,6 +152,16 @@ class DATA_PT_EEVEE_light_shadow(DataButtonsPanel, Panel):
|
||||
layout.use_property_split = True
|
||||
layout.active = context.scene.eevee.use_shadows and light.use_shadow
|
||||
|
||||
col = layout.column(align=False, heading="Jitter")
|
||||
row = col.row(align=True)
|
||||
sub = row.row(align=True)
|
||||
sub.prop(light, "use_shadow_jitter", text="")
|
||||
sub = sub.row(align=True)
|
||||
sub.active = light.use_shadow_jitter
|
||||
sub.prop(light, "shadow_jitter_overblur", text="Overblur")
|
||||
|
||||
col.separator()
|
||||
|
||||
col = layout.column()
|
||||
col.prop(light, "shadow_filter_radius", text="Filter")
|
||||
|
||||
|
||||
@@ -843,7 +843,7 @@ class RENDER_PT_eevee_next_sampling_viewport(RenderButtonsPanel, Panel):
|
||||
col = layout.column()
|
||||
col.prop(props, "taa_samples", text="Samples")
|
||||
col.prop(props, "use_taa_reprojection", text="Temporal Reprojection")
|
||||
col.prop(props, "use_shadow_jittered_viewport", text="Jittered Shadows")
|
||||
col.prop(props, "use_shadow_jitter_viewport", text="Jittered Shadows")
|
||||
|
||||
# Add SSS sample count here.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 40
|
||||
#define BLENDER_FILE_SUBVERSION 41
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -3633,6 +3633,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 41)) {
|
||||
const Light *default_light = DNA_struct_default_get(Light);
|
||||
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||
light->shadow_jitter_overblur = default_light->shadow_jitter_overblur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
||||
@@ -72,7 +72,8 @@ void Light::sync(ShadowModule &shadows,
|
||||
|
||||
this->object_to_world = object_to_world;
|
||||
|
||||
shape_parameters_set(la, scale, threshold);
|
||||
shape_parameters_set(
|
||||
la, scale, object_to_world.z_axis(), threshold, shadows.get_data().use_jitter);
|
||||
|
||||
const bool diffuse_visibility = (visibility_flag & OB_HIDE_DIFFUSE) == 0;
|
||||
const bool glossy_visibility = (visibility_flag & OB_HIDE_GLOSSY) == 0;
|
||||
@@ -86,20 +87,13 @@ void Light::sync(ShadowModule &shadows,
|
||||
this->power[LIGHT_TRANSMISSION] = la->transmission_fac * shape_power * transmission_visibility;
|
||||
this->power[LIGHT_VOLUME] = la->volume_fac * point_power * volume_visibility;
|
||||
|
||||
this->pcf_radius = la->shadow_filter_radius;
|
||||
|
||||
this->lod_bias = (1.0f - la->shadow_resolution_scale) * SHADOW_TILEMAP_LOD;
|
||||
this->lod_min = shadow_lod_min_get(la);
|
||||
this->pcf_radius = la->shadow_filter_radius;
|
||||
this->shadow_jitter = (la->mode & LA_SHADOW_JITTER) != 0;
|
||||
|
||||
if (la->mode & LA_SHADOW) {
|
||||
shadow_ensure(shadows);
|
||||
if (is_sun_light(this->type)) {
|
||||
this->directional->sync(object_to_world, la->sun_angle);
|
||||
}
|
||||
else {
|
||||
this->punctual->sync(
|
||||
this->type, object_to_world, la->spotsize, this->local.influence_radius_max);
|
||||
}
|
||||
}
|
||||
else {
|
||||
shadow_discard_safe(shadows);
|
||||
@@ -152,27 +146,62 @@ float Light::attenuation_radius_get(const ::Light *la, float light_threshold, fl
|
||||
return sqrtf(light_power / light_threshold);
|
||||
}
|
||||
|
||||
void Light::shape_parameters_set(const ::Light *la, const float3 &scale, float threshold)
|
||||
void Light::shape_parameters_set(const ::Light *la,
|
||||
const float3 &scale,
|
||||
const float3 &z_axis,
|
||||
const float threshold,
|
||||
const bool use_jitter)
|
||||
{
|
||||
using namespace blender::math;
|
||||
|
||||
/* Compute influence radius first. Can be amended by shape later. */
|
||||
if (is_local_light(this->type)) {
|
||||
const float max_power = reduce_max(float3(&la->r)) * fabsf(la->energy / 100.0f);
|
||||
const float surface_max_power = max(la->diff_fac, la->spec_fac) * max_power;
|
||||
const float volume_max_power = la->volume_fac * max_power;
|
||||
|
||||
float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
|
||||
float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
|
||||
|
||||
this->local.influence_radius_max = max(influence_radius_surface, influence_radius_volume);
|
||||
this->local.influence_radius_invsqr_surface = safe_rcp(square(influence_radius_surface));
|
||||
this->local.influence_radius_invsqr_volume = safe_rcp(square(influence_radius_volume));
|
||||
/* TODO(fclem): This is just duplicating a member for local lights. */
|
||||
this->clip_far = float_as_int(this->local.influence_radius_max);
|
||||
this->clip_near = float_as_int(this->local.influence_radius_max / 4000.0f);
|
||||
}
|
||||
|
||||
float trace_scaling_fac = (use_jitter && (la->mode & LA_SHADOW_JITTER)) ?
|
||||
la->shadow_jitter_overblur / 100.0f :
|
||||
1.0f;
|
||||
|
||||
if (is_sun_light(this->type)) {
|
||||
this->sun.radius = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f);
|
||||
float sun_half_angle = min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f;
|
||||
/* Use non-clamped radius for soft shadows. Avoid having a minimum blur. */
|
||||
this->sun.shadow_angle = sun_half_angle * trace_scaling_fac;
|
||||
/* Clamp to minimum value before float imprecision artifacts appear. */
|
||||
this->sun.radius = max(0.001f, this->sun.radius);
|
||||
this->sun.shape_radius = clamp(tanf(sun_half_angle), 0.001f, 20.0f);
|
||||
/* Stable shading direction. */
|
||||
this->sun.direction = z_axis;
|
||||
}
|
||||
else if (is_area_light(this->type)) {
|
||||
const bool is_irregular = ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE);
|
||||
this->area.size = float2(la->area_size, is_irregular ? la->area_sizey : la->area_size);
|
||||
/* Scale and clamp to minimum value before float imprecision artifacts appear. */
|
||||
this->area.size = this->area.size * scale.xy() / 2.0f;
|
||||
/* Do not render lights that virtually have no area or clamp to minimum value before float
|
||||
* imprecision artifacts appear. */
|
||||
this->area.size = (this->area.size.x * this->area.size.y < 0.00001f) ?
|
||||
float2(0.0) :
|
||||
max(float2(0.003f), this->area.size * scale.xy() / 2.0f);
|
||||
this->area.size *= scale.xy() / 2.0f;
|
||||
/* In this case, this is just the scaling factor. */
|
||||
this->local.shadow_radius = trace_scaling_fac;
|
||||
/* Set to default position. */
|
||||
this->local.shadow_position = float3(0.0f);
|
||||
/* Do not render lights that have no area. */
|
||||
if (this->area.size.x * this->area.size.y < 0.00001f) {
|
||||
/* Forces light to be culled. */
|
||||
this->local.influence_radius_max = 0.0f;
|
||||
}
|
||||
/* Clamp to minimum value before float imprecision artifacts appear. */
|
||||
this->area.size = max(float2(0.003f), this->area.size);
|
||||
/* For volume point lighting. */
|
||||
this->local.radius_squared = square(max(0.001f, length(this->area.size) / 2.0f));
|
||||
this->local.shape_radius = max(0.001f, length(this->area.size) / 2.0f);
|
||||
}
|
||||
else if (is_point_light(this->type)) {
|
||||
/* Spot size & blend */
|
||||
@@ -192,28 +221,14 @@ void Light::shape_parameters_set(const ::Light *la, const float3 &scale, float t
|
||||
this->spot.spot_bias = 1.0f;
|
||||
this->spot.spot_tan = 0.0f;
|
||||
}
|
||||
|
||||
this->spot.radius = la->radius;
|
||||
/* Use unclamped radius for soft shadows. Avoid having a minimum blur. */
|
||||
this->local.shadow_radius = max(0.0f, la->radius) * trace_scaling_fac;
|
||||
/* Set to default position. */
|
||||
this->local.shadow_position = float3(0.0f);
|
||||
/* Ensure a minimum radius/energy ratio to avoid harsh cut-offs. (See 114284) */
|
||||
this->spot.radius = max(this->spot.radius, la->energy * 2e-05f);
|
||||
this->local.shape_radius = max(la->radius, la->energy * 2e-05f);
|
||||
/* Clamp to minimum value before float imprecision artifacts appear. */
|
||||
this->spot.radius = max(0.001f, this->spot.radius);
|
||||
|
||||
/* For volume point lighting. */
|
||||
this->local.radius_squared = square(this->spot.radius);
|
||||
}
|
||||
|
||||
if (is_local_light(this->type)) {
|
||||
const float max_power = reduce_max(float3(&la->r)) * fabsf(la->energy / 100.0f);
|
||||
const float surface_max_power = max(la->diff_fac, la->spec_fac) * max_power;
|
||||
const float volume_max_power = la->volume_fac * max_power;
|
||||
|
||||
float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
|
||||
float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
|
||||
|
||||
this->local.influence_radius_max = max(influence_radius_surface, influence_radius_volume);
|
||||
this->local.influence_radius_invsqr_surface = safe_rcp(square(influence_radius_surface));
|
||||
this->local.influence_radius_invsqr_volume = safe_rcp(square(influence_radius_volume));
|
||||
this->local.shape_radius = max(0.001f, this->local.shape_radius);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,13 +254,13 @@ float Light::shape_radiance_get()
|
||||
case LIGHT_SPOT_SPHERE:
|
||||
case LIGHT_SPOT_DISK: {
|
||||
/* Sphere area. */
|
||||
float area = float(4.0f * M_PI) * this->spot.radius_squared;
|
||||
float area = float(4.0f * M_PI) * square(this->local.shape_radius);
|
||||
/* Convert radiant flux to radiance. */
|
||||
return 1.0f / (area * float(M_PI));
|
||||
}
|
||||
case LIGHT_SUN_ORTHO:
|
||||
case LIGHT_SUN: {
|
||||
float inv_sin_sq = 1.0f + 1.0f / square(this->sun.radius);
|
||||
float inv_sin_sq = 1.0f + 1.0f / square(this->sun.shape_radius);
|
||||
/* Convert irradiance to radiance. */
|
||||
return float(M_1_PI) * inv_sin_sq;
|
||||
}
|
||||
@@ -510,15 +525,21 @@ void LightModule::culling_pass_sync()
|
||||
|
||||
void LightModule::update_pass_sync()
|
||||
{
|
||||
/* TODO(fclem): This dispatch for all light before culling. This could be made better by
|
||||
* only running on lights that survive culling using an indirect dispatch. */
|
||||
uint safe_lights_len = max_ii(lights_len_, 1);
|
||||
uint shadow_setup_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SELECT_GROUP_SIZE);
|
||||
|
||||
auto &pass = update_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(inst_.shaders.static_shader_get(LIGHT_SHADOW_SETUP));
|
||||
pass.bind_ssbo("light_buf", &culling_light_buf_);
|
||||
pass.bind_ssbo("light_cull_buf", &culling_data_buf_);
|
||||
pass.bind_ssbo("tilemaps_buf", &inst_.shadows.tilemap_pool.tilemaps_data);
|
||||
pass.bind_ssbo("tilemaps_clip_buf", &inst_.shadows.tilemap_pool.tilemaps_clip);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
/* TODO(fclem): Dispatch for all light. */
|
||||
pass.dispatch(int3(1, 1, 1));
|
||||
pass.bind_resources(inst_.sampling);
|
||||
pass.dispatch(int3(shadow_setup_dispatch_size, 1, 1));
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,13 @@ struct Light : public LightData, NonCopyable {
|
||||
|
||||
private:
|
||||
float shadow_lod_min_get(const ::Light *la);
|
||||
float shadow_shape_size_get(const ::Light *la);
|
||||
float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power);
|
||||
void shape_parameters_set(const ::Light *la, const float3 &scale, float threshold);
|
||||
void shape_parameters_set(const ::Light *la,
|
||||
const float3 &scale,
|
||||
const float3 &z_axis,
|
||||
float threshold,
|
||||
bool do_jitter);
|
||||
float shape_radiance_get();
|
||||
float point_radiance_get();
|
||||
};
|
||||
|
||||
@@ -166,6 +166,16 @@ void Sampling::step()
|
||||
data_.dimensions[SAMPLING_RAYTRACE_V] = r[1];
|
||||
data_.dimensions[SAMPLING_RAYTRACE_W] = r[2];
|
||||
}
|
||||
{
|
||||
double3 r, offset = {0, 0, 0};
|
||||
uint3 primes = {2, 3, 5};
|
||||
BLI_halton_3d(primes, offset, sample_ + 1, r);
|
||||
/* WORKAROUND: We offset the distribution to make the first sample (0,0,0). */
|
||||
/* TODO de-correlate. */
|
||||
data_.dimensions[SAMPLING_SHADOW_I] = fractf(r[0] + (1.0 / 2.0));
|
||||
data_.dimensions[SAMPLING_SHADOW_J] = fractf(r[1] + (2.0 / 3.0));
|
||||
data_.dimensions[SAMPLING_SHADOW_K] = fractf(r[2] + (4.0 / 5.0));
|
||||
}
|
||||
{
|
||||
uint64_t sample_volume = sample_;
|
||||
if (interactive_mode()) {
|
||||
|
||||
@@ -41,7 +41,21 @@ constexpr GPUSamplerState with_filter = {GPU_SAMPLER_FILTERING_LINEAR};
|
||||
# define IS_CPP 1
|
||||
#endif
|
||||
|
||||
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
|
||||
/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */
|
||||
#ifndef M_PI
|
||||
# define EEVEE_PI
|
||||
# define M_PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
|
||||
enum eCubeFace : uint32_t {
|
||||
/* Ordering by culling order. If cone aperture is shallow, we cull the later view. */
|
||||
Z_NEG = 0u,
|
||||
X_POS = 1u,
|
||||
X_NEG = 2u,
|
||||
Y_POS = 3u,
|
||||
Y_NEG = 4u,
|
||||
Z_POS = 5u,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform
|
||||
@@ -69,6 +83,23 @@ struct Transform {
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline float4x4 transform_to_matrix(Transform t)
|
||||
{
|
||||
return float4x4(float4(t.x.x, t.y.x, t.z.x, 0.0f),
|
||||
float4(t.x.y, t.y.y, t.z.y, 0.0f),
|
||||
float4(t.x.z, t.y.z, t.z.z, 0.0f),
|
||||
float4(t.x.w, t.y.w, t.z.w, 1.0f));
|
||||
}
|
||||
|
||||
static inline Transform transform_from_matrix(float4x4 m)
|
||||
{
|
||||
Transform t;
|
||||
t.x = float4(m[0][0], m[1][0], m[2][0], m[3][0]);
|
||||
t.y = float4(m[0][1], m[1][1], m[2][1], m[3][1]);
|
||||
t.z = float4(m[0][2], m[1][2], m[2][2], m[3][2]);
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline float3 transform_x_axis(Transform t)
|
||||
{
|
||||
return float3(t.x.x, t.y.x, t.z.x);
|
||||
@@ -86,6 +117,13 @@ static inline float3 transform_location(Transform t)
|
||||
return float3(t.x.w, t.y.w, t.z.w);
|
||||
}
|
||||
|
||||
#if !IS_CPP
|
||||
static inline bool transform_equal(Transform a, Transform b)
|
||||
{
|
||||
return all(equal(a.x, b.x)) && all(equal(a.y, b.y)) && all(equal(a.z, b.z));
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline float3 transform_point(Transform t, float3 point)
|
||||
{
|
||||
return float4(point, 1.0f) * float3x4(t.x, t.y, t.z);
|
||||
@@ -215,7 +253,10 @@ enum eSamplingDimension : uint32_t {
|
||||
SAMPLING_CURVES_U = 21u,
|
||||
SAMPLING_VOLUME_U = 22u,
|
||||
SAMPLING_VOLUME_V = 23u,
|
||||
SAMPLING_VOLUME_W = 24u
|
||||
SAMPLING_VOLUME_W = 24u,
|
||||
SAMPLING_SHADOW_I = 25u,
|
||||
SAMPLING_SHADOW_J = 26u,
|
||||
SAMPLING_SHADOW_K = 27u,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -675,12 +716,6 @@ struct ScatterRect {
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ScatterRect, 16)
|
||||
|
||||
/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */
|
||||
#ifndef M_PI
|
||||
# define EEVEE_PI
|
||||
# define M_PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
|
||||
static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
|
||||
{
|
||||
depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth;
|
||||
@@ -725,10 +760,6 @@ static inline float circle_to_polygon_angle(float sides_count, float theta)
|
||||
return side * side_angle + final_local_theta;
|
||||
}
|
||||
|
||||
#ifdef EEVEE_PI
|
||||
# undef M_PI
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -833,30 +864,27 @@ static inline bool is_local_light(eLightType type)
|
||||
/* Using define because GLSL doesn't have inheritance, and encapsulation forces us to add some
|
||||
* unneeded padding. */
|
||||
#define LOCAL_LIGHT_COMMON \
|
||||
/** Special radius factor for point lighting (volume). */ \
|
||||
float radius_squared; \
|
||||
/** --- Shadow Data --- */ \
|
||||
/** Shift to apply to the light origin to get the shadow projection origin. In light space. */ \
|
||||
packed_float3 shadow_position; \
|
||||
float _pad0; \
|
||||
/** Radius of the light for shadow ray casting. Simple scaling factor for rectangle lights. */ \
|
||||
float shadow_radius; \
|
||||
/** Radius of the light for shading. Bounding radius for rectangle lights. */ \
|
||||
float shape_radius; \
|
||||
/** Maximum influence radius. Used for culling. Equal to clip far distance. */ \
|
||||
float influence_radius_max; \
|
||||
/** Influence radius (inverted and squared) adjusted for Surface / Volume power. */ \
|
||||
float influence_radius_invsqr_surface; \
|
||||
float influence_radius_invsqr_volume; \
|
||||
/** --- Shadow Data --- */ \
|
||||
/** Other parts of the perspective matrix. Assumes symmetric frustum. */ \
|
||||
float clip_side; \
|
||||
/** Number of allocated tilemap for this local light. */ \
|
||||
int tilemaps_count; \
|
||||
float _pad7; \
|
||||
/** Shift to apply to the light origin to get the shadow projection origin. */ \
|
||||
float shadow_projection_shift;
|
||||
int tilemaps_count;
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -870,11 +898,7 @@ 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;
|
||||
float _pad1;
|
||||
/** Scale and bias to spot equation parameter. Used for adjusting the falloff. */
|
||||
float spot_mul;
|
||||
|
||||
@@ -889,9 +913,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;
|
||||
|
||||
@@ -903,21 +924,21 @@ struct LightAreaData {
|
||||
BLI_STATIC_ASSERT(sizeof(LightAreaData) == sizeof(LightLocalData), "Data size must match")
|
||||
|
||||
struct LightSunData {
|
||||
float radius;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
/* Sun direction for shading. Use object_to_world for getting into shadow space. */
|
||||
packed_float3 direction;
|
||||
/* Radius of the sun disk, one unit away from a shading point. */
|
||||
float shape_radius;
|
||||
|
||||
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;
|
||||
float _pad5;
|
||||
float _pad3;
|
||||
float _pad4;
|
||||
|
||||
/** Offset to convert from world units to tile space of the clipmap_lod_max. */
|
||||
float2 clipmap_origin;
|
||||
@@ -942,7 +963,11 @@ BLI_STATIC_ASSERT(sizeof(LightSunData) == sizeof(LightLocalData), "Data size mus
|
||||
#endif
|
||||
|
||||
struct LightData {
|
||||
/** Normalized object to world matrix. Stored transposed for compactness. */
|
||||
/**
|
||||
* Normalized object to world matrix. Stored transposed for compactness.
|
||||
* Used for shading and shadowing local lights, or shadowing sun lights.
|
||||
* IMPORTANT: Not used for shading sun lights as this matrix is jittered.
|
||||
*/
|
||||
Transform object_to_world;
|
||||
|
||||
/** Power depending on shader type. Referenced by LightingType. */
|
||||
@@ -965,7 +990,8 @@ struct LightData {
|
||||
float lod_bias;
|
||||
/* Shadow Map resolution maximum resolution. */
|
||||
float lod_min;
|
||||
float _pad1;
|
||||
/* True if the light uses jittered soft shadows. */
|
||||
bool32_t shadow_jitter;
|
||||
float _pad2;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
@@ -1003,10 +1029,12 @@ static inline float3 light_position_get(LightData light)
|
||||
# define CHECK_TYPE_PAIR(a, b)
|
||||
# define CHECK_TYPE(a, b)
|
||||
# define FLOAT_AS_INT floatBitsToInt
|
||||
# define INT_AS_FLOAT intBitsToFloat
|
||||
# define TYPECAST_NOOP
|
||||
|
||||
#else /* C++ */
|
||||
# define FLOAT_AS_INT float_as_int
|
||||
# define INT_AS_FLOAT int_as_float
|
||||
# define TYPECAST_NOOP
|
||||
#endif
|
||||
|
||||
@@ -1033,8 +1061,9 @@ static inline float3 light_position_get(LightData light)
|
||||
# define GARBAGE_VALUE 0.0f
|
||||
# endif
|
||||
|
||||
# define SAFE_BEGIN(data_type, check) \
|
||||
data_type data; \
|
||||
# define SAFE_BEGIN(dst_type, src_type, src_, check) \
|
||||
src_type _src = src_; \
|
||||
dst_type _dst; \
|
||||
bool _validity_check = check; \
|
||||
float _garbage = GARBAGE_VALUE;
|
||||
|
||||
@@ -1042,7 +1071,11 @@ static inline float3 light_position_get(LightData light)
|
||||
# define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) \
|
||||
(_validity_check ? (_value) : _type(_garbage))
|
||||
#else
|
||||
# define SAFE_BEGIN(data_type, check) data_type data;
|
||||
# define SAFE_BEGIN(dst_type, src_type, src_, check) \
|
||||
UNUSED_VARS(check); \
|
||||
src_type _src = src_; \
|
||||
dst_type _dst;
|
||||
|
||||
# define SAFE_ASSIGN_LIGHT_TYPE_CHECK(_type, _value) _value
|
||||
#endif
|
||||
|
||||
@@ -1052,21 +1085,28 @@ static inline float3 light_position_get(LightData light)
|
||||
# define DATA_MEMBER do_not_access_directly
|
||||
#endif
|
||||
|
||||
#define SAFE_READ_BEGIN(dst_type, light, check) \
|
||||
SAFE_BEGIN(dst_type, LightLocalData, light.DATA_MEMBER, check)
|
||||
#define SAFE_READ_END() _dst
|
||||
|
||||
#define SAFE_WRITE_BEGIN(src_type, src, check) SAFE_BEGIN(LightLocalData, src_type, src, check)
|
||||
#define SAFE_WRITE_END(light) light.DATA_MEMBER = _dst;
|
||||
|
||||
#define ERROR_OFS(a, b) "Offset of " STRINGIFY(a) " mismatch offset of " STRINGIFY(b)
|
||||
|
||||
/* This is a dangerous process, make sure to static assert every assignment. */
|
||||
#define SAFE_ASSIGN(a, reinterpret_fn, in_type, b) \
|
||||
CHECK_TYPE_PAIR(data.a, reinterpret_fn(light.DATA_MEMBER.b)); \
|
||||
data.a = reinterpret_fn(SAFE_ASSIGN_LIGHT_TYPE_CHECK(in_type, light.DATA_MEMBER.b)); \
|
||||
BLI_STATIC_ASSERT(offsetof(decltype(data), a) == offsetof(LightLocalData, b), ERROR_OFS(a, b))
|
||||
CHECK_TYPE_PAIR(_src.b, in_type(_dst.a)); \
|
||||
CHECK_TYPE_PAIR(_dst.a, reinterpret_fn(_src.b)); \
|
||||
_dst.a = reinterpret_fn(SAFE_ASSIGN_LIGHT_TYPE_CHECK(in_type, _src.b)); \
|
||||
BLI_STATIC_ASSERT(offsetof(decltype(_dst), a) == offsetof(decltype(_src), b), ERROR_OFS(a, b))
|
||||
|
||||
#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_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); \
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(a.y, c);
|
||||
#define SAFE_ASSIGN_INT_AS_FLOAT(a, b) SAFE_ASSIGN(a, INT_AS_FLOAT, int, b);
|
||||
|
||||
#if !USE_LIGHT_UNION || IS_CPP
|
||||
|
||||
@@ -1076,62 +1116,98 @@ static inline float3 light_position_get(LightData light)
|
||||
namespace do_not_use {
|
||||
# endif
|
||||
|
||||
static inline LightSpotData light_local_data_get(LightData light)
|
||||
static inline LightSpotData light_local_data_get_ex(LightData light, bool check)
|
||||
{
|
||||
SAFE_BEGIN(LightSpotData, is_local_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT(radius_squared, radius_squared)
|
||||
SAFE_READ_BEGIN(LightSpotData, light, check)
|
||||
SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
|
||||
SAFE_ASSIGN_FLOAT(_pad0, _pad0)
|
||||
SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
|
||||
SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline LightSpotData light_spot_data_get(LightData light)
|
||||
{
|
||||
SAFE_BEGIN(LightSpotData, is_spot_light(light.type) || is_point_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT(radius_squared, radius_squared)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
SAFE_ASSIGN_FLOAT(radius, _pad1)
|
||||
SAFE_ASSIGN_FLOAT(spot_mul, _pad2)
|
||||
SAFE_ASSIGN_FLOAT2(spot_size_inv, _pad3)
|
||||
SAFE_ASSIGN_FLOAT(spot_tan, _pad4)
|
||||
SAFE_ASSIGN_FLOAT(spot_bias, _pad5)
|
||||
return data;
|
||||
return SAFE_READ_END();
|
||||
}
|
||||
|
||||
static inline LightData light_local_data_set(LightData light, LightSpotData spot_data)
|
||||
{
|
||||
SAFE_WRITE_BEGIN(LightSpotData, spot_data, is_local_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
|
||||
SAFE_ASSIGN_FLOAT(_pad0, _pad0)
|
||||
SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
|
||||
SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
SAFE_ASSIGN_FLOAT(_pad2, spot_mul)
|
||||
SAFE_ASSIGN_FLOAT2(_pad3, spot_size_inv)
|
||||
SAFE_ASSIGN_FLOAT(_pad4, spot_tan)
|
||||
SAFE_ASSIGN_FLOAT(_pad5, spot_bias)
|
||||
SAFE_WRITE_END(light)
|
||||
return light;
|
||||
}
|
||||
|
||||
static inline LightSpotData light_local_data_get(LightData light)
|
||||
{
|
||||
return light_local_data_get_ex(light, is_local_light(light.type));
|
||||
}
|
||||
|
||||
static inline LightSpotData light_spot_data_get(LightData light)
|
||||
{
|
||||
return light_local_data_get_ex(light, is_spot_light(light.type) || is_point_light(light.type));
|
||||
}
|
||||
|
||||
static inline LightAreaData light_area_data_get(LightData light)
|
||||
{
|
||||
SAFE_BEGIN(LightAreaData, is_area_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT(radius_squared, radius_squared)
|
||||
SAFE_READ_BEGIN(LightAreaData, light, is_area_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT(shape_radius, shape_radius)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_max, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_surface, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT(clip_side, clip_side)
|
||||
SAFE_ASSIGN_FLOAT(shadow_projection_shift, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT3(shadow_position, shadow_position)
|
||||
SAFE_ASSIGN_FLOAT(shadow_radius, shadow_radius)
|
||||
SAFE_ASSIGN_INT(tilemaps_count, tilemaps_count)
|
||||
SAFE_ASSIGN_FLOAT2(size, _pad3)
|
||||
return data;
|
||||
return SAFE_READ_END();
|
||||
}
|
||||
|
||||
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, _pad7, shadow_projection_shift)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE(clipmap_base_offset_pos, _pad0_reserved, _pad1_reserved)
|
||||
SAFE_ASSIGN_FLOAT(shadow_angle, _pad1)
|
||||
SAFE_READ_BEGIN(LightSunData, light, is_sun_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT3(direction, shadow_position)
|
||||
SAFE_ASSIGN_FLOAT(shape_radius, _pad0)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.x, shadow_radius)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_neg.y, shape_radius)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.x, influence_radius_max)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_base_offset_pos.y, influence_radius_invsqr_surface)
|
||||
SAFE_ASSIGN_FLOAT(shadow_angle, influence_radius_invsqr_volume)
|
||||
SAFE_ASSIGN_FLOAT2(clipmap_origin, _pad3)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_min, _pad4)
|
||||
SAFE_ASSIGN_FLOAT_AS_INT(clipmap_lod_max, _pad5)
|
||||
return data;
|
||||
return SAFE_READ_END();
|
||||
}
|
||||
|
||||
static inline LightData light_sun_data_set(LightData light, LightSunData sun_data)
|
||||
{
|
||||
SAFE_WRITE_BEGIN(LightSunData, sun_data, is_sun_light(light.type))
|
||||
SAFE_ASSIGN_FLOAT3(shadow_position, direction)
|
||||
SAFE_ASSIGN_FLOAT(_pad0, shape_radius)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(shadow_radius, clipmap_base_offset_neg.x)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(shape_radius, clipmap_base_offset_neg.y)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_max, clipmap_base_offset_pos.x)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(influence_radius_invsqr_surface, clipmap_base_offset_pos.y)
|
||||
SAFE_ASSIGN_FLOAT(influence_radius_invsqr_volume, shadow_angle)
|
||||
SAFE_ASSIGN_FLOAT2(_pad3, clipmap_origin)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(_pad4, clipmap_lod_min)
|
||||
SAFE_ASSIGN_INT_AS_FLOAT(_pad5, clipmap_lod_max)
|
||||
SAFE_WRITE_END(light)
|
||||
return light;
|
||||
}
|
||||
|
||||
# if IS_CPP
|
||||
@@ -1159,7 +1235,7 @@ static inline LightSunData light_sun_data_get(LightData light)
|
||||
#undef SAFE_ASSIGN_FLOAT2
|
||||
#undef SAFE_ASSIGN_INT
|
||||
#undef SAFE_ASSIGN_FLOAT_AS_INT
|
||||
#undef SAFE_ASSIGN_FLOAT_AS_INT2_COMBINE
|
||||
#undef SAFE_ASSIGN_INT_AS_FLOAT
|
||||
|
||||
static inline int light_tilemap_max_get(LightData light)
|
||||
{
|
||||
@@ -1171,6 +1247,20 @@ static inline int light_tilemap_max_get(LightData light)
|
||||
return light.tilemap_index + light_local_data_get(light).tilemaps_count - 1;
|
||||
}
|
||||
|
||||
/* Return the number of tilemap needed for a local light. */
|
||||
static inline int light_local_tilemap_count(LightData light)
|
||||
{
|
||||
if (is_spot_light(light.type)) {
|
||||
return (light_spot_data_get(light).spot_tan > tanf(M_PI / 4.0)) ? 5 : 1;
|
||||
}
|
||||
else if (is_area_light(light.type)) {
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -1217,14 +1307,14 @@ struct ShadowTileMapData {
|
||||
int tiles_index;
|
||||
/** Index of persistent data in the persistent data buffer. */
|
||||
int clip_data_index;
|
||||
|
||||
float _pad0;
|
||||
/** Light type this tilemap is from. */
|
||||
eLightType light_type;
|
||||
/** True if the tilemap is part of area light shadow and is one of the side projections. */
|
||||
bool32_t is_area_side;
|
||||
/** Distance behind the area light a shadow is shifted. */
|
||||
float area_shift;
|
||||
/** Entire tilemap (all tiles) needs to be tagged as dirty. */
|
||||
bool32_t is_dirty;
|
||||
|
||||
float _pad1;
|
||||
/** Near and far clip distances for punctual. */
|
||||
float clip_near;
|
||||
float clip_far;
|
||||
@@ -1268,6 +1358,12 @@ struct ShadowTileMapClip {
|
||||
/** NOTE: These are positive just like camera parameters. */
|
||||
int clip_near;
|
||||
int clip_far;
|
||||
/* Transform the shadow is rendered with. Used to detect updates on GPU. */
|
||||
Transform object_to_world;
|
||||
/* Integer offset of the center of the 16x16 tiles from the origin of the tile space. */
|
||||
int2 grid_offset;
|
||||
int _pad0;
|
||||
int _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapClip, 16)
|
||||
|
||||
@@ -1472,8 +1568,8 @@ struct ShadowSceneData {
|
||||
int step_count;
|
||||
/* Bounding radius for a film pixel at 1 unit from the camera. */
|
||||
float film_pixel_radius;
|
||||
|
||||
float _pad0;
|
||||
/* Global switch for jittered shadows. */
|
||||
bool32_t use_jitter;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16)
|
||||
|
||||
@@ -2055,6 +2151,10 @@ float4 utility_tx_sample_lut(sampler2DArray util_tx, float cos_theta, float roug
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef EEVEE_PI
|
||||
# undef M_PI
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
#if IS_CPP
|
||||
|
||||
@@ -68,13 +68,8 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_,
|
||||
1.0);
|
||||
}
|
||||
|
||||
void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
const float4x4 &object_mat_,
|
||||
float near_,
|
||||
float far_,
|
||||
float side_,
|
||||
float shift,
|
||||
eCubeFace face)
|
||||
void ShadowTileMap::sync_cubeface(
|
||||
eLightType light_type_, const float4x4 &object_mat_, float near_, float far_, eCubeFace face)
|
||||
{
|
||||
if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) {
|
||||
set_dirty();
|
||||
@@ -85,14 +80,13 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
light_type = light_type_;
|
||||
is_area_side = is_area_light(light_type) && (face != eCubeFace::Z_NEG);
|
||||
|
||||
if ((clip_near != near_) || (clip_far != far_) || (half_size != side_)) {
|
||||
if ((clip_near != near_) || (clip_far != far_)) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
clip_near = near_;
|
||||
half_size = near_;
|
||||
clip_far = far_;
|
||||
area_shift = shift;
|
||||
half_size = side_;
|
||||
center_offset = float2(0.0f);
|
||||
|
||||
if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) {
|
||||
@@ -102,8 +96,7 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
|
||||
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(float3x3(shadow_face_mat[cubeface])) * math::invert(object_mat);
|
||||
|
||||
/* Update corners. */
|
||||
float4x4 viewinv = object_mat;
|
||||
@@ -222,86 +215,38 @@ void ShadowTileMapPool::end_sync(ShadowModule &module)
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void ShadowPunctual::sync(eLightType light_type,
|
||||
const float4x4 &object_mat,
|
||||
float cone_aperture,
|
||||
float max_distance)
|
||||
void ShadowPunctual::release_excess_tilemaps(const Light &light)
|
||||
{
|
||||
if (is_spot_light(light_type)) {
|
||||
tilemaps_needed_ = (cone_aperture > DEG2RADF(90.0f)) ? 5 : 1;
|
||||
}
|
||||
else if (is_area_light(light_type)) {
|
||||
tilemaps_needed_ = 5;
|
||||
}
|
||||
else {
|
||||
tilemaps_needed_ = 6;
|
||||
}
|
||||
|
||||
light_type_ = light_type;
|
||||
/* Clamp for near/far clip distance calculation. */
|
||||
max_distance_ = max_ff(max_distance, 4e-4f);
|
||||
|
||||
position_ = float3(object_mat[3]);
|
||||
}
|
||||
|
||||
void ShadowPunctual::release_excess_tilemaps()
|
||||
{
|
||||
if (tilemaps_.size() <= tilemaps_needed_) {
|
||||
int tilemaps_needed = light_local_tilemap_count(light);
|
||||
if (tilemaps_.size() <= tilemaps_needed) {
|
||||
return;
|
||||
}
|
||||
auto span = tilemaps_.as_span();
|
||||
shadows_.tilemap_pool.release(span.drop_front(tilemaps_needed_));
|
||||
tilemaps_ = span.take_front(tilemaps_needed_);
|
||||
}
|
||||
|
||||
void ShadowPunctual::compute_projection_boundaries(
|
||||
float max_lit_distance, float &near, float &far, float &side, float &back_shift)
|
||||
{
|
||||
far = max_lit_distance;
|
||||
near = side = (max_lit_distance / 4000.0f) / M_SQRT3;
|
||||
back_shift = 0.0f;
|
||||
shadows_.tilemap_pool.release(span.drop_front(tilemaps_needed));
|
||||
tilemaps_ = span.take_front(tilemaps_needed);
|
||||
}
|
||||
|
||||
void ShadowPunctual::end_sync(Light &light)
|
||||
{
|
||||
ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool;
|
||||
|
||||
float side, near, far, shift;
|
||||
compute_projection_boundaries(max_distance_, near, far, side, shift);
|
||||
|
||||
float4x4 obmat_tmp = light.object_to_world;
|
||||
float4x4 object_to_world = light.object_to_world;
|
||||
|
||||
/* Acquire missing tile-maps. */
|
||||
while (tilemaps_.size() < tilemaps_needed_) {
|
||||
int tilemaps_needed = light_local_tilemap_count(light);
|
||||
while (tilemaps_.size() < tilemaps_needed) {
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
}
|
||||
|
||||
tilemaps_[Z_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_NEG);
|
||||
if (tilemaps_needed_ >= 5) {
|
||||
tilemaps_[X_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, X_POS);
|
||||
tilemaps_[X_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, X_NEG);
|
||||
tilemaps_[Y_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Y_POS);
|
||||
tilemaps_[Y_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Y_NEG);
|
||||
}
|
||||
if (tilemaps_needed_ == 6) {
|
||||
tilemaps_[Z_POS]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_POS);
|
||||
float near = int_as_float(light.clip_near);
|
||||
float far = int_as_float(light.clip_far);
|
||||
for (int i : tilemaps_.index_range()) {
|
||||
eCubeFace face = eCubeFace(Z_NEG + i);
|
||||
tilemaps_[face]->sync_cubeface(light.type, object_to_world, near, far, face);
|
||||
}
|
||||
|
||||
light.local.tilemaps_count = tilemaps_needed;
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
|
||||
light.local.tilemaps_count = tilemaps_needed_;
|
||||
/* TODO(fclem): `as_uint()`. */
|
||||
union {
|
||||
float f;
|
||||
int32_t i;
|
||||
} as_int;
|
||||
as_int.f = near;
|
||||
light.clip_near = as_int.i;
|
||||
as_int.f = far;
|
||||
light.clip_far = as_int.i;
|
||||
light.local.clip_side = side;
|
||||
light.local.shadow_projection_shift = shift;
|
||||
|
||||
for (ShadowTileMap *tilemap : tilemaps_) {
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -339,20 +284,21 @@ eShadowProjectionType ShadowDirectional::directional_distribution_type_get(const
|
||||
************************************************************************/
|
||||
|
||||
void ShadowDirectional::cascade_tilemaps_distribution_near_far_points(const Camera &camera,
|
||||
const Light &light,
|
||||
float3 &near_point,
|
||||
float3 &far_point)
|
||||
{
|
||||
const CameraData &cam_data = camera.data_get();
|
||||
/* Ideally we should only take the intersection with the scene bounds. */
|
||||
far_point = (camera.position() - camera.forward() * cam_data.clip_far) *
|
||||
float3x3(object_mat_.view<3, 3>());
|
||||
near_point = (camera.position() - camera.forward() * cam_data.clip_near) *
|
||||
float3x3(object_mat_.view<3, 3>());
|
||||
far_point = transform_direction_transposed(
|
||||
light.object_to_world, camera.position() - camera.forward() * cam_data.clip_far);
|
||||
near_point = transform_direction_transposed(
|
||||
light.object_to_world, camera.position() - camera.forward() * cam_data.clip_near);
|
||||
}
|
||||
|
||||
/* \note All tile-maps are meant to have the same LOD but we still return a range starting at the
|
||||
* unique LOD. */
|
||||
IndexRange ShadowDirectional::cascade_level_range(const Camera &camera)
|
||||
IndexRange ShadowDirectional::cascade_level_range(const Light &light, const Camera &camera)
|
||||
{
|
||||
using namespace blender::math;
|
||||
|
||||
@@ -361,7 +307,7 @@ IndexRange ShadowDirectional::cascade_level_range(const Camera &camera)
|
||||
const CameraData &cam_data = camera.data_get();
|
||||
|
||||
float3 near_point, far_point;
|
||||
cascade_tilemaps_distribution_near_far_points(camera, near_point, far_point);
|
||||
cascade_tilemaps_distribution_near_far_points(camera, light, near_point, far_point);
|
||||
|
||||
/* This gives the maximum resolution in depth we can have with a fixed set of tile-maps. Gives
|
||||
* the best results when view direction is orthogonal to the light direction. */
|
||||
@@ -395,12 +341,15 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
||||
{
|
||||
using namespace blender::math;
|
||||
|
||||
float4x4 object_mat = light.object_to_world;
|
||||
object_mat.location() = float3(0.0f);
|
||||
|
||||
/* All tile-maps use the first level size. */
|
||||
float half_size = ShadowDirectional::coverage_get(levels_range.first()) / 2.0f;
|
||||
float tile_size = ShadowDirectional::tile_size_get(levels_range.first());
|
||||
|
||||
float3 near_point, far_point;
|
||||
cascade_tilemaps_distribution_near_far_points(camera, near_point, far_point);
|
||||
cascade_tilemaps_distribution_near_far_points(camera, light, near_point, far_point);
|
||||
|
||||
float2 local_view_direction = normalize(far_point.xy() - near_point.xy());
|
||||
float2 farthest_tilemap_center = local_view_direction * half_size * (levels_range.size() - 1);
|
||||
@@ -426,7 +375,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
||||
/* Equal spacing between cascades layers since we want uniform shadow density. */
|
||||
int2 level_offset = origin_offset +
|
||||
shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i);
|
||||
tilemap->sync_orthographic(object_mat_, level_offset, level, SHADOW_PROJECTION_CASCADE);
|
||||
tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -469,6 +418,9 @@ IndexRange ShadowDirectional::clipmap_level_range(const Camera &cam)
|
||||
|
||||
void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera &camera)
|
||||
{
|
||||
float4x4 object_mat = light.object_to_world;
|
||||
object_mat.location() = float3(0.0f);
|
||||
|
||||
for (int lod : IndexRange(levels_range.size())) {
|
||||
ShadowTileMap *tilemap = tilemaps_[lod];
|
||||
|
||||
@@ -477,10 +429,10 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera
|
||||
* camera position. The offset is computed in smallest tile unit. */
|
||||
float tile_size = ShadowDirectional::tile_size_get(level);
|
||||
/* Moving to light space by multiplying by the transpose (which is the inverse). */
|
||||
float2 light_space_camera_position = camera.position() * float2x3(object_mat_.view<2, 3>());
|
||||
float2 light_space_camera_position = camera.position() * float2x3(object_mat.view<2, 3>());
|
||||
int2 level_offset = int2(math::round(light_space_camera_position / tile_size));
|
||||
|
||||
tilemap->sync_orthographic(object_mat_, level_offset, level, SHADOW_PROJECTION_CLIPMAP);
|
||||
tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -513,7 +465,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera
|
||||
light.type = LIGHT_SUN;
|
||||
|
||||
/* Used for selecting the clipmap level. */
|
||||
float3 location = camera.position() * float3x3(object_mat_.view<3, 3>());
|
||||
float3 location = camera.position() * float3x3(object_mat.view<3, 3>());
|
||||
light.object_to_world.x.w = location.x;
|
||||
light.object_to_world.y.w = location.y;
|
||||
light.object_to_world.z.w = location.z;
|
||||
@@ -524,19 +476,10 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera
|
||||
light.sun.clipmap_lod_max = levels_range.last();
|
||||
}
|
||||
|
||||
void ShadowDirectional::sync(const float4x4 &object_mat, float shadow_disk_angle)
|
||||
{
|
||||
object_mat_ = object_mat;
|
||||
/* Remove translation. */
|
||||
object_mat_.location() = float3(0.0f);
|
||||
|
||||
disk_shape_angle_ = min_ff(shadow_disk_angle, DEG2RADF(179.9f)) / 2.0f;
|
||||
}
|
||||
|
||||
void ShadowDirectional::release_excess_tilemaps(const Camera &camera)
|
||||
void ShadowDirectional::release_excess_tilemaps(const Light &light, const Camera &camera)
|
||||
{
|
||||
IndexRange levels_new = directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE ?
|
||||
cascade_level_range(camera) :
|
||||
cascade_level_range(light, camera) :
|
||||
clipmap_level_range(camera);
|
||||
|
||||
if (levels_range == levels_new) {
|
||||
@@ -559,7 +502,7 @@ void ShadowDirectional::end_sync(Light &light, const Camera &camera)
|
||||
{
|
||||
ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool;
|
||||
IndexRange levels_new = directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE ?
|
||||
cascade_level_range(camera) :
|
||||
cascade_level_range(light, camera) :
|
||||
clipmap_level_range(camera);
|
||||
|
||||
if (levels_range != levels_new) {
|
||||
@@ -584,7 +527,6 @@ void ShadowDirectional::end_sync(Light &light, const Camera &camera)
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
light.clip_near = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
|
||||
light.clip_far = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
|
||||
light.sun.shadow_angle = disk_shape_angle_;
|
||||
|
||||
if (directional_distribution_type_get(camera) == SHADOW_PROJECTION_CASCADE) {
|
||||
cascade_tilemaps_distribution(light, camera);
|
||||
@@ -629,17 +571,22 @@ void ShadowModule::init()
|
||||
}
|
||||
|
||||
::Scene &scene = *inst_.scene;
|
||||
bool enabled = (scene.eevee.flag & SCE_EEVEE_SHADOW_ENABLED) != 0;
|
||||
if (assign_if_different(enabled_, enabled)) {
|
||||
|
||||
bool update_lights = false;
|
||||
bool enable_shadow = (scene.eevee.flag & SCE_EEVEE_SHADOW_ENABLED) != 0;
|
||||
bool use_jitter = enable_shadow &&
|
||||
(!inst_.is_viewport() ||
|
||||
(!inst_.is_navigating() && !inst_.is_transforming() && !inst_.is_playback() &&
|
||||
(scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT)));
|
||||
update_lights |= assign_if_different(enabled_, enable_shadow);
|
||||
update_lights |= assign_if_different(data_.use_jitter, bool32_t(use_jitter));
|
||||
if (update_lights) {
|
||||
/* Force light reset. */
|
||||
for (Light &light : inst_.lights.light_map_.values()) {
|
||||
light.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
jittered_transparency_ = !inst_.is_viewport() ||
|
||||
scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT;
|
||||
|
||||
data_.ray_count = clamp_i(scene.eevee.shadow_ray_count, 1, SHADOW_MAX_RAY);
|
||||
data_.step_count = clamp_i(scene.eevee.shadow_step_count, 1, SHADOW_MAX_STEP);
|
||||
|
||||
@@ -798,7 +745,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 && data_.use_jitter;
|
||||
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);
|
||||
@@ -830,10 +777,10 @@ void ShadowModule::end_sync()
|
||||
light.shadow_discard_safe(*this);
|
||||
}
|
||||
else if (light.directional != nullptr) {
|
||||
light.directional->release_excess_tilemaps(inst_.camera);
|
||||
light.directional->release_excess_tilemaps(light, inst_.camera);
|
||||
}
|
||||
else if (light.punctual != nullptr) {
|
||||
light.punctual->release_excess_tilemaps();
|
||||
light.punctual->release_excess_tilemaps(light);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,24 +28,14 @@ class ShadowModule;
|
||||
class ShadowPipeline;
|
||||
struct Light;
|
||||
|
||||
enum eCubeFace {
|
||||
/* Ordering by culling order. If cone aperture is shallow, we cull the later view. */
|
||||
Z_NEG = 0,
|
||||
X_POS,
|
||||
X_NEG,
|
||||
Y_POS,
|
||||
Y_NEG,
|
||||
Z_POS,
|
||||
};
|
||||
|
||||
/* To be applied after view matrix. Follow same order as eCubeFace. */
|
||||
constexpr static const float shadow_face_mat[6][4][4] = {
|
||||
{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */
|
||||
{{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */
|
||||
{{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */
|
||||
{{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */
|
||||
{{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */
|
||||
{{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */
|
||||
constexpr static const float shadow_face_mat[6][3][3] = {
|
||||
{{+1, +0, +0}, {+0, +1, +0}, {+0, +0, +1}}, /* Z_NEG */
|
||||
{{+0, +0, -1}, {-1, +0, +0}, {+0, +1, +0}}, /* X_POS */
|
||||
{{+0, +0, +1}, {+1, +0, +0}, {+0, +1, +0}}, /* X_NEG */
|
||||
{{+1, +0, +0}, {+0, +0, -1}, {+0, +1, +0}}, /* Y_POS */
|
||||
{{-1, +0, +0}, {+0, +0, +1}, {+0, +1, +0}}, /* Y_NEG */
|
||||
{{+1, +0, +0}, {+0, -1, +0}, {+0, +0, -1}}, /* Z_POS */
|
||||
};
|
||||
|
||||
/* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */
|
||||
@@ -102,24 +92,19 @@ struct ShadowTileMap : public ShadowTileMapData {
|
||||
int clipmap_level,
|
||||
eShadowProjectionType projection_type_);
|
||||
|
||||
void sync_cubeface(eLightType light_type_,
|
||||
const float4x4 &object_mat,
|
||||
float near,
|
||||
float far,
|
||||
float side,
|
||||
float shift,
|
||||
eCubeFace face);
|
||||
void sync_cubeface(
|
||||
eLightType light_type_, const float4x4 &object_mat, float near, float far, eCubeFace face);
|
||||
|
||||
void debug_draw() const;
|
||||
|
||||
void set_dirty()
|
||||
{
|
||||
grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
is_dirty = true;
|
||||
}
|
||||
|
||||
void set_updated()
|
||||
{
|
||||
grid_shift = int2(0);
|
||||
is_dirty = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -215,8 +200,6 @@ class ShadowModule {
|
||||
/* Used to call caster_update_ps_ only once per sync (Initialized on begin_sync). */
|
||||
bool update_casters_ = false;
|
||||
|
||||
bool jittered_transparency_ = false;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tile-map Management
|
||||
* \{ */
|
||||
@@ -397,14 +380,6 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
||||
ShadowModule &shadows_;
|
||||
/** Tile-map for each cube-face needed (in eCubeFace order). */
|
||||
Vector<ShadowTileMap *> tilemaps_;
|
||||
/** Shape type. */
|
||||
eLightType light_type_;
|
||||
/** Light position. */
|
||||
float3 position_;
|
||||
/** Used to compute near and far clip distances. */
|
||||
float max_distance_;
|
||||
/** Number of tile-maps needed to cover the light angular extents. */
|
||||
int tilemaps_needed_;
|
||||
|
||||
public:
|
||||
ShadowPunctual(ShadowModule &module) : shadows_(module){};
|
||||
@@ -416,32 +391,15 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
||||
shadows_.tilemap_pool.release(tilemaps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(eLightType light_type,
|
||||
const float4x4 &object_mat,
|
||||
float cone_aperture,
|
||||
float max_distance);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
*/
|
||||
void release_excess_tilemaps();
|
||||
void release_excess_tilemaps(const Light &light);
|
||||
|
||||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
*/
|
||||
void end_sync(Light &light);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Compute the projection matrix inputs.
|
||||
* Make sure that the projection encompass all possible rays that can start in the projection
|
||||
* quadrant.
|
||||
*/
|
||||
void compute_projection_boundaries(
|
||||
float max_lit_distance, float &near, float &far, float &side, float &back_shift);
|
||||
};
|
||||
|
||||
class ShadowDirectional : public NonCopyable, NonMovable {
|
||||
@@ -449,12 +407,8 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
||||
ShadowModule &shadows_;
|
||||
/** Tile-map for each clip-map level. */
|
||||
Vector<ShadowTileMap *> tilemaps_;
|
||||
/** Copy of object matrix. Normalized. */
|
||||
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_;
|
||||
IndexRange levels_range = IndexRange(0);
|
||||
|
||||
public:
|
||||
ShadowDirectional(ShadowModule &module) : shadows_(module){};
|
||||
@@ -466,15 +420,10 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
||||
shadows_.tilemap_pool.release(tilemaps_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync shadow parameters but do not allocate any shadow tile-maps.
|
||||
*/
|
||||
void sync(const float4x4 &object_mat, float shadow_disk_angle);
|
||||
|
||||
/**
|
||||
* Release the tile-maps that will not be used in the current frame.
|
||||
*/
|
||||
void release_excess_tilemaps(const Camera &camera);
|
||||
void release_excess_tilemaps(const Light &light, const Camera &camera);
|
||||
|
||||
/**
|
||||
* Allocate shadow tile-maps and setup views for rendering.
|
||||
@@ -497,12 +446,13 @@ class ShadowDirectional : public NonCopyable, NonMovable {
|
||||
|
||||
private:
|
||||
IndexRange clipmap_level_range(const Camera &camera);
|
||||
IndexRange cascade_level_range(const Camera &camera);
|
||||
IndexRange cascade_level_range(const Light &light, const Camera &camera);
|
||||
|
||||
void cascade_tilemaps_distribution(Light &light, const Camera &camera);
|
||||
void clipmap_tilemaps_distribution(Light &light, const Camera &camera);
|
||||
|
||||
void cascade_tilemaps_distribution_near_far_points(const Camera &camera,
|
||||
const Light &light,
|
||||
float3 &near_point,
|
||||
float3 &far_point);
|
||||
|
||||
|
||||
@@ -26,16 +26,11 @@ void main()
|
||||
if (is_world_sun_light) {
|
||||
light.color = sunlight_buf.color;
|
||||
light.object_to_world = sunlight_buf.object_to_world;
|
||||
|
||||
LightSunData sun_data = light_sun_data_get(light);
|
||||
sun_data.direction = transform_z_axis(sunlight_buf.object_to_world);
|
||||
light = light_sun_data_set(light, sun_data);
|
||||
/* NOTE: Use the radius from UI instead of auto sun size for now. */
|
||||
// light.power = sunlight_buf.power;
|
||||
#if USE_LIGHT_UNION
|
||||
// light.sun.radius = sunlight_buf.sun.radius;
|
||||
// light.sun.shadow_angle = sunlight_buf.sun.shadow_angle;
|
||||
#else
|
||||
// light.do_not_access_directly.radius_squared =
|
||||
// sunlight_buf.do_not_access_directly.radius_squared;
|
||||
// light.do_not_access_directly._pad1 = sunlight_buf.do_not_access_directly._pad1;
|
||||
#endif
|
||||
}
|
||||
/* NOTE: We know the index because sun lights are packed at the start of the input buffer. */
|
||||
out_light_buf[light_cull_buf.local_lights_len + l_idx] = light;
|
||||
|
||||
@@ -228,7 +228,6 @@ void light_eval_single(uint l_idx,
|
||||
thickness,
|
||||
P,
|
||||
Ng,
|
||||
lv.L,
|
||||
ray_count,
|
||||
ray_step_count);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ LightVector light_vector_get(LightData light, const bool is_directional, vec3 P)
|
||||
{
|
||||
LightVector lv;
|
||||
if (is_directional) {
|
||||
lv.L = light_z_axis(light);
|
||||
lv.L = light_sun_data_get(light).direction;
|
||||
lv.dist = 1.0;
|
||||
}
|
||||
else {
|
||||
@@ -63,12 +63,10 @@ LightVector light_shape_vector_get(LightData light, const bool is_directional, v
|
||||
return light_vector_get(light, is_directional, P);
|
||||
}
|
||||
|
||||
/* Rotate vector to light's local space. Does not translate. */
|
||||
vec3 light_world_to_local(LightData light, vec3 L)
|
||||
vec3 light_world_to_local_direction(LightData light, vec3 L)
|
||||
{
|
||||
return transform_direction_transposed(light.object_to_world, L);
|
||||
}
|
||||
|
||||
vec3 light_world_to_local_point(LightData light, vec3 point)
|
||||
{
|
||||
return transform_point_inversed(light.object_to_world, point);
|
||||
@@ -87,7 +85,7 @@ float light_influence_attenuation(float dist, float inv_sqr_influence)
|
||||
float light_spot_attenuation(LightData light, vec3 L)
|
||||
{
|
||||
LightSpotData spot = light_spot_data_get(light);
|
||||
vec3 lL = light_world_to_local(light, L);
|
||||
vec3 lL = light_world_to_local_direction(light, L);
|
||||
float ellipse = inversesqrt(1.0 + length_squared(lL.xy * spot.spot_size_inv / lL.z));
|
||||
float spotmask = smoothstep(0.0, 1.0, ellipse * spot.spot_mul + spot.spot_bias);
|
||||
return (lL.z > 0.0) ? spotmask : 0.0;
|
||||
@@ -109,16 +107,15 @@ float light_attenuation_common(LightData light, const bool is_directional, vec3
|
||||
|
||||
float light_shape_radius(LightData light)
|
||||
{
|
||||
|
||||
float radius;
|
||||
if (is_sun_light(light.type)) {
|
||||
return light_sun_data_get(light).radius;
|
||||
return light_sun_data_get(light).shape_radius;
|
||||
}
|
||||
else if (is_area_light(light.type)) {
|
||||
return length(light_area_data_get(light).size);
|
||||
}
|
||||
else {
|
||||
return light_spot_data_get(light).radius;
|
||||
return light_local_data_get(light).shape_radius;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +187,7 @@ float light_point_light(LightData light, const bool is_directional, LightVector
|
||||
* http://www.cemyuksel.com/research/pointlightattenuation/
|
||||
*/
|
||||
float d_sqr = square(lv.dist);
|
||||
float r_sqr = light_local_data_get(light).radius_squared;
|
||||
float r_sqr = square(light_local_data_get(light).shape_radius);
|
||||
/* Using reformulation that has better numerical precision. */
|
||||
float power = 2.0 / (d_sqr + r_sqr + lv.dist * sqrt(d_sqr + r_sqr));
|
||||
|
||||
@@ -216,7 +213,7 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere)
|
||||
float light_ltc(
|
||||
sampler2DArray utility_tx, LightData light, vec3 N, vec3 V, LightVector lv, vec4 ltc_mat)
|
||||
{
|
||||
if (is_sphere_light(light.type) && lv.dist < light_spot_data_get(light).radius) {
|
||||
if (is_sphere_light(light.type) && lv.dist < light_local_data_get(light).shape_radius) {
|
||||
/* Inside the sphere light, integrate over the hemisphere. */
|
||||
return 1.0;
|
||||
}
|
||||
@@ -251,14 +248,14 @@ float light_ltc(
|
||||
vec2 size;
|
||||
if (is_sphere_light(light.type)) {
|
||||
/* Spherical omni or spot light. */
|
||||
size = vec2(light_sphere_disk_radius(light_spot_data_get(light).radius, lv.dist));
|
||||
size = vec2(light_sphere_disk_radius(light_local_data_get(light).shape_radius, lv.dist));
|
||||
}
|
||||
else if (is_oriented_disk_light(light.type)) {
|
||||
/* View direction-aligned disk. */
|
||||
size = vec2(light_spot_data_get(light).radius);
|
||||
size = vec2(light_local_data_get(light).shape_radius);
|
||||
}
|
||||
else if (is_sun_light(light.type)) {
|
||||
size = vec2(light_sun_data_get(light).radius);
|
||||
size = vec2(light_sun_data_get(light).shape_radius);
|
||||
}
|
||||
else {
|
||||
/* Area light. */
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
* Dispatched one thread per light.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_fast_lib.glsl)
|
||||
|
||||
int shadow_directional_coverage_get(int level)
|
||||
{
|
||||
@@ -15,25 +17,32 @@ int shadow_directional_coverage_get(int level)
|
||||
}
|
||||
|
||||
void orthographic_sync(int tilemap_id,
|
||||
Transform light_tx,
|
||||
Transform object_to_world,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
eShadowProjectionType projection_type)
|
||||
{
|
||||
if (all(equal(tilemaps_buf[tilemap_id].grid_shift, int2(0)))) {
|
||||
/* Only replace shift if it is not already dirty. */
|
||||
tilemaps_buf[tilemap_id].grid_shift = tilemaps_buf[tilemap_id].grid_offset - origin_offset;
|
||||
}
|
||||
tilemaps_buf[tilemap_id].grid_offset = origin_offset;
|
||||
/* Do not check translation. */
|
||||
object_to_world.x.w = 0.0;
|
||||
object_to_world.y.w = 0.0;
|
||||
object_to_world.z.w = 0.0;
|
||||
|
||||
mat3x3 object_to_world_transposed = mat3x3(tilemaps_buf[tilemap_id].viewmat);
|
||||
|
||||
if (!all(equal(object_to_world_transposed[0], light_tx.x.xyz)) ||
|
||||
!all(equal(object_to_world_transposed[1], light_tx.y.xyz)) ||
|
||||
!all(equal(object_to_world_transposed[2], light_tx.z.xyz)))
|
||||
int clip_index = tilemaps_buf[tilemap_id].clip_data_index;
|
||||
if (tilemaps_buf[tilemap_id].is_dirty ||
|
||||
!transform_equal(tilemaps_clip_buf[clip_index].object_to_world, object_to_world))
|
||||
{
|
||||
/* Set dirty as the light direction changed. */
|
||||
tilemaps_buf[tilemap_id].grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
tilemaps_clip_buf[clip_index].object_to_world = object_to_world;
|
||||
}
|
||||
else {
|
||||
/* Same light direction but camera might have moved. Shift tilemap grid. */
|
||||
tilemaps_buf[tilemap_id].grid_shift = origin_offset -
|
||||
tilemaps_clip_buf[clip_index].grid_offset;
|
||||
}
|
||||
tilemaps_clip_buf[clip_index].grid_offset = origin_offset;
|
||||
/* TODO(fclem): Remove this duplicate. Only needed because of the base offset packing. */
|
||||
tilemaps_buf[tilemap_id].grid_offset = origin_offset;
|
||||
|
||||
float level_size = shadow_directional_coverage_get(clipmap_level);
|
||||
float half_size = level_size / 2.0;
|
||||
@@ -42,9 +51,9 @@ void orthographic_sync(int tilemap_id,
|
||||
|
||||
/* object_mat is a rotation matrix. Reduce imprecision by taking the transpose which is also the
|
||||
* inverse in this particular case. */
|
||||
tilemaps_buf[tilemap_id].viewmat[0] = vec4(light_tx.x.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[1] = vec4(light_tx.y.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[2] = vec4(light_tx.z.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[0] = vec4(object_to_world.x.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[1] = vec4(object_to_world.y.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[2] = vec4(object_to_world.z.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[3] = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
tilemaps_buf[tilemap_id].projection_type = projection_type;
|
||||
@@ -113,21 +122,14 @@ void cascade_sync(inout LightData light)
|
||||
|
||||
vec2 clipmap_origin = vec2(origin_offset) * tile_size;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
LightSunData sun_data = light_sun_data_get(light);
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.sun.clipmap_origin = clipmap_origin;
|
||||
sun_data.clipmap_origin = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.sun.clipmap_base_offset_pos = base_offset_pos;
|
||||
light.sun.clipmap_base_offset_neg = ivec2(0);
|
||||
#else
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.do_not_access_directly._pad3 = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.do_not_access_directly._pad0_reserved = intBitsToFloat(base_offset_pos.x);
|
||||
light.do_not_access_directly._pad1_reserved = intBitsToFloat(base_offset_pos.y);
|
||||
light.do_not_access_directly._pad7 = intBitsToFloat(0);
|
||||
light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(0);
|
||||
#endif
|
||||
sun_data.clipmap_base_offset_pos = base_offset_pos;
|
||||
sun_data.clipmap_base_offset_neg = ivec2(0);
|
||||
|
||||
light = light_sun_data_set(light, sun_data);
|
||||
}
|
||||
|
||||
void clipmap_sync(inout LightData light)
|
||||
@@ -176,40 +178,71 @@ void clipmap_sync(inout LightData light)
|
||||
light.object_to_world.x.w = ls_camera_position.x;
|
||||
light.object_to_world.y.w = ls_camera_position.y;
|
||||
light.object_to_world.z.w = ls_camera_position.z;
|
||||
#if USE_LIGHT_UNION
|
||||
|
||||
LightSunData sun_data = light_sun_data_get(light);
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.sun.clipmap_origin = clipmap_origin;
|
||||
sun_data.clipmap_origin = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.sun.clipmap_base_offset_pos = pos_offset;
|
||||
light.sun.clipmap_base_offset_neg = neg_offset;
|
||||
#else
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.do_not_access_directly._pad3 = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.do_not_access_directly._pad0_reserved = intBitsToFloat(pos_offset.x);
|
||||
light.do_not_access_directly._pad1_reserved = intBitsToFloat(pos_offset.y);
|
||||
light.do_not_access_directly._pad7 = intBitsToFloat(neg_offset.x);
|
||||
light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(neg_offset.y);
|
||||
#endif
|
||||
sun_data.clipmap_base_offset_pos = pos_offset;
|
||||
sun_data.clipmap_base_offset_neg = neg_offset;
|
||||
|
||||
light = light_sun_data_set(light, sun_data);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void cubeface_sync(int tilemap_id, vec3 jitter_offset)
|
||||
void cubeface_sync(int tilemap_id,
|
||||
Transform object_to_world,
|
||||
eCubeFace cubeface,
|
||||
vec3 jitter_offset)
|
||||
{
|
||||
/* Update corners. */
|
||||
viewmat = shadow_face_mat[cubeface] * from_location<float4x4>(float3(0.0, 0.0, -shift)) *
|
||||
invert(object_mat);
|
||||
vec3 world_jitter_offset = transform_point(object_to_world, jitter_offset);
|
||||
object_to_world.x.w = world_jitter_offset.x;
|
||||
object_to_world.y.w = world_jitter_offset.y;
|
||||
object_to_world.z.w = world_jitter_offset.z;
|
||||
|
||||
int clip_index = tilemaps_buf[tilemap_id].clip_data_index;
|
||||
if (tilemaps_buf[tilemap_id].is_dirty ||
|
||||
!transform_equal(tilemaps_clip_buf[clip_index].object_to_world, object_to_world))
|
||||
{
|
||||
/* Set dirty as the light direction changed. */
|
||||
tilemaps_buf[tilemap_id].grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
tilemaps_clip_buf[clip_index].object_to_world = object_to_world;
|
||||
}
|
||||
|
||||
/* Update View Matrix. */
|
||||
/* TODO(fclem): Could avoid numerical inversion since the transform is a unit matrix. */
|
||||
mat4x4 viewmat = invert(transform_to_matrix(object_to_world));
|
||||
|
||||
/* Use switch instead of inline array of float3x3. */
|
||||
switch (cubeface) {
|
||||
case Z_NEG:
|
||||
viewmat = mat4x4(mat3x3(+1, +0, +0, +0, +1, +0, +0, +0, +1)) * viewmat;
|
||||
break;
|
||||
case X_POS:
|
||||
viewmat = mat4x4(mat3x3(+0, +0, -1, -1, +0, +0, +0, +1, +0)) * viewmat;
|
||||
break;
|
||||
case X_NEG:
|
||||
viewmat = mat4x4(mat3x3(+0, +0, +1, +1, +0, +0, +0, +1, +0)) * viewmat;
|
||||
break;
|
||||
case Y_POS:
|
||||
viewmat = mat4x4(mat3x3(+1, +0, +0, +0, +0, -1, +0, +1, +0)) * viewmat;
|
||||
break;
|
||||
case Y_NEG:
|
||||
viewmat = mat4x4(mat3x3(-1, +0, +0, +0, +0, +1, +0, +1, +0)) * viewmat;
|
||||
break;
|
||||
case Z_POS:
|
||||
viewmat = mat4x4(mat3x3(+1, +0, +0, +0, -1, +0, +0, +0, -1)) * viewmat;
|
||||
break;
|
||||
}
|
||||
|
||||
mat4x4 prev_viewmat = tilemaps_buf[tilemap_id].viewmat;
|
||||
|
||||
tilemaps_buf[tilemap_id].viewmat = viewmat;
|
||||
|
||||
/* Update corners. */
|
||||
corners[0] += jitter_offset;
|
||||
corners[1] += jitter_offset;
|
||||
corners[2] += jitter_offset;
|
||||
corners[3] += jitter_offset;
|
||||
|
||||
/* Set dirty. */
|
||||
grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
tilemaps_buf[tilemap_id].corners[0].xyz += jitter_offset;
|
||||
tilemaps_buf[tilemap_id].corners[1].xyz += jitter_offset;
|
||||
/* Other corners are deltas. They do not change after jitter. */
|
||||
}
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -226,16 +259,20 @@ void main()
|
||||
|
||||
if (is_sun_light(light.type)) {
|
||||
/* Distant lights. */
|
||||
vec3 position_on_light = vec3(0.0);
|
||||
|
||||
#if 0 /* Jittered shadows. */
|
||||
vec3 position_on_light = random_position_on_light(light);
|
||||
vec3 light_direction = normalize(position_on_light);
|
||||
float3x3 object_to_world_transposed = transpose(from_up_axis(light_direction));
|
||||
if (light.shadow_jitter && uniform_buf.shadow.use_jitter) {
|
||||
/* TODO(fclem): Remove atan here. We only need the cosine of the angle. */
|
||||
float shape_angle = atan_fast(light_sun_data_get(light).shape_radius);
|
||||
|
||||
light.object_to_world.x.xyz = object_to_world_transposed[0];
|
||||
light.object_to_world.y.xyz = object_to_world_transposed[1];
|
||||
light.object_to_world.z.xyz = object_to_world_transposed[2];
|
||||
#endif
|
||||
/* Reverse to that first sample is straight up. */
|
||||
vec2 rand = 1.0 - sampling_rng_2D_get(SAMPLING_SHADOW_I);
|
||||
vec3 shadow_direction = sample_uniform_cone(rand, cos(shape_angle));
|
||||
|
||||
shadow_direction = transform_direction(light.object_to_world, shadow_direction);
|
||||
|
||||
light.object_to_world = transform_from_matrix(mat4x4(from_up_axis(shadow_direction)));
|
||||
}
|
||||
|
||||
if (light.type == LIGHT_SUN_ORTHO) {
|
||||
cascade_sync(light);
|
||||
@@ -244,30 +281,34 @@ void main()
|
||||
clipmap_sync(light);
|
||||
}
|
||||
}
|
||||
#if 0 /* Jittered shadows. */
|
||||
else {
|
||||
/* Local lights. */
|
||||
# if 0 /* Jittered shadows. */
|
||||
vec3 position_on_light = random_position_on_light(light);
|
||||
light_buf[l_idx].shadow_position = position_on_light;
|
||||
vec3 position_on_light = vec3(0.0);
|
||||
|
||||
int tilemap_count = 0;
|
||||
if (is_area_light(light.type)) {
|
||||
tilemap_count = 5;
|
||||
}
|
||||
else if (is_spot_light(light.type)) {
|
||||
tilemap_count = (spot_angle > M_PI * 0.25) ? 5 : 1;
|
||||
}
|
||||
else {
|
||||
tilemap_count = 6;
|
||||
if (light.shadow_jitter && uniform_buf.shadow.use_jitter) {
|
||||
vec3 rand = sampling_rng_3D_get(SAMPLING_SHADOW_I);
|
||||
|
||||
if (is_area_light(light.type)) {
|
||||
vec2 point_on_unit_shape = (light.type == LIGHT_RECT) ? rand.xy * 2.0 - 1.0 :
|
||||
sample_disk(rand.xy);
|
||||
position_on_light = vec3(point_on_unit_shape * light_area_data_get(light).size, 0.0);
|
||||
}
|
||||
else {
|
||||
position_on_light = sample_ball(rand) * light_local_data_get(light).shape_radius;
|
||||
}
|
||||
}
|
||||
|
||||
int tilemap_count = light_local_tilemap_count(light);
|
||||
for (int i = 0; i < tilemap_count; i++) {
|
||||
cubeface_sync(light.tilemap_id + i, position_on_light);
|
||||
cubeface_sync(
|
||||
light.tilemap_index + i, light.object_to_world, eCubeFace(i), position_on_light);
|
||||
}
|
||||
# endif
|
||||
|
||||
LightSpotData local_data = light_local_data_get(light);
|
||||
local_data.shadow_position = position_on_light;
|
||||
|
||||
light = light_local_data_set(light, local_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
light_buf[l_idx] = light;
|
||||
}
|
||||
|
||||
@@ -79,12 +79,6 @@ void main()
|
||||
sunlight_buf.power[LIGHT_TRANSMISSION] = shape_power;
|
||||
sunlight_buf.power[LIGHT_VOLUME] = point_power;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
sunlight_buf.sun.radius = sun_radius;
|
||||
sunlight_buf.sun.shadow_angle = sun_angle;
|
||||
#else
|
||||
sunlight_buf.do_not_access_directly.radius_squared = sun_radius;
|
||||
sunlight_buf.do_not_access_directly._pad1 = sun_angle;
|
||||
#endif
|
||||
/* NOTE: Use the radius from UI instead of auto sun size for now. */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +212,13 @@ vec3 sample_sphere(vec2 rand)
|
||||
return vec3(sin_theta * sample_circle(rand.y), cos_theta);
|
||||
}
|
||||
|
||||
/* Returns a point in a ball that is "uniformly" distributed after projection along any axis. */
|
||||
vec3 sample_ball(vec3 rand)
|
||||
{
|
||||
/* Completely ad-hoc, but works well in practice and is fast. */
|
||||
return sample_sphere(rand.xy) * sqrt(sqrt(rand.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform hemisphere distribution.
|
||||
* \a rand is 2 random float in the [0..1] range.
|
||||
|
||||
@@ -77,7 +77,7 @@ vec3 debug_tile_state_color(eLightType type, ShadowSamplingTile tile)
|
||||
ShadowCoordinates debug_coord_get(vec3 P, LightData light)
|
||||
{
|
||||
if (is_sun_light(light.type)) {
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
vec3 lP = light_world_to_local_direction(light, P);
|
||||
return shadow_directional_coordinates(light, lP);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -45,7 +45,9 @@ float shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
vec3 shadow_position = light_local_data_get(light).shadow_position;
|
||||
vec3 lP = transform_point_inversed(light.object_to_world, P);
|
||||
lP -= shadow_position;
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
|
||||
@@ -34,7 +34,7 @@ void shadow_tag_usage_tilemap_directional_at_level(uint l_idx, vec3 P, int level
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
vec3 lP = light_world_to_local_direction(light, P);
|
||||
|
||||
level = clamp(
|
||||
level, light_sun_data_get(light).clipmap_lod_min, light_sun_data_get(light).clipmap_lod_max);
|
||||
@@ -51,7 +51,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
vec3 lP = light_world_to_local_direction(light, P);
|
||||
|
||||
/* TODO(Miguel Pozo): Implement lod_bias support. */
|
||||
if (radius == 0.0) {
|
||||
@@ -60,8 +60,8 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
||||
shadow_tag_usage_tile(light, coord.tilemap_tile, 0, coord.tilemap_index);
|
||||
}
|
||||
else {
|
||||
vec3 start_lP = light_world_to_local(light, P - V * radius);
|
||||
vec3 end_lP = light_world_to_local(light, P + V * radius);
|
||||
vec3 start_lP = light_world_to_local_direction(light, P - V * radius);
|
||||
vec3 end_lP = light_world_to_local_direction(light, P + V * radius);
|
||||
int min_level = shadow_directional_level(light, start_lP - light_position_get(light));
|
||||
int max_level = shadow_directional_level(light, end_lP - light_position_get(light));
|
||||
|
||||
@@ -107,8 +107,8 @@ void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, float radius, int lod
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(fclem): 3D shift for jittered soft shadows. */
|
||||
lP += vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift);
|
||||
/* Transform to shadow local space. */
|
||||
lP -= light_local_data_get(light).shadow_position;
|
||||
|
||||
int lod = shadow_punctual_level(light,
|
||||
lP,
|
||||
|
||||
@@ -30,10 +30,10 @@ 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._pad7 = 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.tilemaps_count = clipmap_base_offset.x;
|
||||
light.do_not_access_directly.shadow_radius = intBitsToFloat(clipmap_base_offset.y);
|
||||
light.do_not_access_directly.shape_radius = intBitsToFloat(0);
|
||||
light.do_not_access_directly.influence_radius_max = intBitsToFloat(0);
|
||||
}
|
||||
|
||||
void main()
|
||||
|
||||
@@ -140,7 +140,7 @@ void main()
|
||||
/* Clipping setup. */
|
||||
if (tilemap_data.is_area_side) {
|
||||
/* Negative for tagging this case. See shadow_clip_vector_get for explanation. */
|
||||
render_view_buf[view_index].clip_distance_inv = -M_SQRT1_3 / tilemap_data.area_shift;
|
||||
render_view_buf[view_index].clip_distance_inv = -1.0;
|
||||
}
|
||||
else if (is_point_light(tilemap_data.light_type)) {
|
||||
/* Clip as a sphere around the clip_near cube. */
|
||||
|
||||
@@ -68,9 +68,10 @@ void main()
|
||||
barrier();
|
||||
|
||||
ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 tile_shifted = tile_co + clamp(tilemap.grid_shift,
|
||||
ivec2(-SHADOW_TILEMAP_RES),
|
||||
ivec2(SHADOW_TILEMAP_RES));
|
||||
ivec2 tile_shifted = tile_co +
|
||||
clamp(tilemap.is_dirty ? ivec2(SHADOW_TILEMAP_RES) : tilemap.grid_shift,
|
||||
ivec2(-SHADOW_TILEMAP_RES),
|
||||
ivec2(SHADOW_TILEMAP_RES));
|
||||
ivec2 tile_wrapped = ivec2((ivec2(SHADOW_TILEMAP_RES) + tile_shifted) % SHADOW_TILEMAP_RES);
|
||||
|
||||
/* If this tile was shifted in and contains old information, update it.
|
||||
|
||||
@@ -353,10 +353,8 @@ int shadow_punctual_face_index_get(vec3 lL)
|
||||
*/
|
||||
ShadowCoordinates shadow_punctual_coordinates(LightData light, vec3 lP, int face_id)
|
||||
{
|
||||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
/* UVs in [-1..+1] range. */
|
||||
vec2 tilemap_uv = (lP.xy * clip_near) / abs(lP.z * clip_side);
|
||||
vec2 tilemap_uv = lP.xy / abs(lP.z);
|
||||
/* UVs in [0..1] range. */
|
||||
tilemap_uv = saturate(tilemap_uv * 0.5 + 0.5);
|
||||
|
||||
|
||||
@@ -131,11 +131,11 @@ void shadow_map_trace_hit_check(inout ShadowMapTracingState state, ShadowTracing
|
||||
/* 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
|
||||
* 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 L, vec3 N, float max_clip_distance)
|
||||
{
|
||||
float distance_to_plan = dot(L, -N);
|
||||
if (distance_to_plan > 0.0) {
|
||||
L += N * (0.01 + distance_to_plan);
|
||||
float distance_to_plane = dot(L, -N);
|
||||
if (distance_to_plane > 0.0 && distance_to_plane < 0.01 + max_clip_distance) {
|
||||
L += N * (0.01 + distance_to_plane);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
@@ -163,13 +163,14 @@ ShadowRayDirectional shadow_ray_generate_directional(
|
||||
float dist_to_near_plane = -lP.z - clip_near;
|
||||
/* Trace in a radius that is covered by low resolution page inflation. */
|
||||
float max_tracing_distance = texel_radius * float(SHADOW_PAGE_RES << SHADOW_TILEMAP_LOD);
|
||||
/* TODO(fclem): Remove atan here. We only need the cosine of the angle. */
|
||||
float max_tracing_angle = atan_fast(max_tracing_distance / dist_to_near_plane);
|
||||
float shadow_angle = min(light_sun_data_get(light).shadow_angle, max_tracing_angle);
|
||||
|
||||
/* Light shape is 1 unit away from the shading point. */
|
||||
vec3 direction = sample_uniform_cone(sample_cylinder(random_2d), shadow_angle);
|
||||
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg, max_tracing_distance);
|
||||
|
||||
/* It only make sense to trace where there can be occluder. Clamp by distance to near plane. */
|
||||
direction *= dist_to_near_plane / direction.z;
|
||||
@@ -237,33 +238,30 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
||||
|
||||
float clip_far = intBitsToFloat(light.clip_far);
|
||||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
float shape_radius = light_spot_data_get(light).shadow_radius;
|
||||
|
||||
/* 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);
|
||||
vec3 point_on_light_shape = vec3(random_2d * shape_radius, 0.0);
|
||||
|
||||
direction = point_on_light_shape - lP;
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg, shape_radius);
|
||||
|
||||
/* Clip the ray to not cross the near plane.
|
||||
* Scale it so that it encompass the whole cube (with a safety margin). */
|
||||
float clip_distance = clip_near + 0.001;
|
||||
float ray_length = max(abs(direction.x), max(abs(direction.y), abs(direction.z)));
|
||||
float ray_length = reduce_max(abs(direction));
|
||||
direction *= saturate((ray_length - clip_distance) / ray_length);
|
||||
}
|
||||
else {
|
||||
float dist;
|
||||
vec3 L = normalize_and_get_length(lP, dist);
|
||||
vec3 lL = normalize_and_get_length(lP, dist);
|
||||
/* Disk rotated towards light vector. */
|
||||
vec3 right, up;
|
||||
make_orthonormal_basis(L, right, up);
|
||||
make_orthonormal_basis(lL, 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);
|
||||
@@ -273,17 +271,19 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
||||
vec3 point_on_light_shape = right * random_2d.x + up * random_2d.y;
|
||||
|
||||
direction = point_on_light_shape - lP;
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg, shape_radius);
|
||||
|
||||
/* Clip the ray to not cross the light shape. */
|
||||
float clip_distance = light_spot_data_get(light).radius;
|
||||
float clip_distance = clip_near + 0.001;
|
||||
direction *= saturate((dist - clip_distance) / dist);
|
||||
}
|
||||
|
||||
vec3 shadow_position = light_local_data_get(light).shadow_position;
|
||||
/* Compute the ray again. */
|
||||
ShadowRayPunctual ray;
|
||||
ray.origin = lP;
|
||||
ray.direction = direction;
|
||||
/* Transform to shadow local space. */
|
||||
ray.origin = lP - shadow_position;
|
||||
ray.direction = direction + shadow_position;
|
||||
ray.light_tilemap_index = light.tilemap_index;
|
||||
ray.local_ray_up = safe_normalize(cross(cross(ray.origin, ray.direction), ray.direction));
|
||||
ray.light = light;
|
||||
@@ -365,6 +365,7 @@ float shadow_texel_radius_at_position(LightData light, const bool is_directional
|
||||
}
|
||||
}
|
||||
else {
|
||||
lP -= light_local_data_get(light).shadow_position;
|
||||
/* Simplification of `exp2(shadow_punctual_level_fractional)`. */
|
||||
scale = shadow_punctual_pixel_ratio(light,
|
||||
lP,
|
||||
@@ -412,7 +413,6 @@ float shadow_eval(LightData light,
|
||||
float thickness, /* Only used if is_transmission is true. */
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 L,
|
||||
int ray_count,
|
||||
int ray_step_count)
|
||||
{
|
||||
@@ -431,6 +431,12 @@ float shadow_eval(LightData light,
|
||||
vec2 random_pcf_2d = vec2(0.0);
|
||||
#endif
|
||||
|
||||
/* Direction towards the shadow center (punctual) or direction (direction).
|
||||
* Not the same as the light vector if the shadow is jittered. */
|
||||
vec3 L = is_directional ? light_z_axis(light) :
|
||||
normalize(light_position_get(light) +
|
||||
light_local_data_get(light).shadow_position - P);
|
||||
|
||||
bool is_facing_light = (dot(Ng, L) > 0.0);
|
||||
/* Still bias the transmission surfaces towards the light if they are facing away. */
|
||||
vec3 N_bias = (is_transmission && !is_facing_light) ? reflect(Ng, L) : Ng;
|
||||
@@ -451,9 +457,9 @@ float shadow_eval(LightData light,
|
||||
/* Add normal bias to avoid aliasing artifacts. */
|
||||
P += N_bias * (texel_radius * shadow_normal_offset(Ng, L));
|
||||
|
||||
vec3 lP = is_directional ? light_world_to_local(light, P) :
|
||||
light_world_to_local(light, P - light_position_get(light));
|
||||
vec3 lNg = light_world_to_local(light, Ng);
|
||||
vec3 lP = is_directional ? light_world_to_local_direction(light, P) :
|
||||
light_world_to_local_point(light, P);
|
||||
vec3 lNg = light_world_to_local_direction(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. */
|
||||
|
||||
@@ -184,7 +184,7 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
|
||||
{
|
||||
float power = 1.0;
|
||||
if (!is_directional) {
|
||||
float volume_radius_squared = light_local_data_get(light).radius_squared;
|
||||
float light_radius = light_local_data_get(light).shape_radius;
|
||||
/**
|
||||
* Using "Point Light Attenuation Without Singularity" from Cem Yuksel
|
||||
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
|
||||
@@ -192,7 +192,7 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
|
||||
*/
|
||||
float d = lv.dist;
|
||||
float d_sqr = square(d);
|
||||
float r_sqr = volume_radius_squared;
|
||||
float r_sqr = square(light_radius);
|
||||
|
||||
/* Using reformulation that has better numerical precision. */
|
||||
power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr));
|
||||
|
||||
@@ -64,11 +64,16 @@ GPU_SHADER_CREATE_INFO(eevee_light_culling_tile)
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_light_shadow_setup)
|
||||
.do_static_compilation(true)
|
||||
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_global_ubo")
|
||||
.additional_info("eevee_shared",
|
||||
"draw_view",
|
||||
"draw_view_culling",
|
||||
"eevee_sampling_data",
|
||||
"eevee_global_ubo")
|
||||
.local_group_size(CULLING_SELECT_GROUP_SIZE)
|
||||
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "LightData", "light_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(3, Qualifier::READ_WRITE, "ShadowTileMapClip", "tilemaps_clip_buf[]")
|
||||
.compute_source("eevee_light_shadow_setup_comp.glsl");
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -233,8 +233,7 @@ static void test_eevee_shadow_tag_update()
|
||||
|
||||
{
|
||||
ShadowTileMap tilemap(0 * SHADOW_TILEDATA_PER_TILEMAP);
|
||||
tilemap.sync_cubeface(
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG);
|
||||
tilemap.sync_cubeface(LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, Z_NEG);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
{
|
||||
@@ -1542,8 +1541,7 @@ static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap)
|
||||
|
||||
{
|
||||
ShadowTileMap tilemap(0);
|
||||
tilemap.sync_cubeface(
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG);
|
||||
tilemap.sync_cubeface(LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, Z_NEG);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
# define BLI_STATIC_ASSERT_ALIGN(type_, align_)
|
||||
# define BLI_STATIC_ASSERT_SIZE(type_, size_)
|
||||
# define ENUM_OPERATORS(a, b)
|
||||
# define UNUSED_VARS(a)
|
||||
/* Incompatible keywords. */
|
||||
# define static
|
||||
# define inline
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
.shadow_filter_radius = 1.0f, \
|
||||
.shadow_resolution_scale = 1.0f, \
|
||||
.shadow_maximum_resolution = 0.001f, \
|
||||
.shadow_jitter_overblur = 10.0f, \
|
||||
.att_dist = 40.0f, \
|
||||
.sun_angle = DEG2RADF(0.526f), \
|
||||
.area_spread = DEG2RADF(180.0f), \
|
||||
|
||||
@@ -82,7 +82,7 @@ typedef struct Light {
|
||||
float shadow_filter_radius;
|
||||
float shadow_resolution_scale;
|
||||
float shadow_maximum_resolution;
|
||||
char _pad3[4];
|
||||
float shadow_jitter_overblur;
|
||||
|
||||
/* Preview */
|
||||
struct PreviewImage *preview;
|
||||
@@ -147,6 +147,7 @@ enum {
|
||||
LA_USE_SOFT_FALLOFF = 1 << 21,
|
||||
/** Use absolute resolution clamping instead of relative. */
|
||||
LA_SHAD_RES_ABSOLUTE = 1 << 22,
|
||||
LA_SHADOW_JITTER = 1 << 23,
|
||||
};
|
||||
|
||||
/** #Light::falloff_type */
|
||||
|
||||
@@ -326,6 +326,26 @@ 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_jitter", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_SHADOW_JITTER);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Shadow Jitter",
|
||||
"Enable jittered soft shadows to increase shadow precision (disabled in viewport unless "
|
||||
"enabled in the render settings). Has a high performance impact");
|
||||
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", PROP_FLOAT, PROP_PERCENTAGE);
|
||||
RNA_def_property_range(prop, 0.0f, 100.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 20.0f, 10.0f, 0);
|
||||
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");
|
||||
|
||||
@@ -8059,7 +8059,7 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
||||
prop, "Tracing Method", "Select the tracing method used to find scene-ray intersections");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "use_shadow_jittered_viewport", PROP_BOOLEAN, PROP_NONE);
|
||||
prop = RNA_def_property(srna, "use_shadow_jitter_viewport", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_EEVEE_SHADOW_JITTERED_VIEWPORT);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Jittered Shadows (Viewport)",
|
||||
|
||||
Reference in New Issue
Block a user