Cycles: initial commit of microfacet hair bsdf

This is an implementation of the paper [A Microfacet-based Hair Scattering Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by Weizhen Huang, Matthias Hullin, and Johannes Hanika.
Original implementation in [Mitsuba 2](https://github.com/RiverIntheSky/roughhair) by Weizhen Huang.
Adapted to Cycles by Olivier Maury D16682 and Christophe Hery.
This commit is contained in:
Weizhen Huang 2022-12-06 18:43:56 +01:00
parent de9f32a666
commit 68573757bc
25 changed files with 2510 additions and 7 deletions

View File

@ -660,6 +660,21 @@ static ShaderNode *add_node(Scene *scene,
NODE_PRINCIPLED_HAIR_REFLECTANCE));
node = principled_hair;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfHairMicrofacet)) {
BL::ShaderNodeBsdfHairMicrofacet b_microfacet_hair_node(b_node);
MicrofacetHairBsdfNode *microfacet_hair = graph->create_node<MicrofacetHairBsdfNode>();
microfacet_hair->set_parametrization(
(NodeMicrofacetHairParametrization)get_enum(b_microfacet_hair_node.ptr,
"parametrization",
NODE_MICROFACET_HAIR_NUM,
NODE_MICROFACET_HAIR_REFLECTANCE));
microfacet_hair->set_model_type(
(NodeMicrofacetHairModelType)get_enum(b_microfacet_hair_node.ptr,
"model_type",
NODE_MICROFACET_HAIR_MODEL_TYPE_NUM,
NODE_MICROFACET_HAIR_CIRCULAR_GGX));
node = microfacet_hair;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfPrincipled)) {
BL::ShaderNodeBsdfPrincipled b_principled_node(b_node);
PrincipledBsdfNode *principled = graph->create_node<PrincipledBsdfNode>();

View File

@ -128,6 +128,7 @@ set(SRC_KERNEL_CLOSURE_HEADERS
closure/bsdf_principled_diffuse.h
closure/bsdf_principled_sheen.h
closure/bsdf_hair_principled.h
closure/bsdf_hair_microfacet.h
)
set(SRC_KERNEL_SVM_HEADERS

View File

@ -18,6 +18,7 @@
#include "kernel/closure/bsdf_toon.h"
#include "kernel/closure/bsdf_hair.h"
#include "kernel/closure/bsdf_hair_principled.h"
#include "kernel/closure/bsdf_hair_microfacet.h"
#include "kernel/closure/bsdf_principled_diffuse.h"
#include "kernel/closure/bsdf_principled_sheen.h"
#include "kernel/closure/bssrdf.h"
@ -243,6 +244,10 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
label = bsdf_principled_hair_sample(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_HAIR_MICROFACET_ID:
label = bsdf_microfacet_hair_sample(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf);
*sampled_roughness = one_float2();
@ -408,6 +413,11 @@ ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
*roughness = make_float2(alpha, alpha);
*eta = ((ccl_private PrincipledHairBSDF *)sc)->eta;
break;
case CLOSURE_BSDF_HAIR_MICROFACET_ID:
alpha = ((ccl_private MicrofacetHairBSDF *)sc)->roughness;
*roughness = make_float2(alpha, alpha);
*eta = ((ccl_private MicrofacetHairBSDF *)sc)->eta;
break;
case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID:
*roughness = one_float2();
*eta = 1.0f;
@ -504,6 +514,7 @@ ccl_device_inline int bsdf_label(const KernelGlobals kg,
label = LABEL_TRANSMIT | LABEL_GLOSSY;
break;
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
case CLOSURE_BSDF_HAIR_MICROFACET_ID:
if (bsdf_is_transmission(sc, omega_in))
label = LABEL_TRANSMIT | LABEL_GLOSSY;
else
@ -609,6 +620,9 @@ ccl_device_inline
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_MICROFACET_ID:
eval = bsdf_microfacet_hair_eval(kg, sd, sc, omega_in, pdf);
break;
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
eval = bsdf_hair_reflection_eval(sc, sd->I, omega_in, pdf);
break;
@ -676,6 +690,9 @@ ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float
case CLOSURE_BSDF_HAIR_PRINCIPLED_ID:
bsdf_principled_hair_blur(sc, roughness);
break;
case CLOSURE_BSDF_HAIR_MICROFACET_ID:
bsdf_microfacet_hair_blur(sc, roughness);
break;
default:
break;
}

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,9 @@ ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals
}
/* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */
normal += sc->N * sc->sample_weight;
if (sc->type != CLOSURE_BSDF_HAIR_MICROFACET_ID) {
normal += sc->N * sc->sample_weight;
}
sum_weight += sc->sample_weight;
Spectrum closure_albedo = sc->weight;
@ -67,6 +69,11 @@ ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals
else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) {
closure_albedo *= bsdf_principled_hair_albedo(sc);
}
else if (sc->type == CLOSURE_BSDF_HAIR_MICROFACET_ID) {
closure_albedo *= bsdf_microfacet_hair_albedo(sc);
// if hair, use fiber tangent as feature instead of normal
normal += safe_normalize(sd->dPdu);
}
else if (sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) {
/* BSSRDF already accounts for weight, retro-reflection would double up. */
ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)
@ -76,12 +83,19 @@ ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals
}
}
if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) {
if (sc->type == CLOSURE_BSDF_HAIR_MICROFACET_ID) {
/* hair far field models "count" as diffuse */
diffuse_albedo += closure_albedo;
sum_nonspecular_weight += sc->sample_weight;
}
else {
specular_albedo += closure_albedo;
if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) {
diffuse_albedo += closure_albedo;
sum_nonspecular_weight += sc->sample_weight;
}
else {
specular_albedo += closure_albedo;
}
}
}

