EEVEE-Next: Refactor Light evaluation

This allow splitting shadow and light evaluation.
This is the first step to deferred shadowing.

The evaluate closure types can be dynamically
set which mean we can have arbitrary BSDF
evaluation inside the same shader.

This also contain some refactor to `light_lib.glsl`
for more consistency and less clutter.

Note that this breaks the SSS translucency
as the shadow evaluation changes for these.
A new solution for this feature is to be found

Pull Request: https://projects.blender.org/blender/blender/pulls/113257
This commit is contained in:
Clément Foucault
2023-10-08 00:15:41 +02:00
committed by Clément Foucault
parent 29d4779bf9
commit 7c694c9d04
16 changed files with 469 additions and 475 deletions

View File

@@ -71,10 +71,10 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
float shape_power = shape_radiance_get(la);
float point_power = point_radiance_get(la);
this->diffuse_power = la->diff_fac * shape_power;
this->transmit_power = la->diff_fac * point_power;
this->specular_power = la->spec_fac * shape_power;
this->volume_power = la->volume_fac * point_power;
this->power[LIGHT_DIFFUSE] = la->diff_fac * shape_power;
this->power[LIGHT_TRANSMIT] = la->diff_fac * point_power;
this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power;
this->power[LIGHT_VOLUME] = la->volume_fac * point_power;
eLightType new_type = to_light_type(la->type, la->area_shape);
if (assign_if_different(this->type, new_type)) {

View File

@@ -708,6 +708,13 @@ enum eLightType : uint32_t {
LIGHT_ELLIPSE = 21u
};
enum LightingType : uint32_t {
LIGHT_DIFFUSE = 0u,
LIGHT_SPECULAR = 1u,
LIGHT_TRANSMIT = 2u,
LIGHT_VOLUME = 3u,
};
static inline bool is_area_light(eLightType type)
{
return type >= LIGHT_RECT;
@@ -742,6 +749,14 @@ struct LightData {
# define _back object_mat[2].xyz
# define _position object_mat[3].xyz
#endif
/** Power depending on shader type. Referenced by LightingType. */
float4 power;
/** Light Color. */
packed_float3 color;
/** Light Type. */
eLightType type;
/** Inverse spot size (in X and Y axes). Aligned to size of float2. */
float2 spot_size_inv;
/** Punctual : Influence radius (inverted and squared) adjusted for Surface / Volume power. */
float influence_radius_invsqr_surface;
float influence_radius_invsqr_volume;
@@ -749,22 +764,10 @@ struct LightData {
float influence_radius_max;
/** Special radius factor for point lighting. */
float radius_squared;
/** NOTE: It is ok to use float3 here. A float is declared right after it.
* float3 is also aligned to 16 bytes. */
packed_float3 color;
/** Light Type. */
eLightType type;
/** Spot size. Aligned to size of float2. */
float2 spot_size_inv;
/** Spot angle tangent. */
float spot_tan;
/** Reuse for directional LOD bias. */
#define _clipmap_lod_bias spot_tan
/** Power depending on shader type. */
float diffuse_power;
float specular_power;
float volume_power;
float transmit_power;
/** --- Shadow Data --- */
/** Near clip distances. Float stored as int for atomic operations. */

View File

@@ -15,32 +15,21 @@ void main()
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(hiz_tx, texel, 0).r;
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
vec3 V = cameraVec(P);
float vP_z = dot(cameraForward, P) - dot(cameraForward, cameraPos);
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
ClosureLightStack stack;
stack.cl[0].N = gbuf.has_diffuse ? gbuf.diffuse.N : gbuf.reflection.N;
stack.cl[0].ltc_mat = LTC_LAMBERT_MAT;
stack.cl[0].type = LIGHT_DIFFUSE;
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
vec3 Ng = gbuf.diffuse.N;
vec3 diffuse_light = vec3(0.0);
vec3 unused_reflection_light = vec3(0.0);
vec3 unused_refraction_light = vec3(0.0);
float unused_shadow = 1.0;
light_eval(gbuf.diffuse,
gbuf.reflection,
P,
Ng,
V,
vP_z,
gbuf.thickness,
diffuse_light,
unused_reflection_light,
unused_shadow);
vec3 V = cameraVec(P);
float vPz = dot(cameraForward, P) - dot(cameraForward, cameraPos);
light_eval(stack, P, Ng, V, vPz, gbuf.thickness);
vec3 albedo = gbuf.diffuse.color + gbuf.reflection.color + gbuf.refraction.color;
out_radiance = vec4(diffuse_light * albedo, 0.0);
out_radiance = vec4(stack.cl[0].light_shadowed * albedo, 0.0);
}

View File

@@ -18,38 +18,40 @@ void main()
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(hiz_tx, texel, 0).r;
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
vec3 V = cameraVec(P);
float vP_z = dot(cameraForward, P) - dot(cameraForward, cameraPos);
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
vec3 refraction_light = vec3(0.0);
float shadow = 1.0;
vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
vec3 V = cameraVec(P);
ClosureLightStack stack;
ClosureLight cl_diff;
cl_diff.N = gbuf.diffuse.N;
cl_diff.ltc_mat = LTC_LAMBERT_MAT;
cl_diff.type = LIGHT_DIFFUSE;
stack.cl[0] = cl_diff;
ClosureLight cl_refl;
cl_refl.N = gbuf.reflection.N;
cl_refl.ltc_mat = LTC_GGX_MAT(dot(gbuf.reflection.N, V), gbuf.reflection.roughness);
cl_refl.type = LIGHT_SPECULAR;
stack.cl[1] = cl_refl;
/* Assume reflection closure normal is always somewhat representative of the geometric normal.
* Ng is only used for shadow biases and subsurface check in this case. */
vec3 Ng = gbuf.has_reflection ? gbuf.reflection.N : gbuf.diffuse.N;
float vPz = dot(cameraForward, P) - dot(cameraForward, cameraPos);
light_eval(gbuf.diffuse,
gbuf.reflection,
P,
Ng,
V,
vP_z,
gbuf.thickness,
diffuse_light,
reflection_light,
/* TODO(fclem): Implement refraction light. */
// refraction_light,
shadow);
light_eval(stack, P, Ng, V, vPz, gbuf.thickness);
output_renderpass_value(uniform_buf.render_pass.shadow_id, shadow);
vec3 shadows = (stack.cl[0].light_shadowed + stack.cl[1].light_shadowed) /
(stack.cl[0].light_unshadowed + stack.cl[1].light_unshadowed);
imageStore(direct_diffuse_img, texel, vec4(diffuse_light, 1.0));
imageStore(direct_reflect_img, texel, vec4(reflection_light, 1.0));
imageStore(direct_refract_img, texel, vec4(refraction_light, 1.0));
/* TODO(fclem): Change shadow pass to be colored. */
output_renderpass_value(uniform_buf.render_pass.shadow_id, avg(shadows));
imageStore(direct_diffuse_img, texel, vec4(stack.cl[0].light_shadowed, 1.0));
imageStore(direct_reflect_img, texel, vec4(stack.cl[1].light_shadowed, 1.0));
/* TODO(fclem): Support LTC for refraction. */
// imageStore(direct_refract_img, texel, vec4(cl_refr.light_shadowed, 1.0));
}

View File

@@ -35,10 +35,9 @@ void main()
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx)
{
LightData light = light_buf[l_idx];
vec3 L;
float dist;
light_vector_get(light, P, L, dist);
if (light_attenuation(light, L, dist) > 0.0) {
LightVector lv = light_vector_get(light, false, P);
/* Use light vector as Ng to never cull based on angle to light. */
if (light_attenuation_surface(light, false, lv.L, lv) > LIGHT_ATTENUATION_THRESHOLD) {
light_nocull |= 1u << l_idx;
}
}

View File

@@ -18,118 +18,179 @@
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
void light_eval_ex(ClosureDiffuse diffuse,
ClosureReflection reflection,
const bool is_directional,
vec3 P,
vec3 Ng,
vec3 V,
float vP_z, /* TODO(fclem): Remove, is unused. */
float thickness,
vec4 ltc_mat,
uint l_idx,
inout vec3 out_diffuse,
inout vec3 out_specular,
inout float out_shadow)
/* If using compute, the shader should define it's own pixel. */
#if !defined(PIXEL) && defined(GPU_FRAGMENT_SHADER)
# define PIXEL gl_FragCoord.xy
#endif
#if !defined(LIGHT_CLOSURE_EVAL_COUNT)
# define LIGHT_CLOSURE_EVAL_COUNT 1
#endif
uint shadow_pack(float visibility, uint bit_depth, uint shift)
{
LightData light = light_buf[l_idx];
vec3 L;
float dist;
light_vector_get(light, P, L, dist);
return uint(visibility * float((1u << bit_depth) - 1u)) << shift;
}
float visibility = is_directional ? 1.0 : light_attenuation(light, L, dist);
float shadow_unpack(uint shadow_bits, uint bit_depth, uint shift)
{
return float((shadow_bits >> shift) & ~(~0u << bit_depth)) / float((1u << bit_depth) - 1u);
}
if (light.tilemap_index != LIGHT_NO_SHADOW && (visibility > 0.0)) {
#ifdef SURFEL_LIGHT
ShadowEvalResult shadow = shadow_eval(light, is_directional, P, Ng, 16, 8);
#else
ShadowEvalResult shadow = shadow_eval(
light, is_directional, P, Ng, uniform_buf.shadow.ray_count, uniform_buf.shadow.step_count);
#endif
#ifdef SSS_TRANSMITTANCE
/* Transmittance evaluation first to use initial visibility without shadow. */
if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) {
float delta = max(thickness, shadow.subsurface_occluder_distance);
vec3 intensity = visibility * light.transmit_power *
light_translucent(
is_directional, light, diffuse.N, L, dist, diffuse.sss_radius, delta);
out_diffuse += light.color * intensity;
}
#endif
visibility *= shadow.surface_light_visibilty;
out_shadow *= shadow.surface_light_visibilty;
}
if (visibility < 1e-6) {
void light_shadow_single(LightData light,
const bool is_directional,
vec3 P,
vec3 Ng,
float thickness,
inout uint shadow_bits,
inout uint shift)
{
if (light.tilemap_index == LIGHT_NO_SHADOW) {
return;
}
if (light.diffuse_power > 0.0) {
float intensity = visibility * light.diffuse_power *
light_diffuse(utility_tx, is_directional, light, diffuse.N, V, L, dist);
out_diffuse += light.color * intensity;
LightVector lv = light_vector_get(light, is_directional, P);
float attenuation = light_attenuation_surface(light, is_directional, Ng, lv);
if (attenuation < LIGHT_ATTENUATION_THRESHOLD) {
return;
}
int ray_count = uniform_buf.shadow.ray_count;
int ray_step_count = uniform_buf.shadow.step_count;
if (light.specular_power > 0.0) {
float intensity = visibility * light.specular_power *
light_ltc(
utility_tx, is_directional, light, reflection.N, V, L, dist, ltc_mat);
out_specular += light.color * intensity;
ShadowEvalResult result = shadow_eval(light, is_directional, P, Ng, ray_count, ray_step_count);
shadow_bits |= shadow_pack(result.surface_light_visibilty, ray_count, shift);
shift += ray_count;
}
void light_shadow_mask(vec3 P, vec3 Ng, float vPz, float thickness, out uint shadow_bits)
{
int ray_count = uniform_buf.shadow.ray_count;
int ray_step_count = uniform_buf.shadow.step_count;
uint shift = 0u;
shadow_bits = 0u;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
light_shadow_single(light_buf[l_idx], true, P, Ng, thickness, shadow_bits, shift);
}
LIGHT_FOREACH_END
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) {
light_shadow_single(light_buf[l_idx], false, P, Ng, thickness, shadow_bits, shift);
}
LIGHT_FOREACH_END
}
struct ClosureLight {
/* Shading normal. */
vec3 N;
/* LTC matrix. */
vec4 ltc_mat;
/* Enum (used as index) telling how to treat the lighting. */
LightingType type;
/* Output both shadowed and unshadowed for shadow denoising. */
vec3 light_shadowed;
vec3 light_unshadowed;
};
struct ClosureLightStack {
/* NOTE: This is wrapped into a struct to avoid array shenanigans on MSL. */
ClosureLight cl[LIGHT_CLOSURE_EVAL_COUNT];
};
void light_eval_single_closure(LightData light,
LightVector lv,
inout ClosureLight cl,
vec3 P,
vec3 V,
float thickness,
float attenuation,
float visibility)
{
if (light.power[cl.type] > 0.0) {
float ltc_result = light_ltc(utility_tx, light, cl.N, V, lv, cl.ltc_mat);
vec3 out_radiance = light.color * light.power[cl.type] * ltc_result;
cl.light_shadowed += visibility * out_radiance;
cl.light_unshadowed += attenuation * out_radiance;
}
}
void light_eval(ClosureDiffuse diffuse,
ClosureReflection reflection,
void light_eval_single(LightData light,
const bool is_directional,
inout ClosureLightStack stack,
vec3 P,
vec3 Ng,
vec3 V,
float thickness,
uint packed_shadows,
inout uint shift)
{
int ray_count = uniform_buf.shadow.ray_count;
int ray_step_count = uniform_buf.shadow.step_count;
LightVector lv = light_vector_get(light, is_directional, P);
float attenuation = light_attenuation_surface(light, is_directional, Ng, lv);
if (attenuation < LIGHT_ATTENUATION_THRESHOLD) {
return;
}
float shadow = 1.0;
if (light.tilemap_index != LIGHT_NO_SHADOW) {
#ifdef SHADOW_DEFERRED
shadow = shadow_unpack(packed_shadows, ray_count, shift);
shift += ray_count;
#else
shadow = shadow_eval(light, is_directional, P, Ng, ray_count, ray_step_count)
.surface_light_visibilty;
#endif
}
float visibility = attenuation * shadow;
/* WATCH(@fclem): Might have to manually unroll for best perf. */
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT; i++) {
light_eval_single_closure(light, lv, stack.cl[i], P, V, thickness, attenuation, visibility);
}
}
void light_eval(inout ClosureLightStack stack,
vec3 P,
vec3 Ng,
vec3 V,
float vP_z,
float vPz,
float thickness,
inout vec3 out_diffuse,
inout vec3 out_specular,
inout float out_shadow)
uint packed_shadows)
{
vec4 ltc_mat = utility_tx_sample_lut(
utility_tx, dot(reflection.N, V), reflection.roughness, UTIL_LTC_MAT_LAYER);
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT; i++) {
stack.cl[i].light_shadowed = vec3(0.0);
stack.cl[i].light_unshadowed = vec3(0.0);
}
uint shift = 0u;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
light_eval_ex(diffuse,
reflection,
true,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat,
l_idx,
out_diffuse,
out_specular,
out_shadow);
light_eval_single(light_buf[l_idx], true, stack, P, Ng, V, thickness, packed_shadows, shift);
}
LIGHT_FOREACH_END
#ifdef GPU_FRAGMENT_SHADER
vec2 px = gl_FragCoord.xy;
#else
vec2 px = vec2(0.0);
#endif
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx) {
light_eval_ex(diffuse,
reflection,
false,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat,
l_idx,
out_diffuse,
out_specular,
out_shadow);
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) {
light_eval_single(light_buf[l_idx], false, stack, P, Ng, V, thickness, packed_shadows, shift);
}
LIGHT_FOREACH_END
}
/* Variations that have less arguments. */
#if !defined(SHADOW_DEFERRED)
void light_eval(inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz, float thickness)
{
light_eval(stack, P, Ng, V, vPz, thickness, 0u);
}
# if !defined(SSS_TRANSMITTANCE) && defined(LIGHT_ITER_FORCE_NO_CULLING)
void light_eval(inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V)
{
light_eval(stack, P, Ng, V, 0.0, 0.0, 0u);
}
# endif
#endif

