2017-06-28 16:31:25 +02:00
|
|
|
|
2018-05-04 14:39:05 +02:00
|
|
|
uniform sampler2DArray shadowCubeTexture;
|
|
|
|
|
uniform sampler2DArray shadowCascadeTexture;
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2017-10-24 14:49:00 +02:00
|
|
|
#define LAMPS_LIB
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
layout(std140) uniform shadow_block
|
|
|
|
|
{
|
|
|
|
|
ShadowData shadows_data[MAX_SHADOW];
|
|
|
|
|
ShadowCubeData shadows_cube_data[MAX_SHADOW_CUBE];
|
|
|
|
|
ShadowCascadeData shadows_cascade_data[MAX_SHADOW_CASCADE];
|
2017-06-28 16:31:25 +02:00
|
|
|
};
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
layout(std140) uniform light_block
|
|
|
|
|
{
|
|
|
|
|
LightData lights_data[MAX_LIGHT];
|
2017-06-28 16:31:25 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* type */
|
2019-04-17 06:17:24 +02:00
|
|
|
#define POINT 0.0
|
|
|
|
|
#define SUN 1.0
|
|
|
|
|
#define SPOT 2.0
|
|
|
|
|
#define AREA_RECT 4.0
|
2019-02-27 12:02:02 +11:00
|
|
|
/* Used to define the area light shape, doesn't directly correspond to a Blender light type. */
|
Cycles/Eevee: Implement disk and ellipse shapes for area lamps
The implementation is pretty straightforward.
In Cycles, sampling the shapes is currently done w.r.t. area instead of solid angle.
There is a paper on solid angle sampling for disks [1], but the described algorithm is based on
simply sampling the enclosing square and rejecting samples outside of the disk, which is not exactly
great for Cycles' RNG (we'd need to setup a LCG for the repeated sampling) and for GPU divergence.
Even worse, the algorithm is only defined for disks. For ellipses, the basic idea still works, but a
way to analytically calculate the solid angle is required. This is technically possible [2], but the
calculation is extremely complex and still requires a lookup table for the Heuman Lambda function.
Therefore, I've decided to not implement that for now, we could still look into it later on.
In Eevee, the code uses the existing ltc_evaluate_disk to implement the lighting calculations.
[1]: "Solid Angle Sampling of Disk and Cylinder Lights"
[2]: "Analytical solution for the solid angle subtended at any point by an ellipse via a point source radiation vector potential"
Reviewers: sergey, brecht, fclem
Differential Revision: https://developer.blender.org/D3171
2018-05-24 03:50:16 +02:00
|
|
|
#define AREA_ELLIPSE 100.0
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2018-05-04 11:22:21 +02:00
|
|
|
#if defined(SHADOW_VSM)
|
2019-04-17 06:17:24 +02:00
|
|
|
# define ShadowSample vec2
|
|
|
|
|
# define sample_cube(vec, id) texture_octahedron(shadowCubeTexture, vec4(vec, id)).rg
|
|
|
|
|
# define sample_cascade(vec, id) texture(shadowCascadeTexture, vec3(vec, id)).rg
|
2018-05-04 11:22:21 +02:00
|
|
|
#elif defined(SHADOW_ESM)
|
2019-04-17 06:17:24 +02:00
|
|
|
# define ShadowSample float
|
|
|
|
|
# define sample_cube(vec, id) texture_octahedron(shadowCubeTexture, vec4(vec, id)).r
|
|
|
|
|
# define sample_cascade(vec, id) texture(shadowCascadeTexture, vec3(vec, id)).r
|
2018-05-04 11:22:21 +02:00
|
|
|
#else
|
2019-04-17 06:17:24 +02:00
|
|
|
# define ShadowSample float
|
|
|
|
|
# define sample_cube(vec, id) texture_octahedron(shadowCubeTexture, vec4(vec, id)).r
|
|
|
|
|
# define sample_cascade(vec, id) texture(shadowCascadeTexture, vec3(vec, id)).r
|
2018-05-04 11:22:21 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(SHADOW_VSM)
|
2019-04-17 06:17:24 +02:00
|
|
|
# define get_depth_delta(dist, s) (dist - s.x)
|
2018-05-04 11:22:21 +02:00
|
|
|
#else
|
2019-04-17 06:17:24 +02:00
|
|
|
# define get_depth_delta(dist, s) (dist - s)
|
2018-05-04 11:22:21 +02:00
|
|
|
#endif
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
/* ----------------------- Shadow tests ---------------------- */
|
|
|
|
|
/* ----------------------------------------------------------- */
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2018-05-04 11:22:21 +02:00
|
|
|
#if defined(SHADOW_VSM)
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2018-05-04 11:22:21 +02:00
|
|
|
float shadow_test(ShadowSample moments, float dist, ShadowData sd)
|
2017-09-05 21:02:17 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float p = 0.0;
|
2017-09-01 18:39:39 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (dist <= moments.x) {
|
|
|
|
|
p = 1.0;
|
|
|
|
|
}
|
2017-09-01 18:39:39 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
float variance = moments.y - (moments.x * moments.x);
|
|
|
|
|
variance = max(variance, sd.sh_bias / 10.0);
|
2017-09-01 18:39:39 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
float d = moments.x - dist;
|
|
|
|
|
float p_max = variance / (variance + d * d);
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */
|
|
|
|
|
p_max = clamp((p_max - sd.sh_bleed) / (1.0 - sd.sh_bleed), 0.0, 1.0);
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return max(p, p_max);
|
2017-09-05 21:02:17 +02:00
|
|
|
}
|
2017-09-01 18:39:39 +02:00
|
|
|
|
2018-05-04 11:22:21 +02:00
|
|
|
#elif defined(SHADOW_ESM)
|
|
|
|
|
|
|
|
|
|
float shadow_test(ShadowSample z, float dist, ShadowData sd)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return saturate(exp(sd.sh_exp * (z - dist + sd.sh_bias)));
|
2018-05-04 11:22:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
float shadow_test(ShadowSample z, float dist, ShadowData sd)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return step(0, z - dist + sd.sh_bias);
|
2018-05-04 11:22:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
2017-09-01 18:39:39 +02:00
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
/* ----------------------- Shadow types ---------------------- */
|
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
float shadow_cubemap(ShadowData sd, ShadowCubeData scd, float texid, vec3 W)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
vec3 cubevec = W - scd.position.xyz;
|
|
|
|
|
float dist = length(cubevec);
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
cubevec /= dist;
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
ShadowSample s = sample_cube(cubevec, texid);
|
|
|
|
|
return shadow_test(s, dist, sd);
|
2017-06-28 16:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-07 15:31:11 +02:00
|
|
|
float evaluate_cascade(ShadowData sd, mat4 shadowmat, vec3 W, float range, float texid)
|
2017-06-28 16:31:25 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
vec4 shpos = shadowmat * vec4(W, 1.0);
|
|
|
|
|
float dist = shpos.z * range;
|
|
|
|
|
|
|
|
|
|
ShadowSample s = sample_cascade(shpos.xy, texid);
|
|
|
|
|
float vis = shadow_test(s, dist, sd);
|
|
|
|
|
|
|
|
|
|
/* If fragment is out of shadowmap range, do not occlude */
|
|
|
|
|
if (shpos.z < 1.0 && shpos.z > 0.0) {
|
|
|
|
|
return vis;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return 1.0;
|
|
|
|
|
}
|
2017-06-28 16:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-19 11:02:36 -03:00
|
|
|
float shadow_cascade(ShadowData sd, int scd_id, float texid, vec3 W)
|
2017-09-07 15:31:11 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
vec4 view_z = vec4(dot(W - cameraPos, cameraForward));
|
|
|
|
|
vec4 weights = smoothstep(shadows_cascade_data[scd_id].split_end_distances,
|
|
|
|
|
shadows_cascade_data[scd_id].split_start_distances.yzwx,
|
|
|
|
|
view_z);
|
|
|
|
|
|
|
|
|
|
weights.yzw -= weights.xyz;
|
|
|
|
|
|
|
|
|
|
vec4 vis = vec4(1.0);
|
|
|
|
|
float range = abs(sd.sh_far - sd.sh_near); /* Same factor as in get_cascade_world_distance(). */
|
|
|
|
|
|
|
|
|
|
/* Branching using (weights > 0.0) is reaally slooow on intel so avoid it for now. */
|
|
|
|
|
/* TODO OPTI: Only do 2 samples and blend. */
|
|
|
|
|
vis.x = evaluate_cascade(sd, shadows_cascade_data[scd_id].shadowmat[0], W, range, texid + 0);
|
|
|
|
|
vis.y = evaluate_cascade(sd, shadows_cascade_data[scd_id].shadowmat[1], W, range, texid + 1);
|
|
|
|
|
vis.z = evaluate_cascade(sd, shadows_cascade_data[scd_id].shadowmat[2], W, range, texid + 2);
|
|
|
|
|
vis.w = evaluate_cascade(sd, shadows_cascade_data[scd_id].shadowmat[3], W, range, texid + 3);
|
|
|
|
|
|
|
|
|
|
float weight_sum = dot(vec4(1.0), weights);
|
|
|
|
|
if (weight_sum > 0.9999) {
|
|
|
|
|
float vis_sum = dot(vec4(1.0), vis * weights);
|
|
|
|
|
return vis_sum / weight_sum;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
float vis_sum = dot(vec4(1.0), vis * step(0.001, weights));
|
|
|
|
|
return mix(1.0, vis_sum, weight_sum);
|
|
|
|
|
}
|
2017-09-07 15:31:11 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-05 21:02:17 +02:00
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
/* --------------------- Light Functions --------------------- */
|
|
|
|
|
/* ----------------------------------------------------------- */
|
2018-11-15 14:51:51 +01:00
|
|
|
|
2018-11-15 14:44:45 +01:00
|
|
|
/* From Frostbite PBR Course
|
|
|
|
|
* Distance based attenuation
|
|
|
|
|
* http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
|
|
|
|
|
float distance_attenuation(float dist_sqr, float inv_sqr_influence)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float factor = dist_sqr * inv_sqr_influence;
|
|
|
|
|
float fac = saturate(1.0 - factor * factor);
|
|
|
|
|
return fac * fac;
|
2018-11-15 14:44:45 +01:00
|
|
|
}
|
2017-09-05 21:02:17 +02:00
|
|
|
|
2018-11-15 14:51:51 +01:00
|
|
|
float spot_attenuation(LightData ld, vec3 l_vector)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float z = dot(ld.l_forward, l_vector.xyz);
|
|
|
|
|
vec3 lL = l_vector.xyz / z;
|
|
|
|
|
float x = dot(ld.l_right, lL) / ld.l_sizex;
|
|
|
|
|
float y = dot(ld.l_up, lL) / ld.l_sizey;
|
|
|
|
|
float ellipse = inversesqrt(1.0 + x * x + y * y);
|
|
|
|
|
float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.l_spot_size) / ld.l_spot_blend);
|
|
|
|
|
return spotmask;
|
2018-11-15 14:51:51 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
float light_visibility(LightData ld,
|
|
|
|
|
vec3 W,
|
2017-10-08 18:23:45 +02:00
|
|
|
#ifndef VOLUMETRICS
|
|
|
|
|
vec3 viewPosition,
|
|
|
|
|
vec3 viewNormal,
|
|
|
|
|
#endif
|
|
|
|
|
vec4 l_vector)
|
2017-06-28 16:31:25 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float vis = 1.0;
|
|
|
|
|
|
|
|
|
|
if (ld.l_type == SPOT) {
|
|
|
|
|
vis *= spot_attenuation(ld, l_vector.xyz);
|
|
|
|
|
}
|
|
|
|
|
if (ld.l_type >= SPOT) {
|
|
|
|
|
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
|
|
|
|
|
}
|
|
|
|
|
if (ld.l_type != SUN) {
|
|
|
|
|
vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence);
|
|
|
|
|
}
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2017-07-05 18:28:48 +02:00
|
|
|
#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
|
2019-04-17 06:17:24 +02:00
|
|
|
/* shadowing */
|
|
|
|
|
if (ld.l_shadowid >= 0.0 && vis > 0.001) {
|
|
|
|
|
ShadowData data = shadows_data[int(ld.l_shadowid)];
|
|
|
|
|
|
|
|
|
|
if (ld.l_type == SUN) {
|
|
|
|
|
vis *= shadow_cascade(data, int(data.sh_data_start), data.sh_tex_start, W);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
vis *= shadow_cubemap(
|
|
|
|
|
data, shadows_cube_data[int(data.sh_data_start)], data.sh_tex_start, W);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ifndef VOLUMETRICS
|
|
|
|
|
/* Only compute if not already in shadow. */
|
|
|
|
|
if (data.sh_contact_dist > 0.0) {
|
|
|
|
|
vec4 L = (ld.l_type != SUN) ? l_vector : vec4(-ld.l_forward, 1.0);
|
|
|
|
|
float trace_distance = (ld.l_type != SUN) ? min(data.sh_contact_dist, l_vector.w) :
|
|
|
|
|
data.sh_contact_dist;
|
|
|
|
|
|
|
|
|
|
vec3 T, B;
|
|
|
|
|
make_orthonormal_basis(L.xyz / L.w, T, B);
|
|
|
|
|
|
|
|
|
|
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
|
|
|
|
|
rand.zw *= fast_sqrt(rand.y) * data.sh_contact_spread;
|
|
|
|
|
|
|
|
|
|
/* We use the full l_vector.xyz so that the spread is minimize
|
|
|
|
|
* if the shading point is further away from the light source */
|
|
|
|
|
vec3 ray_dir = L.xyz + T * rand.z + B * rand.w;
|
|
|
|
|
ray_dir = transform_direction(ViewMatrix, ray_dir);
|
|
|
|
|
ray_dir = normalize(ray_dir);
|
|
|
|
|
|
|
|
|
|
vec3 ray_ori = viewPosition;
|
|
|
|
|
|
|
|
|
|
if (dot(viewNormal, ray_dir) <= 0.0) {
|
|
|
|
|
return vis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float bias = 0.5; /* Constant Bias */
|
|
|
|
|
bias += 1.0 - abs(dot(viewNormal, ray_dir)); /* Angle dependent bias */
|
|
|
|
|
bias *= gl_FrontFacing ? data.sh_contact_offset : -data.sh_contact_offset;
|
|
|
|
|
|
|
|
|
|
vec3 nor_bias = viewNormal * bias;
|
|
|
|
|
ray_ori += nor_bias;
|
|
|
|
|
|
|
|
|
|
ray_dir *= trace_distance;
|
|
|
|
|
ray_dir -= nor_bias;
|
|
|
|
|
|
|
|
|
|
vec3 hit_pos = raycast(
|
|
|
|
|
-1, ray_ori, ray_dir, data.sh_contact_thickness, rand.x, 0.1, 0.001, false);
|
|
|
|
|
|
|
|
|
|
if (hit_pos.z > 0.0) {
|
|
|
|
|
hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z);
|
|
|
|
|
float hit_dist = distance(viewPosition, hit_pos);
|
|
|
|
|
float dist_ratio = hit_dist / trace_distance;
|
|
|
|
|
return vis * saturate(dist_ratio * dist_ratio * dist_ratio);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
# endif
|
|
|
|
|
}
|
2017-07-04 18:02:13 +02:00
|
|
|
#endif
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return vis;
|
2017-06-28 16:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-09 20:48:19 +02:00
|
|
|
#ifdef USE_LTC
|
2017-06-29 01:20:35 +02:00
|
|
|
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
|
2017-06-28 16:31:25 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ld.l_type == AREA_RECT) {
|
|
|
|
|
vec3 corners[4];
|
|
|
|
|
corners[0] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey);
|
|
|
|
|
corners[1] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey);
|
|
|
|
|
corners[2] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey);
|
|
|
|
|
corners[3] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey);
|
|
|
|
|
|
|
|
|
|
return ltc_evaluate_quad(corners, N);
|
|
|
|
|
}
|
|
|
|
|
else if (ld.l_type == AREA_ELLIPSE) {
|
|
|
|
|
vec3 points[3];
|
|
|
|
|
points[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey;
|
|
|
|
|
points[1] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey;
|
|
|
|
|
points[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey;
|
|
|
|
|
|
|
|
|
|
return ltc_evaluate_disk(N, V, mat3(1.0), points);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
float radius = ld.l_radius;
|
|
|
|
|
radius /= (ld.l_type == SUN) ? 1.0 : l_vector.w;
|
|
|
|
|
vec3 L = (ld.l_type == SUN) ? -ld.l_forward : (l_vector.xyz / l_vector.w);
|
|
|
|
|
|
|
|
|
|
return ltc_evaluate_disk_simple(radius, dot(N, L));
|
|
|
|
|
}
|
2017-06-28 16:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-09 20:48:19 +02:00
|
|
|
float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector)
|
2017-06-28 16:31:25 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ld.l_type == AREA_RECT) {
|
|
|
|
|
vec3 corners[4];
|
|
|
|
|
corners[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey;
|
|
|
|
|
corners[1] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey;
|
|
|
|
|
corners[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey;
|
|
|
|
|
corners[3] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey;
|
|
|
|
|
|
|
|
|
|
ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
|
|
|
|
|
|
|
|
|
|
return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bool is_ellipse = (ld.l_type == AREA_ELLIPSE);
|
|
|
|
|
float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius;
|
|
|
|
|
float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius;
|
|
|
|
|
|
|
|
|
|
vec3 L = (ld.l_type == SUN) ? -ld.l_forward : l_vector.xyz;
|
|
|
|
|
vec3 Px = ld.l_right;
|
|
|
|
|
vec3 Py = ld.l_up;
|
|
|
|
|
|
|
|
|
|
if (ld.l_type == SPOT || ld.l_type == POINT) {
|
|
|
|
|
make_orthonormal_basis(l_vector.xyz / l_vector.w, Px, Py);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 points[3];
|
|
|
|
|
points[0] = (L + Px * -radius_x) + Py * -radius_y;
|
|
|
|
|
points[1] = (L + Px * radius_x) + Py * -radius_y;
|
|
|
|
|
points[2] = (L + Px * radius_x) + Py * radius_y;
|
|
|
|
|
|
|
|
|
|
return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points);
|
|
|
|
|
}
|
2017-06-28 16:31:25 +02:00
|
|
|
}
|
2018-06-09 20:48:19 +02:00
|
|
|
#endif
|
2017-06-28 16:31:25 +02:00
|
|
|
|
2017-11-22 04:51:21 +01:00
|
|
|
#define MAX_SSS_SAMPLES 65
|
|
|
|
|
#define SSS_LUT_SIZE 64.0
|
|
|
|
|
#define SSS_LUT_SCALE ((SSS_LUT_SIZE - 1.0) / float(SSS_LUT_SIZE))
|
|
|
|
|
#define SSS_LUT_BIAS (0.5 / float(SSS_LUT_SIZE))
|
2018-08-02 18:03:47 +02:00
|
|
|
|
|
|
|
|
#ifdef USE_TRANSLUCENCY
|
2019-04-17 06:17:24 +02:00
|
|
|
layout(std140) uniform sssProfile
|
|
|
|
|
{
|
|
|
|
|
vec4 kernel[MAX_SSS_SAMPLES];
|
|
|
|
|
vec4 radii_max_radius;
|
|
|
|
|
int sss_samples;
|
2017-11-22 04:51:21 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uniform sampler1D sssTexProfile;
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
vec3 sss_profile(float s)
|
|
|
|
|
{
|
|
|
|
|
s /= radii_max_radius.w;
|
|
|
|
|
return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb;
|
2017-11-22 04:51:21 +01:00
|
|
|
}
|
2018-08-02 18:03:47 +02:00
|
|
|
#endif
|
2017-11-22 04:51:21 +01:00
|
|
|
|
|
|
|
|
vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
|
|
|
|
|
{
|
2017-11-24 06:15:42 +01:00
|
|
|
#if !defined(USE_TRANSLUCENCY) || defined(VOLUMETRICS)
|
2019-04-17 06:17:24 +02:00
|
|
|
return vec3(0.0);
|
2018-01-15 13:49:50 -02:00
|
|
|
#else
|
2019-04-17 06:17:24 +02:00
|
|
|
vec3 vis = vec3(1.0);
|
|
|
|
|
|
|
|
|
|
if (ld.l_type == SPOT) {
|
|
|
|
|
vis *= spot_attenuation(ld, l_vector.xyz);
|
|
|
|
|
}
|
|
|
|
|
if (ld.l_type >= SPOT) {
|
|
|
|
|
vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
|
|
|
|
|
}
|
|
|
|
|
if (ld.l_type != SUN) {
|
|
|
|
|
vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only shadowed light can produce translucency */
|
|
|
|
|
if (ld.l_shadowid >= 0.0 && vis.x > 0.001) {
|
|
|
|
|
ShadowData data = shadows_data[int(ld.l_shadowid)];
|
|
|
|
|
float delta;
|
|
|
|
|
|
|
|
|
|
vec4 L = (ld.l_type != SUN) ? l_vector : vec4(-ld.l_forward, 1.0);
|
|
|
|
|
|
|
|
|
|
vec3 T, B;
|
|
|
|
|
make_orthonormal_basis(L.xyz / L.w, T, B);
|
|
|
|
|
|
|
|
|
|
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
|
|
|
|
|
rand.zw *= fast_sqrt(rand.y) * data.sh_blur;
|
|
|
|
|
|
|
|
|
|
/* We use the full l_vector.xyz so that the spread is minimize
|
|
|
|
|
* if the shading point is further away from the light source */
|
|
|
|
|
W = W + T * rand.z + B * rand.w;
|
|
|
|
|
|
|
|
|
|
if (ld.l_type == SUN) {
|
|
|
|
|
int scd_id = int(data.sh_data_start);
|
|
|
|
|
vec4 view_z = vec4(dot(W - cameraPos, cameraForward));
|
|
|
|
|
|
|
|
|
|
vec4 weights = step(shadows_cascade_data[scd_id].split_end_distances, view_z);
|
|
|
|
|
float id = abs(4.0 - dot(weights, weights));
|
|
|
|
|
|
|
|
|
|
if (id > 3.0) {
|
|
|
|
|
return vec3(0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float range = abs(data.sh_far -
|
|
|
|
|
data.sh_near); /* Same factor as in get_cascade_world_distance(). */
|
|
|
|
|
|
|
|
|
|
vec4 shpos = shadows_cascade_data[scd_id].shadowmat[int(id)] * vec4(W, 1.0);
|
|
|
|
|
float dist = shpos.z * range;
|
|
|
|
|
|
|
|
|
|
if (shpos.z > 1.0 || shpos.z < 0.0) {
|
|
|
|
|
return vec3(0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShadowSample s = sample_cascade(shpos.xy, data.sh_tex_start + id);
|
|
|
|
|
delta = get_depth_delta(dist, s);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
vec3 cubevec = W - shadows_cube_data[int(data.sh_data_start)].position.xyz;
|
|
|
|
|
float dist = length(cubevec);
|
|
|
|
|
cubevec /= dist;
|
|
|
|
|
|
|
|
|
|
ShadowSample s = sample_cube(cubevec, data.sh_tex_start);
|
|
|
|
|
delta = get_depth_delta(dist, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* XXX : Removing Area Power. */
|
|
|
|
|
/* TODO : put this out of the shader. */
|
|
|
|
|
float falloff;
|
|
|
|
|
if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
|
|
|
|
|
vis *= (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0);
|
|
|
|
|
if (ld.l_type == AREA_ELLIPSE) {
|
|
|
|
|
vis *= M_PI * 0.25;
|
|
|
|
|
}
|
|
|
|
|
vis *= 0.3 * 20.0 *
|
|
|
|
|
max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */
|
|
|
|
|
vis /= (l_vector.w * l_vector.w);
|
|
|
|
|
falloff = dot(N, l_vector.xyz / l_vector.w);
|
|
|
|
|
}
|
|
|
|
|
else if (ld.l_type == SUN) {
|
|
|
|
|
vis /= 1.0f + (ld.l_radius * ld.l_radius * 0.5f);
|
|
|
|
|
vis *= ld.l_radius * ld.l_radius * M_PI; /* Removing area light power*/
|
|
|
|
|
vis *= M_2PI * 0.78; /* Matching cycles with point light. */
|
|
|
|
|
vis *= 0.082; /* XXX ad hoc, empirical */
|
|
|
|
|
falloff = dot(N, -ld.l_forward);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
vis *= (4.0 * ld.l_radius * ld.l_radius) * (1.0 / 10.0);
|
|
|
|
|
vis *= 1.5; /* XXX ad hoc, empirical */
|
|
|
|
|
vis /= (l_vector.w * l_vector.w);
|
|
|
|
|
falloff = dot(N, l_vector.xyz / l_vector.w);
|
|
|
|
|
}
|
|
|
|
|
// vis *= M_1_PI; /* Normalize */
|
|
|
|
|
|
|
|
|
|
/* Applying profile */
|
|
|
|
|
vis *= sss_profile(abs(delta) / scale);
|
|
|
|
|
|
|
|
|
|
/* No transmittance at grazing angle (hide artifacts) */
|
|
|
|
|
vis *= saturate(falloff * 2.0);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
vis = vec3(0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return vis;
|
2017-11-24 06:15:42 +01:00
|
|
|
#endif
|
2017-11-22 04:51:21 +01:00
|
|
|
}
|