Cycles: Tweak Principled BSDF Subsurface parameters #110989

Merged
Lukas Stockner merged 9 commits from LukasStockner/blender:subsurface-principled into main 2023-09-13 02:45:40 +02:00
13 changed files with 312 additions and 248 deletions

View File

@ -11,8 +11,11 @@ typedef struct Bssrdf {
Spectrum radius;
Spectrum albedo;
float roughness;
float anisotropy;
/* Parameters for refractive entry bounce. */
float ior;
float alpha;
} Bssrdf;
static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!");
@ -54,9 +57,7 @@ ccl_device float bssrdf_dipole_compute_alpha_prime(float rd, float fourthirdA)
return xmid;
}
ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf,
const ClosureType type,
const float eta)
ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, const ClosureType type)
{
if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) {
/* Scale mean free path length so it gives similar looking result to older
@ -65,8 +66,8 @@ ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf,
}
else {
/* Adjust radius based on IOR and albedo. */
const float inv_eta = 1.0f / eta;
const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * eta;
const float inv_eta = 1.0f / bssrdf->ior;
const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * bssrdf->ior;
const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) /
(1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */
@ -281,17 +282,34 @@ ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, S
ccl_device int bssrdf_setup(ccl_private ShaderData *sd,
ccl_private Bssrdf *bssrdf,
ClosureType type,
const float ior)
int path_flag,
ClosureType type)
{
/* Clamps protecting against bad/extreme and non physical values. */
bssrdf->anisotropy = clamp(bssrdf->anisotropy, 0.0f, 0.9f);
bssrdf->ior = clamp(bssrdf->ior, 1.01f, 3.8f);
int flag = 0;
if (type == CLOSURE_BSSRDF_RANDOM_WALK_ID) {
/* CLOSURE_BSSRDF_RANDOM_WALK_ID uses a fixed roughness. */
bssrdf->alpha = 1.0f;
}
/* Verify if the radii are large enough to sample without precision issues. */
int bssrdf_channels = SPECTRUM_CHANNELS;
Spectrum diffuse_weight = zero_spectrum();
/* Fall back to diffuse if the radius is smaller than a quarter pixel. */
float min_radius = max(0.25f * sd->dP, BSSRDF_MIN_RADIUS);
if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) {
/* Always fall back to diffuse after a diffuse ancestor. Can't see it well then and adds
* considerable noise due to probabilities of continuing the path getting lower and lower. */
min_radius = FLT_MAX;
}
FOREACH_SPECTRUM_CHANNEL (i) {
if (GET_SPECTRUM_CHANNEL(bssrdf->radius, i) < BSSRDF_MIN_RADIUS) {
if (GET_SPECTRUM_CHANNEL(bssrdf->radius, i) < min_radius) {
GET_SPECTRUM_CHANNEL(diffuse_weight, i) = GET_SPECTRUM_CHANNEL(bssrdf->weight, i);
GET_SPECTRUM_CHANNEL(bssrdf->weight, i) = 0.0f;
GET_SPECTRUM_CHANNEL(bssrdf->radius, i) = 0.0f;
@ -315,12 +333,12 @@ ccl_device int bssrdf_setup(ccl_private ShaderData *sd,
bssrdf->type = type;
bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf_channels;
bssrdf_setup_radius(bssrdf, type, ior);
bssrdf_setup_radius(bssrdf, type);
flag |= SD_BSSRDF;
}
else {
bssrdf->type = type;
bssrdf->type = CLOSURE_NONE_ID;
bssrdf->sample_weight = 0.0f;
}

View File

@ -95,7 +95,7 @@ KERNEL_STRUCT_BEGIN(subsurface)
KERNEL_STRUCT_MEMBER(subsurface, PackedSpectrum, albedo, KERNEL_FEATURE_SUBSURFACE)
KERNEL_STRUCT_MEMBER(subsurface, PackedSpectrum, radius, KERNEL_FEATURE_SUBSURFACE)
KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE)
KERNEL_STRUCT_MEMBER(subsurface, packed_float3, Ng, KERNEL_FEATURE_SUBSURFACE)
KERNEL_STRUCT_MEMBER(subsurface, packed_float3, N, KERNEL_FEATURE_SUBSURFACE)
KERNEL_STRUCT_END(subsurface)
/********************************** Volume Stack ******************************/

View File

@ -23,6 +23,60 @@ CCL_NAMESPACE_BEGIN
#ifdef __SUBSURFACE__
ccl_device_inline bool subsurface_entry_bounce(KernelGlobals kg,
ccl_private const Bssrdf *bssrdf,
ccl_private ShaderData *sd,
ccl_private RNGState *rng_state,
ccl_private float3 *wo)
{
float2 rand_bsdf = path_state_rng_2D(kg, rng_state, PRNG_SUBSURFACE_BSDF);
if (bssrdf->type == CLOSURE_BSSRDF_RANDOM_WALK_ID) {
/* CLOSURE_BSSRDF_RANDOM_WALK_ID has a 50% chance to sample a diffuse entry bounce.
* Also, for the refractive entry, it uses a fixed roughness of 1.0. */
if (rand_bsdf.x < 0.5f) {
rand_bsdf.x *= 2.0f;
float pdf;
sample_cos_hemisphere(-bssrdf->N, rand_bsdf, wo, &pdf);
return true;
}
rand_bsdf.x = 2.0f * (rand_bsdf.x - 0.5f);
}
const float cos_NI = dot(bssrdf->N, sd->wi);
if (cos_NI <= 0.0f) {
return false;
}
float3 X, Y, Z = bssrdf->N;
make_orthonormals(Z, &X, &Y);
const float alpha = bssrdf->alpha;
const float neta = 1.0f / bssrdf->ior;
LukasStockner marked this conversation as resolved Outdated

Unused variable.

Unused variable.
/* Sample microfacet normal by transforming to/from local coordinates. */
const float3 local_I = make_float3(dot(X, sd->wi), dot(Y, sd->wi), cos_NI);
const float3 local_H = microfacet_ggx_sample_vndf(local_I, alpha, alpha, rand_bsdf);
const float3 H = X * local_H.x + Y * local_H.y + Z * local_H.z;
const float cos_HI = dot(H, sd->wi);
const float arg = 1.0f - (sqr(neta) * (1.0f - sqr(cos_HI)));
/* We clamp subsurface IOR to be above 1, so there should never be TIR. */
kernel_assert(arg >= 0.0f);
const float dnp = max(sqrtf(arg), 1e-7f);
const float nK = (neta * cos_HI) - dnp;
*wo = -(neta * sd->wi) + (nK * H);
return true;
/* Note: For a proper refractive GGX interface, we should be computing lambdaI and lambdaO
* and multiplying the throughput by BSDF/pdf, which for VNDF sampling works out to
* (1 + lambdaI) / (1 + lambdaI + lambdaO).
* However, this causes darkening due to the single-scattering approximation, which we'd
* then have to correct with a lookup table.
* Since we only really care about the directional distribution here, it's much easier to
* just skip all that instead. */
}
ccl_device int subsurface_bounce(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
@ -37,32 +91,44 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
/* Setup ray into surface. */
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = bssrdf->N;
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact();
/* Pass along object info, reusing isect to save memory. */
INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng;
uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) |
((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK :
PATH_RAY_SUBSURFACE_RANDOM_WALK);
/* Advance random number offset for bounce. */
INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM;
/* Compute weight, optionally including Fresnel from entry point. */
Spectrum weight = surface_shader_bssrdf_sample_weight(sd, sc);
INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight;
uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA);
if (sc->type == CLOSURE_BSSRDF_BURLEY_ID) {
path_flag |= PATH_RAY_SUBSURFACE_DISK;
INTEGRATOR_STATE_WRITE(state, subsurface, N) = sd->Ng;
}
else {
path_flag |= PATH_RAY_SUBSURFACE_RANDOM_WALK;
/* Sample entry bounce into the material. */
RNGState rng_state;
path_state_rng_load(state, &rng_state);
float3 wo;
if (!subsurface_entry_bounce(kg, bssrdf, sd, &rng_state, &wo) || dot(sd->Ng, wo) >= 0.0f) {
/* Sampling failed, give up on this bounce. */
return LABEL_NONE;
}
INTEGRATOR_STATE_WRITE(state, ray, D) = wo;
INTEGRATOR_STATE_WRITE(state, subsurface, N) = sd->N;
}
if (sd->flag & SD_BACKFACING) {
path_flag |= PATH_RAY_SUBSURFACE_BACKFACING;
}
INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight;
INTEGRATOR_STATE_WRITE(state, path, flag) = path_flag;
/* Advance random number offset for bounce. */
INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM;
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_spectrum();

View File

@ -34,7 +34,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
const float3 P = INTEGRATOR_STATE(state, ray, P);
const float ray_dP = INTEGRATOR_STATE(state, ray, dP);
const float time = INTEGRATOR_STATE(state, ray, time);
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
const float3 Ng = INTEGRATOR_STATE(state, subsurface, N);
const int object = INTEGRATOR_STATE(state, isect, object);
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);

View File

@ -168,24 +168,14 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
ccl_private Ray &ray,
ccl_private LocalIntersection &ss_isect)
{
const float2 rand_bsdf = path_state_rng_2D(kg, &rng_state, PRNG_SUBSURFACE_BSDF);
const float3 P = INTEGRATOR_STATE(state, ray, P);
const float3 N = INTEGRATOR_STATE(state, ray, D);
const float3 D = INTEGRATOR_STATE(state, ray, D);
const float ray_dP = INTEGRATOR_STATE(state, ray, dP);
const float time = INTEGRATOR_STATE(state, ray, time);
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
const float3 N = INTEGRATOR_STATE(state, subsurface, N);
const int object = INTEGRATOR_STATE(state, isect, object);
const int prim = INTEGRATOR_STATE(state, isect, prim);
/* Sample diffuse surface scatter into the object. */
float3 D;
float pdf;
sample_cos_hemisphere(-N, rand_bsdf, &D, &pdf);
if (dot(-Ng, D) <= 0.0f) {
return false;
}
/* Setup ray. */
ray.P = P;
ray.D = D;
@ -436,10 +426,11 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
if (hit) {
kernel_assert(isfinite_safe(throughput));
/* TODO(lukas): Which PDF should we report here? Entry bounce? The random walk? Just 1.0? */
guiding_record_bssrdf_bounce(
kg,
state,
pdf,
1.0f,
N,
D,
safe_divide_color(throughput, INTEGRATOR_STATE(state, path, throughput)),

View File

@ -740,23 +740,16 @@ ccl_device void osl_closure_bssrdf_setup(KernelGlobals kg,
return;
}
/* disable in case of diffuse ancestor, can't see it well then and
* adds considerably noise due to probabilities of continuing path
* getting lower and lower */
if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) {
bssrdf->radius = zero_spectrum();
}
else {
bssrdf->radius = closure->radius;
}
bssrdf->radius = closure->radius;
/* create one closure per color channel */
bssrdf->albedo = closure->albedo;
bssrdf->N = closure->N;
bssrdf->roughness = closure->roughness;
bssrdf->anisotropy = clamp(closure->anisotropy, 0.0f, 0.9f);
bssrdf->alpha = sqr(closure->roughness);
bssrdf->ior = closure->ior;
bssrdf->anisotropy = closure->anisotropy;
sd->flag |= bssrdf_setup(sd, bssrdf, type, clamp(closure->ior, 1.01f, 3.8f));
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, type);
}
/* Hair */

