This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/draw/engines/eevee/shaders/lights_lib.glsl

456 lines
14 KiB
GLSL
Raw Normal View History

uniform sampler2DArray shadowCubeTexture;
uniform sampler2DArray shadowCascadeTexture;
#define LAMPS_LIB
layout(std140) uniform shadow_block
{
ShadowData shadows_data[MAX_SHADOW];
ShadowCubeData shadows_cube_data[MAX_SHADOW_CUBE];
ShadowCascadeData shadows_cascade_data[MAX_SHADOW_CASCADE];
};
layout(std140) uniform light_block
{
LightData lights_data[MAX_LIGHT];
};
/* type */
#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. */
#define AREA_ELLIPSE 100.0
#if defined(SHADOW_VSM)
# 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
#elif defined(SHADOW_ESM)
# 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
#else
# 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
#endif
#if defined(SHADOW_VSM)
# define get_depth_delta(dist, s) (dist - s.x)
#else
# define get_depth_delta(dist, s) (dist - s)
#endif
/* ----------------------------------------------------------- */
/* ----------------------- Shadow tests ---------------------- */
/* ----------------------------------------------------------- */
#if defined(SHADOW_VSM)
float shadow_test(ShadowSample moments, float dist, ShadowData sd)
{
float p = 0.0;
if (dist <= moments.x) {
p = 1.0;
}
float variance = moments.y - (moments.x * moments.x);
variance = max(variance, sd.sh_bias / 10.0);
float d = moments.x - dist;
float p_max = variance / (variance + d * d);
/* 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);
return max(p, p_max);
}
#elif defined(SHADOW_ESM)
float shadow_test(ShadowSample z, float dist, ShadowData sd)
{
return saturate(exp(sd.sh_exp * (z - dist + sd.sh_bias)));
}
#else
float shadow_test(ShadowSample z, float dist, ShadowData sd)
{
return step(0, z - dist + sd.sh_bias);
}
#endif
/* ----------------------------------------------------------- */
/* ----------------------- Shadow types ---------------------- */
/* ----------------------------------------------------------- */
float shadow_cubemap(ShadowData sd, ShadowCubeData scd, float texid, vec3 W)
{
vec3 cubevec = W - scd.position.xyz;
float dist = length(cubevec);
cubevec /= dist;
ShadowSample s = sample_cube(cubevec, texid);
return shadow_test(s, dist, sd);
}
float evaluate_cascade(ShadowData sd, mat4 shadowmat, vec3 W, float range, float texid)
{
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;
}
}
float shadow_cascade(ShadowData sd, int scd_id, float texid, vec3 W)
{
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);
}
}
/* ----------------------------------------------------------- */
/* --------------------- Light Functions --------------------- */
/* ----------------------------------------------------------- */
2018-11-15 14:51:51 +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)
{
float factor = dist_sqr * inv_sqr_influence;
float fac = saturate(1.0 - factor * factor);
return fac * fac;
}
2018-11-15 14:51:51 +01:00
float spot_attenuation(LightData ld, vec3 l_vector)
{
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
}
float light_visibility(LightData ld,
vec3 W,
#ifndef VOLUMETRICS
vec3 viewPosition,
vec3 viewNormal,
#endif
vec4 l_vector)
{
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-07-05 18:28:48 +02:00
#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
/* 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
}
#endif
return vis;
}
#ifdef USE_LTC
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
{
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));
}
}
float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector)
{
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);
}
}
#endif
Eevee : SSS : Add Translucency support. This adds the possibility to simulate things like red ears with strong backlight or material with high scattering distances. To enable it you need to turn on the "Subsurface Translucency" option in the "Options" tab of the Material Panel (and of course to have "regular" SSS enabled in both render settings and material options). Since the effect is adding another overhead I prefer to make it optional. But this is open to discussion. Be aware that the effect only works for direct lights (so no indirect/world lighting) that have shadowmaps, and is affected by the "softness" of the shadowmap and resolution. Technical notes: This is inspired by http://www.iryoku.com/translucency/ but goes a bit beyond that. We do not use a sum of gaussian to apply in regards to the object thickness but we precompute a 1D kernel texture. This texture stores the light transmited to a point at the back of an infinite slab of material of variying thickness. We make the assumption that the slab is perpendicular to the light so that no fresnel or diffusion term is taken into account. The light is considered constant. If the setup is similar to the one assume during the profile baking, the realtime render matches cycles reference. Due to these assumptions the computed transmitted light is in most cases too bright for curvy objects. Finally we jitter the shadow map sample per pixel so we can simulate dispersion inside the medium. Radius of the dispersion is in world space and derived by from the "soft" shadowmap parameter. Idea for this come from this presentation http://www.iryoku.com/stare-into-the-future (slide 164).
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))
#ifdef USE_TRANSLUCENCY
layout(std140) uniform sssProfile
{
vec4 kernel[MAX_SSS_SAMPLES];
vec4 radii_max_radius;
int sss_samples;
Eevee : SSS : Add Translucency support. This adds the possibility to simulate things like red ears with strong backlight or material with high scattering distances. To enable it you need to turn on the "Subsurface Translucency" option in the "Options" tab of the Material Panel (and of course to have "regular" SSS enabled in both render settings and material options). Since the effect is adding another overhead I prefer to make it optional. But this is open to discussion. Be aware that the effect only works for direct lights (so no indirect/world lighting) that have shadowmaps, and is affected by the "softness" of the shadowmap and resolution. Technical notes: This is inspired by http://www.iryoku.com/translucency/ but goes a bit beyond that. We do not use a sum of gaussian to apply in regards to the object thickness but we precompute a 1D kernel texture. This texture stores the light transmited to a point at the back of an infinite slab of material of variying thickness. We make the assumption that the slab is perpendicular to the light so that no fresnel or diffusion term is taken into account. The light is considered constant. If the setup is similar to the one assume during the profile baking, the realtime render matches cycles reference. Due to these assumptions the computed transmitted light is in most cases too bright for curvy objects. Finally we jitter the shadow map sample per pixel so we can simulate dispersion inside the medium. Radius of the dispersion is in world space and derived by from the "soft" shadowmap parameter. Idea for this come from this presentation http://www.iryoku.com/stare-into-the-future (slide 164).
2017-11-22 04:51:21 +01:00
};
uniform sampler1D sssTexProfile;
vec3 sss_profile(float s)
{
s /= radii_max_radius.w;
return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb;
Eevee : SSS : Add Translucency support. This adds the possibility to simulate things like red ears with strong backlight or material with high scattering distances. To enable it you need to turn on the "Subsurface Translucency" option in the "Options" tab of the Material Panel (and of course to have "regular" SSS enabled in both render settings and material options). Since the effect is adding another overhead I prefer to make it optional. But this is open to discussion. Be aware that the effect only works for direct lights (so no indirect/world lighting) that have shadowmaps, and is affected by the "softness" of the shadowmap and resolution. Technical notes: This is inspired by http://www.iryoku.com/translucency/ but goes a bit beyond that. We do not use a sum of gaussian to apply in regards to the object thickness but we precompute a 1D kernel texture. This texture stores the light transmited to a point at the back of an infinite slab of material of variying thickness. We make the assumption that the slab is perpendicular to the light so that no fresnel or diffusion term is taken into account. The light is considered constant. If the setup is similar to the one assume during the profile baking, the realtime render matches cycles reference. Due to these assumptions the computed transmitted light is in most cases too bright for curvy objects. Finally we jitter the shadow map sample per pixel so we can simulate dispersion inside the medium. Radius of the dispersion is in world space and derived by from the "soft" shadowmap parameter. Idea for this come from this presentation http://www.iryoku.com/stare-into-the-future (slide 164).
2017-11-22 04:51:21 +01:00
}
#endif
Eevee : SSS : Add Translucency support. This adds the possibility to simulate things like red ears with strong backlight or material with high scattering distances. To enable it you need to turn on the "Subsurface Translucency" option in the "Options" tab of the Material Panel (and of course to have "regular" SSS enabled in both render settings and material options). Since the effect is adding another overhead I prefer to make it optional. But this is open to discussion. Be aware that the effect only works for direct lights (so no indirect/world lighting) that have shadowmaps, and is affected by the "softness" of the shadowmap and resolution. Technical notes: This is inspired by http://www.iryoku.com/translucency/ but goes a bit beyond that. We do not use a sum of gaussian to apply in regards to the object thickness but we precompute a 1D kernel texture. This texture stores the light transmited to a point at the back of an infinite slab of material of variying thickness. We make the assumption that the slab is perpendicular to the light so that no fresnel or diffusion term is taken into account. The light is considered constant. If the setup is similar to the one assume during the profile baking, the realtime render matches cycles reference. Due to these assumptions the computed transmitted light is in most cases too bright for curvy objects. Finally we jitter the shadow map sample per pixel so we can simulate dispersion inside the medium. Radius of the dispersion is in world space and derived by from the "soft" shadowmap parameter. Idea for this come from this presentation http://www.iryoku.com/stare-into-the-future (slide 164).
2017-11-22 04:51:21 +01:00
vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
{
#if !defined(USE_TRANSLUCENCY) || defined(VOLUMETRICS)
return vec3(0.0);
#else
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;
#endif
Eevee : SSS : Add Translucency support. This adds the possibility to simulate things like red ears with strong backlight or material with high scattering distances. To enable it you need to turn on the "Subsurface Translucency" option in the "Options" tab of the Material Panel (and of course to have "regular" SSS enabled in both render settings and material options). Since the effect is adding another overhead I prefer to make it optional. But this is open to discussion. Be aware that the effect only works for direct lights (so no indirect/world lighting) that have shadowmaps, and is affected by the "softness" of the shadowmap and resolution. Technical notes: This is inspired by http://www.iryoku.com/translucency/ but goes a bit beyond that. We do not use a sum of gaussian to apply in regards to the object thickness but we precompute a 1D kernel texture. This texture stores the light transmited to a point at the back of an infinite slab of material of variying thickness. We make the assumption that the slab is perpendicular to the light so that no fresnel or diffusion term is taken into account. The light is considered constant. If the setup is similar to the one assume during the profile baking, the realtime render matches cycles reference. Due to these assumptions the computed transmitted light is in most cases too bright for curvy objects. Finally we jitter the shadow map sample per pixel so we can simulate dispersion inside the medium. Radius of the dispersion is in world space and derived by from the "soft" shadowmap parameter. Idea for this come from this presentation http://www.iryoku.com/stare-into-the-future (slide 164).
2017-11-22 04:51:21 +01:00
}