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
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-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
-rw-r--r--source/blender/gpu/GPU_material.h5
-rw-r--r--source/blender/gpu/intern/gpu_material.c241
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl11
-rw-r--r--source/blender/makesdna/DNA_material_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_material.c5
13 files changed, 390 insertions, 105 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;
+}
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index aeb268cef36..71bf6c897e7 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -235,8 +235,9 @@ void GPU_material_enable_alpha(GPUMaterial *material);
GPUBuiltin GPU_get_material_builtins(GPUMaterial *material);
GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4]);
-void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short int *falloff_type, float *sharpness);
-struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct);
+void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short *falloff_type, float *sharpness);
+struct GPUUniformBuffer *GPU_material_sss_profile_get(
+ GPUMaterial *material, int sample_ct, struct GPUTexture **tex_profile);
/* High level functions to create and use GPU materials */
GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo);
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 728e0033660..c30a28616e0 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -45,6 +45,7 @@
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_rand.h"
#include "BKE_anim.h"
#include "BKE_colortools.h"
@@ -143,6 +144,7 @@ struct GPUMaterial {
GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */
+ GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
float *sss_radii; /* UBO containing SSS profile. */
int sss_samples;
short int *sss_falloff;
@@ -275,6 +277,10 @@ void GPU_material_free(ListBase *gpumaterial)
GPU_uniformbuffer_free(material->ubo);
}
+ if (material->sss_tex_profile != NULL) {
+ GPU_texture_free(material->sss_tex_profile);
+ }
+
if (material->sss_profile != NULL) {
GPU_uniformbuffer_free(material->sss_profile);
}
@@ -493,52 +499,35 @@ void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials)
typedef struct GPUSssKernelData {
float kernel[SSS_SAMPLES][4];
- float radii_n[3], max_radius;
+ float param[3], max_radius;
+ int samples;
} GPUSssKernelData;
-static void sss_calculate_offsets(GPUSssKernelData *kd, int count)
+static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent)
{
float step = 2.0f / (float)(count - 1);
for (int i = 0; i < count; i++) {
float o = ((float)i) * step - 1.0f;
float sign = (o < 0.0f) ? -1.0f : 1.0f;
- float ofs = sign * fabsf(powf(o, SSS_EXPONENT));
+ float ofs = sign * fabsf(powf(o, exponent));
kd->kernel[i][3] = ofs;
}
}
-static float error_function(float x) {
- /* Approximation of the error function by Abramowitz and Stegun
- * https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions */
- const float a1 = 0.254829592f;
- const float a2 = -0.284496736f;
- const float a3 = 1.421413741f;
- const float a4 = -1.453152027f;
- const float a5 = 1.061405429f;
- const float p = 0.3275911f;
-
- float sign = (x < 0.0f) ? -1.0f : 1.0f;
- x = fabsf(x);
-
- float t = 1.0f / (1.0f + p * x);
- float y = 1.0f - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * expf(-(x * x));
-
- return sign * y;
-}
+#define GAUSS_TRUNCATE 12.46f
+static float gaussian_profile(float r, float radius)
+{
+ const float v = radius * radius * (0.25f * 0.25f);
+ const float Rm = sqrtf(v * GAUSS_TRUNCATE);
-static float gaussian_primitive(float x) {
- const float sigma = 0.3f; /* Contained mostly between -1..1 */
- return 0.5f * error_function(x / ((float)M_SQRT2 * sigma));
-}
+ if(r >= Rm)
+ return 0.0f;
-static float gaussian_integral(float x0, float x1) {
- return gaussian_primitive(x1) - gaussian_primitive(x0);
+ return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v);
}
-/* Resolution for each sample of the precomputed kernel profile */
-#define INTEGRAL_RESOLUTION 32
#define BURLEY_TRUNCATE 16.0f
-
+#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
static float burley_profile(float r, float d)
{
float exp_r_3_d = expf(-r / (3.0f * d));
@@ -546,21 +535,6 @@ static float burley_profile(float r, float d)
return (exp_r_d + exp_r_3_d) / (4.0f * d);
}
-static float burley_integral(float x0, float x1, float d)
-{
- const float range = x1 - x0;
- const float step = range / INTEGRAL_RESOLUTION;
- float integral = 0.0f;
-
- for(int i = 0; i < INTEGRAL_RESOLUTION; ++i) {
- float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
- float y = burley_profile(fabsf(x), d);
- integral += y * step;
- }
-
- return integral;
-}
-
static float cubic_profile(float r, float radius, float sharpness)
{
float Rm = radius * (1.0f + sharpness);
@@ -583,7 +557,24 @@ static float cubic_profile(float r, float radius, float sharpness)
return (10.0f * num) / (Rmy5 * M_PI);
}
-static float cubic_integral(float x0, float x1, float radius, float sharpness)
+static float eval_profile(float r, short falloff_type, float sharpness, float param)
+{
+ r = fabsf(r);
+
+ if (falloff_type == SHD_SUBSURFACE_BURLEY) {
+ return burley_profile(r, param) / BURLEY_TRUNCATE_CDF;
+ }
+ else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
+ return cubic_profile(r, param, sharpness);
+ }
+ else {
+ return gaussian_profile(r, param);
+ }
+}
+
+/* Resolution for each sample of the precomputed kernel profile */
+#define INTEGRAL_RESOLUTION 32
+static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param)
{
const float range = x1 - x0;
const float step = range / INTEGRAL_RESOLUTION;
@@ -591,46 +582,50 @@ static float cubic_integral(float x0, float x1, float radius, float sharpness)
for(int i = 0; i < INTEGRAL_RESOLUTION; ++i) {
float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
- float y = cubic_profile(fabsf(x), radius, sharpness);
+ float y = eval_profile(x, falloff_type, sharpness, param);
integral += y * step;
}
return integral;
}
+#undef INTEGRAL_RESOLUTION
-static void compute_sss_kernel(GPUSssKernelData *kd, float *radii, int sample_ct, int falloff_type, float sharpness)
+static void compute_sss_kernel(
+ GPUSssKernelData *kd, float *radii, int sample_ct, int falloff_type, float sharpness)
{
- for (int i = 0; i < 3; ++i) {
- /* Minimum radius */
- kd->radii_n[i] = MAX2(radii[i], 1e-15f);
- }
+ float rad[3];
+ /* Minimum radius */
+ rad[0] = MAX2(radii[0], 1e-15f);
+ rad[1] = MAX2(radii[1], 1e-15f);
+ rad[2] = MAX2(radii[2], 1e-15f);
/* Christensen-Burley fitting */
float l[3], d[3];
if (falloff_type == SHD_SUBSURFACE_BURLEY) {
- mul_v3_v3fl(l, kd->radii_n, 0.25f * M_1_PI);
+ mul_v3_v3fl(l, rad, 0.25f * M_1_PI);
const float A = 1.0f;
const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
/* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */
mul_v3_v3fl(d, l, 0.6f / s);
- mul_v3_v3fl(kd->radii_n, d, BURLEY_TRUNCATE);
+ mul_v3_v3fl(rad, d, BURLEY_TRUNCATE);
+ kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
+
+ copy_v3_v3(kd->param, d);
}
else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
- /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */
- sharpness *= 0.5f;
-
- mul_v3_fl(kd->radii_n, 1.0f + sharpness);
+ copy_v3_v3(kd->param, rad);
+ mul_v3_fl(rad, 1.0f + sharpness);
+ kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
}
+ else {
+ kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
- /* Normalize size */
- kd->max_radius = MAX3(kd->radii_n[0], kd->radii_n[1], kd->radii_n[2]);
- kd->radii_n[0] /= kd->max_radius;
- kd->radii_n[1] /= kd->max_radius;
- kd->radii_n[2] /= kd->max_radius;
+ copy_v3_v3(kd->param, rad);
+ }
/* Compute samples locations on the 1d kernel [-1..1] */
- sss_calculate_offsets(kd, sample_ct);
+ sss_calculate_offsets(kd, sample_ct, SSS_EXPONENT);
/* Weights sum for normalization */
float sum[3] = {0.0f, 0.0f, 0.0f};
@@ -653,25 +648,12 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii, int sample_ct
x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
}
- if (falloff_type == SHD_SUBSURFACE_BURLEY) {
- x0 *= kd->max_radius;
- x1 *= kd->max_radius;
- kd->kernel[i][0] = burley_integral(x0, x1, d[0]);
- kd->kernel[i][1] = burley_integral(x0, x1, d[1]);
- kd->kernel[i][2] = burley_integral(x0, x1, d[2]);
- }
- else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
- x0 *= kd->max_radius;
- x1 *= kd->max_radius;
- kd->kernel[i][0] = cubic_integral(x0, x1, radii[0], sharpness);
- kd->kernel[i][1] = cubic_integral(x0, x1, radii[1], sharpness);
- kd->kernel[i][2] = cubic_integral(x0, x1, radii[2], sharpness);
- }
- else {
- kd->kernel[i][0] = gaussian_integral(x0 / kd->radii_n[0], x1 / kd->radii_n[0]);
- kd->kernel[i][1] = gaussian_integral(x0 / kd->radii_n[1], x1 / kd->radii_n[1]);
- kd->kernel[i][2] = gaussian_integral(x0 / kd->radii_n[2], x1 / kd->radii_n[2]);
- }
+ x0 *= kd->max_radius;
+ x1 *= kd->max_radius;
+
+ kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]);
+ kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]);
+ kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]);
sum[0] += kd->kernel[i][0];
sum[1] += kd->kernel[i][1];
@@ -682,7 +664,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii, int sample_ct
if (sum[i] > 0.0f) {
/* Normalize */
for (int j = 0; j < sample_ct; j++) {
- kd->kernel[j][i] /= sum[i];
+ kd->kernel[j][i] /= sum[i];
}
}
else {
@@ -698,9 +680,71 @@ static void compute_sss_kernel(GPUSssKernelData *kd, float *radii, int sample_ct
copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
}
copy_v4_v4(kd->kernel[0], tmpv);
+
+ kd->samples = sample_ct;
+}
+
+#define INTEGRAL_RESOLUTION 512
+static void compute_sss_translucence_kernel(
+ const GPUSssKernelData *kd, int resolution, short falloff_type, float sharpness, float **output)
+{
+ float (*texels)[4];
+ texels = MEM_callocN(sizeof(float) * 4 * resolution, "compute_sss_translucence_kernel");
+ *output = (float *)texels;
+
+ /* Last texel should be black, hence the - 1. */
+ for (int i = 0; i < resolution - 1; ++i) {
+ /* Distance from surface. */
+ float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
+
+ /* For each distance d we compute the radiance incomming from an hypothetic parallel plane. */
+ /* Compute radius of the footprint on the hypothetic plane */
+ float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
+ float r_step = r_fp / INTEGRAL_RESOLUTION;
+ float area_accum = 0.0f;
+ for (float r = 0.0f; r < r_fp; r += r_step) {
+ /* Compute distance to the "shading" point through the medium. */
+ /* r_step * 0.5f to put sample between the area borders */
+ float dist = hypotf(r + r_step * 0.5f, d);
+
+ float profile[3];
+ profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]);
+ profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]);
+ profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]);
+
+ /* Since the profile and configuration are radially symetrical we
+ * can just evaluate it once and weight it accordingly */
+ float r_next = r + r_step;
+ float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r);
+
+ mul_v3_fl(profile, disk_area);
+ add_v3_v3(texels[i], profile);
+ area_accum += disk_area;
+ }
+ /* Normalize over the disk. */
+ mul_v3_fl(texels[i], 1.0f / (area_accum));
+ }
+
+ /* Normalize */
+ for (int j = resolution - 2; j > 0; j--) {
+ texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f;
+ texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f;
+ texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f;
+ }
+
+ /* First texel should be white */
+ texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f;
+ texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f;
+ texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f;
+
+ /* dim the last few texels for smoother transition */
+ mul_v3_fl(texels[resolution - 2], 0.25f);
+ mul_v3_fl(texels[resolution - 3], 0.5f);
+ mul_v3_fl(texels[resolution - 4], 0.75f);
}
+#undef INTEGRAL_RESOLUTION
-void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short int *falloff_type, float *sharpness)
+void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short *falloff_type, float *sharpness)
{
material->sss_radii = radii;
material->sss_falloff = falloff_type;
@@ -713,10 +757,7 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short
}
}
-#undef SSS_EXPONENT
-#undef SSS_SAMPLES
-
-struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct)
+struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct, GPUTexture **tex_profile)
{
if (material->sss_radii == NULL)
return NULL;
@@ -726,17 +767,39 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int
float sharpness = (material->sss_sharpness != NULL) ? *material->sss_sharpness : 0.0f;
+ /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */
+ sharpness *= 0.5f;
+
compute_sss_kernel(&kd, material->sss_radii, sample_ct, *material->sss_falloff, sharpness);
/* Update / Create UBO */
GPU_uniformbuffer_update(material->sss_profile, &kd);
+ /* Update / Create Tex */
+ float *translucence_profile;
+ compute_sss_translucence_kernel(&kd, 64, *material->sss_falloff, sharpness, &translucence_profile);
+
+ if (material->sss_tex_profile != NULL) {
+ GPU_texture_free(material->sss_tex_profile);
+ }
+
+ material->sss_tex_profile = GPU_texture_create_1D_custom(64, 4, GPU_RGBA16F, translucence_profile, NULL);
+
+ MEM_freeN(translucence_profile);
+
material->sss_samples = sample_ct;
material->sss_dirty = false;
}
+
+ if (tex_profile != NULL) {
+ *tex_profile = material->sss_tex_profile;
+ }
return material->sss_profile;
}
+#undef SSS_EXPONENT
+#undef SSS_SAMPLES
+
void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs)
{
*attribs = material->attribs;
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 918ae2f6f92..4aaaab2c5ad 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -2880,7 +2880,9 @@ void node_bsdf_principled_simple(vec4 base_color, float subsurface, vec3 subsurf
#ifdef USE_SSS
/* OPTI : Make irradiance computation shared with the diffuse. */
- result.sss_data.rgb = eevee_surface_diffuse_lit(N, vec3(1.0), 1.0) * mix(vec3(0.0), subsurface_color.rgb, subsurface);
+ result.sss_data.rgb = eevee_surface_diffuse_lit(N, vec3(1.0), 1.0);
+ result.sss_data.rgb += eevee_surface_translucent_lit(N, vec3(1.0), 1.0);
+ result.sss_data.rgb *= mix(vec3(0.0), subsurface_color.rgb, subsurface);
result.sss_data.a = 1.0; /* TODO Find a parametrization */
#endif
#else
@@ -2951,7 +2953,9 @@ void node_bsdf_principled_clearcoat(vec4 base_color, float subsurface, vec3 subs
#ifdef USE_SSS
/* OPTI : Make irradiance computation shared with the diffuse. */
- result.sss_data.rgb = eevee_surface_diffuse_lit(N, vec3(1.0), 1.0) * mix(vec3(0.0), subsurface_color.rgb, subsurface);
+ result.sss_data.rgb = eevee_surface_translucent_lit(N, subsurface_color.rgb, 1.0);
+ result.sss_data.rgb += eevee_surface_diffuse_lit(N, vec3(1.0), 1.0);
+ result.sss_data.rgb *= mix(vec3(0.0), subsurface_color.rgb, subsurface);
result.sss_data.a = 1.0; /* TODO Find a parametrization */
#endif
@@ -2995,7 +2999,8 @@ void node_subsurface_scattering(
result.ssr_data = vec4(0.0);
result.ssr_normal = normal_encode(vN, viewCameraVec);
result.ssr_id = -1;
- result.sss_data.rgb = eevee_surface_diffuse_lit(N, vec3(1.0), 1.0) * color.rgb;
+ result.sss_data.rgb = eevee_surface_translucent_lit(N, color.rgb, scale);
+ result.sss_data.rgb += eevee_surface_diffuse_lit(N, color.rgb, 1.0);
result.sss_data.a = scale;
#else
node_bsdf_diffuse(color, 0.0, N, result);
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index 34b9f9f83bf..bc33b7ba6e2 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -514,6 +514,7 @@ enum {
MA_BL_HIDE_BACKSIDE = (1 << 0),
MA_BL_SS_REFRACTION = (1 << 1),
MA_BL_SS_SUBSURFACE = (1 << 2),
+ MA_BL_TRANSLUCENCY = (1 << 3),
};
/* blend_shadow */
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index a78ad4262ce..a2845b3a2f8 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -1882,6 +1882,11 @@ void RNA_def_material(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Screen Space Subsurface Scattering", "Use post process subsurface scattering");
RNA_def_property_update(prop, 0, "rna_Material_draw_update");
+ prop = RNA_def_property(srna, "use_sss_translucency", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "blend_flag", MA_BL_TRANSLUCENCY);
+ RNA_def_property_ui_text(prop, "Subsurface Translucency", "Add translucency effect to subsurface");
+ RNA_def_property_update(prop, 0, "rna_Material_draw_update");
+
prop = RNA_def_property(srna, "refraction_depth", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "refract_depth");
RNA_def_property_range(prop, 0.0f, FLT_MAX);