diff options
30 files changed, 1228 insertions, 32 deletions
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 956f8f767a6..25e6db18588 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -524,6 +524,12 @@ static ShaderNode *add_node(Scene *scene, } node = hair; } + else if(b_node.is_a(&RNA_ShaderNodeBsdfHairPrincipled)) { + BL::ShaderNodeBsdfHairPrincipled b_principled_hair_node(b_node); + PrincipledHairBsdfNode *principled_hair = new PrincipledHairBsdfNode(); + principled_hair->parametrization = (NodePrincipledHairParametrization) get_enum(b_principled_hair_node.ptr, "parametrization", NODE_PRINCIPLED_HAIR_NUM, NODE_PRINCIPLED_HAIR_REFLECTANCE); + node = principled_hair; + } else if(b_node.is_a(&RNA_ShaderNodeBsdfPrincipled)) { BL::ShaderNodeBsdfPrincipled b_principled_node(b_node); PrincipledBsdfNode *principled = new PrincipledBsdfNode(); diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 092bec08a51..c4cad17429d 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -156,6 +156,7 @@ set(SRC_CLOSURE_HEADERS closure/volume.h closure/bsdf_principled_diffuse.h closure/bsdf_principled_sheen.h + closure/bsdf_hair_principled.h ) set(SRC_SVM_HEADERS diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index f191b812f11..3a9629ea9d7 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -27,6 +27,7 @@ #include "kernel/closure/bsdf_ashikhmin_shirley.h" #include "kernel/closure/bsdf_toon.h" #include "kernel/closure/bsdf_hair.h" +#include "kernel/closure/bsdf_hair_principled.h" #include "kernel/closure/bsdf_principled_diffuse.h" #include "kernel/closure/bsdf_principled_sheen.h" #include "kernel/closure/bssrdf.h" @@ -171,6 +172,10 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, label = bsdf_hair_transmission_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); break; + case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: + label = bsdf_principled_hair_sample(kg, sc, sd, randu, randv, + eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); + break; #ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: @@ -284,6 +289,9 @@ float3 bsdf_eval(KernelGlobals *kg, case CLOSURE_BSDF_GLOSSY_TOON_ID: eval = bsdf_glossy_toon_eval_reflect(sc, sd->I, omega_in, pdf); break; + case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: + eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf); + break; case CLOSURE_BSDF_HAIR_REFLECTION_ID: eval = bsdf_hair_reflection_eval_reflect(sc, sd->I, omega_in, pdf); break; @@ -366,6 +374,9 @@ float3 bsdf_eval(KernelGlobals *kg, case CLOSURE_BSDF_GLOSSY_TOON_ID: eval = bsdf_glossy_toon_eval_transmit(sc, sd->I, omega_in, pdf); break; + case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: + eval = bsdf_principled_hair_eval(kg, sd, sc, omega_in, pdf); + break; case CLOSURE_BSDF_HAIR_REFLECTION_ID: eval = bsdf_hair_reflection_eval_transmit(sc, sd->I, omega_in, pdf); break; @@ -424,6 +435,9 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID: bsdf_ashikhmin_shirley_blur(sc, roughness); break; + case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: + bsdf_principled_hair_blur(sc, roughness); + break; default: break; } diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h new file mode 100644 index 00000000000..4ee58089384 --- /dev/null +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -0,0 +1,502 @@ +/* + * 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. + */ + +#ifdef __KERNEL_CPU__ +#include <fenv.h> +#endif + +#include "kernel/kernel_color.h" + +#ifndef __BSDF_HAIR_PRINCIPLED_H__ +#define __BSDF_HAIR_PRINCIPLED_H__ + +CCL_NAMESPACE_BEGIN + +typedef ccl_addr_space struct PrincipledHairExtra { + /* Geometry data. */ + float4 geom; +} PrincipledHairExtra; + +typedef ccl_addr_space struct PrincipledHairBSDF { + SHADER_CLOSURE_BASE; + + /* Absorption coefficient. */ + float3 sigma; + /* Variance of the underlying logistic distribution. */ + float v; + /* Scale factor of the underlying logistic distribution. */ + float s; + /* Cuticle tilt angle. */ + float alpha; + /* IOR. */ + float eta; + /* Effective variance for the diffuse bounce only. */ + float m0_roughness; + + /* Extra closure. */ + PrincipledHairExtra *extra; +} PrincipledHairBSDF; + +static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledHairBSDF), "PrincipledHairBSDF is too large!"); +static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledHairExtra), "PrincipledHairExtra is too large!"); + +ccl_device_inline float cos_from_sin(const float s) +{ + return safe_sqrtf(1.0f - s*s); +} + +/* Gives the change in direction in the normal plane for the given angles and p-th-order scattering. */ +ccl_device_inline float delta_phi(int p, float gamma_o, float gamma_t) +{ + return 2.0f * p * gamma_t - 2.0f * gamma_o + p * M_PI_F; +} + +/* Remaps the given angle to [-pi, pi]. */ +ccl_device_inline float wrap_angle(float a) +{ + while(a > M_PI_F) { + a -= M_2PI_F; + } + while(a < -M_PI_F) { + a += M_2PI_F; + } + return a; +} + +/* Logistic distribution function. */ +ccl_device_inline float logistic(float x, float s) +{ + float v = expf(-fabsf(x)/s); + return v / (s * sqr(1.0f + v)); +} + +/* Logistic cumulative density function. */ +ccl_device_inline float logistic_cdf(float x, float s) +{ + float arg = -x/s; + /* expf() overflows if arg >= 89.0. */ + if(arg > 88.0f) { + return 0.0f; + } + else { + return 1.0f / (1.0f + expf(arg)); + } +} + +/* Numerical approximation to the Bessel function of the first kind. */ +ccl_device_inline float bessel_I0(float x) +{ + x = sqr(x); + float val = 1.0f + 0.25f*x; + float pow_x_2i = sqr(x); + uint64_t i_fac_2 = 1; + int pow_4_i = 16; + for(int i = 2; i < 10; i++) { + i_fac_2 *= i*i; + float newval = val + pow_x_2i / (pow_4_i * i_fac_2); + if(val == newval) { + return val; + } + val = newval; + pow_x_2i *= x; + pow_4_i *= 4; + } + return val; +} + +/* Logarithm of the Bessel function of the first kind. */ +ccl_device_inline float log_bessel_I0(float x) +{ + if (x > 12.0f) { + /* log(1/x) == -log(x) iff x > 0. + * This is only used with positive cosines */ + return x + 0.5f * (1.f / (8.0f * x) - M_LN_2PI_F - logf(x)); + } + else { + return logf(bessel_I0(x)); + } +} + +/* Logistic distribution limited to the interval [-pi, pi]. */ +ccl_device_inline float trimmed_logistic(float x, float s) +{ + /* The logistic distribution is symmetric and centered around zero, + * so logistic_cdf(x, s) = 1 - logistic_cdf(-x, s). + * Therefore, logistic_cdf(x, s)-logistic_cdf(-x, s) = 1 - 2*logistic_cdf(-x, s) */ + float scaling_fac = 1.0f - 2.0f*logistic_cdf(-M_PI_F, s); + float val = logistic(x, s); + return safe_divide(val, scaling_fac); +} + +/* Sampling function for the trimmed logistic function. */ +ccl_device_inline float sample_trimmed_logistic(float u, float s) +{ + float cdf_minuspi = logistic_cdf(-M_PI_F, s); + float x = -s*logf(1.0f / (u*(1.0f - 2.0f*cdf_minuspi) + cdf_minuspi) - 1.0f); + return clamp(x, -M_PI_F, M_PI_F); +} + +/* Azimuthal scattering function Np. */ +ccl_device_inline float azimuthal_scattering(float phi, + int p, + float s, + float gamma_o, + float gamma_t) +{ + float phi_o = wrap_angle(phi - delta_phi(p, gamma_o, gamma_t)); + float val = trimmed_logistic(phi_o, s); + return val; +} + +/* Longitudinal scattering function Mp. */ +ccl_device_inline float longitudinal_scattering(float sin_theta_i, + float cos_theta_i, + float sin_theta_o, + float cos_theta_o, + float v) +{ + float inv_v = 1.0f/v; + float cos_arg = cos_theta_i * cos_theta_o * inv_v; + float sin_arg = sin_theta_i * sin_theta_o * inv_v; + if(v <= 0.1f) { + float i0 = log_bessel_I0(cos_arg); + float val = expf(i0 - sin_arg - inv_v + 0.6931f + logf(0.5f*inv_v)); + return val; + } + else { + float i0 = bessel_I0(cos_arg); + float val = (expf(-sin_arg) * i0) / (sinhf(inv_v) * 2.0f * v); + return val; + } +} + +/* Combine the three values using their luminances. */ +ccl_device_inline float4 combine_with_energy(KernelGlobals *kg, float3 c) +{ + return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); +} + +#ifdef __HAIR__ +/* Set up the hair closure. */ +ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bsdf) +{ + bsdf->type = CLOSURE_BSDF_HAIR_PRINCIPLED_ID; + bsdf->v = clamp(bsdf->v, 0.001f, 1.0f); + bsdf->s = clamp(bsdf->s, 0.001f, 1.0f); + /* Apply Primary Reflection Roughness modifier. */ + bsdf->m0_roughness = clamp(bsdf->m0_roughness*bsdf->v, 0.001f, 1.0f); + + /* Map from roughness_u and roughness_v to variance and scale factor. */ + bsdf->v = sqr(0.726f*bsdf->v + 0.812f*sqr(bsdf->v) + 3.700f*pow20(bsdf->v)); + bsdf->s = (0.265f*bsdf->s + 1.194f*sqr(bsdf->s) + 5.372f*pow22(bsdf->s))*M_SQRT_PI_8_F; + bsdf->m0_roughness = sqr(0.726f*bsdf->m0_roughness + 0.812f*sqr(bsdf->m0_roughness) + 3.700f*pow20(bsdf->m0_roughness)); + + /* Compute local frame, aligned to curve tangent and ray direction. */ + float3 X = safe_normalize(sd->dPdu); + float3 Y = safe_normalize(cross(X, sd->I)); + float3 Z = safe_normalize(cross(X, Y)); + /* TODO: the solution below works where sd->Ng is the normal + * pointing from the center of the curve to the shading point. + * It doesn't work for triangles, see https://developer.blender.org/T43625 */ + + /* h -1..0..1 means the rays goes from grazing the hair, to hitting it at + * the center, to grazing the other edge. This is the sine of the angle + * between sd->Ng and Z, as seen from the tangent X. */ + + /* TODO: we convert this value to a cosine later and discard the sign, so + * we could probably save some operations. */ + float h = dot(cross(sd->Ng, X), Z); + + kernel_assert(fabsf(h) < 1.0f + 1e-4f); + kernel_assert(isfinite3_safe(Y)); + kernel_assert(isfinite_safe(h)); + + bsdf->extra->geom = make_float4(Y.x, Y.y, Y.z, h); + + return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG; +} + +#endif /* __HAIR__ */ + +/* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ +ccl_device_inline void hair_attenuation(KernelGlobals *kg, + float f, + float3 T, + float4 *Ap) +{ + /* Primary specular (R). */ + Ap[0] = make_float4(f, f, f, f); + + /* Transmission (TT). */ + float3 col = sqr(1.0f - f) * T; + Ap[1] = combine_with_energy(kg, col); + + /* Secondary specular (TRT). */ + col *= T*f; + Ap[2] = combine_with_energy(kg, col); + + /* Residual component (TRRT+). */ + col *= safe_divide_color(T*f, make_float3(1.0f, 1.0f, 1.0f) - T*f); + Ap[3] = combine_with_energy(kg, col); + + /* Normalize sampling weights. */ + float totweight = Ap[0].w + Ap[1].w + Ap[2].w + Ap[3].w; + float fac = safe_divide(1.0f, totweight); + + Ap[0].w *= fac; + Ap[1].w *= fac; + Ap[2].w *= fac; + Ap[3].w *= fac; +} + +/* Given the tilt angle, generate the rotated theta_i for the different bounces. */ +ccl_device_inline void hair_alpha_angles(float sin_theta_i, + float cos_theta_i, + float alpha, + float *angles) +{ + float sin_1alpha = sinf(alpha); + float cos_1alpha = cos_from_sin(sin_1alpha); + float sin_2alpha = 2.0f*sin_1alpha*cos_1alpha; + float cos_2alpha = sqr(cos_1alpha) - sqr(sin_1alpha); + float sin_4alpha = 2.0f*sin_2alpha*cos_2alpha; + float cos_4alpha = sqr(cos_2alpha) - sqr(sin_2alpha); + + angles[0] = sin_theta_i*cos_2alpha + cos_theta_i*sin_2alpha; + angles[1] = fabsf(cos_theta_i*cos_2alpha - sin_theta_i*sin_2alpha); + angles[2] = sin_theta_i*cos_1alpha - cos_theta_i*sin_1alpha; + angles[3] = fabsf(cos_theta_i*cos_1alpha + sin_theta_i*sin_1alpha); + angles[4] = sin_theta_i*cos_4alpha - cos_theta_i*sin_4alpha; + angles[5] = fabsf(cos_theta_i*cos_4alpha + sin_theta_i*sin_4alpha); +} + +/* Evaluation function for our shader. */ +ccl_device float3 bsdf_principled_hair_eval(KernelGlobals *kg, + const ShaderData *sd, + const ShaderClosure *sc, + const float3 omega_in, + float *pdf) +{ + kernel_assert(isfinite3_safe(sd->P) && isfinite_safe(sd->ray_length)); + + const PrincipledHairBSDF *bsdf = (const PrincipledHairBSDF*) sc; + float3 Y = float4_to_float3(bsdf->extra->geom); + + float3 X = safe_normalize(sd->dPdu); + kernel_assert(fabsf(dot(X, Y)) < 1e-4f); + float3 Z = safe_normalize(cross(X, Y)); + + float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z)); + float3 wi = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z)); + + float sin_theta_o = wo.x; + float cos_theta_o = cos_from_sin(sin_theta_o); + float phi_o = atan2f(wo.z, wo.y); + + float sin_theta_t = sin_theta_o / bsdf->eta; + float cos_theta_t = cos_from_sin(sin_theta_t); + + float sin_gamma_o = bsdf->extra->geom.w; + float cos_gamma_o = cos_from_sin(sin_gamma_o); + float gamma_o = safe_asinf(sin_gamma_o); + + float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o)); + float cos_gamma_t = cos_from_sin(sin_gamma_t); + float gamma_t = safe_asinf(sin_gamma_t); + + float3 T = exp3(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); + float4 Ap[4]; + hair_attenuation(kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap); + + float sin_theta_i = wi.x; + float cos_theta_i = cos_from_sin(sin_theta_i); + float phi_i = atan2f(wi.z, wi.y); + + float phi = phi_i - phi_o; + + float angles[6]; + hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles); + + float4 F; + float Mp, Np; + + /* Primary specular (R). */ + Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness); + Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t); + F = Ap[0] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Transmission (TT). */ + Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f*bsdf->v); + Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t); + F += Ap[1] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Secondary specular (TRT). */ + Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f*bsdf->v); + Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t); + F += Ap[2] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Residual component (TRRT+). */ + Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f*bsdf->v); + Np = M_1_2PI_F; + F += Ap[3] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + *pdf = F.w; + return float4_to_float3(F); +} + +/* Sampling function for the hair shader. */ +ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, + const ShaderClosure *sc, + ShaderData *sd, + float randu, + float randv, + float3 *eval, + float3 *omega_in, + float3 *domega_in_dx, + float3 *domega_in_dy, + float *pdf) +{ + PrincipledHairBSDF *bsdf = (PrincipledHairBSDF*) sc; + + float3 Y = float4_to_float3(bsdf->extra->geom); + + float3 X = safe_normalize(sd->dPdu); + kernel_assert(fabsf(dot(X, Y)) < 1e-4f); + float3 Z = safe_normalize(cross(X, Y)); + + float3 wo = make_float3(dot(sd->I, X), dot(sd->I, Y), dot(sd->I, Z)); + + float2 u[2]; + u[0] = make_float2(randu, randv); + u[1].x = lcg_step_float_addrspace(&sd->lcg_state); + u[1].y = lcg_step_float_addrspace(&sd->lcg_state); + + float sin_theta_o = wo.x; + float cos_theta_o = cos_from_sin(sin_theta_o); + float phi_o = atan2f(wo.z, wo.y); + + float sin_theta_t = sin_theta_o / bsdf->eta; + float cos_theta_t = cos_from_sin(sin_theta_t); + + float sin_gamma_o = bsdf->extra->geom.w; + float cos_gamma_o = cos_from_sin(sin_gamma_o); + float gamma_o = safe_asinf(sin_gamma_o); + + float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o)); + float cos_gamma_t = cos_from_sin(sin_gamma_t); + float gamma_t = safe_asinf(sin_gamma_t); + + float3 T = exp3(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); + float4 Ap[4]; + hair_attenuation(kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap); + + int p = 0; + for(; p < 3; p++) { + if(u[0].x < Ap[p].w) { + break; + } + u[0].x -= Ap[p].w; + } + + float v = bsdf->v; + if(p == 1) { + v *= 0.25f; + } + if(p >= 2) { + v *= 4.0f; + } + + u[1].x = max(u[1].x, 1e-5f); + float fac = 1.0f + v*logf(u[1].x + (1.0f - u[1].x)*expf(-2.0f/v)); + float sin_theta_i = -fac * sin_theta_o + cos_from_sin(fac) * cosf(M_2PI_F * u[1].y) * cos_theta_o; + float cos_theta_i = cos_from_sin(sin_theta_i); + + float angles[6]; + if(p < 3) { + hair_alpha_angles(sin_theta_i, cos_theta_i, -bsdf->alpha, angles); + sin_theta_i = angles[2*p]; + cos_theta_i = angles[2*p+1]; + } + + float phi; + if(p < 3) { + phi = delta_phi(p, gamma_o, gamma_t) + sample_trimmed_logistic(u[0].y, bsdf->s); + } + else { + phi = M_2PI_F*u[0].y; + } + float phi_i = phi_o + phi; + + hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles); + + float4 F; + float Mp, Np; + + /* Primary specular (R). */ + Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness); + Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t); + F = Ap[0] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Transmission (TT). */ + Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f*bsdf->v); + Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t); + F += Ap[1] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Secondary specular (TRT). */ + Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f*bsdf->v); + Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t); + F += Ap[2] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + /* Residual component (TRRT+). */ + Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f*bsdf->v); + Np = M_1_2PI_F; + F += Ap[3] * Mp * Np; + kernel_assert(isfinite3_safe(float4_to_float3(F))); + + *eval = float4_to_float3(F); + *pdf = F.w; + + *omega_in = X*sin_theta_i + Y*cos_theta_i*cosf(phi_i) + Z*cos_theta_i*sinf(phi_i); + +#ifdef __RAY_DIFFERENTIALS__ + float3 N = safe_normalize(sd->I + *omega_in); + *domega_in_dx = (2 * dot(N, sd->dI.dx)) * N - sd->dI.dx; + *domega_in_dy = (2 * dot(N, sd->dI.dy)) * N - sd->dI.dy; +#endif + + return LABEL_GLOSSY|((p == 0)? LABEL_REFLECT : LABEL_TRANSMIT); +} + +/* Implements Filter Glossy by capping the effective roughness. */ +ccl_device void bsdf_principled_hair_blur(ShaderClosure *sc, float roughness) +{ + PrincipledHairBSDF *bsdf = (PrincipledHairBSDF*)sc; + + bsdf->v = fmaxf(roughness, bsdf->v); + bsdf->s = fmaxf(roughness, bsdf->s); + bsdf->m0_roughness = fmaxf(roughness, bsdf->m0_roughness); +} + +CCL_NAMESPACE_END + +#endif /* __BSDF_HAIR_PRINCIPLED_H__ */ diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index e35267f02bf..dea0c742ed7 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -23,6 +23,33 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ +/* Interpolation of curve geometry */ + +ccl_device_inline float3 curvetangent(float t, float3 p0, float3 p1, float3 p2, float3 p3) +{ + float fc = 0.71f; + float data[4]; + float t2 = t * t; + data[0] = -3.0f * fc * t2 + 4.0f * fc * t - fc; + data[1] = 3.0f * (2.0f - fc) * t2 + 2.0f * (fc - 3.0f) * t; + data[2] = 3.0f * (fc - 2.0f) * t2 + 2.0f * (3.0f - 2.0f * fc) * t + fc; + data[3] = 3.0f * fc * t2 - 2.0f * fc * t; + return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; +} + +ccl_device_inline float3 curvepoint(float t, float3 p0, float3 p1, float3 p2, float3 p3) +{ + float data[4]; + float fc = 0.71f; + float t2 = t * t; + float t3 = t2 * t; + data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t; + data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f; + data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t; + data[3] = fc * t3 - fc * t2; + return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; +} + /* Reading attributes on various curve elements */ ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index 46c3f408f0b..4cfbe21685c 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -752,31 +752,6 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, #endif } -ccl_device_inline float3 curvetangent(float t, float3 p0, float3 p1, float3 p2, float3 p3) -{ - float fc = 0.71f; - float data[4]; - float t2 = t * t; - data[0] = -3.0f * fc * t2 + 4.0f * fc * t - fc; - data[1] = 3.0f * (2.0f - fc) * t2 + 2.0f * (fc - 3.0f) * t; - data[2] = 3.0f * (fc - 2.0f) * t2 + 2.0f * (3.0f - 2.0f * fc) * t + fc; - data[3] = 3.0f * fc * t2 - 2.0f * fc * t; - return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; -} - -ccl_device_inline float3 curvepoint(float t, float3 p0, float3 p1, float3 p2, float3 p3) -{ - float data[4]; - float fc = 0.71f; - float t2 = t * t; - float t3 = t2 * t; - data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t; - data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f; - data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t; - data[3] = fc * t3 - fc * t2; - return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; -} - ccl_device_inline float3 curve_refine(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h index d1ae10a0384..3f7e264fbee 100644 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ b/intern/cycles/kernel/kernel_compat_opencl.h @@ -123,6 +123,7 @@ #define fmaxf(x, y) fmax(((float)(x)), ((float)(y))) #define fminf(x, y) fmin(((float)(x)), ((float)(y))) #define fmodf(x, y) fmod((float)(x), (float)(y)) +#define sinhf(x) sinh(((float)(x))) #ifndef __CL_USE_NATIVE__ # define sinf(x) native_sin(((float)(x))) diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index a7072c3ad03..d71761a97bc 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -91,7 +91,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg, ccl_device float3 volume_color_transmittance(float3 sigma, float t) { - return make_float3(expf(-sigma.x * t), expf(-sigma.y * t), expf(-sigma.z * t)); + return exp3(-sigma * t); } ccl_device float kernel_volume_channel_get(float3 value, int channel) @@ -234,7 +234,7 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, sum += (-sigma_t * (new_t - t)); if((i & 0x07) == 0) { /* ToDo: Other interval? */ - tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); + tp = *throughput * exp3(sum); /* stop if nearly all light is blocked */ if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) @@ -246,7 +246,7 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, t = new_t; if(t == ray->t) { /* Update throughput in case we haven't done it above */ - tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); + tp = *throughput * exp3(sum); break; } } diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index 581b38e65c0..8c7ae30725c 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -4,7 +4,7 @@ * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. * All Rights Reserved. * - * Modifications Copyright 2011, Blender Foundation. + * Modifications Copyright 2011-2018, Blender Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -59,6 +59,7 @@ #include "kernel/closure/bsdf_ashikhmin_shirley.h" #include "kernel/closure/bsdf_toon.h" #include "kernel/closure/bsdf_hair.h" +#include "kernel/closure/bsdf_hair_principled.h" #include "kernel/closure/bsdf_principled_diffuse.h" #include "kernel/closure/bsdf_principled_sheen.h" #include "kernel/closure/volume.h" @@ -176,6 +177,61 @@ BSDF_CLOSURE_CLASS_BEGIN(PrincipledSheen, principled_sheen, PrincipledSheenBsdf, CLOSURE_FLOAT3_PARAM(PrincipledSheenClosure, params.N), BSDF_CLOSURE_CLASS_END(PrincipledSheen, principled_sheen) +/* PRINCIPLED HAIR BSDF */ +class PrincipledHairClosure : public CBSDFClosure { +public: + PrincipledHairBSDF params; + + PrincipledHairBSDF *alloc(ShaderData *sd, int path_flag, float3 weight) + { + PrincipledHairBSDF *bsdf = (PrincipledHairBSDF*)bsdf_alloc_osl(sd, sizeof(PrincipledHairBSDF), weight, ¶ms); + if(!bsdf) { + return NULL; + } + + PrincipledHairExtra *extra = (PrincipledHairExtra*)closure_alloc_extra(sd, sizeof(PrincipledHairExtra)); + if(!extra) { + return NULL; + } + + bsdf->extra = extra; + return bsdf; + } + + void setup(ShaderData *sd, int path_flag, float3 weight) + { + if(!skip(sd, path_flag, LABEL_GLOSSY)) + { + PrincipledHairBSDF *bsdf = (PrincipledHairBSDF*)alloc(sd, path_flag, weight); + if (!bsdf) + { + return; + } + + sd->flag |= (bsdf) ? bsdf_principled_hair_setup(sd, bsdf) : 0; + } + } +}; + +static ClosureParam *closure_bsdf_principled_hair_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(PrincipledHairClosure, params.N), + CLOSURE_FLOAT3_PARAM(PrincipledHairClosure, params.sigma), + CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.v), + CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.s), + CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.m0_roughness), + CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.alpha), + CLOSURE_FLOAT_PARAM(PrincipledHairClosure, params.eta), + CLOSURE_STRING_KEYPARAM(PrincipledHairClosure, label, "label"), + CLOSURE_FINISH_PARAM(PrincipledHairClosure) + }; + + return params; +} + +CCLOSURE_PREPARE(closure_bsdf_principled_hair_prepare, PrincipledHairClosure) + /* DISNEY PRINCIPLED CLEARCOAT */ class PrincipledClearcoatClosure : public CBSDFClosure { public: @@ -322,6 +378,9 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) register_closure(ss, "hair_transmission", id++, bsdf_hair_transmission_params(), bsdf_hair_transmission_prepare); + register_closure(ss, "principled_hair", id++, + closure_bsdf_principled_hair_params(), closure_bsdf_principled_hair_prepare); + register_closure(ss, "henyey_greenstein", id++, closure_henyey_greenstein_params(), closure_henyey_greenstein_prepare); register_closure(ss, "absorption", id++, diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index 857cc84afd2..d9aeb9ab9fb 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -79,6 +79,7 @@ void closure_bsdf_microfacet_multi_ggx_fresnel_prepare(OSL::RendererServices *, void closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_principled_clearcoat_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void *data); #define CCLOSURE_PREPARE(name, classname) \ void name(RendererServices *, int id, void *data) \ diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index 9ee78d160a4..4740db27d4e 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC_OSL node_wave_texture.osl node_wireframe.osl node_hair_bsdf.osl + node_principled_hair_bsdf.osl node_uv_map.osl node_principled_bsdf.osl node_rgb_to_bw.osl diff --git a/intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl b/intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl new file mode 100644 index 00000000000..757a88f8ece --- /dev/null +++ b/intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl @@ -0,0 +1,105 @@ +/* + * 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 "stdosl.h" + +color log3(color a) +{ + return color(log(a[0]), log(a[1]), log(a[2])); +} + +color 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 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 = log3(c) / roughness_fac; + return sigma * sigma; +} + +shader node_principled_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 = "Absorption coefficient", + float Offset = radians(2), + float Roughness = 0.3, + float RadialRoughness = 0.3, + float RandomRoughness = 0.0, + float Coat = 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 m0_roughness = 1.0 - clamp(Coat, 0.0, 1.0); + float roughness = Roughness*factor_random_roughness; + float radial_roughness = RadialRoughness*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 = sigma_from_concentration(eumelanin, pheomelanin); + + /* Optional tint. */ + color tint_sigma = sigma_from_reflectance(Tint, radial_roughness); + sigma = melanin_sigma + tint_sigma; + } + else if (parametrization == "Direct coloring"){ + sigma = sigma_from_reflectance(Color, radial_roughness); + } + else { + /* Fallback to brownish hair, same as defaults for melanin. */ + sigma = sigma_from_concentration(0.0, 0.8054375); + } + + BSDF = principled_hair(Normal, sigma, roughness, radial_roughness, m0_roughness, Offset, IOR); +} diff --git a/intern/cycles/kernel/shaders/stdosl.h b/intern/cycles/kernel/shaders/stdosl.h index 82223ca0219..df9c2010872 100644 --- a/intern/cycles/kernel/shaders/stdosl.h +++ b/intern/cycles/kernel/shaders/stdosl.h @@ -554,6 +554,7 @@ closure color bssrdf(string method, normal N, vector radius, color albedo) BUILT // Hair closure color hair_reflection(normal N, float roughnessu, float roughnessv, vector T, float offset) BUILTIN; closure color hair_transmission(normal N, float roughnessu, float roughnessv, vector T, float offset) BUILTIN; +closure color principled_hair(normal N, color sigma, float roughnessu, float roughnessv, float coat, float alpha, float eta) BUILTIN; // Volume closure color henyey_greenstein(float g) BUILTIN; diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index 4de9cfb88db..aa253223c93 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -16,6 +16,21 @@ CCL_NAMESPACE_BEGIN +/* Hair Melanin */ + +ccl_device_inline float3 sigma_from_concentration(float eumelanin, float pheomelanin) +{ + return eumelanin*make_float3(0.506f, 0.841f, 1.653f) + pheomelanin*make_float3(0.343f, 0.733f, 1.924f); +} + +ccl_device_inline float3 sigma_from_reflectance(float3 color, float azimuthal_roughness) +{ + float x = azimuthal_roughness; + float roughness_fac = (((((0.245f*x) + 5.574f)*x - 10.73f)*x + 2.532f)*x - 0.215f)*x + 5.969f; + float3 sigma = log3(color) / roughness_fac; + return sigma * sigma; +} + /* Closure Nodes */ ccl_device void svm_node_glass_setup(ShaderData *sd, MicrofacetBsdf *bsdf, int type, float eta, float roughness, bool refract) @@ -243,7 +258,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float * float3 spec_weight = weight * specular_weight; MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), spec_weight); - if(!bsdf){ + if(!bsdf) { break; } @@ -722,6 +737,107 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float * break; } #ifdef __HAIR__ + case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: { + uint4 data_node2 = read_node(kg, offset); + uint4 data_node3 = read_node(kg, offset); + uint4 data_node4 = read_node(kg, offset); + + float3 weight = sd->svm_closure_weight * mix_weight; + + uint offset_ofs, ior_ofs, color_ofs, parametrization; + decode_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; + decode_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; + decode_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_attribute_float(kg, sd, attr_descr_random, NULL, NULL); + } + else { + random = stack_load_float_default(stack, random_ofs, data_node3.y); + } + + + PrincipledHairBSDF *bsdf = (PrincipledHairBSDF*)bsdf_alloc(sd, sizeof(PrincipledHairBSDF), weight); + if(bsdf) { + PrincipledHairExtra *extra = (PrincipledHairExtra*)closure_alloc_extra(sd, sizeof(PrincipledHairExtra)); + + if (!extra) + break; + + /* 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; + float radial_roughness = param2 * factor_random_roughness; + + /* Remap Coat value to [0, 100]% of Roughness. */ + float coat = stack_load_float_default(stack, coat_ofs, data_node2.y); + float m0_roughness = 1.0f - clamp(coat, 0.0f, 1.0f); + + bsdf->N = N; + bsdf->v = roughness; + bsdf->s = radial_roughness; + bsdf->m0_roughness = m0_roughness; + bsdf->alpha = alpha; + bsdf->eta = ior; + bsdf->extra = extra; + + switch(parametrization) { + case NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION: { + float3 absorption_coefficient = stack_load_float3(stack, absorption_coefficient_ofs); + bsdf->sigma = absorption_coefficient; + break; + } + case NODE_PRINCIPLED_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; + float3 melanin_sigma = sigma_from_concentration(eumelanin, pheomelanin); + + /* Optional tint. */ + float3 tint = stack_load_float3(stack, tint_ofs); + float3 tint_sigma = sigma_from_reflectance(tint, radial_roughness); + + bsdf->sigma = melanin_sigma + tint_sigma; + break; + } + case NODE_PRINCIPLED_HAIR_REFLECTANCE: { + float3 color = stack_load_float3(stack, color_ofs); + bsdf->sigma = sigma_from_reflectance(color, radial_roughness); + break; + } + default: { + /* Fallback to brownish hair, same as defaults for melanin. */ + kernel_assert(!"Invalid Principled Hair parametrization!"); + bsdf->sigma = sigma_from_concentration(0.0f, 0.8054375f); + break; + } + } + + sd->flag |= bsdf_principled_hair_setup(sd, bsdf); + } + break; + } case CLOSURE_BSDF_HAIR_REFLECTION_ID: case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: { float3 weight = sd->svm_closure_weight * mix_weight; @@ -764,7 +880,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float * break; } -#endif +#endif /* __HAIR__ */ #ifdef __SUBSURFACE__ case CLOSURE_BSSRDF_CUBIC_ID: diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index e03ad3a0cfe..910537a2539 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -418,6 +418,13 @@ typedef enum ShaderType { SHADER_TYPE_BUMP, } ShaderType; +typedef enum NodePrincipledHairParametrization { + NODE_PRINCIPLED_HAIR_REFLECTANCE = 0, + NODE_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION = 1, + NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION = 2, + NODE_PRINCIPLED_HAIR_NUM, +} NodePrincipledHairParametrization; + /* Closure */ typedef enum ClosureType { @@ -464,6 +471,7 @@ typedef enum ClosureType { CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID, CLOSURE_BSDF_SHARP_GLASS_ID, + CLOSURE_BSDF_HAIR_PRINCIPLED_ID, CLOSURE_BSDF_HAIR_TRANSMISSION_ID, /* Special cases */ @@ -495,7 +503,7 @@ typedef enum ClosureType { /* watch this, being lazy with memory usage */ #define CLOSURE_IS_BSDF(type) (type <= CLOSURE_BSDF_TRANSPARENT_ID) #define CLOSURE_IS_BSDF_DIFFUSE(type) (type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_DIFFUSE_TOON_ID) -#define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) +#define CLOSURE_IS_BSDF_GLOSSY(type) ((type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID )|| (type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID)) #define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSLUCENT_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) #define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID || type == CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID) #define CLOSURE_IS_BSDF_SINGULAR(type) (type == CLOSURE_BSDF_REFLECTION_ID || \ diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 78a2039c423..3a9e2981418 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -1091,6 +1091,9 @@ int ShaderGraph::get_num_closures() else if(CLOSURE_IS_VOLUME(closure_type)) { num_closures += VOLUME_STACK_SIZE; } + else if(closure_type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { + num_closures += 4; + } else { ++num_closures; } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 986004433e4..96e7459a48c 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -3092,6 +3092,139 @@ void PrincipledVolumeNode::compile(OSLCompiler& compiler) compiler.add(this, "node_principled_volume"); } +/* Principled Hair BSDF Closure */ + +NODE_DEFINE(PrincipledHairBsdfNode) +{ + NodeType* type = NodeType::add("principled_hair_bsdf", create, NodeType::SHADER); + + /* Color parametrization specified as enum. */ + static NodeEnum parametrization_enum; + parametrization_enum.insert("Direct coloring", NODE_PRINCIPLED_HAIR_REFLECTANCE); + parametrization_enum.insert("Melanin concentration", NODE_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); + parametrization_enum.insert("Absorption coefficient", NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION); + SOCKET_ENUM(parametrization, "Parametrization", parametrization_enum, NODE_PRINCIPLED_HAIR_REFLECTANCE); + + /* 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(offset, "Offset", 2.f*M_PI_F/180.f); + SOCKET_IN_FLOAT(roughness, "Roughness", 0.3f); + SOCKET_IN_FLOAT(radial_roughness, "Radial Roughness", 0.3f); + SOCKET_IN_FLOAT(coat, "Coat", 0.0f); + SOCKET_IN_FLOAT(ior, "IOR", 1.55f); + + 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", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); + SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); + + SOCKET_OUT_CLOSURE(BSDF, "BSDF"); + + return type; +} + +PrincipledHairBsdfNode::PrincipledHairBsdfNode() +: BsdfBaseNode(node_type) +{ + closure = CLOSURE_BSDF_HAIR_PRINCIPLED_ID; +} + +/* Enable retrieving Hair Info -> Random if Random isn't linked. */ +void PrincipledHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) +{ + if(!input("Random")->link) { + attributes->add(ATTR_STD_CURVE_RANDOM); + } + ShaderNode::attributes(shader, attributes); +} + +/* Prepares the input data for the SVM shader. */ +void PrincipledHairBsdfNode::compile(SVMCompiler& compiler) +{ + compiler.add_node(NODE_CLOSURE_SET_WEIGHT, make_float3(1.0f, 1.0f, 1.0f)); + + ShaderInput *roughness_in = input("Roughness"); + ShaderInput *radial_roughness_in = input("Radial Roughness"); + ShaderInput *random_roughness_in = input("Random Roughness"); + ShaderInput *offset_in = input("Offset"); + ShaderInput *coat_in = input("Coat"); + ShaderInput *ior_in = input("IOR"); + ShaderInput *melanin_in = input("Melanin"); + ShaderInput *melanin_redness_in = input("Melanin Redness"); + ShaderInput *random_color_in = input("Random Color"); + + 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")); + + ShaderInput *random_in = input("Random"); + int attr_random = random_in->link ? SVM_STACK_INVALID : compiler.attribute(ATTR_STD_CURVE_RANDOM); + + /* Encode all parameters into data nodes. */ + compiler.add_node(NODE_CLOSURE_BSDF, + /* Socket IDs can be packed 4 at a time into a single data packet */ + compiler.encode_uchar4(closure, + compiler.stack_assign_if_linked(roughness_in), + compiler.stack_assign_if_linked(radial_roughness_in), + compiler.closure_mix_weight_offset()), + /* The rest are stored as unsigned integers */ + __float_as_uint(roughness), + __float_as_uint(radial_roughness)); + + compiler.add_node(compiler.stack_assign_if_linked(input("Normal")), + compiler.encode_uchar4( + compiler.stack_assign_if_linked(offset_in), + compiler.stack_assign_if_linked(ior_in), + color_ofs, + parametrization), + __float_as_uint(offset), + __float_as_uint(ior)); + + compiler.add_node( + compiler.encode_uchar4( + compiler.stack_assign_if_linked(coat_in), + compiler.stack_assign_if_linked(melanin_in), + compiler.stack_assign_if_linked(melanin_redness_in), + absorption_coefficient_ofs), + __float_as_uint(coat), + __float_as_uint(melanin), + __float_as_uint(melanin_redness)); + + compiler.add_node( + compiler.encode_uchar4( + tint_ofs, + compiler.stack_assign_if_linked(random_in), + compiler.stack_assign_if_linked(random_color_in), + compiler.stack_assign_if_linked(random_roughness_in)), + __float_as_uint(random), + __float_as_uint(random_color), + __float_as_uint(random_roughness)); + + compiler.add_node( + compiler.encode_uchar4( + SVM_STACK_INVALID, + SVM_STACK_INVALID, + SVM_STACK_INVALID, + SVM_STACK_INVALID), + attr_random, + SVM_STACK_INVALID, + SVM_STACK_INVALID); +} + +/* Prepares the input data for the OSL shader. */ +void PrincipledHairBsdfNode::compile(OSLCompiler& compiler) +{ + compiler.parameter(this, "parametrization"); + compiler.add(this, "node_principled_hair_bsdf"); +} + /* Hair BSDF Closure */ NODE_DEFINE(HairBsdfNode) diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index ebe6db6e362..28bbe2de05a 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -608,6 +608,45 @@ public: float temperature; }; +/* Interface between the I/O sockets and the SVM/OSL backend. */ +class PrincipledHairBsdfNode : public BsdfBaseNode { +public: + SHADER_NODE_CLASS(PrincipledHairBsdfNode) + void attributes(Shader *shader, AttributeRequestSet *attributes); + + /* Longitudinal roughness. */ + float roughness; + /* Azimuthal roughness. */ + float radial_roughness; + /* Randomization factor for roughnesses. */ + float random_roughness; + /* Longitudinal roughness factor for only the diffuse bounce (shiny undercoat). */ + float coat; + /* Index of reflection. */ + float ior; + /* Cuticle tilt angle. */ + float offset; + /* Direct coloring's color. */ + float3 color; + /* Melanin concentration. */ + float melanin; + /* Melanin redness ratio. */ + float melanin_redness; + /* Dye color. */ + float3 tint; + /* Randomization factor for melanin quantities. */ + float random_color; + /* Absorption coefficient (unfiltered). */ + float3 absorption_coefficient; + + float3 normal; + float surface_mix_weight; + /* If linked, here will be the given random number. */ + float random; + /* Selected coloring parametrization. */ + NodePrincipledHairParametrization parametrization; +}; + class HairBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(HairBsdfNode) diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 85cbd18b7ba..52aeb8d8599 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -55,6 +55,15 @@ CCL_NAMESPACE_BEGIN #ifndef M_2_PI_F # define M_2_PI_F (0.6366197723675813f) /* 2/pi */ #endif +#ifndef M_1_2PI_F +# define M_1_2PI_F (0.1591549430918953f) /* 1/(2*pi) */ +#endif +#ifndef M_SQRT_PI_8_F +# define M_SQRT_PI_8_F (0.6266570686577501f) /* sqrt(pi/8) */ +#endif +#ifndef M_LN_2PI_F +# define M_LN_2PI_F (1.8378770664093454f) /* ln(2*pi) */ +#endif /* Multiplication */ #ifndef M_2PI_F @@ -541,6 +550,16 @@ ccl_device_inline float sqr(float a) return a * a; } +ccl_device_inline float pow20(float a) +{ + return sqr(sqr(sqr(sqr(a))*a)); +} + +ccl_device_inline float pow22(float a) +{ + return sqr(a*sqr(sqr(sqr(a))*a)); +} + ccl_device_inline float beta(float x, float y) { #ifndef __KERNEL_OPENCL__ diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/util_math_float3.h index 3a5a2ab2244..ba1c117cdea 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/util_math_float3.h @@ -382,6 +382,16 @@ ccl_device_inline bool isequal_float3(const float3 a, const float3 b) #endif } +ccl_device_inline float3 exp3(float3 v) +{ + return make_float3(expf(v.x), expf(v.y), expf(v.z)); +} + +ccl_device_inline float3 log3(float3 v) +{ + return make_float3(logf(v.x), logf(v.y), logf(v.z)); +} + ccl_device_inline int3 quick_floor_to_int3(const float3 a) { #ifdef __KERNEL_SSE__ diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c9a15f12f7c..4f4190da30b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -246,6 +246,7 @@ shader_node_categories = [ NodeItem("ShaderNodeVolumeAbsorption"), NodeItem("ShaderNodeVolumeScatter"), NodeItem("ShaderNodeVolumePrincipled"), + NodeItem("ShaderNodeBsdfHairPrincipled", poll=object_shader_nodes_poll) ]), ShaderNewNodeCategory("SH_NEW_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexImage"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8e54c6a87c4..23ef45416f7 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -794,6 +794,8 @@ struct ShadeResult; #define SH_NODE_DISPLACEMENT 198 #define SH_NODE_VECTOR_DISPLACEMENT 199 #define SH_NODE_VOLUME_PRINCIPLED 200 +/* 201..700 occupied by other node types, continue from 701 */ +#define SH_NODE_BSDF_HAIR_PRINCIPLED 701 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index f15d90100d2..c830917a547 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3622,6 +3622,7 @@ static void registerShaderNodes(void) register_node_type_sh_bsdf_velvet(); register_node_type_sh_bsdf_toon(); register_node_type_sh_bsdf_hair(); + register_node_type_sh_bsdf_hair_principled(); register_node_type_sh_emission(); register_node_type_sh_holdout(); register_node_type_sh_volume_absorption(); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 23df1b72c37..6e52af2898e 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1111,6 +1111,11 @@ static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), Pointer uiItemR(layout, ptr, "component", 0, "", ICON_NONE); } +static void node_shader_buts_principled_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "parametrization", 0, "", ICON_NONE); +} + static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *row; @@ -1301,6 +1306,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_BSDF_HAIR: ntype->draw_buttons = node_shader_buts_hair; break; + case SH_NODE_BSDF_HAIR_PRINCIPLED: + ntype->draw_buttons = node_shader_buts_principled_hair; + break; case SH_NODE_SCRIPT: ntype->draw_buttons = node_shader_buts_script; ntype->draw_buttons_ex = node_shader_buts_script_ex; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 00758bd1379..5c692cf85da 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -963,6 +963,11 @@ typedef struct NodeCryptomatte { #define SHD_HAIR_REFLECTION 0 #define SHD_HAIR_TRANSMISSION 1 +/* principled hair parametrization */ +#define SHD_PRINCIPLED_HAIR_REFLECTANCE 0 +#define SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION 1 +#define SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION 2 + /* blend texture */ #define SHD_BLEND_LINEAR 0 #define SHD_BLEND_QUADRATIC 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 52fbadd0f54..b41e9b1c4e5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3350,6 +3350,13 @@ static const EnumPropertyItem node_hair_items[] = { {0, NULL, 0, NULL, NULL} }; +static const EnumPropertyItem node_principled_hair_items[] = { + {SHD_PRINCIPLED_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_PRINCIPLED_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_PRINCIPLED_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_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"}, @@ -4409,6 +4416,21 @@ static void def_hair(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +/* RNA initialization for the custom property. */ +static void def_hair_principled(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_principled_hair_items); + RNA_def_property_enum_default(prop, SHD_PRINCIPLED_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"); +} + static void def_sh_uvmap(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5a5d5997878..7ef41b25de8 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -173,6 +173,7 @@ set(SRC shader/nodes/node_shader_bsdf_transparent.c shader/nodes/node_shader_bsdf_velvet.c shader/nodes/node_shader_bsdf_hair.c + shader/nodes/node_shader_bsdf_hair_principled.c shader/nodes/node_shader_bump.c shader/nodes/node_shader_emission.c shader/nodes/node_shader_fresnel.c diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index ca604363e35..d4134f09597 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -116,6 +116,7 @@ void register_node_type_sh_volume_absorption(void); void register_node_type_sh_volume_scatter(void); void register_node_type_sh_volume_principled(void); void register_node_type_sh_bsdf_hair(void); +void register_node_type_sh_bsdf_hair_principled(void); void register_node_type_sh_subsurface_scattering(void); void register_node_type_sh_mix_shader(void); void register_node_type_sh_add_shader(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a4f8a798576..2fa8cda56a5 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -89,6 +89,7 @@ DefNode( ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BS DefNode( ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" ) +DefNode( ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "") DefNode( ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","") DefNode( ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" ) DefNode( ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c new file mode 100644 index 00000000000..c5029852033 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c @@ -0,0 +1,133 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Stockner, L. E. Segovia + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../node_shader_util.h" + +/* **************** OUTPUT ******************** */ + +/* Color, melanin and absorption coefficient default to approximately same brownish hair. */ +static bNodeSocketTemplate sh_node_bsdf_hair_principled_in[] = { + { SOCK_RGBA, 1, N_("Color"), 0.017513f, 0.005763f, 0.002059f, 1.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Melanin"), 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Melanin Redness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_RGBA, 1, N_("Tint"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, + { SOCK_VECTOR, 1, N_("Absorption Coefficient"), 0.245531f, 0.52f, 1.365f, 0.0f, 0.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Radial Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Coat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("IOR"), 1.55f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Offset"), 2.f*((float)M_PI)/180.f, 0.0f, 0.0f, 0.0f, -M_PI_2, M_PI_2, PROP_ANGLE}, + { SOCK_FLOAT, 1, N_("Random Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Random Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Random"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { -1, 0, "" }, +}; + +static bNodeSocketTemplate sh_node_bsdf_hair_principled_out[] = { + { SOCK_SHADER, 0, N_("BSDF")}, + { -1, 0, "" } +}; + +/* Initialize the custom Parametrization property to Color. */ +static void node_shader_init_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_PRINCIPLED_HAIR_REFLECTANCE; +} + +/* Triggers (in)visibility of some sockets when changing Parametrization. */ +static void node_shader_update_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock; + int parametrization = node->custom1; + + for (sock = node->inputs.first; sock; sock = sock->next) { + if (STREQ(sock->name, "Color")) { + if (parametrization == SHD_PRINCIPLED_HAIR_REFLECTANCE) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Melanin")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Melanin Redness")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Tint")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Absorption Coefficient")) { + if (parametrization == SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Random Color")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + } +} + +/* node type definition */ +void register_node_type_sh_bsdf_hair_principled(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_HAIR_PRINCIPLED, "Principled Hair BSDF", NODE_CLASS_SHADER, 0); + node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_socket_templates(&ntype, sh_node_bsdf_hair_principled_in, sh_node_bsdf_hair_principled_out); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, node_shader_init_hair_principled); + node_type_storage(&ntype, "", NULL, NULL); + node_type_update(&ntype, node_shader_update_hair_principled, NULL); + + nodeRegisterType(&ntype); +} |