Cycles: Add Metallic Tint to Principled BSDF using F82-Tint model #112551

Manually merged
Brecht Van Lommel merged 26 commits from LukasStockner/blender:principled-metallic into main 2023-09-25 19:54:27 +02:00
15 changed files with 346 additions and 92 deletions

View File

@ -26,6 +26,7 @@ enum MicrofacetFresnel {
DIELECTRIC_TINT, /* used by the OSL MaterialX closures */
CONDUCTOR,
GENERALIZED_SCHLICK,
F82_TINT,
};
typedef struct FresnelDielectricTint {
@ -46,6 +47,13 @@ typedef struct FresnelGeneralizedSchlick {
float exponent;
} FresnelGeneralizedSchlick;
typedef struct FresnelF82Tint {
/* Perpendicular reflectivity. */
Spectrum f0;
/* Precomputed (1-cos)^6 factor for edge tint. */
Spectrum b;
} FresnelF82Tint;
typedef struct MicrofacetBsdf {
SHADER_CLOSURE_BASE;
@ -236,6 +244,17 @@ ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf
*r_reflectance = fresnel_conductor(cos_theta_i, fresnel->n, fresnel->k);
*r_transmittance = zero_spectrum();
}
else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) {
/* F82-Tint model, described in "Novel aspects of the Adobe Standard Material" by Kutz et al.
* Essentially, this is the usual Schlick Fresnel with an additional cosI*(1-cosI)^6
* term which modulates the reflectivity around acos(1/7) degrees (ca. 82°). */
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel;
weizhen marked this conversation as resolved Outdated

We should add a reference to the formula. I guess Fresnel and Schlick is well-known enough so people know how to search for them, but this F82 is not.

We should add a reference to the formula. I guess Fresnel and Schlick is well-known enough so people know how to search for them, but this F82 is not.
const float mu = saturatef(1.0f - cos_theta_i);
const float mu5 = sqr(sqr(mu)) * mu;
const Spectrum F_schlick = mix(fresnel->f0, one_spectrum(), mu5) *r_reflectance = saturate(
brecht marked this conversation as resolved
Review

Seems to miss a semicolon

Seems to miss a semicolon
F_schlick - fresnel->b * cos_theta_i * mu5 * mu);
*r_transmittance = zero_spectrum();
}
else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) {
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
bsdf->fresnel;
@ -379,6 +398,14 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg,
}
reflectance = mix(fresnel->f0, fresnel->f90, s) * fresnel->reflection_tint;
}
else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) {
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel;
float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y));
const float s = lookup_table_read_3D(
kg, rough, cos_NI, 0.5f, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16);
/* TODO: Precompute B factor term and account for it here. */
reflectance = mix(fresnel->f0, one_spectrum(), s);
}
return reflectance + transmittance;
}
@ -738,6 +765,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));
@ -780,6 +808,40 @@ ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
}
}
ccl_device void bsdf_microfacet_setup_fresnel_f82_tint(KernelGlobals kg,
ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd,
ccl_private FresnelF82Tint *fresnel,
const Spectrum f82_tint,
const bool preserve_energy)
{
if (isequal(f82_tint, one_spectrum())) {
fresnel->b = zero_spectrum();
}
else {
/* Precompute the F82 term factor for the Fresnel model.
* In the classic F82 model, the F82 input directly determines the value of the Fresnel
* model at ~82°, similar to F0 and F90.
* With F82-Tint, on the other hand, the value at 82° is the value of the classic Schlick
* model multiplied by the tint input.
* Therefore, the factor follows by setting F82Tint(cosI) = FSchlick(cosI) - b*cosI*(1-cosI)^6
* and F82Tint(acos(1/7)) = FSchlick(acos(1/7)) * f82_tint and solving for b. */
const float f = 6.0f / 7.0f;
const float f5 = sqr(sqr(f)) * f;
const Spectrum F_schlick = mix(fresnel->f0, one_spectrum(), mu5);
fresnel->b = F_schlick * (7.0f / (f5 * f)) * (one_spectrum() - f82_tint);
}
bsdf->fresnel_type = MicrofacetFresnel::F82_TINT;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, sd, bsdf, true, true));
if (preserve_energy) {
Spectrum Fss = mix(fresnel->f0, one_spectrum(), 1.0f / 21.0f) - fresnel->b * (1.0f / 126.0f);
microfacet_ggx_preserve_energy(kg, bsdf, sd, Fss);
}
}
ccl_device void bsdf_microfacet_setup_fresnel_constant(KernelGlobals kg,
ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd,

View File

@ -474,6 +474,53 @@ ccl_device void osl_closure_microfacet_setup(KernelGlobals kg,
/* Special-purpose Microfacet closures */
ccl_device void osl_closure_microfacet_f82_tint_setup(
KernelGlobals kg,
ccl_private ShaderData *sd,
uint32_t path_flag,
float3 weight,
ccl_private const MicrofacetF82TintClosure *closure,
float3 *layer_albedo)
{
if (osl_closure_skip(kg, sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) {
return;
}
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight));
if (!bsdf) {
return;
}
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)closure_alloc_extra(
sd, sizeof(FresnelF82Tint));
if (!fresnel) {
return;
}
bsdf->N = maybe_ensure_valid_specular_reflection(sd, closure->N);
bsdf->alpha_x = closure->alpha_x;
bsdf->alpha_y = closure->alpha_y;
bsdf->ior = 0.0f;
bsdf->T = closure->T;
bool preserve_energy = false;
/* Beckmann */
if (closure->distribution == make_string("beckmann", 14712237670914973463ull)) {
sd->flag |= bsdf_microfacet_beckmann_setup(bsdf);
}
/* GGX (either single- or multi-scattering) */
else {
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
preserve_energy = (closure->distribution == make_string("multi_ggx", 16842698693386468366ull));
}
fresnel->f0 = rgb_to_spectrum(closure->f0);
bsdf_microfacet_setup_fresnel_f82_tint(
kg, bsdf, sd, fresnel, rgb_to_spectrum(closure->f82), preserve_energy);
}
ccl_device void osl_closure_microfacet_multi_ggx_glass_setup(
KernelGlobals kg,
ccl_private ShaderData *sd,

View File

@ -84,6 +84,16 @@ OSL_CLOSURE_STRUCT_BEGIN(Microfacet, microfacet)
OSL_CLOSURE_STRUCT_MEMBER(Microfacet, INT, int, refract, NULL)
OSL_CLOSURE_STRUCT_END(Microfacet, microfacet)
OSL_CLOSURE_STRUCT_BEGIN(MicrofacetF82Tint, microfacet_f82_tint)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, STRING, DeviceString, distribution, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, N, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, T, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, FLOAT, float, alpha_x, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, FLOAT, float, alpha_y, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, f0, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, f82, NULL)
OSL_CLOSURE_STRUCT_END(MicrofacetF82Tint, microfacet)
OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXGlass, microfacet_multi_ggx_glass)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, VECTOR, packed_float3, N, NULL)
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetMultiGGXGlass, FLOAT, float, alpha_x, NULL)

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));
}
@ -97,8 +94,8 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
if (Metallic > 0.0) {
color f0 = BaseColor;
color f90 = color(1.0);
MetallicBSDF = generalized_schlick_bsdf(
Normal, T, color(1.0), color(0.0), alpha_x, alpha_y, f0, f90, 5.0, distribution);
MetallicBSDF = microfacet_f82_tint(
distribution, Normal, T, alpha_x, alpha_y, f0, SpecularTint);
BSDF = mix(BSDF, MetallicBSDF, clamp(Metallic, 0.0, 1.0));
}

View File

@ -26,6 +26,9 @@ closure color ashikhmin_velvet(normal N, float sigma) BUILTIN;
closure color sheen(normal N, float roughness) BUILTIN;
closure color ambient_occlusion() BUILTIN;
closure color microfacet_f82_tint(
string distribution, vector N, vector T, float ax, float ay, color f0, color f82) BUILTIN;
/* Needed to pass along the color for multi-scattering saturation adjustment,
* otherwise could be replaced by microfacet() */
closure color microfacet_multi_ggx_glass(normal N, float ag, float eta, color C) BUILTIN;

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);
@ -255,10 +255,10 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
if (reflective_caustics && metallic > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
ccl_private FresnelF82Tint *fresnel =
(bsdf != NULL) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
NULL;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
@ -268,15 +268,11 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
bsdf->alpha_y = alpha_y;
fresnel->f0 = rgb_to_spectrum(base_color);
fresnel->f90 = one_spectrum();
fresnel->exponent = 5.0f;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, is_multiggx);
const bool multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd, fresnel, specular_tint, multiggx);
/* Attenuate other components */
weight *= (1.0f - metallic);
@ -287,9 +283,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 +295,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 +327,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,
weizhen marked this conversation as resolved Outdated

If this is not implemented yet I would suggest to add a TODO in the metallic block.

If this is not implemented yet I would suggest to add a TODO in the metallic block.
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)