Cycles/EEVEE: Change specular tint in Principled BSDF from float to color #112192

Manually merged
Brecht Van Lommel merged 25 commits from weizhen/blender:principled-specular-tint into main 2023-09-25 19:54:45 +02:00
12 changed files with 217 additions and 80 deletions

View File

@ -738,6 +738,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
ccl_private FresnelGeneralizedSchlick *fresnel,
const bool preserve_energy)
{
fresnel->f0 = saturate(fresnel->f0);
bsdf->fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, sd, bsdf, true, true));

View File

@ -15,7 +15,7 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
float SubsurfaceAnisotropy = 0.0,
float Metallic = 0.0,
float Specular = 0.5,
float SpecularTint = 0.0,
color SpecularTint = color(1.0),
float Roughness = 0.5,
float Anisotropic = 0.0,
float AnisotropicRotation = 0.0,
@ -67,14 +67,9 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
BSDF = mix(BSDF, BaseColor * SubsurfBSDF, Subsurface);
}
color f0 = color(F0_from_ior(IOR));
color f90 = color(1.0);
/* Apply specular tint */
float m_cdlum = luminance(BaseColor);
color m_ctint = m_cdlum > 0.0 ? BaseColor / m_cdlum : color(1.0);
color specTint = mix(color(1.0), m_ctint, SpecularTint);
f0 *= (specTint * 2.0 * Specular);
color f0 = F0_from_ior(IOR) * SpecularTint * 2.0 * Specular;
color f90 = color(1.0);
BSDF = layer(
generalized_schlick_bsdf(
@ -84,12 +79,14 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
closure color TransmissionBSDF = 0;
if (Metallic < 1.0 && Transmission > 0.0) {
color reflectTint = mix(color(1.0), BaseColor, SpecularTint);
float eta = max(IOR, 1e-5);
eta = backfacing() ? 1.0 / eta : eta;
TransmissionBSDF = dielectric_bsdf(
Normal, vector(0.0), reflectTint, sqrt(BaseColor), r2, r2, eta, distribution);
color f0 = F0_from_ior(eta) * SpecularTint;
color f90 = color(1.0);
TransmissionBSDF = generalized_schlick_bsdf(
Normal, vector(0.0), color(1.0), sqrt(BaseColor), r2, r2, f0, f90, -eta, distribution),
BSDF = mix(BSDF, TransmissionBSDF, clamp(Transmission, 0.0, 1.0));
}

View File

@ -102,7 +102,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
float subsurface = param2;
float specular = stack_load_float(stack, specular_offset);
float roughness = stack_load_float(stack, roughness_offset);
float specular_tint = stack_load_float(stack, specular_tint_offset);
Spectrum specular_tint = rgb_to_spectrum(stack_load_float3(stack, specular_tint_offset));
float anisotropic = stack_load_float(stack, anisotropic_offset);
float sheen = stack_load_float(stack, sheen_offset);
float3 sheen_tint = stack_load_float3(stack, sheen_tint_offset);
@ -287,9 +287,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
if (glass_caustics && transmission > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission * weight);
ccl_private FresnelDielectricTint *fresnel =
(bsdf != NULL) ? (ccl_private FresnelDielectricTint *)closure_alloc_extra(
sd, sizeof(FresnelDielectricTint)) :
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
@ -299,14 +299,16 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta;
fresnel->reflection_tint = mix(
one_spectrum(), rgb_to_spectrum(base_color), specular_tint);
fresnel->f0 = F0_from_ior(eta) * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -eta;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = sqrt(rgb_to_spectrum(base_color));
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_dielectric_tint(kg, bsdf, sd, fresnel, is_multiggx);
bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, is_multiggx);
/* Attenuate other components */
weight *= (1.0f - transmission);
@ -329,11 +331,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
float m_cdlum = linear_rgb_to_gray(kg, base_color);
float3 m_ctint = m_cdlum > 0.0f ? base_color / m_cdlum : one_float3();
float3 specTint = mix(one_spectrum(), rgb_to_spectrum(m_ctint), specular_tint);
fresnel->f0 = F0_from_ior(eta) * 2.0f * specular * specTint;
fresnel->f0 = F0_from_ior(eta) * 2.0f * specular * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -eta;
fresnel->reflection_tint = one_spectrum();

View File

@ -2707,7 +2707,7 @@ NODE_DEFINE(PrincipledBsdfNode)
SOCKET_IN_FLOAT(subsurface_anisotropy, "Subsurface Anisotropy", 0.0f);
SOCKET_IN_FLOAT(specular, "Specular", 0.0f);
SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f);
SOCKET_IN_FLOAT(specular_tint, "Specular Tint", 0.0f);
SOCKET_IN_COLOR(specular_tint, "Specular Tint", one_float3());
SOCKET_IN_FLOAT(anisotropic, "Anisotropic", 0.0f);
SOCKET_IN_FLOAT(sheen, "Sheen", 0.0f);
SOCKET_IN_FLOAT(sheen_roughness, "Sheen Roughness", 0.5f);