View File

@ -9,8 +9,8 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
string subsurface_method = "random_walk",
color BaseColor = color(0.8, 0.8, 0.8),
float Subsurface = 0.0,
float SubsurfaceScale = 0.1,
vector SubsurfaceRadius = vector(1.0, 1.0, 1.0),
color SubsurfaceColor = color(0.7, 0.1, 0.1),
float SubsurfaceIOR = 1.4,
float SubsurfaceAnisotropy = 0.0,
float Metallic = 0.0,
@ -47,21 +47,21 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
}
if (Metallic < 1.0 && Transmission < 1.0) {
color diffuse_color = mix(BaseColor, SubsurfaceColor, Subsurface);
BSDF = BaseColor * diffuse(Normal);
if (Subsurface > 1e-5) {
BSDF = diffuse_color * bssrdf(subsurface_method,
Normal,
Subsurface * SubsurfaceRadius,
diffuse_color,
"roughness",
Roughness,
"ior",
SubsurfaceIOR,
"anisotropy",
SubsurfaceAnisotropy);
}
else {
BSDF = diffuse_color * diffuse(Normal);
vector radius = SubsurfaceScale * SubsurfaceRadius;
float subsurface_ior = (subsurface_method == "random_walk") ? SubsurfaceIOR : IOR;
closure color SubsurfBSDF = bssrdf(subsurface_method,
Normal,
SubsurfaceScale * SubsurfaceRadius,
BaseColor,
"roughness",
Roughness,
"ior",
subsurface_ior,
"anisotropy",
SubsurfaceAnisotropy);
BSDF = mix(BSDF, BaseColor * SubsurfBSDF, Subsurface);
}
color f0 = color(F0_from_ior(IOR));

View File

@ -22,7 +22,6 @@ ccl_device_inline int svm_node_closure_bsdf_skip(KernelGlobals kg, int offset, u
read_node(kg, &offset);
read_node(kg, &offset);
read_node(kg, &offset);
read_node(kg, &offset);
}
return offset;
@ -71,7 +70,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
uint specular_offset, roughness_offset, specular_tint_offset, anisotropic_offset,
sheen_offset, sheen_tint_offset, sheen_roughness_offset, coat_offset,
coat_roughness_offset, coat_ior_offset, eta_offset, transmission_offset,
anisotropic_rotation_offset, coat_tint_offset, dummy;
anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, dummy;
uint4 data_node2 = read_node(kg, &offset);
float3 T = stack_load_float3(stack, data_node.y);
@ -82,8 +81,11 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
&anisotropic_offset);
svm_unpack_node_uchar4(
data_node.w, &sheen_offset, &sheen_tint_offset, &sheen_roughness_offset, &dummy);
svm_unpack_node_uchar4(
data_node2.x, &eta_offset, &transmission_offset, &anisotropic_rotation_offset, &dummy);
svm_unpack_node_uchar4(data_node2.x,
&eta_offset,
&transmission_offset,
&anisotropic_rotation_offset,
&coat_normal_offset);
svm_unpack_node_uchar4(
data_node2.w, &coat_offset, &coat_roughness_offset, &coat_ior_offset, &coat_tint_offset);
@ -118,27 +120,14 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
__uint_as_float(data_base_color.z),
__uint_as_float(data_base_color.w));
// get the additional coat normal and subsurface scattering radius
uint4 data_cn_ssr = read_node(kg, &offset);
float3 coat_normal = stack_valid(data_cn_ssr.x) ? stack_load_float3(stack, data_cn_ssr.x) :
sd->N;
coat_normal = maybe_ensure_valid_specular_reflection(sd, coat_normal);
float3 subsurface_radius = stack_valid(data_cn_ssr.y) ?
stack_load_float3(stack, data_cn_ssr.y) :
one_float3();
float subsurface_ior = stack_valid(data_cn_ssr.z) ? stack_load_float(stack, data_cn_ssr.z) :
1.4f;
float subsurface_anisotropy = stack_valid(data_cn_ssr.w) ?
stack_load_float(stack, data_cn_ssr.w) :
0.0f;
// get the subsurface scattering data
uint4 data_subsurf = read_node(kg, &offset);
// get the subsurface color
uint4 data_subsurface_color = read_node(kg, &offset);
float3 subsurface_color = stack_valid(data_subsurface_color.x) ?
stack_load_float3(stack, data_subsurface_color.x) :
make_float3(__uint_as_float(data_subsurface_color.y),
__uint_as_float(data_subsurface_color.z),
__uint_as_float(data_subsurface_color.w));
// get the additional coat normal
float3 coat_normal = stack_valid(coat_normal_offset) ?
stack_load_float3(stack, coat_normal_offset) :
sd->N;
coat_normal = maybe_ensure_valid_specular_reflection(sd, coat_normal);
Spectrum weight = closure_weight * mix_weight;
@ -327,44 +316,38 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
}
}
/* Diffuse component */
float3 diffuse_color = mix(base_color, subsurface_color, subsurface);
/* Diffuse/Subsurface component */
#ifdef __SUBSURFACE__
/* disable in case of diffuse ancestor, can't see it well then and
* adds considerably noise due to probabilities of continuing path
* getting lower and lower */
if ((subsurface > CLOSURE_WEIGHT_CUTOFF) && !(path_flag & PATH_RAY_DIFFUSE_ANCESTOR)) {
/* Skip in case of extremely low albedo. */
if (fabsf(average(diffuse_color)) > CLOSURE_WEIGHT_CUTOFF) {
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, rgb_to_spectrum(diffuse_color) * weight);
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd,
rgb_to_spectrum(base_color) * subsurface * weight);
if (bssrdf) {
float3 subsurface_radius = stack_load_float3(stack, data_subsurf.y);
float subsurface_scale = stack_load_float(stack, data_subsurf.z);
if (bssrdf) {
bssrdf->radius = rgb_to_spectrum(subsurface_radius * subsurface);
bssrdf->albedo = rgb_to_spectrum(diffuse_color);
bssrdf->N = N;
bssrdf->roughness = roughness;
/* Clamps protecting against bad/extreme and non physical values. */
subsurface_ior = clamp(subsurface_ior, 1.01f, 3.8f);
bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f);
/* setup bsdf */
sd->flag |= bssrdf_setup(sd, bssrdf, subsurface_method, subsurface_ior);
}
bssrdf->radius = rgb_to_spectrum(subsurface_radius * subsurface_scale);
bssrdf->albedo = rgb_to_spectrum(base_color);
bssrdf->N = N;
bssrdf->alpha = sqr(roughness);
bssrdf->ior = eta;
bssrdf->anisotropy = stack_load_float(stack, data_subsurf.w);
if (subsurface_method == CLOSURE_BSSRDF_RANDOM_WALK_ID) {
bssrdf->ior = stack_load_float(stack, data_subsurf.x);
}
/* setup bsdf */
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, subsurface_method);
}
else
#else
subsurface = 0.0f;
#endif
{
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
sd, sizeof(DiffuseBsdf), rgb_to_spectrum(diffuse_color) * weight);
if (bsdf) {
bsdf->N = N;
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
sd, sizeof(DiffuseBsdf), rgb_to_spectrum(base_color) * (1.0f - subsurface) * weight);
if (bsdf) {
bsdf->N = N;
/* setup bsdf */
sd->flag |= bsdf_diffuse_setup(bsdf);
}
/* setup bsdf */
sd->flag |= bsdf_diffuse_setup(bsdf);
}
break;
@ -797,22 +780,14 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, weight);
if (bssrdf) {
/* disable in case of diffuse ancestor, can't see it well then and
* adds considerably noise due to probabilities of continuing path
* getting lower and lower */
if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR)
param1 = 0.0f;
bssrdf->radius = rgb_to_spectrum(stack_load_float3(stack, data_node.z) * param1);
bssrdf->albedo = closure_weight;
bssrdf->N = N;
bssrdf->roughness = FLT_MAX;
bssrdf->ior = param2;
bssrdf->alpha = 1.0f;
bssrdf->anisotropy = stack_load_float(stack, data_node.w);
const float subsurface_ior = clamp(param2, 1.01f, 3.8f);
const float subsurface_anisotropy = stack_load_float(stack, data_node.w);
bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f);
sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, subsurface_ior);
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, (ClosureType)type);
}
break;