View File

@ -102,6 +102,7 @@ set(SRC_OSL
node_wireframe.osl
node_hair_bsdf.osl
node_principled_hair_bsdf.osl
node_microfacet_hair_bsdf.osl
node_uv_map.osl
node_principled_bsdf.osl
node_rgb_to_bw.osl

View File

@ -0,0 +1,102 @@
/*
* Copyright 2018 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "stdcycles.h"
color m_log3(color a)
{
return color(log(a[0]), log(a[1]), log(a[2]));
}
color m_sigma_from_concentration(float eumelanin, float pheomelanin)
{
return eumelanin * color(0.506, 0.841, 1.653) + pheomelanin * color(0.343, 0.733, 1.924);
}
color m_sigma_from_reflectance(color c, float azimuthal_roughness)
{
float x = azimuthal_roughness;
float roughness_fac = (((((0.245 * x) + 5.574) * x - 10.73) * x + 2.532) * x - 0.215) * x +
5.969;
color sigma = m_log3(c) / roughness_fac;
return sigma * sigma;
}
shader node_microfacet_hair_bsdf(color Color = color(0.017513, 0.005763, 0.002059),
float Melanin = 0.8,
float MelaninRedness = 1.0,
float RandomColor = 0.0,
color Tint = 1.0,
color AbsorptionCoefficient = color(0.245531, 0.52, 1.365),
normal Normal = Ng,
string parametrization = "Direct Coloring",
string model_type = "Microfacet Circular GGX",
float Offset = radians(2),
float Roughness = 0.3,
float RandomRoughness = 0.0,
float IOR = 1.55,
string AttrRandom = "geom:curve_random",
float Random = 0.0,
output closure color BSDF = 0)
{
/* Get random value from curve in none is specified. */
float random_value = 0.0;
if (isconnected(Random)) {
random_value = Random;
}
else {
getattribute(AttrRandom, random_value);
}
/* Compute roughness. */
float factor_random_roughness = 1.0 + 2.0 * (random_value - 0.5) * RandomRoughness;
float roughness = Roughness * factor_random_roughness;
/* Compute absorption. */
color sigma;
if (parametrization == "Absorption coefficient") {
sigma = AbsorptionCoefficient;
}
else if (parametrization == "Melanin concentration") {
/* Randomize melanin. */
float factor_random_color = 1.0 + 2.0 * (random_value - 0.5) * RandomColor;
float melanin = Melanin * factor_random_color;
/* Map melanin 0..inf from more perceptually linear 0..1. */
melanin = -log(max(1.0 - melanin, 0.0001));
/* Benedikt Bitterli's melanin ratio remapping. */
float eumelanin = melanin * (1.0 - MelaninRedness);
float pheomelanin = melanin * MelaninRedness;
color melanin_sigma = m_sigma_from_concentration(eumelanin, pheomelanin);
/* Optional tint. */
color tint_sigma = m_sigma_from_reflectance(Tint, roughness);
sigma = melanin_sigma + tint_sigma;
}
else if (parametrization == "Direct coloring") {
sigma = m_sigma_from_reflectance(Color, roughness);
}
else {
/* Fallback to brownish hair, same as defaults for melanin. */
sigma = m_sigma_from_concentration(0.0, 0.8054375);
}
BSDF = microfacet_hair(Normal, sigma, roughness, Offset, IOR);
}

View File

@ -60,6 +60,8 @@ closure color principled_hair(normal N,
float coat,
float alpha,
float eta) BUILTIN;
closure color
microfacet_hair(normal N, color sigma, float roughness, float alpha, float eta) BUILTIN;
// Volume
closure color henyey_greenstein(float g) BUILTIN;

View File