View File

@ -527,7 +527,7 @@ class PrincipledBsdfNode : public BsdfBaseNode {
NODE_SOCKET_API(float, subsurface)
NODE_SOCKET_API(float, specular)
NODE_SOCKET_API(float, roughness)
NODE_SOCKET_API(float, specular_tint)
NODE_SOCKET_API(float3, specular_tint)
NODE_SOCKET_API(float, anisotropic)
NODE_SOCKET_API(float, sheen)
NODE_SOCKET_API(float, sheen_roughness)

View File

@ -791,6 +791,65 @@ static void version_principled_bsdf_coat(bNodeTree *ntree)
ntree, SH_NODE_BSDF_PRINCIPLED, "Clearcoat Normal", "Coat Normal");
}
/* Convert specular tint in Principled BSDF. */
static void version_principled_bsdf_specular_tint(bNodeTree *ntree)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != SH_NODE_BSDF_PRINCIPLED) {
continue;
}
bNodeSocket *specular_tint_sock = nodeFindSocket(node, SOCK_IN, "Specular Tint");
if (specular_tint_sock->type == SOCK_RGBA) {
/* Node is already updated. */
continue;
}
bNodeSocket *base_color_sock = nodeFindSocket(node, SOCK_IN, "Base Color");
float specular_tint_old = *version_cycles_node_socket_float_value(specular_tint_sock);
float *base_color = version_cycles_node_socket_rgba_value(base_color_sock);
/* Change socket type to Color. */
nodeModifySocketTypeStatic(ntree, node, specular_tint_sock, SOCK_RGBA, 0);
static float one[] = {1.0f, 1.0f, 1.0f, 1.0f};
/* If any of the two inputs is dynamic, we add a Mix node. */
if (base_color_sock->link || specular_tint_sock->link) {
bNode *mix = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX);
static_cast<NodeShaderMix *>(mix->storage)->data_type = SOCK_RGBA;
mix->locx = node->locx - 170;
mix->locy = node->locy - 120;
bNodeSocket *a_in = nodeFindSocket(mix, SOCK_IN, "A_Color");
bNodeSocket *b_in = nodeFindSocket(mix, SOCK_IN, "B_Color");
bNodeSocket *fac_in = nodeFindSocket(mix, SOCK_IN, "Factor_Float");
bNodeSocket *result_out = nodeFindSocket(mix, SOCK_OUT, "Result_Color");
copy_v4_v4(version_cycles_node_socket_rgba_value(a_in), one);
copy_v4_v4(version_cycles_node_socket_rgba_value(b_in), base_color);
*version_cycles_node_socket_float_value(fac_in) = specular_tint_old;
if (base_color_sock->link) {
nodeAddLink(
ntree, base_color_sock->link->fromnode, base_color_sock->link->fromsock, mix, b_in);
}
if (specular_tint_sock->link) {
nodeAddLink(ntree,
specular_tint_sock->link->fromnode,
specular_tint_sock->link->fromsock,
mix,
fac_in);
nodeRemLink(ntree, specular_tint_sock->link);
}
nodeAddLink(ntree, mix, result_out, node, specular_tint_sock);
}
float *specular_tint = version_cycles_node_socket_rgba_value(specular_tint_sock);
/* Mix the fixed values. */
interp_v4_v4v4(specular_tint, one, base_color, specular_tint_old);
}
}
static void version_copy_socket(bNodeTreeInterfaceSocket &dst,
const bNodeTreeInterfaceSocket &src,
char *identifier)
@ -1366,5 +1425,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
*/
{
/* Keep this block, even when empty. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_SHADER) {
/* Convert specular tint on the Principled BSDF. */
version_principled_bsdf_specular_tint(ntree);
}
}
FOREACH_NODETREE_END;
}
}