View File

@ -2660,10 +2660,10 @@ NODE_DEFINE(PrincipledBsdfNode)
subsurface_method_enum,
CLOSURE_BSSRDF_RANDOM_WALK_ID);
SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f));
SOCKET_IN_COLOR(subsurface_color, "Subsurface Color", make_float3(0.8f, 0.8f, 0.8f));
SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f))
SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f);
SOCKET_IN_FLOAT(subsurface, "Subsurface", 0.0f);
SOCKET_IN_FLOAT(subsurface_scale, "Subsurface Scale", 0.1f);
SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f));
SOCKET_IN_FLOAT(subsurface_ior, "Subsurface IOR", 1.4f);
SOCKET_IN_FLOAT(subsurface_anisotropy, "Subsurface Anisotropy", 0.0f);
@ -2774,57 +2774,38 @@ void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attribu
ShaderNode::attributes(shader, attributes);
}
void PrincipledBsdfNode::compile(SVMCompiler &compiler,
ShaderInput *p_metallic,
ShaderInput *p_subsurface,
ShaderInput *p_subsurface_radius,
ShaderInput *p_subsurface_ior,
ShaderInput *p_subsurface_anisotropy,
ShaderInput *p_specular,
ShaderInput *p_roughness,
ShaderInput *p_specular_tint,
ShaderInput *p_anisotropic,
ShaderInput *p_sheen,
ShaderInput *p_sheen_roughness,
ShaderInput *p_sheen_tint,
ShaderInput *p_coat,
ShaderInput *p_coat_roughness,
ShaderInput *p_coat_ior,
ShaderInput *p_coat_tint,
ShaderInput *p_ior,
ShaderInput *p_transmission,
ShaderInput *p_anisotropic_rotation)
void PrincipledBsdfNode::compile(SVMCompiler &compiler)
{
ShaderInput *base_color_in = input("Base Color");
ShaderInput *subsurface_color_in = input("Subsurface Color");
ShaderInput *normal_in = input("Normal");
ShaderInput *coat_normal_in = input("Coat Normal");
ShaderInput *tangent_in = input("Tangent");
ShaderInput *p_metallic = input("Metallic");
ShaderInput *p_subsurface = input("Subsurface");
float3 weight = one_float3();
compiler.add_node(NODE_CLOSURE_SET_WEIGHT, weight);
int normal_offset = compiler.stack_assign_if_linked(normal_in);
int coat_normal_offset = compiler.stack_assign_if_linked(coat_normal_in);
int tangent_offset = compiler.stack_assign_if_linked(tangent_in);
int specular_offset = compiler.stack_assign(p_specular);
int roughness_offset = compiler.stack_assign(p_roughness);
int specular_tint_offset = compiler.stack_assign(p_specular_tint);
int anisotropic_offset = compiler.stack_assign(p_anisotropic);
int sheen_offset = compiler.stack_assign(p_sheen);
int sheen_roughness_offset = compiler.stack_assign(p_sheen_roughness);
int sheen_tint_offset = compiler.stack_assign(p_sheen_tint);
int coat_offset = compiler.stack_assign(p_coat);
int coat_roughness_offset = compiler.stack_assign(p_coat_roughness);
int coat_ior_offset = compiler.stack_assign(p_coat_ior);
int coat_tint_offset = compiler.stack_assign(p_coat_tint);
int ior_offset = compiler.stack_assign(p_ior);
int transmission_offset = compiler.stack_assign(p_transmission);
int anisotropic_rotation_offset = compiler.stack_assign(p_anisotropic_rotation);
int subsurface_radius_offset = compiler.stack_assign(p_subsurface_radius);
int subsurface_ior_offset = compiler.stack_assign(p_subsurface_ior);
int subsurface_anisotropy_offset = compiler.stack_assign(p_subsurface_anisotropy);
int normal_offset = compiler.stack_assign_if_linked(input("Normal"));
int coat_normal_offset = compiler.stack_assign_if_linked(input("Coat Normal"));
int tangent_offset = compiler.stack_assign_if_linked(input("Tangent"));
int specular_offset = compiler.stack_assign(input("Specular"));
int roughness_offset = compiler.stack_assign(input("Roughness"));
int specular_tint_offset = compiler.stack_assign(input("Specular Tint"));
int anisotropic_offset = compiler.stack_assign(input("Anisotropic"));
int sheen_offset = compiler.stack_assign(input("Sheen"));
int sheen_roughness_offset = compiler.stack_assign(input("Sheen Roughness"));
int sheen_tint_offset = compiler.stack_assign(input("Sheen Tint"));
int coat_offset = compiler.stack_assign(input("Coat"));
int coat_roughness_offset = compiler.stack_assign(input("Coat Roughness"));
int coat_ior_offset = compiler.stack_assign(input("Coat IOR"));
int coat_tint_offset = compiler.stack_assign(input("Coat Tint"));
int ior_offset = compiler.stack_assign(input("IOR"));
int transmission_offset = compiler.stack_assign(input("Transmission"));
int anisotropic_rotation_offset = compiler.stack_assign(input("Anisotropic Rotation"));
int subsurface_radius_offset = compiler.stack_assign(input("Subsurface Radius"));
int subsurface_scale_offset = compiler.stack_assign(input("Subsurface Scale"));
int subsurface_ior_offset = compiler.stack_assign(input("Subsurface IOR"));
int subsurface_anisotropy_offset = compiler.stack_assign(input("Subsurface Anisotropy"));
compiler.add_node(NODE_CLOSURE_BSDF,
compiler.encode_uchar4(closure,
@ -2844,7 +2825,7 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler,
compiler.add_node(
compiler.encode_uchar4(
ior_offset, transmission_offset, anisotropic_rotation_offset, SVM_STACK_INVALID),
ior_offset, transmission_offset, anisotropic_rotation_offset, coat_normal_offset),
distribution,
subsurface_method,
compiler.encode_uchar4(
@ -2858,42 +2839,10 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler,
__float_as_int(bc_default.y),
__float_as_int(bc_default.z));
compiler.add_node(coat_normal_offset,
compiler.add_node(subsurface_ior_offset,
subsurface_radius_offset,
subsurface_ior_offset,
subsurface_scale_offset,
subsurface_anisotropy_offset);
float3 ss_default = get_float3(subsurface_color_in->socket_type);
compiler.add_node(((subsurface_color_in->link) ? compiler.stack_assign(subsurface_color_in) :
SVM_STACK_INVALID),
__float_as_int(ss_default.x),
__float_as_int(ss_default.y),
__float_as_int(ss_default.z));
}
void PrincipledBsdfNode::compile(SVMCompiler &compiler)
{
compile(compiler,
input("Metallic"),
input("Subsurface"),
input("Subsurface Radius"),
input("Subsurface IOR"),
input("Subsurface Anisotropy"),
input("Specular"),
input("Roughness"),
input("Specular Tint"),
input("Anisotropic"),
input("Sheen"),
input("Sheen Roughness"),
input("Sheen Tint"),
input("Coat"),
input("Coat Roughness"),
input("Coat IOR"),
input("Coat Tint"),
input("IOR"),
input("Transmission"),
input("Anisotropic Rotation"));
}
void PrincipledBsdfNode::compile(OSLCompiler &compiler)

View File

@ -517,30 +517,10 @@ class PrincipledBsdfNode : public BsdfBaseNode {
void expand(ShaderGraph *graph);
bool has_surface_bssrdf();
bool has_bssrdf_bump();
void compile(SVMCompiler &compiler,
ShaderInput *metallic,
ShaderInput *subsurface,
ShaderInput *subsurface_radius,
ShaderInput *subsurface_ior,
ShaderInput *subsurface_anisotropy,
ShaderInput *specular,
ShaderInput *roughness,
ShaderInput *specular_tint,
ShaderInput *anisotropic,
ShaderInput *sheen,
ShaderInput *sheen_roughness,
ShaderInput *sheen_tint,
ShaderInput *coat,
ShaderInput *coat_roughness,
ShaderInput *coat_ior,
ShaderInput *coat_tint,
ShaderInput *ior,
ShaderInput *transmission,
ShaderInput *anisotropic_rotation);
NODE_SOCKET_API(float3, base_color)
NODE_SOCKET_API(float3, subsurface_color)
NODE_SOCKET_API(float3, subsurface_radius)
NODE_SOCKET_API(float, subsurface_scale)
NODE_SOCKET_API(float, subsurface_ior)
NODE_SOCKET_API(float, subsurface_anisotropy)
NODE_SOCKET_API(float, metallic)

View File

@ -580,6 +580,86 @@ static void version_principled_bsdf_sheen(bNodeTree *ntree)
}
}
/* Convert subsurface inputs on the Principled BSDF. */
static void version_principled_bsdf_subsurface(bNodeTree *ntree)
{
/* - Create Subsurface Scale input
* - If a node's Subsurface input was connected or nonzero:
* - Make the Base Color a mix of old Base Color and Subsurface Color,
* using Subsurface as the mix factor
* - Move Subsurface link and default value to the new Subsurface Scale input
* - Set the Subsurface input to 1.0
* - Remove Subsurface Color input
*/
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != SH_NODE_BSDF_PRINCIPLED) {
continue;
}
if (nodeFindSocket(node, SOCK_IN, "Subsurface Scale")) {
/* Node is already updated. */
continue;
}
/* Add Scale input */
bNodeSocket *scale_in = nodeAddStaticSocket(
ntree, node, SOCK_IN, SOCK_FLOAT, PROP_DISTANCE, "Subsurface Scale", "Subsurface Scale");
bNodeSocket *subsurf = nodeFindSocket(node, SOCK_IN, "Subsurface");
float *subsurf_val = version_cycles_node_socket_float_value(subsurf);
*version_cycles_node_socket_float_value(scale_in) = *subsurf_val;
if (subsurf->link == nullptr && *subsurf_val == 0.0f) {
/* Node doesn't use Subsurf, we're done here. */
continue;
}
/* Fix up Subsurface Color input */
bNodeSocket *base_col = nodeFindSocket(node, SOCK_IN, "Base Color");
bNodeSocket *subsurf_col = nodeFindSocket(node, SOCK_IN, "Subsurface Color");
float *base_col_val = version_cycles_node_socket_rgba_value(base_col);
float *subsurf_col_val = version_cycles_node_socket_rgba_value(subsurf_col);
/* If any of the three inputs is dynamic, we need a Mix node. */
if (subsurf->link || subsurf_col->link || base_col->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), base_col_val);
copy_v4_v4(version_cycles_node_socket_rgba_value(b_in), subsurf_col_val);
*version_cycles_node_socket_float_value(fac_in) = *subsurf_val;
if (base_col->link) {
nodeAddLink(ntree, base_col->link->fromnode, base_col->link->fromsock, mix, a_in);
nodeRemLink(ntree, base_col->link);
}
if (subsurf_col->link) {
nodeAddLink(ntree, subsurf_col->link->fromnode, subsurf_col->link->fromsock, mix, b_in);
nodeRemLink(ntree, subsurf_col->link);
}
if (subsurf->link) {
nodeAddLink(ntree, subsurf->link->fromnode, subsurf->link->fromsock, mix, fac_in);
nodeAddLink(ntree, subsurf->link->fromnode, subsurf->link->fromsock, node, scale_in);
nodeRemLink(ntree, subsurf->link);
}
nodeAddLink(ntree, mix, result_out, node, base_col);
}
/* Mix the fixed values. */
interp_v4_v4v4(base_col_val, base_col_val, subsurf_col_val, *subsurf_val);
/* Set node to 100% subsurface, 0% diffuse. */
*subsurf_val = 1.0f;
/* Delete Subsurface Color input */
nodeRemoveSocket(ntree, node, subsurf_col);
}
}
/* Replace old Principled Hair BSDF as a variant in the new Principled Hair BSDF. */
static void version_replace_principled_hair_model(bNodeTree *ntree)
{
@ -1096,6 +1176,8 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
if (ntree->type == NTREE_SHADER) {
/* Convert coat inputs on the Principled BSDF. */
version_principled_bsdf_coat(ntree);
/* Convert subsurface inputs on the Principled BSDF. */
version_principled_bsdf_subsurface(ntree);
}
}
FOREACH_NODETREE_END;