@ -879,6 +879,181 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
}
break;
}
case CLOSURE_BSDF_HAIR_MICROFACET_ID: {
uint4 data_node2 = read_node(kg, &offset);
uint4 data_node3 = read_node(kg, &offset);
uint4 data_node4 = read_node(kg, &offset);
uint4 data_node5 = read_node(kg, &offset);
uint4 data_node6 = read_node(kg, &offset);
uint4 data_node7 = read_node(kg, &offset);
Spectrum weight = sd->svm_closure_weight * mix_weight;
uint offset_ofs, ior_ofs, color_ofs, parametrization;
svm_unpack_node_uchar4(data_node.y, &offset_ofs, &ior_ofs, &color_ofs, &parametrization);
float alpha = stack_load_float_default(stack, offset_ofs, data_node.z);
float ior = stack_load_float_default(stack, ior_ofs, data_node.w);
uint coat_ofs, melanin_ofs, melanin_redness_ofs, absorption_coefficient_ofs;
svm_unpack_node_uchar4(data_node2.x,
&coat_ofs,
&melanin_ofs,
&melanin_redness_ofs,
&absorption_coefficient_ofs);
uint tint_ofs, random_ofs, random_color_ofs, random_roughness_ofs;
svm_unpack_node_uchar4(
data_node3.x, &tint_ofs, &random_ofs, &random_color_ofs, &random_roughness_ofs);
const AttributeDescriptor attr_descr_random = find_attribute(kg, sd, data_node4.y);
float random = 0.0f;
if (attr_descr_random.offset != ATTR_STD_NOT_FOUND) {
random = primitive_surface_attribute_float(kg, sd, attr_descr_random, NULL, NULL);
}
else {
random = stack_load_float_default(stack, random_ofs, data_node3.y);
}
uint R_ofs, TT_ofs, TRT_ofs, temp;
uint Blur_ofs, model_type;
svm_unpack_node_uchar4(data_node5.x, &R_ofs, &TT_ofs, &TRT_ofs, &temp);
float R = stack_load_float_default(stack, R_ofs, data_node5.y);
float TT = stack_load_float_default(stack, TT_ofs, data_node5.z);
float TRT = stack_load_float_default(stack, TRT_ofs, data_node5.w);
svm_unpack_node_uchar4(data_node6.x, &temp, &Blur_ofs, &temp, &model_type);
float blur = stack_load_float_default(stack, Blur_ofs, data_node6.z);
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)bsdf_alloc(
sd, sizeof(MicrofacetHairBSDF), weight);
if (bsdf) {
ccl_private MicrofacetHairExtra *extra = (ccl_private MicrofacetHairExtra *)
closure_alloc_extra(sd, sizeof(MicrofacetHairExtra));
if (!extra)
break;
bsdf->extra = extra;
bsdf->extra->R = fmaxf(0.0f, R);
bsdf->extra->TT = fmaxf(0.0f, TT);
bsdf->extra->TRT = fmaxf(0.0f, TRT);
bsdf->blur = clamp(blur, 0.0f, 1.0f);
/* Random factors range: [-randomization/2, +randomization/2]. */
float random_roughness = stack_load_float_default(
stack, random_roughness_ofs, data_node3.w);
float factor_random_roughness = 1.0f + 2.0f * (random - 0.5f) * random_roughness;
float roughness = param1 * factor_random_roughness;
/* if (model_type >= NODE_MICROFACET_HAIR_HYBRID_NEAR_FIELD && model_type <
* NODE_MICROFACET_HAIR_MODEL_TYPE_NUM) */
/* model_type is a uint, so no need to compare to 0
* (NODE_MICROFACET_HAIR_HYBRID_NEAR_FIELD), which generates a warning */
if (model_type < NODE_MICROFACET_HAIR_MODEL_TYPE_NUM)
bsdf->model_type = model_type;
else
bsdf->model_type = NODE_MICROFACET_HAIR_CIRCULAR_GGX; // default
if (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_GGX ||
bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC ||
bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_GGX) {
// empirical equivalences
roughness *= 0.5f;
}
else if (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN ||
bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN) {
// empirical equivalences
roughness *= 2.f / 3.f;
}
bsdf->N = N;
bsdf->roughness = roughness;
bsdf->alpha = alpha;
bsdf->eta = ior;
switch (parametrization) {
case NODE_MICROFACET_HAIR_DIRECT_ABSORPTION: {
float3 absorption_coefficient = stack_load_float3(stack, absorption_coefficient_ofs);
bsdf->sigma = rgb_to_spectrum(absorption_coefficient);
break;
}
case NODE_MICROFACET_HAIR_PIGMENT_CONCENTRATION: {
float melanin = stack_load_float_default(stack, melanin_ofs, data_node2.z);
float melanin_redness = stack_load_float_default(
stack, melanin_redness_ofs, data_node2.w);
/* Randomize melanin. */
float random_color = stack_load_float_default(stack, random_color_ofs, data_node3.z);
random_color = clamp(random_color, 0.0f, 1.0f);
float factor_random_color = 1.0f + 2.0f * (random - 0.5f) * random_color;
melanin *= factor_random_color;
/* Map melanin 0..inf from more perceptually linear 0..1. */
melanin = -logf(fmaxf(1.0f - melanin, 0.0001f));
/* Benedikt Bitterli's melanin ratio remapping. */
float eumelanin = melanin * (1.0f - melanin_redness);
float pheomelanin = melanin * melanin_redness;
Spectrum melanin_sigma = bsdf_microfacet_hair_sigma_from_concentration(eumelanin,
pheomelanin);
/* Optional tint. */
float3 tint = stack_load_float3(stack, tint_ofs);
Spectrum tint_sigma = bsdf_microfacet_hair_sigma_from_reflectance(
rgb_to_spectrum(tint), roughness);
bsdf->sigma = melanin_sigma + tint_sigma;
break;
}
case NODE_MICROFACET_HAIR_REFLECTANCE: {
float3 color = stack_load_float3(stack, color_ofs);
bsdf->sigma = bsdf_microfacet_hair_sigma_from_reflectance(rgb_to_spectrum(color),
roughness);
break;
}
default: {
/* Fallback to brownish hair, same as defaults for melanin. */
kernel_assert(!"Invalid Microfacet Hair parametrization!");
bsdf->sigma = bsdf_microfacet_hair_sigma_from_concentration(0.0f, 0.8054375f);
break;
}
}
if (model_type == NODE_MICROFACET_HAIR_ELLIPTIC_GGX ||
model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN) {
uint eccentricity_ofs, twist_rate_ofs, axis_ofs;
svm_unpack_node_uchar4(
data_node7.x, &eccentricity_ofs, &twist_rate_ofs, &axis_ofs, &temp);
float eccentricity = stack_load_float_default(stack, eccentricity_ofs, data_node7.y);
float twist_rate = stack_load_float_default(stack, twist_rate_ofs, data_node7.z);
float axis_rot = stack_load_float_default(stack, axis_ofs, data_node7.w);
/* Eccentricity */
bsdf->extra->eccentricity = (eccentricity > 1.f) ? 1.f / eccentricity : eccentricity;
/* Angular twist rate per unit length */
bsdf->extra->twist_rate = twist_rate;
const AttributeDescriptor attr_descr_intercept = find_attribute(kg, sd, data_node4.z);
bsdf->extra->attr_descr_intercept = curve_attribute_float(
kg, sd, attr_descr_intercept, NULL, NULL);
const AttributeDescriptor attr_descr_length = find_attribute(kg, sd, data_node4.w);
bsdf->extra->attr_descr_length = curve_attribute_float(
kg, sd, attr_descr_length, NULL, NULL);
bsdf->extra->axis_rot = axis_rot > 0.0f ?
random * axis_rot :
-axis_rot; // allowing this negative mode for debugging
}
sd->flag |= bsdf_microfacet_hair_setup(sd, bsdf);
}
break;
}
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: {
Spectrum weight = sd->svm_closure_weight * mix_weight;

View File

@ -394,6 +394,22 @@ typedef enum NodePrincipledHairParametrization {
NODE_PRINCIPLED_HAIR_NUM,
} NodePrincipledHairParametrization;
typedef enum NodeMicrofacetHairParametrization {
NODE_MICROFACET_HAIR_REFLECTANCE = 0,
NODE_MICROFACET_HAIR_PIGMENT_CONCENTRATION = 1,
NODE_MICROFACET_HAIR_DIRECT_ABSORPTION = 2,
NODE_MICROFACET_HAIR_NUM,
} NodeMicrofacetHairParametrization;
typedef enum NodeMicrofacetHairModelType {
NODE_MICROFACET_HAIR_CIRCULAR_GGX = 0,
NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC = 1,
NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN = 2,
NODE_MICROFACET_HAIR_ELLIPTIC_GGX = 3,
NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN = 4,
NODE_MICROFACET_HAIR_MODEL_TYPE_NUM,
} NodeMicrofacetHairModelType;
typedef enum NodeCombSepColorType {
NODE_COMBSEP_COLOR_RGB,
NODE_COMBSEP_COLOR_HSV,
@ -441,6 +457,7 @@ typedef enum ClosureType {
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID,
CLOSURE_BSDF_SHARP_GLASS_ID,
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_MICROFACET_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Special cases */
@ -470,7 +487,7 @@ typedef enum ClosureType {
(type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_TRANSLUCENT_ID)
#define CLOSURE_IS_BSDF_GLOSSY(type) \
((type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) || \
(type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID))
(type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) || (type == CLOSURE_BSDF_HAIR_MICROFACET_ID))
#define CLOSURE_IS_BSDF_TRANSMISSION(type) \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_SINGULAR(type) \

View File

@ -1140,7 +1140,8 @@ int ShaderGraph::get_num_closures()
* for the volume steps. */
num_closures += MAX_VOLUME_STACK_SIZE;
}
else if (closure_type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) {
else if (closure_type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID ||
closure_type == CLOSURE_BSDF_HAIR_MICROFACET_ID) {
num_closures += 4;
}
else {

View File

@ -3629,6 +3629,201 @@ void PrincipledHairBsdfNode::compile(OSLCompiler &compiler)
compiler.add(this, "node_principled_hair_bsdf");
}
/* Microfacet Hair BSDF Closure */
NODE_DEFINE(MicrofacetHairBsdfNode)
{
NodeType *type = NodeType::add("microfacet_hair_bsdf", create, NodeType::SHADER);
/* Color parametrization specified as enum. */
static NodeEnum parametrization_enum;
parametrization_enum.insert("Direct coloring", NODE_MICROFACET_HAIR_REFLECTANCE);
parametrization_enum.insert("Melanin concentration", NODE_MICROFACET_HAIR_PIGMENT_CONCENTRATION);
parametrization_enum.insert("Absorption coefficient", NODE_MICROFACET_HAIR_DIRECT_ABSORPTION);
SOCKET_ENUM(
parametrization, "Parametrization", parametrization_enum, NODE_MICROFACET_HAIR_REFLECTANCE);
/* Hair integration mode specified as enum. */
static NodeEnum model_type_enum;
model_type_enum.insert("Microfacet Circular GGX", NODE_MICROFACET_HAIR_CIRCULAR_GGX);
model_type_enum.insert("Microfacet Circular GGX Analytical",
NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC);
model_type_enum.insert("Microfacet Circular Beckmann", NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN);
model_type_enum.insert("Microfacet Elliptical GGX", NODE_MICROFACET_HAIR_ELLIPTIC_GGX);
model_type_enum.insert("Microfacet Elliptical Beckmann", NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
SOCKET_ENUM(model_type, "model_type", model_type_enum, NODE_MICROFACET_HAIR_CIRCULAR_GGX);
/* Initialize sockets to their default values. */
SOCKET_IN_COLOR(color, "Color", make_float3(0.017513f, 0.005763f, 0.002059f));
SOCKET_IN_FLOAT(melanin, "Melanin", 0.8f);
SOCKET_IN_FLOAT(melanin_redness, "Melanin Redness", 1.0f);
SOCKET_IN_COLOR(tint, "Tint", make_float3(1.f, 1.f, 1.f));
SOCKET_IN_VECTOR(absorption_coefficient,
"Absorption Coefficient",
make_float3(0.245531f, 0.52f, 1.365f),
SocketType::VECTOR);
SOCKET_IN_FLOAT(eccentricity, "Eccentricity", 0.85f);
SOCKET_IN_FLOAT(random_axis, "Random Axis", 0.0f);
SOCKET_IN_FLOAT(twist_rate, "Twist Rate", 0.0f);
SOCKET_IN_FLOAT(offset, "Offset", 2.f * M_PI_F / 180.f);
SOCKET_IN_FLOAT(roughness, "Roughness", 0.3f);
SOCKET_IN_FLOAT(ior, "IOR", 1.55f);
SOCKET_IN_FLOAT(R, "R lobe", 1.0f);
SOCKET_IN_FLOAT(TT, "TT lobe", 1.0f);
SOCKET_IN_FLOAT(TRT, "TRT lobe", 1.0f);
SOCKET_IN_FLOAT(Blur, "Blur", 1.0f);
SOCKET_IN_FLOAT(random_roughness, "Random Roughness", 0.0f);
SOCKET_IN_FLOAT(random_color, "Random Color", 0.0f);
SOCKET_IN_FLOAT(random, "Random", 0.0f);
SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL);
SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
SOCKET_OUT_CLOSURE(BSDF, "BSDF");
return type;
}
MicrofacetHairBsdfNode::MicrofacetHairBsdfNode() : BsdfBaseNode(get_node_type())
{
closure = CLOSURE_BSDF_HAIR_MICROFACET_ID;
}
/* Enable retrieving Hair Info -> Random if Random isn't linked. */
void MicrofacetHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
/* Make sure we have these 2 for elliptical cross section tracking */
attributes->add(ATTR_STD_CURVE_INTERCEPT);
attributes->add(ATTR_STD_CURVE_LENGTH);
if (!input("Random")->link) {
attributes->add(ATTR_STD_CURVE_RANDOM);
}
ShaderNode::attributes(shader, attributes);
}
/* Prepares the input data for the SVM shader. */
void MicrofacetHairBsdfNode::compile(SVMCompiler &compiler)
{
compiler.add_node(NODE_CLOSURE_SET_WEIGHT, one_float3());
ShaderInput *roughness_in = input("Roughness");
ShaderInput *random_roughness_in = input("Random Roughness");
ShaderInput *offset_in = input("Offset");
ShaderInput *ior_in = input("IOR");
ShaderInput *melanin_in = input("Melanin");
ShaderInput *melanin_redness_in = input("Melanin Redness");
ShaderInput *random_color_in = input("Random Color");
ShaderInput *R_in = input("R lobe");
ShaderInput *TT_in = input("TT lobe");
ShaderInput *TRT_in = input("TRT lobe");
ShaderInput *Blur_in = input("Blur");
ShaderInput *eccentricity_in = input("Eccentricity");
ShaderInput *random_axis_in = input("Random Axis");
ShaderInput *twist_rate_in = input("Twist Rate");
int color_ofs = compiler.stack_assign(input("Color"));
int tint_ofs = compiler.stack_assign(input("Tint"));
int absorption_coefficient_ofs = compiler.stack_assign(input("Absorption Coefficient"));
int roughness_ofs = compiler.stack_assign_if_linked(roughness_in);
int normal_ofs = compiler.stack_assign_if_linked(input("Normal"));
int offset_ofs = compiler.stack_assign_if_linked(offset_in);
int ior_ofs = compiler.stack_assign_if_linked(ior_in);
int melanin_ofs = compiler.stack_assign_if_linked(melanin_in);
int melanin_redness_ofs = compiler.stack_assign_if_linked(melanin_redness_in);
ShaderInput *random_in = input("Random");
int attr_random = random_in->link ? SVM_STACK_INVALID :
compiler.attribute(ATTR_STD_CURVE_RANDOM);
int random_in_ofs = compiler.stack_assign_if_linked(random_in);
int random_color_ofs = compiler.stack_assign_if_linked(random_color_in);
int random_roughness_ofs = compiler.stack_assign_if_linked(random_roughness_in);
/* Encode all parameters into data nodes. */
/* node */
compiler.add_node(
NODE_CLOSURE_BSDF,
/* Socket IDs can be packed 4 at a time into a single data packet */
compiler.encode_uchar4(closure, roughness_ofs, 0, compiler.closure_mix_weight_offset()),
/* The rest are stored as unsigned integers */
__float_as_uint(roughness),
0);
/* data node */
compiler.add_node(normal_ofs,
compiler.encode_uchar4(offset_ofs, ior_ofs, color_ofs, parametrization),
__float_as_uint(offset),
__float_as_uint(ior));
/* data node 2 */
compiler.add_node(
compiler.encode_uchar4(0, melanin_ofs, melanin_redness_ofs, absorption_coefficient_ofs),
0,
__float_as_uint(melanin),
__float_as_uint(melanin_redness));
/* data node 3 */
compiler.add_node(
compiler.encode_uchar4(tint_ofs, random_in_ofs, random_color_ofs, random_roughness_ofs),
__float_as_uint(random),
__float_as_uint(random_color),
__float_as_uint(random_roughness));
int attr_intercept = compiler.attribute(ATTR_STD_CURVE_INTERCEPT);
int attr_length = compiler.attribute(ATTR_STD_CURVE_LENGTH);
/* data node 4 */
compiler.add_node(
compiler.encode_uchar4(
SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID),
attr_random,
attr_intercept,
attr_length);
/* data node 5 */
compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(R_in),
compiler.stack_assign_if_linked(TT_in),
compiler.stack_assign_if_linked(TRT_in),
SVM_STACK_INVALID),
__float_as_uint(R),
__float_as_uint(TT),
__float_as_uint(TRT));
/* data node 6 */
compiler.add_node(
compiler.encode_uchar4(0, compiler.stack_assign_if_linked(Blur_in), 0, model_type),
0,
__float_as_uint(Blur),
0);
/* data node 7 */
compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(eccentricity_in),
compiler.stack_assign_if_linked(twist_rate_in),
compiler.stack_assign_if_linked(random_axis_in),
SVM_STACK_INVALID),
__float_as_uint(eccentricity),
__float_as_uint(twist_rate),
__float_as_uint(random_axis));
}
/* Prepares the input data for the OSL shader. */
void MicrofacetHairBsdfNode::compile(OSLCompiler &compiler)
{
compiler.parameter(this, "parametrization");
compiler.parameter(this, "model_type");
compiler.add(this, "node_microfacet_hair_bsdf");
}
/* Hair BSDF Closure */
NODE_DEFINE(HairBsdfNode)

