Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600

Merged
Weizhen Huang merged 114 commits from weizhen/blender:microfacet_hair into main 2023-08-18 12:46:20 +02:00
14 changed files with 158 additions and 197 deletions
Showing only changes of commit f88b91fe61 - Show all commits

View File

@ -668,11 +668,16 @@ static ShaderNode *add_node(Scene *scene,
"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));
microfacet_hair->set_cross_section_type(
(NodeMicrofacetHairCrossSectionType)get_enum(b_microfacet_hair_node.ptr,
"cross_section_type",
NODE_MICROFACET_HAIR_CROSS_SECTION_TYPE_NUM,
NODE_MICROFACET_HAIR_CIRCULAR));
microfacet_hair->set_distribution_type(
(NodeMicrofacetHairDistributionType)get_enum(b_microfacet_hair_node.ptr,
"distribution_type",
NODE_MICROFACET_HAIR_DISTRIBUTION_TYPE_NUM,
NODE_MICROFACET_HAIR_GGX));
node = microfacet_hair;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfPrincipled)) {

View File

@ -37,8 +37,11 @@ typedef struct MicrofacetHairBSDF {
/* Blur. */
float blur;
/* Circular/Ellipitic and GGX/Beckmann. */
int model_type;
/* GGX/Beckmann. */
int distribution_type;
/* Circular/Elliptical */
int cross_section_type;
/* Extra closure. */
ccl_private MicrofacetHairExtra *extra;
@ -72,8 +75,7 @@ ccl_device int bsdf_microfacet_hair_setup(ccl_private ShaderData *sd,
kernel_assert(isfinite_safe(X));
kernel_assert(isfinite_safe(h));
if (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_GGX ||
bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN) {
if (bsdf->cross_section_type == NODE_MICROFACET_HAIR_ELLIPTIC) {
/* Local frame is independent of the ray direction for elliptical hairs. */
bsdf->extra->geom.w = h;
}
@ -329,8 +331,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN);
const bool analytical_ggx = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
float3 R = zero_float3();
if (bsdf->extra->R <= 0.0f)
@ -359,68 +360,26 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
float integral = 0.0f;
/* analytical for ggx (no masking term) */
if (analytical_ggx) {
const float phi_h = dir_phi(wh);
const float d_max = phi_h - phi_m_max;
const float d_min = phi_h - phi_m_min;
/* Maximal sample resolution. */
float res = roughness * 0.7f;
/* Number of intervals should be even. */
const size_t intervals = 2 * (size_t)ceilf((phi_m_max - phi_m_min) / res * 0.5f);
const float roughness_squared = roughness * roughness;
/* Modified resolution based on numbers of intervals. */
res = (phi_m_max - phi_m_min) / float(intervals);
float sm, cm;
fast_sincosf(tilt, &sm, &cm);
/* Integrate using Simpson's rule. */
for (size_t i = 0; i <= intervals; i++) {
const float C = sqrtf(1.0f - roughness_squared);
const float A = cm * cos_theta(wh) * C;
const float B = sm * sin_theta(wh) * C;
const float A2 = sqr(A);
const float B2 = sqr(B);
const float tmp1 = 1.0f / sqrtf(sqr(B - 1.0f) - A2);
const float tmp2 = 1.0f / sqrtf(sqr(B + 1.0f) - A2);
const float phi_m = phi_m_min + i * res;
const float3 wm = sph_dir(tilt, phi_m);
float smax, cmax, smin, cmin;
fast_sincosf(d_max, &smax, &cmax);
fast_sincosf(d_min, &smin, &cmin);
const float tmax = smax / (1.0f + cmax);
const float tmin = smin / (1.0f + cmin);
const float temp1 = 2.0f * (A2 - B2 + 3.0f * B - 2) * sqr(tmp1) * tmp1 *
(atanf((A - B + 1.0f) * tmp1 * tmax) -
atanf((A - B + 1.0f) * tmp1 * tmin));
const float temp2 = 2.0f * (A2 - B2 - 3.0f * B - 2) * sqr(tmp2) * tmp2 *
(atanf((B - A + 1.0f) * tmp2 * tmax) -
atanf((B - A + 1.0f) * tmp2 * tmin));
const float temp3 = A * sqr(tmp1) *
(smax / (A * cmax + B - 1.0f) - smin / (A * cmin + B - 1.0f));
const float temp4 = A * sqr(tmp2) *
(smax / (A * cmax + B + 1.0f) - smin / (A * cmin + B + 1.0f));
integral = roughness_squared * M_1_PI_F * 0.5f * (temp1 + temp2 + temp3 + temp4);
}
else { /* Falls back to numerical integration. */
/* Maximal sample resolution. */
float res = roughness * 0.7f;
/* Number of intervals should be even. */
const size_t intervals = 2 * (size_t)ceilf((phi_m_max - phi_m_min) / res * 0.5f);
/* Modified resolution based on numbers of intervals. */
res = (phi_m_max - phi_m_min) / float(intervals);
/* Integrate using Simpson's rule. */
for (size_t i = 0; i <= intervals; i++) {
const float phi_m = phi_m_min + i * res;
const float3 wm = sph_dir(tilt, phi_m);
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals) ? 0.5f : (i % 2 + 1);
integral += weight * D(beckmann, roughness, wm, wh) *
G(beckmann, roughness, wi, wo, wm, wh);
}
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals) ? 0.5f : (i % 2 + 1);
integral += weight * D(beckmann, roughness, wm, wh) * G(beckmann, roughness, wi, wo, wm, wh);
}
integral *= (2.0f / 3.0f * res);
}
integral *= (2.0f / 3.0f * res);
const float F = fresnel_dielectric_cos(dot(wi, wh), eta);
@ -438,7 +397,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f)
return zero_float3();
@ -652,7 +611,7 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
weizhen marked this conversation as resolved Outdated