View File

@ -93,7 +93,15 @@ float ambient_occlusion_eval(vec3 normal,
vec3 safe_normalize(vec3 N);
float fast_sqrt(float a);
vec3 cameraVec(vec3 P);
vec2 bsdf_lut(float a, float b, float c, float d);
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
float do_multiscatter,
out vec3 reflectance,
out vec3 transmittance);
vec2 brdf_lut(float a, float b);
vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c);
vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c);

View File

@ -101,24 +101,35 @@ vec3 lut_coords_bsdf(float cos_theta, float roughness, float ior)
return coords;
}
/* Computes the reflectance and transmittance based on the BSDF LUT. */
vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
/* Computes the reflectance and transmittance based on the tint (`f0`, `f90`, `transmission_tint`)
* and the BSDF LUT. */
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
float do_multiscatter,
out vec3 reflectance,
out vec3 transmittance)
{
if (ior == 1.0) {
return vec2(0.0, 1.0);
reflectance = vec3(0.0);
transmittance = transmission_tint;
return;
}
vec2 split_sum;
float transmission_factor;
float F0 = F0_from_ior(ior);
float F90 = 1.0;
if (ior >= 1.0) {
if (ior > 1.0) {
split_sum = brdf_lut(cos_theta, roughness);
transmission_factor = sample_3D_texture(utilTex, lut_coords_btdf(cos_theta, roughness, ior)).a;
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
F90 = saturate(2.33 / 0.33 * (ior - 1.0) / (ior + 1.0));
if (all(equal(F90, vec3(1.0)))) {
F90 = vec3(saturate(2.33 / 0.33 * (ior - 1.0) / (ior + 1.0)));
}
}
else {
vec3 bsdf = sample_3D_texture(utilTex, lut_coords_bsdf(cos_theta, roughness, ior)).rgb;
@ -126,20 +137,34 @@ vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter
transmission_factor = bsdf.b;
}
float reflectance = F_brdf_single_scatter(vec3(F0), vec3(F90), split_sum).r;
float transmittance = (1.0 - F0) * transmission_factor;
reflectance = F_brdf_single_scatter(F0, F90, split_sum);
transmittance = (vec3(1.0) - F0) * transmission_factor * transmission_tint;
if (do_multiscatter != 0.0) {
float Ess = F0 * split_sum.x + split_sum.y + (1.0 - F0) * transmission_factor;
/* TODO: maybe add saturation for higher roughness similar as in `F_brdf_multi_scatter()`.
* However, it is not necessarily desirable that the users see a different color than they
* picked. */
float scale = 1.0 / Ess;
float real_F0 = F0_from_ior(ior);
float Ess = real_F0 * split_sum.x + split_sum.y + (1.0 - real_F0) * transmission_factor;
float Ems = 1.0 - Ess;
/* Assume that the transmissive tint makes up most of the overall color if it's not zero. */
vec3 Favg = all(equal(transmission_tint, vec3(0.0))) ? F0 + (F90 - F0) / 21.0 :
transmission_tint;
vec3 scale = 1.0 / (1.0 - Ems * Favg);
reflectance *= scale;
transmittance *= scale;
}
return vec2(reflectance, transmittance);
return;
}
/* Computes the reflectance and transmittance based on the BSDF LUT. */
vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
{
float F0 = F0_from_ior(ior);
vec3 color = vec3(1.0);
vec3 reflectance, transmittance;
bsdf_lut(
F0, color, color, cos_theta, roughness, ior, do_multiscatter, reflectance, transmittance);
return vec2(reflectance.r, transmittance.r);
}
/** \} */

View File

@ -43,6 +43,21 @@ vec2 bsdf_lut(float a, float b, float c, float d)
return vec2(0.0);
}
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
float do_multiscatter,
out vec3 reflectance,
out vec3 transmittance)
{
reflectance = vec3(0.0);
transmittance = vec3(0.0);
return;
}
vec2 brdf_lut(float a, float b)
{
return vec2(0.0);

View File

@ -320,26 +320,37 @@ vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
return vec3(sqrt((ior - 1.0) / (ior + 1.0)), sqrt(1.0 - cos_theta), roughness);
}
/* Computes the reflectance and transmittance based on the BSDF LUT. */
vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
/* Computes the reflectance and transmittance based on the tint (`f0`, `f90`, `transmission_tint`)
* and the BSDF LUT. */
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
float do_multiscatter,
out vec3 reflectance,
out vec3 transmittance)
{
#ifdef EEVEE_UTILITY_TX
if (ior == 1.0) {
return vec2(0.0, 1.0);
reflectance = vec3(0.0);
transmittance = transmission_tint;
return;
}
vec2 split_sum;
float transmission_factor;
float F0 = F0_from_ior(ior);
float F90 = 1.0;
if (ior >= 1.0) {
if (ior > 1.0) {
split_sum = brdf_lut(cos_theta, roughness);
vec3 coords = lut_coords_btdf(cos_theta, roughness, ior);
transmission_factor = utility_tx_sample_bsdf_lut(utility_tx, coords.xy, coords.z).a;
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
F90 = saturate(2.33 / 0.33 * (ior - 1.0) / (ior + 1.0));
if (all(equal(F90, vec3(1.0)))) {
F90 = vec3(saturate(2.33 / 0.33 * (ior - 1.0) / (ior + 1.0)));
}
}
else {
vec3 coords = lut_coords_bsdf(cos_theta, roughness, ior);
@ -348,23 +359,37 @@ vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter
transmission_factor = bsdf.b;
}
float reflectance = F_brdf_single_scatter(vec3(F0), vec3(F90), split_sum).r;
float transmittance = (1.0 - F0) * transmission_factor;
reflectance = F_brdf_single_scatter(F0, F90, split_sum);
transmittance = (vec3(1.0) - F0) * transmission_factor * transmission_tint;
if (do_multiscatter != 0.0) {
float Ess = F0 * split_sum.x + split_sum.y + (1.0 - F0) * transmission_factor;
/* TODO: maybe add saturation for higher roughness similar as in `F_brdf_multi_scatter()`.
* However, it is not necessarily desirable that the users see a different color than they
* picked. */
float scale = 1.0 / Ess;
float real_F0 = F0_from_ior(ior);
float Ess = real_F0 * split_sum.x + split_sum.y + (1.0 - real_F0) * transmission_factor;
float Ems = 1.0 - Ess;
/* Assume that the transmissive tint makes up most of the overall color if it's not zero. */
vec3 Favg = all(equal(transmission_tint, vec3(0.0))) ? F0 + (F90 - F0) / 21.0 :
transmission_tint;
vec3 scale = 1.0 / (1.0 - Ems * Favg);
reflectance *= scale;
transmittance *= scale;
}
return vec2(reflectance, transmittance);
#else
return vec2(0.0);
reflectance = vec3(0.0);
transmittance = vec3(0.0);
#endif
return;
}
/* Computes the reflectance and transmittance based on the BSDF LUT. */
vec2 bsdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
{
float F0 = F0_from_ior(ior);
vec3 color = vec3(1.0);
vec3 reflectance, transmittance;
bsdf_lut(
F0, color, color, cos_theta, roughness, ior, do_multiscatter, reflectance, transmittance);
return vec2(reflectance.r, transmittance.r);
}
#ifdef EEVEE_MATERIAL_STUBS

View File

@ -32,7 +32,7 @@ void node_bsdf_principled(vec4 base_color,
float subsurface_ior,
float subsurface_anisotropy,
float specular,
float specular_tint,
vec4 specular_tint,
float anisotropic,
float anisotropic_rotation,
vec3 T,
@ -109,10 +109,11 @@ void node_bsdf_principled(vec4 base_color,
ClosureReflection reflection_data;
reflection_data.N = N;
reflection_data.roughness = roughness;
vec2 split_sum = brdf_lut(NV, roughness);
if (true) {
vec3 f0 = base_color.rgb;
vec3 f90 = vec3(1.0);
vec2 split_sum = brdf_lut(NV, roughness);
vec3 metallic_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
reflection_data.color = weight * metallic * metallic_brdf;
@ -122,15 +123,18 @@ void node_bsdf_principled(vec4 base_color,
/* Transmission component */
ClosureRefraction refraction_data;
/* TODO: change `specular_tint` to rgb. */
vec3 reflection_tint = mix(vec3(1.0), base_color.rgb, specular_tint);
vec3 reflection_tint = specular_tint.rgb;
if (true) {
vec2 bsdf = bsdf_lut(NV, roughness, ior, do_multiscatter);
vec3 F0 = vec3(F0_from_ior(ior)) * reflection_tint;
vec3 F90 = vec3(1.0);
vec3 reflectance, transmittance;
bsdf_lut(
F0, F90, base_color.rgb, NV, roughness, ior, do_multiscatter, reflectance, transmittance);
reflection_data.color += weight * transmission * bsdf.x * reflection_tint;
reflection_data.color += weight * transmission * reflectance;
refraction_data.weight = weight * transmission * bsdf.y;
refraction_data.color = base_color.rgb * coat_tint.rgb;
refraction_data.weight = weight * transmission;
refraction_data.color = transmittance * coat_tint.rgb;
refraction_data.N = N;
refraction_data.roughness = roughness;
refraction_data.ior = ior;
@ -140,17 +144,15 @@ void node_bsdf_principled(vec4 base_color,
/* Specular component */
if (true) {
vec3 f0 = vec3(F0_from_ior(ior));
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
vec3 f90 = sqrt(saturate(f0 / 0.02));
f0 *= 2.0 * specular * reflection_tint;
vec3 F0 = vec3(F0_from_ior(ior)) * 2.0 * specular * reflection_tint;
F0 = clamp(F0, vec3(0.0), vec3(1.0));
vec3 F90 = vec3(1.0);
vec3 reflectance, unused;
bsdf_lut(F0, F90, vec3(0.0), NV, roughness, ior, do_multiscatter, reflectance, unused);
vec3 specular_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
reflection_data.color += weight * specular_brdf;
reflection_data.color += weight * reflectance;
/* Attenuate lower layers */
weight *= (1.0 - max_v3(specular_brdf));
weight *= (1.0 - max_v3(reflectance));
}
/* Diffuse component */

View File

@ -110,11 +110,10 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
#define SOCK_SPECULAR_ID 13
spec.add_input<decl::Float>("Specular Tint")
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
spec.add_input<decl::Color>("Specular Tint")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.description(
"Tint of the specular reflection at normal incidence. Affects dielectric materials");
#define SOCK_SPECULAR_TINT_ID 14
spec.add_input<decl::Float>("Anisotropic")
.default_value(0.0f)