Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2017-11-22 06:51:21 +0300
committerClément Foucault <foucault.clem@gmail.com>2017-11-22 06:51:21 +0300
commit846cdf53187174ac87dd24af4b7b0d413db4ad03 (patch)
tree11139716a842f878f7d350a3cffeb6e4ecfbf02c /source/blender/draw/engines
parentff810f08d8670b3d016e865e9b32df44c270ddaa (diff)
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).
Diffstat (limited to 'source/blender/draw/engines')
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c24
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h5
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl1
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/lamps_lib.glsl136
-rw-r--r--source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl58
8 files changed, 221 insertions, 11 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index 09a090955c2..17295713001 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -530,6 +530,7 @@ static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_La
ubo_data->shadow_start = (float)(sh_data->layer_id);
ubo_data->data_start = (float)(sh_data->cube_id);
ubo_data->multi_shadow_count = (float)(sh_nbr);
+ ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
ubo_data->contact_bias = 0.05f * la->contact_bias;
@@ -777,6 +778,7 @@ static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE
ubo_data->shadow_start = (float)(sh_data->layer_id);
ubo_data->data_start = (float)(sh_data->cascade_id);
ubo_data->multi_shadow_count = (float)(sh_nbr);
+ ubo_data->shadow_blur = la->soft * 0.02f; /* Used by translucence shadowmap blur */
ubo_data->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
ubo_data->contact_bias = 0.05f * la->contact_bias;
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index dbd953553cd..4a9633b4f44 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -307,6 +307,9 @@ static char *eevee_get_defines(int options)
if ((options & VAR_MAT_SSS) != 0) {
BLI_dynstr_appendf(ds, "#define USE_SSS\n");
}
+ if ((options & VAR_MAT_TRANSLUC) != 0) {
+ BLI_dynstr_appendf(ds, "#define USE_TRANSLUCENCY\n");
+ }
if ((options & VAR_MAT_VSM) != 0) {
BLI_dynstr_appendf(ds, "#define SHADOW_VSM\n");
}
@@ -636,7 +639,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, World *
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma, EEVEE_Data *vedata,
- bool use_blend, bool use_multiply, bool use_refract, bool use_sss, int shadow_method)
+ bool use_blend, bool use_multiply, bool use_refract, bool use_sss, bool use_translucency, int shadow_method)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_MESH;
@@ -645,6 +648,7 @@ struct GPUMaterial *EEVEE_material_mesh_get(
if (use_multiply) options |= VAR_MAT_MULT;
if (use_refract) options |= VAR_MAT_REFRACT;
if (use_sss) options |= VAR_MAT_SSS;
+ if (use_translucency) options |= VAR_MAT_TRANSLUC;
if (vedata->stl->effects->use_volumetrics && use_blend) options |= VAR_MAT_VOLUME;
options |= eevee_material_shadow_option(shadow_method);
@@ -977,6 +981,7 @@ static void material_opaque(
const bool use_gpumat = (ma->use_nodes && ma->nodetree);
const bool use_refract = ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((stl->effects->enabled_effects & EFFECT_REFRACT) != 0);
const bool use_sss = ((ma->blend_flag & MA_BL_SS_SUBSURFACE) != 0) && ((stl->effects->enabled_effects & EFFECT_SSS) != 0);
+ const bool use_translucency = ((ma->blend_flag & MA_BL_TRANSLUCENCY) != 0) && ((stl->effects->enabled_effects & EFFECT_SSS) != 0);
EeveeMaterialShadingGroups *emsg = BLI_ghash_lookup(material_hash, (const void *)ma);
@@ -987,7 +992,7 @@ static void material_opaque(
/* This will have been created already, just perform a lookup. */
*gpumat = (use_gpumat) ? EEVEE_material_mesh_get(
- scene, ma, vedata, false, false, use_refract, use_sss, linfo->shadow_method) : NULL;
+ scene, ma, vedata, false, false, use_refract, use_sss, use_translucency, linfo->shadow_method) : NULL;
*gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get(
scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL;
return;
@@ -995,7 +1000,8 @@ static void material_opaque(
if (use_gpumat) {
/* Shading */
- *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, false, false, use_refract, use_sss, linfo->shadow_method);
+ *gpumat = EEVEE_material_mesh_get(scene, ma, vedata, false, false, use_refract,
+ use_sss, use_translucency, linfo->shadow_method);
*shgrp = DRW_shgroup_material_create(*gpumat,
(use_refract) ? psl->refract_pass :
@@ -1007,9 +1013,17 @@ static void material_opaque(
add_standard_uniforms(*shgrp, sldata, vedata, ssr_id, &ma->refract_depth, use_refract, false);
if (use_sss) {
+ struct GPUTexture *sss_tex_profile = NULL;
struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get(*gpumat,
- stl->effects->sss_sample_count);
+ stl->effects->sss_sample_count,
+ &sss_tex_profile);
+
if (sss_profile) {
+ if (use_translucency) {
+ DRW_shgroup_uniform_block(*shgrp, "sssProfile", sss_profile);
+ DRW_shgroup_uniform_texture(*shgrp, "sssTexProfile", sss_tex_profile);
+ }
+
DRW_shgroup_stencil_mask(*shgrp, e_data.sss_count + 1);
EEVEE_subsurface_add_pass(vedata, e_data.sss_count + 1, sss_profile);
e_data.sss_count++;
@@ -1099,7 +1113,7 @@ static void material_transparent(
if (ma->use_nodes && ma->nodetree) {
/* Shading */
*gpumat = EEVEE_material_mesh_get(scene, ma, vedata, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract,
- false, linfo->shadow_method);
+ false, false, linfo->shadow_method);
*shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass);
if (*shgrp) {
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index b1ed108bad0..6a3adb36a7c 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -116,6 +116,7 @@ enum {
VAR_MAT_REFRACT = (1 << 12),
VAR_MAT_VOLUME = (1 << 13),
VAR_MAT_SSS = (1 << 14),
+ VAR_MAT_TRANSLUC = (1 << 15),
};
/* Shadow Technique */
@@ -280,7 +281,7 @@ typedef struct EEVEE_Light {
typedef struct EEVEE_Shadow {
float near, far, bias, exp;
- float shadow_start, data_start, multi_shadow_count, pad;
+ float shadow_start, data_start, multi_shadow_count, shadow_blur;
float contact_dist, contact_bias, contact_spread, contact_thickness;
} EEVEE_Shadow;
@@ -639,7 +640,7 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, str
struct GPUMaterial *EEVEE_material_world_volume_get(struct Scene *scene, struct World *wo);
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma, EEVEE_Data *vedata,
- bool use_blend, bool use_multiply, bool use_refract, bool use_sss, int shadow_method);
+ bool use_blend, bool use_multiply, bool use_refract, bool use_sss, bool use_translucency, int shadow_method);
struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma);
struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow);
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method);
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 6827d44aea4..fe6e77ddf29 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -123,7 +123,6 @@ void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct G
DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_data);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
- DRW_shgroup_uniform_int(grp, "sampleCount", &effects->sss_sample_count, 1);
DRW_shgroup_uniform_float(grp, "jitterThreshold", &effects->sss_jitter_threshold, 1);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_add(grp, quad, NULL);
@@ -134,7 +133,6 @@ void EEVEE_subsurface_add_pass(EEVEE_Data *vedata, unsigned int sss_id, struct G
DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_blur);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
- DRW_shgroup_uniform_int(grp, "sampleCount", &effects->sss_sample_count, 1);
DRW_shgroup_uniform_float(grp, "jitterThreshold", &effects->sss_jitter_threshold, 1);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_add(grp, quad, NULL);
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index bd6c1923bfe..32d27838da3 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -103,6 +103,7 @@ struct ShadowCascadeData {
#define sh_tex_start shadow_data_start_end.x
#define sh_data_start shadow_data_start_end.y
#define sh_multi_nbr shadow_data_start_end.z
+#define sh_blur shadow_data_start_end.w
#define sh_contact_dist contact_shadow_data.x
#define sh_contact_offset contact_shadow_data.y
#define sh_contact_spread contact_shadow_data.z
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
index 9ee713ab483..ea9711a6cad 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
@@ -5,9 +5,9 @@
layout(std140) uniform sssProfile {
vec4 kernel[MAX_SSS_SAMPLES];
vec4 radii_max_radius;
+ int sss_samples;
};
-uniform int sampleCount;
uniform float jitterThreshold;
uniform sampler2D depthBuffer;
uniform sampler2D sssData;
@@ -59,7 +59,7 @@ void main(void)
/* Center sample */
vec3 accum = sss_data.rgb * kernel[0].rgb;
- for (int i = 1; i < sampleCount && i < MAX_SSS_SAMPLES; i++) {
+ for (int i = 1; i < sss_samples && i < MAX_SSS_SAMPLES; i++) {
vec2 sample_uv = uvs + kernel[i].a * finalStep * ((abs(kernel[i].a) > jitterThreshold) ? dir : dir_rand);
vec3 color = texture(sssData, sample_uv).rgb;
float sample_depth = texture(depthBuffer, sample_uv).r;
diff --git a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
index 5ca40cd06a2..c2e3f81aefd 100644
--- a/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lamps_lib.glsl
@@ -277,6 +277,142 @@ vec3 light_specular(LightData ld, vec3 N, vec3 V, vec4 l_vector, float roughness
#endif
}
+#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))
+layout(std140) uniform sssProfile {
+ vec4 kernel[MAX_SSS_SAMPLES];
+ vec4 radii_max_radius;
+ int sss_samples;
+};
+
+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;
+}
+
+vec3 light_translucent(LightData ld, vec3 W, vec3 N, vec4 l_vector, float scale)
+{
+ vec3 vis = vec3(1.0);
+
+ /* Only shadowed light can produce translucency */
+ if (ld.l_shadowid >= 0.0) {
+ 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);
+
+ vec3 rand = texture(utilTex, vec3(gl_FragCoord.xy / LUT_SIZE, 2.0)).xzw;
+ /* XXX This is a hack to not have noise correlation artifacts.
+ * A better solution to have better noise is welcome. */
+ rand.yz *= fast_sqrt(fract(rand.x * 7919.0)) * 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.y + B * rand.z;
+
+ if (ld.l_type == SUN) {
+ ShadowCascadeData scd = shadows_cascade_data[int(data.sh_data_start)];
+ vec4 view_z = vec4(dot(W - cameraPos, cameraForward));
+
+ vec4 weights = step(scd.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 = scd.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);
+ }
+
+#if defined(SHADOW_VSM)
+ vec2 moments = texture(shadowTexture, vec3(shpos.xy, data.sh_tex_start + id)).rg;
+ delta = dist - moments.x;
+#else
+ float z = texture(shadowTexture, vec3(shpos.xy, data.sh_tex_start + id)).r;
+ delta = dist - z;
+#endif
+ }
+ else {
+ vec3 cubevec = W - shadows_cube_data[int(data.sh_data_start)].position.xyz;
+ float dist = length(cubevec);
+
+ /* If fragment is out of shadowmap range, do not occlude */
+ /* XXX : we check radial distance against a cubeface distance.
+ * We loose quite a bit of valid area. */
+ if (dist < data.sh_far) {
+ cubevec /= dist;
+
+#if defined(SHADOW_VSM)
+ vec2 moments = texture_octahedron(shadowTexture, vec4(cubevec, data.sh_tex_start)).rg;
+ delta = dist - moments.x;
+#else
+ float z = texture_octahedron(shadowTexture, vec4(cubevec, data.sh_tex_start)).r;
+ delta = dist - z;
+#endif
+ }
+ }
+
+ /* XXX : Removing Area Power. */
+ /* TODO : put this out of the shader. */
+ float falloff;
+ if (ld.l_type == AREA) {
+ vis *= 0.0962 * (ld.l_sizex * ld.l_sizey * 4.0 * M_PI);
+ vis /= (l_vector.w * l_vector.w);
+ falloff = dot(N, l_vector.xyz / l_vector.w);
+ }
+ else if (ld.l_type == SUN) {
+ falloff = dot(N, -ld.l_forward);
+ }
+ else {
+ vis *= 0.0248 * (4.0 * ld.l_radius * ld.l_radius * M_PI * M_PI);
+ 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);
+
+ if (ld.l_type == SPOT) {
+ 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 = 1.0 / sqrt(1.0 + x * x + y * y);
+
+ float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.l_spot_size) / ld.l_spot_blend);
+
+ vis *= spotmask;
+ vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
+ }
+ else if (ld.l_type == AREA) {
+ vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
+ }
+ }
+ else {
+ vis = vec3(0.0);
+ }
+
+ return vis;
+}
+
#ifdef HAIR_SHADER
void light_hair_common(
LightData ld, vec3 N, vec3 V, vec4 l_vector, vec3 norm_view,
diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
index f63a9665810..44410be700b 100644
--- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
@@ -810,3 +810,61 @@ vec3 eevee_surface_glass(vec3 N, vec3 transmission_col, float roughness, float i
return out_light;
}
+
+/* ----------- Translucency ----------- */
+
+vec3 eevee_surface_translucent_lit(vec3 N, vec3 albedo, float sss_scale)
+{
+#ifndef USE_TRANSLUCENCY
+ return vec3(0.0);
+#endif
+
+ vec3 V = cameraVec;
+
+ /* Zero length vectors cause issues, see: T51979. */
+#if 0
+ N = normalize(N);
+#else
+ {
+ float len = length(N);
+ if (isnan(len)) {
+ return vec3(0.0);
+ }
+ N /= len;
+ }
+#endif
+
+ /* We only enlit the backfaces */
+ N = -N;
+
+ /* ---------------- SCENE LAMPS LIGHTING ----------------- */
+
+#ifdef HAIR_SHADER
+ vec3 norm_view = cross(V, N);
+ norm_view = normalize(cross(norm_view, N)); /* Normal facing view */
+#endif
+
+ vec3 diff = vec3(0.0);
+ for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) {
+ LightData ld = lights_data[i];
+
+ vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */
+ l_vector.xyz = ld.l_position - worldPosition;
+ l_vector.w = length(l_vector.xyz);
+
+#ifdef HAIR_SHADER
+ vec3 norm_lamp, view_vec;
+ float occlu_trans, occlu;
+ light_hair_common(ld, N, V, l_vector, norm_view, occlu_trans, occlu, norm_lamp, view_vec);
+
+ diff += ld.l_color * light_translucent(ld, worldPosition, norm_lamp, l_vector, sss_scale) * occlu_trans;
+#else
+ diff += ld.l_color * light_translucent(ld, worldPosition, N, l_vector, sss_scale);
+#endif
+ }
+
+ /* Accumulate outgoing radiance */
+ vec3 out_light = diff * albedo;
+
+ return out_light;
+}