Since both of these are already known in the setup function at shader evaluation time, we might want to implement the "default to transparent" logic there?

Since both of these are already known in the setup function at shader evaluation time, we might want to implement the "default to transparent" logic there?
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
/* generate sample */
float sample_lobe = randu;
@ -833,7 +792,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_elliptic(ccl_private const ShaderC
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
float3 R = zero_float3();
if (bsdf->extra->R <= 0.0f) {
@ -933,7 +892,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
return zero_float3();
@ -1203,7 +1162,7 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
const float tilt = -bsdf->alpha;
const float roughness = bsdf->roughness;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
const bool beckmann = (bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN);
/* generate sample */
float sample_lobe = randu;
@ -1387,17 +1346,12 @@ ccl_device Spectrum bsdf_microfacet_hair_eval(KernelGlobals kg,
{
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)sc;
switch (bsdf->model_type) {
case NODE_MICROFACET_HAIR_CIRCULAR_GGX:
case NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC:
case NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN:
return bsdf_microfacet_hair_eval_circular(kg, sd, sc, omega_in, pdf);
case NODE_MICROFACET_HAIR_ELLIPTIC_GGX:
case NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN:
return bsdf_microfacet_hair_eval_elliptic(kg, sd, sc, omega_in, pdf);
default:
return bsdf_microfacet_hair_eval_circular(kg, sd, sc, omega_in, pdf);
if (bsdf->cross_section_type == NODE_MICROFACET_HAIR_CIRCULAR ||
bsdf->extra->aspect_ratio == 1.0f) {
return bsdf_microfacet_hair_eval_circular(kg, sd, sc, omega_in, pdf);
}
return bsdf_microfacet_hair_eval_elliptic(kg, sd, sc, omega_in, pdf);
}
ccl_device int bsdf_microfacet_hair_sample(KernelGlobals kg,
@ -1413,20 +1367,14 @@ ccl_device int bsdf_microfacet_hair_sample(KernelGlobals kg,
{
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)sc;
switch (bsdf->model_type) {
case NODE_MICROFACET_HAIR_CIRCULAR_GGX:
case NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC:
case NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN:
return bsdf_microfacet_hair_sample_circular(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
case NODE_MICROFACET_HAIR_ELLIPTIC_GGX:
case NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN:
return bsdf_microfacet_hair_sample_elliptic(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
default:
return bsdf_microfacet_hair_sample_circular(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
if (bsdf->cross_section_type == NODE_MICROFACET_HAIR_CIRCULAR ||
bsdf->extra->aspect_ratio == 1.0f) {
return bsdf_microfacet_hair_sample_circular(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
}
return bsdf_microfacet_hair_sample_elliptic(
kg, sc, sd, randu, randv, eval, omega_in, pdf, sampled_roughness, eta);
}
/* Implements Filter Glossy by capping the effective roughness. */

View File

@ -43,7 +43,8 @@ shader node_microfacet_hair_bsdf(color Color = color(0.017513, 0.005763, 0.00205
color AbsorptionCoefficient = color(0.245531, 0.52, 1.365),
normal Normal = Ng,
string parametrization = "Direct Coloring",
string model_type = "Microfacet Circular GGX",
string cross_section_type = "Circular",
string distribution_type = "GGX",
float Offset = radians(2),
float Roughness = 0.3,
float RandomRoughness = 0.0,

View File

@ -899,9 +899,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
&melanin_redness_ofs,
&absorption_coefficient_ofs);
uint tint_ofs, random_ofs, random_color_ofs, random_roughness_ofs;
uint tint_ofs, random_ofs, random_color_ofs, cross_section_type;
svm_unpack_node_uchar4(
data_node3.x, &tint_ofs, &random_ofs, &random_color_ofs, &random_roughness_ofs);
data_node3.x, &tint_ofs, &random_ofs, &random_color_ofs, &cross_section_type);
const AttributeDescriptor attr_descr_random = find_attribute(kg, sd, data_node3.w);
float random = 0.0f;
@ -912,9 +912,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
random = stack_load_float_default(stack, random_ofs, data_node3.y);
}
uint R_ofs, TT_ofs, TRT_ofs, model_type;
svm_unpack_node_uchar4(data_node4.x, &R_ofs, &TT_ofs, &TRT_ofs, &model_type);
uint R_ofs, TT_ofs, TRT_ofs, distribution_type;
svm_unpack_node_uchar4(data_node4.x, &R_ofs, &TT_ofs, &TRT_ofs, &distribution_type);
float R = stack_load_float_default(stack, R_ofs, data_node4.y);
float TT = stack_load_float_default(stack, TT_ofs, data_node4.z);
float TRT = stack_load_float_default(stack, TRT_ofs, data_node4.w);
@ -940,24 +939,17 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
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
bsdf->cross_section_type = clamp(
cross_section_type, NODE_MICROFACET_HAIR_CIRCULAR, NODE_MICROFACET_HAIR_ELLIPTIC);
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
bsdf->distribution_type = clamp(
distribution_type, NODE_MICROFACET_HAIR_GGX, NODE_MICROFACET_HAIR_BECKMANN);
/* Empirical equivalences compared with principled hair BSDF. */
if (bsdf->distribution_type == NODE_MICROFACET_HAIR_GGX) {
roughness *= 0.5f;
}
else if (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN ||
bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN) {
// empirical equivalences
else { /* bsdf->distribution_type == NODE_MICROFACET_HAIR_BECKMANN*/
roughness *= 2.f / 3.f;
}
@ -1014,8 +1006,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
}
}
if (model_type == NODE_MICROFACET_HAIR_ELLIPTIC_GGX ||
model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN) {
if (cross_section_type == NODE_MICROFACET_HAIR_ELLIPTIC) {
uint aspect_ratio_ofs, temp;
svm_unpack_node_uchar4(data_node5.x, &aspect_ratio_ofs, &temp, &temp, &temp);

View File

@ -402,14 +402,17 @@ typedef enum NodeMicrofacetHairParametrization {
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 NodeMicrofacetHairDistributionType {
NODE_MICROFACET_HAIR_GGX = 0,
NODE_MICROFACET_HAIR_BECKMANN = 1,
NODE_MICROFACET_HAIR_DISTRIBUTION_TYPE_NUM,
} NodeMicrofacetHairDistributionType;
typedef enum NodeMicrofacetHairCrossSectionType {
NODE_MICROFACET_HAIR_CIRCULAR = 0,
NODE_MICROFACET_HAIR_ELLIPTIC = 1,
NODE_MICROFACET_HAIR_CROSS_SECTION_TYPE_NUM,
} NodeMicrofacetHairCrossSectionType;
typedef enum NodeCombSepColorType {
NODE_COMBSEP_COLOR_RGB,

View File

@ -3644,16 +3644,21 @@ NODE_DEFINE(MicrofacetHairBsdfNode)
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);
/* Hair microfacet normal distribution mode specified as enum. */
static NodeEnum distribution_type_enum;
distribution_type_enum.insert("GGX", NODE_MICROFACET_HAIR_GGX);
distribution_type_enum.insert("Beckmann", NODE_MICROFACET_HAIR_BECKMANN);
SOCKET_ENUM(
distribution_type, "Distribution Type", distribution_type_enum, NODE_MICROFACET_HAIR_GGX);
SOCKET_ENUM(model_type, "model_type", model_type_enum, NODE_MICROFACET_HAIR_CIRCULAR_GGX);
/* Hair cross-section type specified as enum. */
static NodeEnum cross_section_type_enum;
distribution_type_enum.insert("Circular", NODE_MICROFACET_HAIR_CIRCULAR);
distribution_type_enum.insert("Elliptical", NODE_MICROFACET_HAIR_ELLIPTIC);
SOCKET_ENUM(cross_section_type,
"Cross Section Type",
cross_section_type_enum,
NODE_MICROFACET_HAIR_CIRCULAR);
/* Initialize sockets to their default values. */
SOCKET_IN_COLOR(color, "Color", make_float3(0.017513f, 0.005763f, 0.002059f));
@ -3698,8 +3703,7 @@ MicrofacetHairBsdfNode::MicrofacetHairBsdfNode() : BsdfBaseNode(get_node_type())
void MicrofacetHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
/* Make sure we have the normal for elliptical cross section tracking */
if (model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN ||
model_type == NODE_MICROFACET_HAIR_ELLIPTIC_GGX) {
if (cross_section_type == NODE_MICROFACET_HAIR_ELLIPTIC) {
attributes->add(ATTR_STD_VERTEX_NORMAL);
}
@ -3777,16 +3781,17 @@ void MicrofacetHairBsdfNode::compile(SVMCompiler &compiler)
__float_as_uint(melanin_redness));
/* data node 3 */
compiler.add_node(compiler.encode_uchar4(tint_ofs, random_in_ofs, random_color_ofs, 0),
__float_as_uint(random),
__float_as_uint(random_color),
attr_random);
compiler.add_node(
compiler.encode_uchar4(tint_ofs, random_in_ofs, random_color_ofs, cross_section_type),
__float_as_uint(random),
__float_as_uint(random_color),
attr_random);
/* data node 4 */
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),
model_type),
distribution_type),
__float_as_uint(R),
__float_as_uint(TT),
__float_as_uint(TRT));
@ -3805,7 +3810,8 @@ void MicrofacetHairBsdfNode::compile(SVMCompiler &compiler)
void MicrofacetHairBsdfNode::compile(OSLCompiler &compiler)
{
compiler.parameter(this, "parametrization");
compiler.parameter(this, "model_type");
compiler.parameter(this, "cross_section_type");
compiler.parameter(this, "distribution_type");
compiler.add(this, "node_microfacet_hair_bsdf");
}

View File

@ -922,8 +922,10 @@ class MicrofacetHairBsdfNode : public BsdfBaseNode {
NODE_SOCKET_API(float, random)
/* Selected coloring parametrization. */
NODE_SOCKET_API(NodeMicrofacetHairParametrization, parametrization)
/* Selected model type. */
NODE_SOCKET_API(NodeMicrofacetHairModelType, model_type)
/* Selected cross-section type. */
NODE_SOCKET_API(NodeMicrofacetHairCrossSectionType, cross_section_type)
/* Selected microfacet distribution type. */
NODE_SOCKET_API(NodeMicrofacetHairDistributionType, distribution_type)
};
class HairBsdfNode : public BsdfNode {

@ -1 +1 @@
Subproject commit 7be7aff5a18c550465b3f7634539ed4168af7c51
Subproject commit 4f6dbb69893bd6bdf73467effe77ae46c8e4ee37

@ -1 +1 @@
Subproject commit c226f867affd12881533a54c8c90ac6eebfaca6c
Subproject commit 5a774a6dad4378f173ec7fdcdcd406048fcc9a29

@ -1 +1 @@
Subproject commit bdcfdd47ec3451822b21d1cff2ea2db751093c9a
Subproject commit 0f72f6c85c3743a9072273acb6a8a34b1cf1064b

View File

@ -322,10 +322,8 @@ typedef struct bNode {
*/
int16_t type;
char _pad1[2];
/** Used for some builtin nodes that store properties but don't have a storage struct . */
int16_t custom1, custom2;
int16_t custom0, custom1, custom2;
float custom3, custom4;
/** Optional link to libdata. */
@ -1687,12 +1685,13 @@ enum {
#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
/* microfacet hair cross-section */
#define SHD_MICROFACET_HAIR_CIRCULAR 0
#define SHD_MICROFACET_HAIR_ELLIPTIC 1
/* microfacet hair distribution */
#define SHD_MICROFACET_HAIR_GGX 0
#define SHD_MICROFACET_HAIR_BECKMANN 1
/* blend texture */
#define SHD_BLEND_LINEAR 0

View File

@ -4671,37 +4671,31 @@ static const EnumPropertyItem node_microfacet_hair_parametrization_items[] = {
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem node_microfacet_hair_mode_items[] = {
{SHD_MICROFACET_HAIR_CIRCULAR_GGX,
"HAIR_MICROFACET_CIRCULAR_GGX",
static const EnumPropertyItem node_microfacet_hair_cross_section_items[] = {
{SHD_MICROFACET_HAIR_CIRCULAR,
"HAIR_MICROFACET_CIRCULAR",
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",
"Circular",
"Microfacet-based hair scattering model with circular cross section"},
{SHD_MICROFACET_HAIR_ELLIPTIC,
"HAIR_MICROFACET_CIRCULAR_ELLIPTIC",
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",
"Elliptical",
"Microfacet-based hair scattering model with elliptical cross section fiber"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem node_microfacet_hair_distribution_items[] = {
{SHD_MICROFACET_HAIR_GGX,
"HAIR_MICROFACET_GGX",
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,
"GGX",
"Microfacet-based hair scattering model with GGX distribution"},
{SHD_MICROFACET_HAIR_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"},
"Beckmann",
"Microfacet-based hair scattering model with Beckmann distribution"},
{0, NULL, 0, NULL, NULL},
};
@ -6231,20 +6225,31 @@ 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_enum_sdna(prop, NULL, "custom0");
RNA_def_property_ui_text(
prop, "Color parametrization", "Select the shader's color parametrization");
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);
prop = RNA_def_property(srna, "cross_section_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_ui_text(
prop, "Hair Cross Section Shape", "Select the hair's cross section shape");
RNA_def_property_enum_items(prop, node_microfacet_hair_cross_section_items);
RNA_def_property_enum_default(prop, SHD_MICROFACET_HAIR_CIRCULAR);
/* 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, "distribution_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);
RNA_def_property_ui_text(
prop, "Microfacet Distribution", "Select the microfacet distribution of the hair surface");
RNA_def_property_enum_items(prop, node_microfacet_hair_distribution_items);
RNA_def_property_enum_default(prop, SHD_MICROFACET_HAIR_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");

View File

@ -81,24 +81,25 @@ static void node_declare(NodeDeclarationBuilder &b)
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);
uiItemR(layout, ptr, "cross_section_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
uiItemR(layout, ptr, "distribution_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;
node->custom0 = SHD_MICROFACET_HAIR_REFLECTANCE;
node->custom1 = SHD_MICROFACET_HAIR_CIRCULAR;
node->custom2 = SHD_MICROFACET_HAIR_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;
int parametrization = node->custom0;
int cross_section_type = node->custom1;
bool elliptical = (model_type == SHD_MICROFACET_HAIR_ELLIPTIC_GGX ||
model_type == SHD_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
bool elliptical = (cross_section_type == SHD_MICROFACET_HAIR_ELLIPTIC);
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (STREQ(sock->name, "Color")) {

@ -1 +1 @@
Subproject commit f542f4d21a077d85ffb3e43aa7f170976dee20b6
Subproject commit e1744b9bd82527cf7e8af63362b61bd309b5711b