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:
parent
de9f32a666
commit
68573757bc
|
@ -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>();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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, ¶metrization);
|
||||
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;
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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", "" )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue