EEVEE/EEVEE-Next: Split Diffuse and Subsurface closure
Even if related, they don't have the same performance impact. To avoid any performance hit, we replace the Diffuse by a Subsurface Closure for legacy EEVEE and use the subsurface closure only where needed for EEVEE-Next leveraging the random sampling. This increases the compatibility with cycles that doesn't modulate the radius of the subsurface anymore. This change is only present in EEVEE-Next. This commit changes the principled BSDF code so that it is easier to follow the flow of data. For legacy EEVEE, the SSS switch is moved to a `radius == -1` check.
This commit is contained in:
@@ -27,9 +27,9 @@ vec3 out_ssr_N;
|
||||
bool aov_is_valid = false;
|
||||
vec3 out_aov;
|
||||
|
||||
bool output_sss(ClosureDiffuse diffuse, ClosureOutputDiffuse diffuse_out)
|
||||
bool output_sss(ClosureSubsurface diffuse, ClosureOutputDiffuse diffuse_out)
|
||||
{
|
||||
if (diffuse.sss_id == 0u || !do_sss || !sssToggle || outputSssId == 0) {
|
||||
if (diffuse.sss_radius.r == -1.0 || !do_sss || !sssToggle || outputSssId == 0) {
|
||||
return false;
|
||||
}
|
||||
if (renderPassSSSColor) {
|
||||
@@ -71,6 +71,7 @@ void output_aov(vec4 color, float value, uint hash)
|
||||
}
|
||||
|
||||
/* Single BSDFs. */
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_1(DiffuseBSDF, Diffuse)
|
||||
Closure closure_eval(ClosureDiffuse diffuse)
|
||||
{
|
||||
@@ -83,8 +84,24 @@ Closure closure_eval(ClosureDiffuse diffuse)
|
||||
CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse);
|
||||
|
||||
Closure closure = CLOSURE_DEFAULT;
|
||||
if (!output_sss(diffuse, out_Diffuse_0)) {
|
||||
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
|
||||
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
|
||||
return closure;
|
||||
}
|
||||
|
||||
/* NOTE: Reuse the diffuse eval function. */
|
||||
Closure closure_eval(ClosureSubsurface subsurface)
|
||||
{
|
||||
/* Glue with the old system. */
|
||||
CLOSURE_VARS_DECLARE_1(Diffuse);
|
||||
|
||||
in_Diffuse_0.N = subsurface.N;
|
||||
in_Diffuse_0.albedo = subsurface.color;
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse);
|
||||
|
||||
Closure closure = CLOSURE_DEFAULT;
|
||||
if (!output_sss(subsurface, out_Diffuse_0)) {
|
||||
closure.radiance += out_Diffuse_0.radiance * subsurface.color * subsurface.weight;
|
||||
}
|
||||
return closure;
|
||||
}
|
||||
@@ -197,7 +214,7 @@ Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
|
||||
|
||||
/* Dielectric BSDF */
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy)
|
||||
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
|
||||
Closure closure_eval(ClosureSubsurface diffuse, ClosureReflection reflection)
|
||||
{
|
||||
#if defined(DO_SPLIT_CLOSURE_EVAL)
|
||||
Closure closure = closure_eval(diffuse);
|
||||
@@ -230,7 +247,9 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
|
||||
|
||||
/* Specular BSDF */
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_3(SpecularBSDF, Diffuse, Glossy, Glossy)
|
||||
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, ClosureReflection coat)
|
||||
Closure closure_eval(ClosureSubsurface diffuse,
|
||||
ClosureReflection reflection,
|
||||
ClosureReflection coat)
|
||||
{
|
||||
#if defined(DO_SPLIT_CLOSURE_EVAL)
|
||||
Closure closure = closure_eval(diffuse);
|
||||
@@ -267,7 +286,7 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, Closu
|
||||
|
||||
/* Principled BSDF */
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction)
|
||||
Closure closure_eval(ClosureDiffuse diffuse,
|
||||
Closure closure_eval(ClosureSubsurface diffuse,
|
||||
ClosureReflection reflection,
|
||||
ClosureReflection coat,
|
||||
ClosureRefraction refraction)
|
||||
|
||||
@@ -51,10 +51,10 @@ ClosureType closure_type_get(ClosureRefraction cl)
|
||||
return CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
|
||||
}
|
||||
|
||||
// ClosureType closure_type_get(ClosureSubsurface cl)
|
||||
// {
|
||||
// return CLOSURE_BSSRDF_BURLEY_ID;
|
||||
// }
|
||||
ClosureType closure_type_get(ClosureSubsurface cl)
|
||||
{
|
||||
return CLOSURE_BSSRDF_BURLEY_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the closure is to be selected based on the input weight.
|
||||
@@ -127,11 +127,15 @@ Closure closure_eval(ClosureDiffuse diffuse)
|
||||
{
|
||||
ClosureUndetermined cl;
|
||||
closure_base_copy(cl, diffuse);
|
||||
/* TODO: Have dedicated ClosureSubsurface */
|
||||
if (diffuse.sss_id != 0u) {
|
||||
cl.type = CLOSURE_BSSRDF_BURLEY_ID;
|
||||
cl.data.rgb = diffuse.sss_radius;
|
||||
}
|
||||
closure_select(g_diffuse_data, g_diffuse_rand, cl);
|
||||
return Closure(0);
|
||||
}
|
||||
|
||||
Closure closure_eval(ClosureSubsurface diffuse)
|
||||
{
|
||||
ClosureUndetermined cl;
|
||||
closure_base_copy(cl, diffuse);
|
||||
cl.data.rgb = diffuse.sss_radius;
|
||||
closure_select(g_diffuse_data, g_diffuse_rand, cl);
|
||||
return Closure(0);
|
||||
}
|
||||
|
||||
@@ -97,9 +97,7 @@ void main(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO SSS closure. */
|
||||
ClosureDiffuse closure = to_closure_diffuse(gbuffer_closure_get(gbuf, 0));
|
||||
|
||||
ClosureSubsurface closure = to_closure_subsurface(gbuffer_closure_get(gbuf, 0));
|
||||
float max_radius = reduce_max(closure.sss_radius);
|
||||
|
||||
float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3];
|
||||
|
||||
@@ -26,12 +26,10 @@ void main(void)
|
||||
GBufferReader gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel);
|
||||
|
||||
if (gbuffer_closure_get(gbuf, 0).type == CLOSURE_BSSRDF_BURLEY_ID) {
|
||||
/* TODO SSS closure. */
|
||||
|
||||
vec3 radiance = imageLoad(direct_light_img, texel).rgb +
|
||||
imageLoad(indirect_light_img, texel).rgb;
|
||||
|
||||
ClosureDiffuse closure = to_closure_diffuse(gbuffer_closure_get(gbuf, 0));
|
||||
ClosureSubsurface closure = to_closure_subsurface(gbuffer_closure_get(gbuf, 0));
|
||||
float max_radius = reduce_max(closure.sss_radius);
|
||||
|
||||
imageStore(radiance_img, texel, vec4(radiance, 0.0));
|
||||
|
||||
@@ -174,8 +174,13 @@ struct ClosureDiffuse {
|
||||
float weight;
|
||||
vec3 color;
|
||||
vec3 N;
|
||||
};
|
||||
|
||||
struct ClosureSubsurface {
|
||||
float weight;
|
||||
vec3 color;
|
||||
vec3 N;
|
||||
vec3 sss_radius;
|
||||
uint sss_id;
|
||||
};
|
||||
|
||||
struct ClosureTranslucent {
|
||||
@@ -234,7 +239,14 @@ ClosureDiffuse to_closure_diffuse(ClosureUndetermined cl)
|
||||
ClosureDiffuse closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
/* TODO(fclem): BSSSRDF closure. */
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureSubsurface to_closure_subsurface(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureSubsurface closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
closure.sss_radius = cl.data.xyz;
|
||||
return closure;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Cl
|
||||
diffuse_data.weight = weight;
|
||||
diffuse_data.color = color.rgb;
|
||||
diffuse_data.N = N;
|
||||
diffuse_data.sss_id = 0u;
|
||||
|
||||
result = closure_eval(diffuse_data);
|
||||
}
|
||||
|
||||
@@ -39,18 +39,26 @@ void node_eevee_specular(vec4 diffuse,
|
||||
|
||||
float alpha = (1.0 - transp) * weight;
|
||||
|
||||
#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES
|
||||
/* EEVEE Legacy evaluates the subsurface as a diffuse closure.
|
||||
* So this has no performance penalty. However, using a separate closure for subsurface
|
||||
* (just like for EEVEE-Next) would induce a huge performance hit. */
|
||||
ClosureSubsurface diffuse_data;
|
||||
#else
|
||||
ClosureDiffuse diffuse_data;
|
||||
#endif
|
||||
diffuse_data.weight = alpha;
|
||||
diffuse_data.color = diffuse.rgb;
|
||||
diffuse_data.N = N;
|
||||
diffuse_data.sss_id = 0u;
|
||||
|
||||
#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES
|
||||
/* WORKAROUND: Nasty workaround to the current interface with the closure evaluation.
|
||||
* Ideally the occlusion input should be move to the output node or removed all-together.
|
||||
* This is temporary to avoid a regression in 3.2 and should be removed after EEVEE-Next rewrite.
|
||||
*/
|
||||
diffuse_data.sss_radius.r = occlusion;
|
||||
diffuse_data.sss_radius.g = -1.0; /* Flag */
|
||||
#endif
|
||||
|
||||
ClosureReflection reflection_data;
|
||||
reflection_data.weight = alpha;
|
||||
|
||||
@@ -27,8 +27,6 @@ void node_bsdf_hair(vec4 color,
|
||||
hair_data.weight = weight;
|
||||
hair_data.color = color.rgb;
|
||||
hair_data.N = g_data.N;
|
||||
hair_data.sss_radius = vec3(0.0);
|
||||
hair_data.sss_id = 0u;
|
||||
#endif
|
||||
result = closure_eval(hair_data);
|
||||
}
|
||||
@@ -68,8 +66,6 @@ void node_bsdf_hair_principled(vec4 color,
|
||||
hair_data.weight = weight;
|
||||
hair_data.color = color.rgb;
|
||||
hair_data.N = g_data.N;
|
||||
hair_data.sss_radius = vec3(0.0);
|
||||
hair_data.sss_id = 0u;
|
||||
#endif
|
||||
result = closure_eval(hair_data);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ void node_bsdf_principled(vec4 base_color,
|
||||
const float do_coat,
|
||||
const float do_refraction,
|
||||
const float do_multiscatter,
|
||||
float do_sss,
|
||||
const float do_sss,
|
||||
out Closure result)
|
||||
{
|
||||
/* Match cycles. */
|
||||
@@ -87,13 +87,6 @@ void node_bsdf_principled(vec4 base_color,
|
||||
base_color = max(base_color, vec4(0.0));
|
||||
vec4 clamped_base_color = min(base_color, vec4(1.0));
|
||||
|
||||
vec4 diffuse_sss_base_color = base_color;
|
||||
if (subsurface_weight > 0.0) {
|
||||
/* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in Cycles.
|
||||
* So it's clamped there and we clamp here for consistency with Cycles. */
|
||||
diffuse_sss_base_color = mix(diffuse_sss_base_color, clamped_base_color, subsurface_weight);
|
||||
}
|
||||
|
||||
N = safe_normalize(N);
|
||||
CN = safe_normalize(CN);
|
||||
vec3 V = coordinate_incoming(g_data.P);
|
||||
@@ -106,19 +99,14 @@ void node_bsdf_principled(vec4 base_color,
|
||||
weight *= alpha;
|
||||
|
||||
/* First layer: Sheen */
|
||||
ClosureDiffuse diffuse_data;
|
||||
diffuse_data.N = N;
|
||||
|
||||
vec3 sheen_data_color = vec3(0.0);
|
||||
if (sheen_weight > 0.0) {
|
||||
/* TODO: Maybe sheen_weight should be specular. */
|
||||
vec3 sheen_color = sheen_weight * sheen_tint.rgb * principled_sheen(NV, sheen_roughness);
|
||||
diffuse_data.color = weight * sheen_color;
|
||||
sheen_data_color = weight * sheen_color;
|
||||
/* Attenuate lower layers */
|
||||
weight *= max((1.0 - math_reduce_max(sheen_color)), 0.0);
|
||||
}
|
||||
else {
|
||||
diffuse_data.color = vec3(0.0);
|
||||
}
|
||||
|
||||
/* Second layer: Coat */
|
||||
ClosureReflection coat_data;
|
||||
@@ -218,25 +206,69 @@ void node_bsdf_principled(vec4 base_color,
|
||||
bsdf_lut(F0, F90, vec3(0.0), NV, roughness, eta, do_multiscatter != 0.0, reflectance, unused);
|
||||
|
||||
reflection_data.color += weight * reflectance;
|
||||
/* Adjust the weight of picking the closure. */
|
||||
reflection_data.color *= coat_tint.rgb;
|
||||
reflection_data.weight = math_average(reflection_data.color);
|
||||
reflection_data.color *= safe_rcp(reflection_data.weight);
|
||||
|
||||
/* Attenuate lower layers */
|
||||
weight *= max((1.0 - math_reduce_max(reflectance)), 0.0);
|
||||
}
|
||||
|
||||
/* Diffuse component */
|
||||
if (true) {
|
||||
diffuse_data.sss_radius = subsurface_weight *
|
||||
max(subsurface_radius * subsurface_scale, vec3(0.0));
|
||||
diffuse_data.sss_id = uint(do_sss);
|
||||
diffuse_data.color += weight * diffuse_sss_base_color.rgb * coat_tint.rgb;
|
||||
#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES
|
||||
/* EEVEE Legacy evaluates the subsurface as a diffuse closure.
|
||||
* So this has no performance penalty. However, using a separate closure for subsurface
|
||||
* (just like for EEVEE-Next) would induce a huge performance hit. */
|
||||
ClosureSubsurface diffuse_data;
|
||||
#else
|
||||
ClosureDiffuse diffuse_data;
|
||||
#endif
|
||||
diffuse_data.N = N;
|
||||
|
||||
subsurface_radius = max(subsurface_radius * subsurface_scale, vec3(0.0));
|
||||
|
||||
/* Subsurface component */
|
||||
if (subsurface_weight > 0.0) {
|
||||
#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES
|
||||
/* For Legacy EEVEE, Subsurface is just part of the diffuse. Just evaluate the mixed color. */
|
||||
/* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in
|
||||
* Cycles. So it's clamped there and we clamp here for consistency with Cycles. */
|
||||
base_color = mix(base_color, clamped_base_color, subsurface_weight);
|
||||
|
||||
if (do_sss == 0.0) {
|
||||
diffuse_data.sss_radius = vec3(-1);
|
||||
}
|
||||
else {
|
||||
diffuse_data.sss_radius = subsurface_weight * subsurface_radius;
|
||||
}
|
||||
#else
|
||||
ClosureSubsurface sss_data;
|
||||
sss_data.N = N;
|
||||
sss_data.sss_radius = subsurface_radius;
|
||||
/* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in
|
||||
* Cycles. So it's clamped there and we clamp here for consistency with Cycles. */
|
||||
sss_data.color = (subsurface_weight * weight) * clamped_base_color.rgb * coat_tint.rgb;
|
||||
/* Add energy of the sheen layer until we have proper sheen BSDF. */
|
||||
sss_data.color += sheen_data_color;
|
||||
|
||||
sss_data.weight = math_average(sss_data.color);
|
||||
sss_data.color *= safe_rcp(sss_data.weight);
|
||||
result = closure_eval(sss_data);
|
||||
|
||||
/* Attenuate lower layers */
|
||||
weight *= max((1.0 - subsurface_weight), 0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Adjust the weight of picking the closure. */
|
||||
reflection_data.color *= coat_tint.rgb;
|
||||
reflection_data.weight = math_average(reflection_data.color);
|
||||
reflection_data.color *= safe_rcp(reflection_data.weight);
|
||||
/* Diffuse component */
|
||||
if (true) {
|
||||
diffuse_data.color = weight * base_color.rgb * coat_tint.rgb;
|
||||
/* Add energy of the sheen layer until we have proper sheen BSDF. */
|
||||
diffuse_data.color += sheen_data_color;
|
||||
|
||||
diffuse_data.weight = math_average(diffuse_data.color);
|
||||
diffuse_data.color *= safe_rcp(diffuse_data.weight);
|
||||
diffuse_data.weight = math_average(diffuse_data.color);
|
||||
diffuse_data.color *= safe_rcp(diffuse_data.weight);
|
||||
}
|
||||
|
||||
/* Ref. #98190: Defines are optimizations for old compilers.
|
||||
* Might become unnecessary with EEVEE-Next. */
|
||||
|
||||
@@ -13,7 +13,6 @@ void node_bsdf_sheen(vec4 color, float roughness, vec3 N, float weight, out Clos
|
||||
diffuse_data.weight = weight;
|
||||
diffuse_data.color = color.rgb;
|
||||
diffuse_data.N = N;
|
||||
diffuse_data.sss_id = 0u;
|
||||
|
||||
result = closure_eval(diffuse_data);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ void node_subsurface_scattering(vec4 color,
|
||||
float anisotropy,
|
||||
vec3 N,
|
||||
float weight,
|
||||
float do_sss,
|
||||
const float do_sss,
|
||||
out Closure result)
|
||||
{
|
||||
color = max(color, vec4(0.0));
|
||||
@@ -18,12 +18,16 @@ void node_subsurface_scattering(vec4 color,
|
||||
ior = max(ior, 1e-5);
|
||||
N = safe_normalize(N);
|
||||
|
||||
ClosureDiffuse diffuse_data;
|
||||
diffuse_data.weight = weight;
|
||||
diffuse_data.color = color.rgb;
|
||||
diffuse_data.N = N;
|
||||
diffuse_data.sss_radius = radius * scale;
|
||||
diffuse_data.sss_id = uint(do_sss);
|
||||
ClosureSubsurface sss_data;
|
||||
sss_data.weight = weight;
|
||||
sss_data.color = color.rgb;
|
||||
sss_data.N = N;
|
||||
sss_data.sss_radius = radius * scale;
|
||||
|
||||
result = closure_eval(diffuse_data);
|
||||
#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES
|
||||
if (do_sss == 0.0) {
|
||||
sss_data.sss_radius = vec3(-1);
|
||||
}
|
||||
#endif
|
||||
result = closure_eval(sss_data);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ void node_bsdf_toon(
|
||||
diffuse_data.weight = weight;
|
||||
diffuse_data.color = color.rgb;
|
||||
diffuse_data.N = N;
|
||||
diffuse_data.sss_id = 0u;
|
||||
|
||||
result = closure_eval(diffuse_data);
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
|
||||
GPU_constant(&use_coat_f),
|
||||
GPU_constant(&use_refract_f),
|
||||
GPU_constant(&use_multi_scatter),
|
||||
GPU_uniform(&use_sss));
|
||||
GPU_constant(&use_sss));
|
||||
}
|
||||
|
||||
static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
|
||||
|
||||
@@ -59,7 +59,7 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat,
|
||||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE);
|
||||
|
||||
return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_uniform(&use_sss));
|
||||
return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_constant(&use_sss));
|
||||
}
|
||||
|
||||
static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node)
|
||||
|
||||
Reference in New Issue
Block a user