View File

@ -879,6 +879,57 @@ class PrincipledHairBsdfNode : public BsdfBaseNode {
NODE_SOCKET_API(NodePrincipledHairParametrization, parametrization)
};
/* Interface between the I/O sockets and the SVM/OSL backend. */
class MicrofacetHairBsdfNode : public BsdfBaseNode {
public:
SHADER_NODE_CLASS(MicrofacetHairBsdfNode)
void attributes(Shader *shader, AttributeRequestSet *attributes);
/* Roughness. */
NODE_SOCKET_API(float, roughness)
/* Randomization factor for roughnesses. */
NODE_SOCKET_API(float, random_roughness)
/* Index of reflection. */
NODE_SOCKET_API(float, ior)
/* Cuticle tilt angle. */
NODE_SOCKET_API(float, offset)
/* Direct coloring's color. */
NODE_SOCKET_API(float3, color)
/* Melanin concentration. */
NODE_SOCKET_API(float, melanin)
/* Melanin redness ratio. */
NODE_SOCKET_API(float, melanin_redness)
/* Dye color. */
NODE_SOCKET_API(float3, tint)
/* Randomization factor for melanin quantities. */
NODE_SOCKET_API(float, random_color)
/* Absorption coefficient (unfiltered). */
NODE_SOCKET_API(float3, absorption_coefficient)
/* Eccentricity. */
NODE_SOCKET_API(float, eccentricity)
/* Randomization factor for axis rotation. */
NODE_SOCKET_API(float, random_axis)
/* Twist rate. */
NODE_SOCKET_API(float, twist_rate)
NODE_SOCKET_API(float, R)
NODE_SOCKET_API(float, TT)
NODE_SOCKET_API(float, TRT)
NODE_SOCKET_API(float, Blur)
NODE_SOCKET_API(float3, normal)
NODE_SOCKET_API(float, surface_mix_weight)
/* If linked, here will be the given random number. */
NODE_SOCKET_API(float, random)
/* Selected coloring parametrization. */
NODE_SOCKET_API(NodeMicrofacetHairParametrization, parametrization)
/* Selected model type. */
NODE_SOCKET_API(NodeMicrofacetHairModelType, model_type)
};
class HairBsdfNode : public BsdfNode {
public:
SHADER_NODE_CLASS(HairBsdfNode)

@ -1 +1 @@
Subproject commit 4a581c54af9b92cb670d750951b9382160f10f3e
Subproject commit e398d3c4969a37ae2ecff388344dd780bc1cfe82

@ -1 +1 @@
Subproject commit 0b0052bd53ad8249ed07dfb87705c338af698bde
Subproject commit 90c87dd771e027e0ffa157a0e294399bfd605d99

View File

@ -1220,6 +1220,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_SEPARATE_COLOR 712
#define SH_NODE_MIX 713
#define SH_NODE_BSDF_HAIR_MICROFACET 800
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -44,3 +44,36 @@ void node_bsdf_hair_principled(vec4 color,
result = closure_eval(hair_data);
}
void node_bsdf_hair_microfacet(vec4 color,
float melanin,
float melanin_redness,
vec4 tint,
vec3 absorption_coefficient,
float roughness,
float R,
float TT,
float TRT,
float ior,
float offset,
float eccentricity,
float random_axis,
float twist_rate,
float Blur,
float random_color,
float random_roughness,
float random,
float weight,
out Closure result)
{
/* Placeholder closure.
* Some computation will have to happen here just like the Principled BSDF. */
ClosureHair hair_data;
hair_data.weight = weight;
hair_data.color = color.rgb;
hair_data.offset = offset;
hair_data.roughness = vec2(0.0);
hair_data.T = g_data.curve_B;
result = closure_eval(hair_data);
}

View File

@ -1645,6 +1645,18 @@ enum {
#define SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION 1
#define SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION 2
/* microfacet hair parametrization */
#define SHD_MICROFACET_HAIR_REFLECTANCE 0
#define SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION 1
#define SHD_MICROFACET_HAIR_DIRECT_ABSORPTION 2
/* microfacet hair modes */
#define SHD_MICROFACET_HAIR_CIRCULAR_GGX 0
#define SHD_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC 1
#define SHD_MICROFACET_HAIR_CIRCULAR_BECKMANN 2
#define SHD_MICROFACET_HAIR_ELLIPTIC_GGX 3
#define SHD_MICROFACET_HAIR_ELLIPTIC_BECKMANN 4
/* blend texture */
#define SHD_BLEND_LINEAR 0
#define SHD_BLEND_QUADRATIC 1

View File

@ -4632,6 +4632,62 @@ static const EnumPropertyItem node_principled_hair_items[] = {
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem node_microfacet_hair_parametrization_items[] = {
{SHD_MICROFACET_HAIR_DIRECT_ABSORPTION,
"ABSORPTION",
0,
"Absorption Coefficient",
"Directly set the absorption coefficient \"sigma_a\" (this is not the most intuitive way to "
"color hair)"},
{SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION,
"MELANIN",
0,
"Melanin Concentration",
"Define the melanin concentrations below to get the most realistic-looking hair "
"(you can get the concentrations for different types of hair online)"},
{SHD_MICROFACET_HAIR_REFLECTANCE,
"COLOR",
0,
"Direct Coloring",
"Choose the color of your preference, and the shader will approximate the absorption "
"coefficient to render lookalike hair"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem node_microfacet_hair_mode_items[] = {
{SHD_MICROFACET_HAIR_CIRCULAR_GGX,
"HAIR_MICROFACET_CIRCULAR_GGX",
0,
"Microfacet Circular GGX",
"GGX microfacet-based hair scattering model with circular cross section fiber, numerically "
"integrated across the width"},
{SHD_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC,
"HAIR_MICROFACET_CIRCULAR_GGX_ANALYTIC",
0,
"Microfacet Circular GGX Analytic",
"GGX microfacet-based hair scattering model with circular cross section fiber, analytically "
"integrated across the width with no masking term"},
{SHD_MICROFACET_HAIR_CIRCULAR_BECKMANN,
"HAIR_MICROFACET_CIRCULAR_BECKMANN",
0,
"Microfacet Circular Beckmann",
"Beckmann microfacet-based hair scattering model with circular cross section fiber, "
"numerically integrated across the width"},
{SHD_MICROFACET_HAIR_ELLIPTIC_GGX,
"HAIR_MICROFACET_ELLIPTIC_GGX",
0,
"Microfacet Elliptical GGX",
"GGX microfacet-based hair scattering model with elliptical cross section fiber, numerically "
"integrated across the width"},
{SHD_MICROFACET_HAIR_ELLIPTIC_BECKMANN,
"HAIR_MICROFACET_ELLIPTIC_BECKMANN",
0,
"Microfacet Elliptical Beckmann",
"Beckmann microfacet-based hair scattering model with elliptical cross section fiber, "
"numerically integrated across the width"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem node_script_mode_items[] = {
{NODE_SCRIPT_INTERNAL, "INTERNAL", 0, "Internal", "Use internal text data-block"},
{NODE_SCRIPT_EXTERNAL, "EXTERNAL", 0, "External", "Use external .osl or .oso file"},
@ -6166,6 +6222,31 @@ static void def_hair_principled(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
}
/* RNA initialization for the custom property. */
static void def_hair_microfacet(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "parametrization", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_ui_text(
prop, "Color parametrization", "Select the shader's color parametrization");
RNA_def_property_enum_items(prop, node_microfacet_hair_parametrization_items);
RNA_def_property_enum_default(prop, SHD_MICROFACET_HAIR_REFLECTANCE);
/* Upon editing, update both the node data AND the UI representation */
/* (This effectively shows/hides the relevant sockets) */
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "model_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_ui_text(prop, "Mode", "Select the shader's mode");
RNA_def_property_enum_items(prop, node_microfacet_hair_mode_items);
RNA_def_property_enum_default(prop, SHD_MICROFACET_HAIR_CIRCULAR_GGX);
/* Upon editing, update both the node data AND the UI representation */
/* (This effectively shows/hides the relevant sockets) */
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
}
static void def_sh_uvmap(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -124,6 +124,8 @@ DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COM
DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(ShaderNode, SH_NODE_BSDF_HAIR_MICROFACET, def_hair_microfacet, "BSDF_HAIR_MICROFACET", BsdfHairMicrofacet, "Microfacet Hair BSDF", "Microfect Hair Scattering Model (EGSR 2022)")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
DefNode(CompositorNode, CMP_NODE_VALUE, 0, "VALUE", Value, "Value", "" )

View File

@ -38,6 +38,7 @@ set(SRC
nodes/node_shader_bsdf_glossy.cc
nodes/node_shader_bsdf_hair.cc
nodes/node_shader_bsdf_hair_principled.cc
nodes/node_shader_bsdf_hair_microfacet.cc
nodes/node_shader_bsdf_principled.cc
nodes/node_shader_bsdf_refraction.cc
nodes/node_shader_bsdf_toon.cc

View File

@ -22,6 +22,7 @@ void register_shader_nodes()
register_node_type_sh_bsdf_glass();
register_node_type_sh_bsdf_glossy();
register_node_type_sh_bsdf_hair_principled();
register_node_type_sh_bsdf_hair_microfacet();
register_node_type_sh_bsdf_hair();
register_node_type_sh_bsdf_principled();
register_node_type_sh_bsdf_refraction();

View File

@ -18,6 +18,7 @@ void register_node_type_sh_bsdf_diffuse();
void register_node_type_sh_bsdf_glass();
void register_node_type_sh_bsdf_glossy();
void register_node_type_sh_bsdf_hair_principled();
void register_node_type_sh_bsdf_hair_microfacet();
void register_node_type_sh_bsdf_hair();
void register_node_type_sh_bsdf_principled();
void register_node_type_sh_bsdf_refraction();

View File

@ -881,6 +881,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_BSDF_GLASS:
case SH_NODE_BSDF_GLOSSY:
case SH_NODE_BSDF_HAIR_PRINCIPLED:
case SH_NODE_BSDF_HAIR_MICROFACET:
case SH_NODE_BSDF_HAIR:
case SH_NODE_BSDF_PRINCIPLED:
case SH_NODE_BSDF_REFRACTION:
@ -939,6 +940,7 @@ static bool closure_node_filter(const bNode *node)
case SH_NODE_BSDF_GLASS:
case SH_NODE_BSDF_GLOSSY:
case SH_NODE_BSDF_HAIR_PRINCIPLED:
case SH_NODE_BSDF_HAIR_MICROFACET:
case SH_NODE_BSDF_HAIR:
case SH_NODE_BSDF_PRINCIPLED:
case SH_NODE_BSDF_REFRACTION:

View File

@ -0,0 +1,179 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2018 Blender Foundation. All rights reserved. */
#include "node_shader_util.hh"
#include "UI_interface.h"
#include "UI_resources.h"
namespace blender::nodes::node_shader_bsdf_hair_microfacet_cc {
/* Color, melanin and absorption coefficient default to approximately same brownish hair. */
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({0.017513f, 0.005763f, 0.002059f, 1.0f});
b.add_input<decl::Float>(N_("Melanin"))
.default_value(0.8f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Melanin Redness"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Color>(N_("Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Vector>(N_("Absorption Coefficient"))
.default_value({0.245531f, 0.52f, 1.365f})
.min(0.0f)
.max(1000.0f);
b.add_input<decl::Float>(N_("Eccentricity"))
.default_value(0.85f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Random Axis"))
.default_value(0.0f)
.min(-1.0f)
.max(1.0f);
b.add_input<decl::Float>(N_("Twist Rate"))
.default_value(0.0f)
.min(0.0f)
.max(2.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Roughness"))
.default_value(0.3f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("R lobe"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("TT lobe"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("TRT lobe"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f);
b.add_input<decl::Float>(N_("Offset"))
.default_value(2.0f * ((float)M_PI) / 180.0f)
.min(-M_PI_2)
.max(M_PI_2)
.subtype(PROP_ANGLE);
b.add_input<decl::Float>(N_("Blur"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Random Color"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Random Roughness"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Random")).hide_value();
b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
static void node_shader_buts_microfacet_hair(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "parametrization", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(layout, ptr, "model_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
/* Initialize the custom Parametrization property to Color. */
static void node_shader_init_hair_microfacet(bNodeTree * /*ntree*/, bNode *node)
{
node->custom1 = SHD_MICROFACET_HAIR_REFLECTANCE;
node->custom2 = SHD_MICROFACET_HAIR_CIRCULAR_GGX;
}
/* Triggers (in)visibility of some sockets when changing Parametrization. */
static void node_shader_update_hair_microfacet(bNodeTree *ntree, bNode *node)
{
int parametrization = node->custom1;
int model_type = node->custom2;
bool circular = (model_type == SHD_MICROFACET_HAIR_CIRCULAR_GGX ||
model_type == SHD_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC ||
model_type == SHD_MICROFACET_HAIR_CIRCULAR_BECKMANN);
bool elliptical = (model_type == SHD_MICROFACET_HAIR_ELLIPTIC_GGX ||
model_type == SHD_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (STREQ(sock->name, "Color")) {
nodeSetSocketAvailability(ntree, sock, parametrization == SHD_MICROFACET_HAIR_REFLECTANCE);
}
else if (STREQ(sock->name, "Melanin")) {
nodeSetSocketAvailability(
ntree, sock, parametrization == SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION);
}
else if (STREQ(sock->name, "Melanin Redness")) {
nodeSetSocketAvailability(
ntree, sock, parametrization == SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION);
}
else if (STREQ(sock->name, "Tint")) {
nodeSetSocketAvailability(
ntree, sock, parametrization == SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION);
}
else if (STREQ(sock->name, "Absorption Coefficient")) {
nodeSetSocketAvailability(
ntree, sock, parametrization == SHD_MICROFACET_HAIR_DIRECT_ABSORPTION);
}
else if (STREQ(sock->name, "Random Color")) {
nodeSetSocketAvailability(
ntree, sock, parametrization == SHD_MICROFACET_HAIR_PIGMENT_CONCENTRATION);
}
else if (STREQ(sock->name, "Eccentricity")) {
nodeSetSocketAvailability(ntree, sock, elliptical);
}
else if (STREQ(sock->name, "Random Axis")) {
nodeSetSocketAvailability(ntree, sock, elliptical);
}
else if (STREQ(sock->name, "Twist Rate")) {
nodeSetSocketAvailability(ntree, sock, elliptical);
}
}
}
static int node_shader_gpu_hair_microfacet(GPUMaterial *mat,
bNode *node,
bNodeExecData * /*execdata*/,
GPUNodeStack *in,
GPUNodeStack *out)
{
return GPU_stack_link(mat, node, "node_bsdf_hair_microfacet", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_hair_microfacet_cc
/* node type definition */
void register_node_type_sh_bsdf_hair_microfacet()
{
namespace file_ns = blender::nodes::node_shader_bsdf_hair_microfacet_cc;
static bNodeType ntype;
sh_node_type_base(
&ntype, SH_NODE_BSDF_HAIR_MICROFACET, "Microfacet Hair BSDF", NODE_CLASS_SHADER);
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_shader_buts_microfacet_hair;
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
ntype.initfunc = file_ns::node_shader_init_hair_microfacet;
ntype.updatefunc = file_ns::node_shader_update_hair_microfacet;
ntype.gpu_fn = file_ns::node_shader_gpu_hair_microfacet;
nodeRegisterType(&ntype);
}