View File

@@ -37,38 +37,49 @@ int culling_z_to_zbin(float scale, float bias, float z)
{ \
for (uint _index = _culling.local_lights_len; _index < _culling.items_count; _index++) {
#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
{ \
uvec2 tile_co = uvec2(_pixel / _culling.tile_size); \
uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \
_culling.tile_word_len; \
int zbin_index = culling_z_to_zbin(_culling.zbin_scale, _culling.zbin_bias, _linearz); \
zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \
uint zbin_data = _zbins[zbin_index]; \
uint min_index = zbin_data & 0xFFFFu; \
uint max_index = zbin_data >> 16u; \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \
max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \
/* Same as divide by 32 but avoid integer division. */ \
uint word_min = min_index >> 5u; \
uint word_max = max_index >> 5u; \
for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \
uint word = _words[tile_word_offset + word_idx]; \
word &= zbin_mask(word_idx, min_index, max_index); \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
word = subgroupBroadcastFirst(subgroupOr(word)); \
int bit_index; \
while ((bit_index = findLSB(word)) != -1) { \
word &= ~1u << uint(bit_index); \
uint _item_index = word_idx * 32u + bit_index;
/* No culling. Iterate over all items. */
#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \
{ \
{ \
for (uint _item_index = 0; _item_index < _culling.visible_count; _item_index++) {
#ifdef LIGHT_ITER_FORCE_NO_CULLING
/* Skip the optimization structure traversal for cases where the acceleration structure
* doesn't match. */
# define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index)
#else
# define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
{ \
uvec2 tile_co = uvec2(_pixel / _culling.tile_size); \
uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \
_culling.tile_word_len; \
int zbin_index = culling_z_to_zbin(_culling.zbin_scale, _culling.zbin_bias, _linearz); \
zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \
uint zbin_data = _zbins[zbin_index]; \
uint min_index = zbin_data & 0xFFFFu; \
uint max_index = zbin_data >> 16u; \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \
max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \
/* Same as divide by 32 but avoid integer division. */ \
uint word_min = min_index >> 5u; \
uint word_max = max_index >> 5u; \
for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \
uint word = _words[tile_word_offset + word_idx]; \
word &= zbin_mask(word_idx, min_index, max_index); \
/* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
word = subgroupBroadcastFirst(subgroupOr(word)); \
int bit_index; \
while ((bit_index = findLSB(word)) != -1) { \
word &= ~1u << uint(bit_index); \
uint _item_index = word_idx * 32u + bit_index;
#endif
#define LIGHT_FOREACH_END \
} \
} \

View File

@@ -6,35 +6,46 @@
#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
/* Attenuation cutoff needs to be the same in the shadow loop and the light eval loop. */
#define LIGHT_ATTENUATION_THRESHOLD 1e-6
/* ---------------------------------------------------------------------- */
/** \name Light Functions
* \{ */
void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
struct LightVector {
/* World space light vector. From the shading point to the light center. Normalized. */
vec3 L;
/* Distance from the shading point to the light center. */
float dist;
};
LightVector light_vector_get(LightData light, const bool is_directional, vec3 P)
{
/* TODO(fclem): Static branching. */
if (is_sun_light(ld.type)) {
L = ld._back;
dist = 1.0;
LightVector lv;
if (is_directional) {
lv.L = light._back;
lv.dist = 1.0;
}
else {
L = ld._position - P;
dist = inversesqrt(len_squared(L));
L *= dist;
dist = 1.0 / dist;
lv.L = light._position - P;
float inv_distance = inversesqrt(len_squared(lv.L));
lv.L *= inv_distance;
lv.dist = 1.0 / inv_distance;
}
return lv;
}
/* Light vector to the closest point in the light shape. */
void light_shape_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
LightVector light_shape_vector_get(LightData light, const bool is_directional, vec3 P)
{
if (ld.type == LIGHT_RECT || ld.type == LIGHT_ELLIPSE) {
L = P - ld._position;
vec2 closest_point = vec2(dot(ld._right, L), dot(ld._up, L));
vec2 max_pos = vec2(ld._area_size_x, ld._area_size_y);
if (!is_directional && is_area_light(light.type)) {
vec3 L = P - light._position;
vec2 closest_point = vec2(dot(light._right, L), dot(light._up, L));
vec2 max_pos = vec2(light._area_size_x, light._area_size_y);
closest_point /= max_pos;
if (ld.type == LIGHT_ELLIPSE) {
if (light.type == LIGHT_ELLIPSE) {
closest_point /= max(1.0, length(closest_point));
}
else {
@@ -42,27 +53,28 @@ void light_shape_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
}
closest_point *= max_pos;
vec3 L_prime = ld._right * closest_point.x + ld._up * closest_point.y;
vec3 L_prime = light._right * closest_point.x + light._up * closest_point.y;
L = L_prime - L;
dist = inversesqrt(len_squared(L));
L *= dist;
dist = 1.0 / dist;
}
else {
light_vector_get(ld, P, L, dist);
float inv_distance = inversesqrt(len_squared(L));
LightVector lv;
lv.L = L * inv_distance;
lv.dist = 1.0 / inv_distance;
return lv;
}
/* TODO(@fclem): other light shape? */
return light_vector_get(light, is_directional, P);
}
/* Rotate vector to light's local space. Does not translate. */
vec3 light_world_to_local(LightData ld, vec3 L)
vec3 light_world_to_local(LightData light, vec3 L)
{
/* Avoid relying on compiler to optimize this.
* vec3 lL = transpose(mat3(ld.object_mat)) * L; */
* vec3 lL = transpose(mat3(light.object_mat)) * L; */
vec3 lL;
lL.x = dot(ld.object_mat[0].xyz, L);
lL.y = dot(ld.object_mat[1].xyz, L);
lL.z = dot(ld.object_mat[2].xyz, L);
lL.x = dot(light.object_mat[0].xyz, L);
lL.y = dot(light.object_mat[1].xyz, L);
lL.z = dot(light.object_mat[2].xyz, L);
return lL;
}
@@ -82,36 +94,47 @@ float light_influence_attenuation(float dist, float inv_sqr_influence)
return sqr(fac);
}
float light_spot_attenuation(LightData ld, vec3 L)
float light_spot_attenuation(LightData light, vec3 L)
{
vec3 lL = light_world_to_local(ld, L);
float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z));
float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias);
return spotmask;
vec3 lL = light_world_to_local(light, L);
float ellipse = inversesqrt(1.0 + len_squared(lL.xy * light.spot_size_inv / lL.z));
float spotmask = smoothstep(0.0, 1.0, ellipse * light._spot_mul + light._spot_bias);
return spotmask * step(0.0, -dot(L, -light._back));
}
float light_attenuation(LightData ld, vec3 L, float dist)
float light_attenuation_common(LightData light, const bool is_directional, vec3 L)
{
float vis = 1.0;
if (ld.type == LIGHT_SPOT) {
vis *= light_spot_attenuation(ld, L);
if (is_directional) {
return 1.0;
}
if (ld.type >= LIGHT_SPOT) {
vis *= step(0.0, -dot(L, -ld._back));
if (light.type == LIGHT_SPOT) {
return light_spot_attenuation(light, L);
}
if (is_area_light(light.type)) {
return step(0.0, -dot(L, -light._back));
}
return 1.0;
}
#ifdef VOLUME_LIGHTING
vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume);
#else
vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface);
#endif
return vis;
float light_attenuation_surface(LightData light,
const bool is_directional,
vec3 Ng,
LightVector lv)
{
/* TODO(fclem): add cutoff attenuation when backfacing. For now do nothing with Ng. */
return light_attenuation_common(light, is_directional, lv.L) *
light_influence_attenuation(lv.dist, light.influence_radius_invsqr_surface);
}
float light_attenuation_volume(LightData light, const bool is_directional, LightVector lv)
{
return light_attenuation_common(light, is_directional, lv.L) *
light_influence_attenuation(lv.dist, light.influence_radius_invsqr_volume);
}
/* Cheaper alternative than evaluating the LTC.
* The result needs to be multiplied by BSDF or Phase Function. */
float light_point_light(LightData ld, const bool is_directional, vec3 L, float dist)
float light_point_light(LightData light, const bool is_directional, LightVector lv)
{
if (is_directional) {
return 1.0;
@@ -120,14 +143,14 @@ float light_point_light(LightData ld, const bool is_directional, vec3 L, float d
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
* http://www.cemyuksel.com/research/pointlightattenuation/
*/
float d_sqr = sqr(dist);
float r_sqr = ld.radius_squared;
float d_sqr = sqr(lv.dist);
float r_sqr = light.radius_squared;
/* Using reformulation that has better numerical precision. */
float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr));
float power = 2.0 / (d_sqr + r_sqr + lv.dist * sqrt(d_sqr + r_sqr));
if (is_area_light(ld.type)) {
if (is_area_light(light.type)) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(ld._back, L));
power *= saturate(dot(light._back, lv.L));
}
return power;
}
@@ -144,117 +167,57 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere)
return sphere_radius * inversesqrt(1.0 - sqr(sphere_radius / distance_to_sphere));
}
float light_diffuse(sampler2DArray utility_tx,
const bool is_directional,
LightData ld,
vec3 N,
vec3 V,
vec3 L,
float dist)
float light_ltc(
sampler2DArray utility_tx, LightData light, vec3 N, vec3 V, LightVector lv, vec4 ltc_mat)
{
if (ld.type == LIGHT_POINT) {
if (dist < ld._radius) {
/* Inside, treat as hemispherical light. */
return 1.0;
}
else {
/* Outside, treat as disk light spanning the same solid angle. */
/* The result is the same as passing the scaled radius to #ltc_evaluate_disk_simple (see
* #light_ltc), using simplified math here. */
float r_sq = sqr(ld._radius / dist);
return r_sq * ltc_diffuse_sphere_integral(utility_tx, dot(N, L), r_sq);
}
}
else if (is_directional || ld.type == LIGHT_SPOT) {
float radius = ld._radius / dist;
return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L));
}
else if (ld.type == LIGHT_RECT) {
vec3 corners[4];
corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
corners[2] = -corners[0];
corners[3] = -corners[1];
corners[0] = normalize(L * dist + corners[0]);
corners[1] = normalize(L * dist + corners[1]);
corners[2] = normalize(L * dist + corners[2]);
corners[3] = normalize(L * dist + corners[3]);
return ltc_evaluate_quad(utility_tx, corners, N);
}
else /* (ld.type == LIGHT_ELLIPSE) */ {
vec3 points[3];
points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y;
points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
points[2] = -points[0];
points[0] += L * dist;
points[1] += L * dist;
points[2] += L * dist;
return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points);
}
}
float light_ltc(sampler2DArray utility_tx,
const bool is_directional,
LightData ld,
vec3 N,
vec3 V,
vec3 L,
float dist,
vec4 ltc_mat)
{
if (ld.type == LIGHT_POINT && dist < ld._radius) {
if (light.type == LIGHT_POINT && lv.dist < light._radius) {
/* Inside the sphere light, integrate over the hemisphere. */
return 1.0;
}
else if (is_directional || ld.type != LIGHT_RECT) {
vec3 Px = ld._right;
vec3 Py = ld._up;
if (is_directional || !is_area_light(ld.type)) {
make_orthonormal_basis(L, Px, Py);
}
vec3 points[3];
if (ld.type == LIGHT_POINT) {
/* 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. */
float radius = light_sphere_disk_radius(ld._radius, dist);
points[0] = Px * -radius + Py * -radius;
points[1] = Px * radius + Py * -radius;
}
else {
points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y;
points[1] = Px * ld._area_size_x + Py * -ld._area_size_y;
}
points[2] = -points[0];
points[0] += L * dist;
points[1] += L * dist;
points[2] += L * dist;
return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points);
}
else {
if (light.type == LIGHT_RECT) {
vec3 corners[4];
corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
corners[0] = light._right * light._area_size_x + light._up * -light._area_size_y;
corners[1] = light._right * light._area_size_x + light._up * light._area_size_y;
corners[2] = -corners[0];
corners[3] = -corners[1];
corners[0] += L * dist;
corners[1] += L * dist;
corners[2] += L * dist;
corners[3] += L * dist;
vec3 L = lv.L * lv.dist;
corners[0] += L;
corners[1] += L;
corners[2] += L;
corners[3] += L;
ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0));
}
else {
vec3 Px = light._right;
vec3 Py = light._up;
if (!is_area_light(light.type)) {
make_orthonormal_basis(lv.L, Px, Py);
}
vec2 size = vec2(light._area_size_x, light._area_size_y);
if (light.type == LIGHT_POINT) {
/* 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. */
size = vec2(light_sphere_disk_radius(light._radius, lv.dist));
}
vec3 points[3];
points[0] = Px * -size.x + Py * -size.y;
points[1] = Px * size.x + Py * -size.y;
points[2] = -points[0];
vec3 L = lv.L * lv.dist;
points[0] += L;
points[1] += L;
points[2] += L;
return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points);
}
}
#ifdef SSS_TRANSMITTANCE
@@ -264,19 +227,18 @@ float sample_transmittance_profile(float u)
}
vec3 light_translucent(const bool is_directional,
LightData ld,
LightData light,
vec3 N,
vec3 L,
float dist,
LightVector lv,
vec3 sss_radius,
float delta)
{
/* TODO(fclem): We should compute the power at the entry point. */
/* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance
* using the shadow depth delta. */
float power = light_point_light(ld, is_directional, L, dist);
float power = light_point_light(light, is_directional, lv);
/* Do not add more energy on front faces. Also apply lambertian BSDF. */
power *= max(0.0, dot(-N, L)) * M_1_PI;
power *= max(0.0, dot(-N, lv.L)) * M_1_PI;
sss_radius *= SSS_TRANSMIT_LUT_RADIUS;
vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS;

View File

@@ -10,6 +10,10 @@
* Project page: https://eheitzresearch.wordpress.com/415-2/
*/
#define LTC_LAMBERT_MAT vec4(1.0, 0.0, 0.0, 1.0)
#define LTC_GGX_MAT(cos_theta, roughness) \
utility_tx_sample_lut(utility_tx, cos_theta, roughness, UTIL_LTC_MAT_LAYER)
/* Diffuse *clipped* sphere integral. */
float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor)
{

View File

@@ -27,19 +27,25 @@ vec4 closure_to_rgba(Closure cl)
vec3 refraction_light = vec3(0.0);
float shadow = 1.0;
float vPz = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
vec3 V = cameraVec(g_data.P);
float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
light_eval(g_diffuse_data,
g_reflection_data,
g_data.P,
g_data.Ng,
V,
vP_z,
0.01 /* TODO(fclem) thickness. */,
diffuse_light,
reflection_light,
shadow);
ClosureLightStack stack;
ClosureLight cl_diff;
cl_diff.N = g_diffuse_data.N;
cl_diff.ltc_mat = LTC_LAMBERT_MAT;
cl_diff.type = LIGHT_DIFFUSE;
stack.cl[0] = cl_diff;
ClosureLight cl_refl;
cl_refl.N = g_reflection_data.N;
cl_refl.ltc_mat = LTC_GGX_MAT(dot(g_reflection_data.N, V), g_reflection_data.roughness);
cl_refl.type = LIGHT_SPECULAR;
stack.cl[1] = cl_refl;
float thickness = 0.01; /* TODO(fclem) thickness. */
light_eval(stack, g_data.P, g_data.Ng, V, vPz, thickness);
vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0));
LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V);
@@ -49,9 +55,8 @@ vec4 closure_to_rgba(Closure cl)
vec4 out_color;
out_color.rgb = g_emission;
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light;
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light;
out_color.rgb += g_refraction_data.color * g_refraction_data.weight * refraction_light;
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * stack.cl[0].light_shadowed;
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * stack.cl[1].light_shadowed;
out_color.a = saturate(1.0 - avg(g_transmittance));
@@ -79,24 +84,28 @@ void main()
float thickness = nodetree_thickness();
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
vec3 refraction_light = vec3(0.0);
float shadow = 1.0;
float vPz = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
vec3 V = cameraVec(g_data.P);
float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
light_eval(g_diffuse_data,
g_reflection_data,
g_data.P,
g_data.Ng,
V,
vP_z,
thickness,
diffuse_light,
reflection_light,
shadow);
ClosureLightStack stack;
ClosureLight cl_diff;
cl_diff.N = g_diffuse_data.N;
cl_diff.ltc_mat = LTC_LAMBERT_MAT;
cl_diff.type = LIGHT_DIFFUSE;
stack.cl[0] = cl_diff;
ClosureLight cl_refl;
cl_refl.N = g_reflection_data.N;
cl_refl.ltc_mat = LTC_GGX_MAT(dot(g_reflection_data.N, V), g_reflection_data.roughness);
cl_refl.type = LIGHT_SPECULAR;
stack.cl[1] = cl_diff;
light_eval(stack, g_data.P, g_data.Ng, V, vPz, thickness);
vec3 diffuse_light = stack.cl[0].light_shadowed;
vec3 reflection_light = stack.cl[1].light_shadowed;
vec3 refraction_light = vec3(0.0);
vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0));
LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V);
@@ -136,6 +145,10 @@ void main()
cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0);
imageStore(rp_cryptomatte_img, out_texel, cryptomatte_output);
}
vec3 shadows = (stack.cl[0].light_shadowed + stack.cl[1].light_shadowed) /
(stack.cl[0].light_unshadowed + stack.cl[1].light_unshadowed);
output_renderpass_color(uniform_buf.render_pass.normal_id, vec4(out_normal, 1.0));
output_renderpass_color(uniform_buf.render_pass.position_id, vec4(g_data.P, 1.0));
output_renderpass_color(uniform_buf.render_pass.diffuse_color_id,
@@ -144,7 +157,7 @@ void main()
output_renderpass_color(uniform_buf.render_pass.specular_color_id, vec4(specular_color, 1.0));
output_renderpass_color(uniform_buf.render_pass.specular_light_id, vec4(specular_light, 1.0));
output_renderpass_color(uniform_buf.render_pass.emission_id, vec4(g_emission, 1.0));
output_renderpass_value(uniform_buf.render_pass.shadow_id, shadow);
output_renderpass_value(uniform_buf.render_pass.shadow_id, avg(shadows));
/** NOTE: AO is done on its own pass. */
#endif

View File

@@ -8,89 +8,35 @@
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
void light_eval_surfel(
ClosureDiffuse diffuse, vec3 P, vec3 Ng, float thickness, inout vec3 out_diffuse)
{
/* Dummy closure. Not used. */
ClosureReflection reflection;
reflection.N = vec3(1.0, 0.0, 0.0);
reflection.roughness = 0.0;
vec3 out_specular = vec3(0.0);
/* Dummy ltc mat parameters. Not used since we have no reflections. */
vec4 ltc_mat_dummy = utility_tx_sample(utility_tx, vec2(0.0), UTIL_LTC_MAT_LAYER);
vec3 V = Ng;
float vP_z = 0.0;
float out_shadow_unused;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
light_eval_ex(diffuse,
reflection,
true,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat_dummy,
l_idx,
out_diffuse,
out_specular,
out_shadow_unused);
}
LIGHT_FOREACH_END
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx)
{
light_eval_ex(diffuse,
reflection,
false,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat_dummy,
l_idx,
out_diffuse,
out_specular,
out_shadow_unused);
}
LIGHT_FOREACH_END
}
void main()
{
int index = int(gl_GlobalInvocationID.x);
if (index >= capture_info_buf.surfel_len) {
if (index >= int(capture_info_buf.surfel_len)) {
return;
}
Surfel surfel = surfel_buf[index];
ClosureDiffuse diffuse_data;
diffuse_data.N = surfel.normal;
/* TODO: These could be saved inside the surfel. */
diffuse_data.sss_radius = vec3(0.0);
diffuse_data.sss_id = 0u;
float thickness = 0.0;
ClosureLightStack stack;
stack.cl[0].N = surfel.normal;
stack.cl[0].ltc_mat = LTC_LAMBERT_MAT;
stack.cl[0].type = LIGHT_DIFFUSE;
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
light_eval_surfel(diffuse_data, surfel.position, surfel.normal, thickness, diffuse_light);
/* There is no view dependent effect as we evaluate everything using diffuse. */
vec3 V = surfel.normal;
vec3 Ng = surfel.normal;
vec3 P = surfel.position;
light_eval(stack, P, Ng, V);
if (capture_info_buf.capture_indirect) {
surfel_buf[index].radiance_direct.front.rgb += diffuse_light * surfel.albedo_front;
surfel_buf[index].radiance_direct.front.rgb += stack.cl[0].light_shadowed *
surfel.albedo_front;
}
diffuse_data.N = -surfel.normal;
diffuse_light = vec3(0.0);
reflection_light = vec3(0.0);
light_eval_surfel(diffuse_data, surfel.position, -surfel.normal, thickness, diffuse_light);
stack.cl[0].N = -surfel.normal;
light_eval(stack, P, Ng, V);
if (capture_info_buf.capture_indirect) {
surfel_buf[index].radiance_direct.back.rgb += diffuse_light * surfel.albedo_back;
surfel_buf[index].radiance_direct.back.rgb += stack.cl[0].light_shadowed * surfel.albedo_back;
}
}

View File

@@ -59,30 +59,29 @@ float volume_phase_function_isotropic()
return 1.0 / (4.0 * M_PI);
}
float volume_phase_function(vec3 v, vec3 l, float g)
float volume_phase_function(vec3 V, vec3 L, float g)
{
/* Henyey-Greenstein. */
float cos_theta = dot(v, l);
float cos_theta = dot(V, L);
g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3);
float sqr_g = g * g;
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
}
vec3 volume_light(LightData ld, vec3 L, float l_dist)
vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
{
float power = 1.0;
if (ld.type != LIGHT_SUN) {
float volume_radius_squared = ld.radius_squared;
if (!is_directional) {
float volume_radius_squared = light.radius_squared;
float light_clamp = uniform_buf.volumes.light_clamp;
if (light_clamp != 0.0) {
/* 0.0 light clamp means it's disabled. */
float max_power = max_v3(ld.color) * ld.volume_power;
float max_power = max_v3(light.color) * light.power[LIGHT_VOLUME];
if (max_power > 0.0) {
/* The limit of the power attenuation function when the distance to the light goes to 0 is
* `2 / r^2` where r is the light radius. We need to find the right radius that emits at
* most the volume light upper bound. Inverting the function we get: */
float min_radius_squared = 1.0f / (0.5f * light_clamp / max_power);
float min_radius_squared = 1.0 / (0.5 * light_clamp / max_power);
/* Square it here to avoid a multiplication inside the shader. */
volume_radius_squared = max(volume_radius_squared, min_radius_squared);
}
@@ -93,57 +92,55 @@ vec3 volume_light(LightData ld, vec3 L, float l_dist)
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
* http://www.cemyuksel.com/research/pointlightattenuation/
*/
float d = l_dist;
float d = lv.dist;
float d_sqr = sqr(d);
float r_sqr = volume_radius_squared;
/* Using reformulation that has better numerical precision. */
power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr));
if (ld.type == LIGHT_RECT || ld.type == LIGHT_ELLIPSE) {
if (light.type == LIGHT_RECT || light.type == LIGHT_ELLIPSE) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(ld._back, L));
power *= saturate(dot(light._back, lv.L));
}
}
return ld.color * ld.volume_power * power;
return light.color * light.power[LIGHT_VOLUME] * power;
}
#define VOLUMETRIC_SHADOW_MAX_STEP 128.0
vec3 volume_shadow(LightData ld, vec3 ray_wpos, vec3 L, float l_dist, sampler3D extinction_tx)
vec3 volume_shadow(
LightData ld, const bool is_directional, vec3 P, LightVector lv, sampler3D extinction_tx)
{
#if defined(VOLUME_SHADOW)
if (uniform_buf.volumes.shadow_steps == 0) {
return vec3(1.0);
}
vec4 l_vector = vec4(L * l_dist, l_dist);
/* Heterogeneous volume shadows. */
float dd = l_vector.w / uniform_buf.volumes.shadow_steps;
L = l_vector.xyz / uniform_buf.volumes.shadow_steps;
float dd = lv.dist / uniform_buf.volumes.shadow_steps;
vec3 L = lv.L * lv.dist / uniform_buf.volumes.shadow_steps;
if (is_sun_light(ld.type)) {
if (is_directional) {
/* For sun light we scan the whole frustum. So we need to get the correct endpoints. */
vec3 ndcP = project_point(ProjectionMatrix, transform_point(ViewMatrix, ray_wpos));
vec3 ndcL = project_point(ProjectionMatrix,
transform_point(ViewMatrix, ray_wpos + l_vector.xyz)) -
vec3 ndcP = project_point(ProjectionMatrix, transform_point(ViewMatrix, P));
vec3 ndcL = project_point(ProjectionMatrix, transform_point(ViewMatrix, P + lv.L * lv.dist)) -
ndcP;
vec3 frustum_isect = ndcP + ndcL * line_unit_box_intersect_dist_safe(ndcP, ndcL);
vec4 L_hom = ViewMatrixInverse * (ProjectionMatrixInverse * vec4(frustum_isect, 1.0));
L = (L_hom.xyz / L_hom.w) - ray_wpos;
L = (L_hom.xyz / L_hom.w) - P;
L /= uniform_buf.volumes.shadow_steps;
dd = length(L);
}
/* TODO use shadow maps instead. */
vec3 shadow = vec3(1.0);
for (float s = 1.0; s < VOLUMETRIC_SHADOW_MAX_STEP && s <= uniform_buf.volumes.shadow_steps;
s += 1.0)
for (float t = 1.0; t < VOLUMETRIC_SHADOW_MAX_STEP && t <= uniform_buf.volumes.shadow_steps;
t += 1.0)
{
vec3 pos = ray_wpos + L * s;
vec3 pos = P + L * t;
vec3 ndc = project_point(ProjectionMatrix, transform_point(ViewMatrix, pos));
vec3 volume_co = ndc_to_volume(ndc * 0.5 + 0.5);

View File

@@ -26,27 +26,30 @@ vec3 volume_scatter_light_eval(
{
LightData light = light_buf[l_idx];
if (light.volume_power == 0.0) {
if (light.power[LIGHT_VOLUME] == 0.0) {
return vec3(0);
}
vec3 L;
float l_dist;
light_shape_vector_get(light, P, L, l_dist);
LightVector lv = light_shape_vector_get(light, is_directional, P);
float visibility = light_attenuation(light, L, l_dist);
if (light.tilemap_index != LIGHT_NO_SHADOW && (visibility > 0.0)) {
ShadowEvalResult samp = shadow_sample(
is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P);
visibility *= samp.surface_light_visibilty;
}
if (visibility < 1e-4) {
float attenuation = light_attenuation_volume(light, is_directional, lv);
if (attenuation < LIGHT_ATTENUATION_THRESHOLD) {
return vec3(0);
}
vec3 Li = volume_light(light, L, l_dist) * volume_shadow(light, P, L, l_dist, extinction_tx);
return Li * visibility * volume_phase_function(-V, L, s_anisotropy);
float visibility = attenuation;
if (light.tilemap_index != LIGHT_NO_SHADOW) {
visibility *= shadow_sample(is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P)
.surface_light_visibilty;
}
if (visibility < LIGHT_ATTENUATION_THRESHOLD) {
return vec3(0);
}
vec3 Li = volume_light(light, is_directional, lv) *
volume_shadow(light, is_directional, P, lv, extinction_tx);
return Li * visibility * volume_phase_function(-V, lv.L, s_anisotropy);
}
#endif

View File

@@ -25,6 +25,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light)
.image_out(3, GPU_RGBA16F, "direct_reflect_img")
.image_out(4, GPU_RGBA16F, "direct_refract_img")
.define("SSS_TRANSMITTANCE")
.define("LIGHT_CLOSURE_EVAL_COUNT", "2")
.additional_info("eevee_shared",
"eevee_gbuffer_data",
"eevee_utility_texture",

View File

@@ -73,9 +73,11 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_common)
GPU_SHADER_CREATE_INFO(eevee_surfel_light)
.define("SURFEL_LIGHT")
.define("LIGHT_ITER_FORCE_NO_CULLING")
.local_group_size(SURFEL_GROUP_SIZE)
.additional_info("eevee_shared",
"draw_view",
"eevee_global_ubo",
"eevee_utility_texture",
"eevee_surfel_common",
"eevee_light_data",

View File

@@ -164,6 +164,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward)
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
.fragment_source("eevee_surf_forward_frag.glsl")
.define("LIGHT_CLOSURE_EVAL_COUNT", "2")
.additional_info("eevee_global_ubo",
"eevee_light_data",
"eevee_lightprobe_data",