View File

@ -20,8 +20,8 @@ float principled_sheen(float NV, float rough)
void node_bsdf_principled(vec4 base_color,
float subsurface,
float subsurface_scale,
vec3 subsurface_radius,
vec4 subsurface_color,
float subsurface_ior,
float subsurface_anisotropy,
float metallic,
@ -155,10 +155,9 @@ void node_bsdf_principled(vec4 base_color,
/* Diffuse component */
if (true) {
vec3 diffuse_color = mix(base_color.rgb, subsurface_color.rgb, subsurface);
diffuse_data.sss_radius = subsurface_radius * subsurface;
diffuse_data.sss_radius = subsurface_radius * subsurface_scale;
diffuse_data.sss_id = uint(do_sss);
diffuse_data.color += weight * diffuse_color * coat_tint.rgb;
diffuse_data.color += weight * base_color.rgb * coat_tint.rgb;
}
/* Adjust the weight of picking the closure. */

View File

@ -25,21 +25,31 @@ static void node_declare(NodeDeclarationBuilder &b)
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
.subtype(PROP_FACTOR)
.description(
"Blend between diffuse surface and subsurface scattering. "
"Typically should be zero or one (either fully diffuse or subsurface)");
#define SOCK_SUBSURFACE_ID 1
b.add_input<decl::Float>("Subsurface Scale")
.default_value(0.05f)
.min(0.0f)
.max(10.0f)
.subtype(PROP_DISTANCE)
.description("Scale of the subsurface scattering (multiplied with Radius)");
#define SOCK_SUBSURFACE_SCALE_ID 2
b.add_input<decl::Vector>("Subsurface Radius")
.default_value({1.0f, 0.2f, 0.1f})
.min(0.0f)
.max(100.0f)
.compact();
#define SOCK_SUBSURFACE_RADIUS_ID 2
b.add_input<decl::Color>("Subsurface Color").default_value({0.8f, 0.8f, 0.8f, 1.0f});
#define SOCK_SUBSURFACE_COLOR_ID 3
.compact()
.description("Scattering radius to use for subsurface component (multiplied with Scale)");
#define SOCK_SUBSURFACE_RADIUS_ID 3
b.add_input<decl::Float>("Subsurface IOR")
.default_value(1.4f)
.min(1.01f)
.max(3.8f)
.subtype(PROP_FACTOR);
.subtype(PROP_FACTOR)
.description("Index of refraction used for rays that enter the subsurface component");
#define SOCK_SUBSURFACE_IOR_ID 4
b.add_input<decl::Float>("Subsurface Anisotropy")
.default_value(0.0f)
@ -266,11 +276,12 @@ static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
{
const int sss_method = node->custom2;
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (STR_ELEM(sock->name, "Subsurface IOR", "Subsurface Anisotropy")) {
bke::nodeSetSocketAvailability(ntree, sock, sss_method != SHD_SUBSURFACE_BURLEY);
}
}
bke::nodeSetSocketAvailability(ntree,
nodeFindSocket(node, SOCK_IN, "Subsurface IOR"),
sss_method == SHD_SUBSURFACE_RANDOM_WALK);
bke::nodeSetSocketAvailability(ntree,
nodeFindSocket(node, SOCK_IN, "Subsurface Anisotropy"),
sss_method != SHD_SUBSURFACE_BURLEY);
}
} // namespace blender::nodes::node_shader_bsdf_principled_cc