Cycles: Add Metallic Tint to Principled BSDF using F82-Tint model #112551
|
@ -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
|
||||
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
Weizhen Huang
commented
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Weizhen Huang
commented
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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
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.