Merge branch 'master' into blender2.8

This commit is contained in:
Brecht Van Lommel 2018-07-18 14:13:02 +02:00
commit 3d67819a17
52 changed files with 2287 additions and 75 deletions

@ -517,6 +517,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();

@ -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

@ -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;
}

@ -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__ */

@ -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)

@ -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,

@ -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)))

@ -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;
}
}

@ -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, &params);
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++,

@ -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) \

@ -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

@ -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);
}

@ -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;

@ -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, &parametrization);
float alpha = stack_load_float_default(stack, offset_ofs, data_node.z);
float ior = stack_load_float_default(stack, ior_ofs, data_node.w);
uint coat_ofs, melanin_ofs, melanin_redness_ofs, absorption_coefficient_ofs;
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:

@ -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 || \

@ -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;
}

@ -3089,6 +3089,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)

@ -610,6 +610,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)

@ -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__

@ -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__

@ -216,6 +216,7 @@ shader_node_categories = [
NodeItem("ShaderNodeVolumeScatter", poll=eevee_cycles_shader_nodes_poll),
NodeItem("ShaderNodeVolumePrincipled"),
NodeItem("ShaderNodeEeveeSpecular", poll=object_eevee_shader_nodes_poll),
NodeItem("ShaderNodeBsdfHairPrincipled", poll=object_shader_nodes_poll)
]),
ShaderNodeCategory("SH_NEW_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexImage"),
@ -365,6 +366,7 @@ compositor_node_categories = [
NodeItem("CompositorNodeChromaMatte"),
NodeItem("CompositorNodeColorMatte"),
NodeItem("CompositorNodeDoubleEdgeMask"),
NodeItem("CompositorNodeCryptomatte"),
]),
CompositorNodeCategory("CMP_DISTORT", "Distort", items=[
NodeItem("CompositorNodeScale"),

@ -796,6 +796,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, c
#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
@ -944,6 +946,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria
#define CMP_NODE_PLANETRACKDEFORM 320
#define CMP_NODE_CORNERPIN 321
#define CMP_NODE_SWITCH_VIEW 322
#define CMP_NODE_CRYPTOMATTE 323
/* channel toggles */
#define CMP_CHAN_RGB 1
@ -996,6 +999,11 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, struct bNodeSocke
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *ntree, bNode *node);
void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *ntree, bNode *node);
struct bNodeSocket *ntreeCompositCryptomatteAddSocket(struct bNodeTree *ntree, struct bNode *node);
int ntreeCompositCryptomatteRemoveSocket(struct bNodeTree *ntree, struct bNode *node);
/** \} */
/* -------------------------------------------------------------------- */

@ -3432,6 +3432,7 @@ static void registerCompositNodes(void)
register_node_type_cmp_doubleedgemask();
register_node_type_cmp_keyingscreen();
register_node_type_cmp_keying();
register_node_type_cmp_cryptomatte();
register_node_type_cmp_translate();
register_node_type_cmp_rotate();
@ -3520,6 +3521,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();

@ -0,0 +1,40 @@
/*
* ***** 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.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BLI_HASH_MM3_H__
#define __BLI_HASH_MM3_H__
/** \file BLI_hash_mm3.h
* \ingroup bli
*/
#include "BLI_sys_types.h"
#ifdef __cplusplus
extern "C" {
#endif
uint32_t BLI_hash_mm3(const unsigned char *data, size_t len, uint32_t seed);
#ifdef __cplusplus
}
#endif
#endif /* __BLI_HASH_MM2A_H__ */

@ -74,6 +74,7 @@ set(SRC
intern/gsqueue.c
intern/hash_md5.c
intern/hash_mm2a.c
intern/hash_mm3.c
intern/jitter_2d.c
intern/lasso_2d.c
intern/list_sort_impl.h
@ -158,6 +159,7 @@ set(SRC
BLI_hash.h
BLI_hash_md5.h
BLI_hash_mm2a.h
BLI_hash_mm3.h
BLI_heap.h
BLI_iterator.h
BLI_jitter_2d.h

@ -0,0 +1,147 @@
/*
* ***** 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.
*
* ***** END GPL LICENSE BLOCK *****
*
* Copyright (C) 2018 Blender Foundation.
*
*/
/** \file blender/blenlib/intern/hash_mm3.c
* \ingroup bli
*
* Functions to compute Murmur3 hash key.
*
* This Code is based on alShaders/Cryptomatte/MurmurHash3.h:
*
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*
*/
#include "BLI_compiler_compat.h"
#include "BLI_compiler_attrs.h"
#include "BLI_hash_mm3.h" /* own include */
#if defined(_MSC_VER)
# include <stdlib.h>
# define ROTL32(x,y) _rotl(x,y)
# define BIG_CONSTANT(x) (x)
/* Other compilers */
#else /* defined(_MSC_VER) */
static inline uint32_t rotl32(uint32_t x, int8_t r)
{
return (x << r) | (x >> (32 - r));
}
# define ROTL32(x,y) rotl32(x,y)
# define BIG_CONSTANT(x) (x##LLU)
#endif /* !defined(_MSC_VER) */
/* Block read - if your platform needs to do endian-swapping or can only
* handle aligned reads, do the conversion here
*/
BLI_INLINE uint32_t getblock32(const uint32_t * p, int i)
{
return p[i];
}
BLI_INLINE uint64_t getblock64(const uint64_t * p, int i)
{
return p[i];
}
/* Finalization mix - force all bits of a hash block to avalanche */
BLI_INLINE uint32_t fmix32(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
BLI_INLINE uint64_t fmix64(uint64_t k)
{
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
uint32_t BLI_hash_mm3(const unsigned char *in, size_t len, uint32_t seed)
{
const uint8_t *data = (const uint8_t*)in;
const int nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
/* body */
const uint32_t *blocks = (const uint32_t *)(data + nblocks*4);
for (int i = -nblocks; i; i++) {
uint32_t k1 = getblock32(blocks,i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
/* tail */
const uint8_t *tail = (const uint8_t*)(data + nblocks*4);
uint32_t k1 = 0;
switch (len & 3) {
case 3:
k1 ^= tail[2] << 16;
ATTR_FALLTHROUGH;
case 2:
k1 ^= tail[1] << 8;
ATTR_FALLTHROUGH;
case 1:
k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
};
/* finalization */
h1 ^= len;
h1 = fmix32(h1);
return h1;
}

@ -3343,6 +3343,10 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
direct_link_curvemapping(fd, node->storage);
else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
((ImageUser *)node->storage)->ok = 1;
else if (node->type==CMP_NODE_CRYPTOMATTE) {
NodeCryptomatte *nc = (NodeCryptomatte *) node->storage;
nc->matte_id = newdataadr(fd, nc->matte_id);
}
}
else if ( ntree->type==NTREE_TEXTURE) {
if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME)

@ -1103,6 +1103,13 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree)
}
writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage);
}
else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_CRYPTOMATTE)) {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
if (nc->matte_id) {
writedata(wd, DATA, strlen(nc->matte_id) + 1, nc->matte_id);
}
writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage);
}
else {
writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage);
}

@ -183,6 +183,11 @@ set(SRC
operations/COM_SunBeamsOperation.cpp
operations/COM_SunBeamsOperation.h
nodes/COM_CryptomatteNode.cpp
nodes/COM_CryptomatteNode.h
operations/COM_CryptomatteOperation.cpp
operations/COM_CryptomatteOperation.h
nodes/COM_CornerPinNode.cpp
nodes/COM_CornerPinNode.h
nodes/COM_PlaneTrackDeformNode.cpp

@ -55,6 +55,7 @@ extern "C" {
#include "COM_Converter.h"
#include "COM_CornerPinNode.h"
#include "COM_CropNode.h"
#include "COM_CryptomatteNode.h"
#include "COM_DefocusNode.h"
#include "COM_DespeckleNode.h"
#include "COM_DifferenceMatteNode.h"
@ -406,6 +407,9 @@ Node *Converter::convert(bNode *b_node)
case CMP_NODE_SUNBEAMS:
node = new SunBeamsNode(b_node);
break;
case CMP_NODE_CRYPTOMATTE:
node = new CryptomatteNode(b_node);
break;
}
return node;
}

@ -0,0 +1,121 @@
/*
* Copyright 2018, Blender Foundation.
*
* 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.
*
* Contributor:
* Lukas Stockner
* Stefan Werner
*/
#include "COM_CryptomatteNode.h"
#include "COM_CryptomatteOperation.h"
#include "COM_SetAlphaOperation.h"
#include "COM_ConvertOperation.h"
#include "BLI_string.h"
#include "BLI_hash_mm3.h"
#include "BLI_assert.h"
#include <iterator>
CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
}
/* This is taken from the Cryptomatte specification 1.0. */
static inline float hash_to_float(uint32_t hash)
{
uint32_t mantissa = hash & (( 1 << 23) - 1);
uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
exponent = max(exponent, (uint32_t) 1);
exponent = min(exponent, (uint32_t) 254);
exponent = exponent << 23;
uint32_t sign = (hash >> 31);
sign = sign << 31;
uint32_t float_bits = sign | exponent | mantissa;
float f;
/* Bit casting relies on equal size for both types. */
BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size")
::memcpy(&f, &float_bits, sizeof(float));
return f;
}
void CryptomatteNode::convertToOperations(NodeConverter &converter, const CompositorContext &/*context*/) const
{
NodeInput *inputSocketImage = this->getInputSocket(0);
NodeOutput *outputSocketImage = this->getOutputSocket(0);
NodeOutput *outputSocketMatte = this->getOutputSocket(1);
NodeOutput *outputSocketPick = this->getOutputSocket(2);
bNode *node = this->getbNode();
NodeCryptomatte *cryptoMatteSettings = (NodeCryptomatte *)node->storage;
CryptomatteOperation *operation = new CryptomatteOperation(getNumberOfInputSockets()-1);
if (cryptoMatteSettings) {
if (cryptoMatteSettings->matte_id) {
/* Split the string by commas, ignoring white space. */
std::string input = cryptoMatteSettings->matte_id;
std::istringstream ss(input);
while (ss.good()) {
std::string token;
getline(ss, token, ',');
/* Ignore empty tokens. */
if (token.length() > 0) {
size_t first = token.find_first_not_of(' ');
size_t last = token.find_last_not_of(' ');
if (first == std::string::npos || last == std::string::npos) {
break;
}
token = token.substr(first, (last - first + 1));
if (*token.begin() == '<' && *(--token.end()) == '>') {
operation->addObjectIndex(atof(token.substr(1, token.length() - 2).c_str()));
}
else {
uint32_t hash = BLI_hash_mm3((const unsigned char*)token.c_str(), token.length(), 0);
operation->addObjectIndex(hash_to_float(hash));
}
}
}
}
}
converter.addOperation(operation);
for (int i = 0; i < getNumberOfInputSockets()-1; ++i) {
converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i));
}
SeparateChannelOperation *separateOperation = new SeparateChannelOperation;
separateOperation->setChannel(3);
converter.addOperation(separateOperation);
SetAlphaOperation *operationAlpha = new SetAlphaOperation();
converter.addOperation(operationAlpha);
converter.addLink(operation->getOutputSocket(0), separateOperation->getInputSocket(0));
converter.addLink(separateOperation->getOutputSocket(0), operationAlpha->getInputSocket(1));
SetAlphaOperation *clearAlphaOperation = new SetAlphaOperation();
converter.addOperation(clearAlphaOperation);
converter.addInputValue(clearAlphaOperation->getInputSocket(1), 1.0f);
converter.addLink(operation->getOutputSocket(0), clearAlphaOperation->getInputSocket(0));
converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0));
converter.mapOutputSocket(outputSocketMatte, separateOperation->getOutputSocket(0));
converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket(0));
converter.mapOutputSocket(outputSocketPick, clearAlphaOperation->getOutputSocket(0));
}

@ -0,0 +1,38 @@
/*
* Copyright 2018, Blender Foundation.
*
* 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.
*
* Contributor:
* Lukas Stockner
*/
#ifndef _COM_CryptomatteNode_h_
#define _COM_CryptomatteNode_h_
#include "COM_Node.h"
/**
* @brief CryptomatteNode
* @ingroup Node
*/
class CryptomatteNode : public Node {
public:
CryptomatteNode(bNode *editorNode);
void convertToOperations(NodeConverter &converter, const CompositorContext &context) const;
};
#endif

@ -0,0 +1,75 @@
/*
* Copyright 2018, Blender Foundation.
*
* 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.
*
* Contributor: Lukas Stockner, Stefan Werner
*/
#include "COM_CryptomatteOperation.h"
CryptomatteOperation::CryptomatteOperation(size_t num_inputs) : NodeOperation()
{
for(size_t i = 0; i < num_inputs; i++) {
this->addInputSocket(COM_DT_COLOR);
}
inputs.resize(num_inputs);
this->addOutputSocket(COM_DT_COLOR);
this->setComplex(true);
}
void CryptomatteOperation::initExecution()
{
for (size_t i = 0; i < inputs.size(); i++) {
inputs[i] = this->getInputSocketReader(i);
}
}
void CryptomatteOperation::addObjectIndex(float objectIndex)
{
if (objectIndex != 0.0f) {
m_objectIndex.push_back(objectIndex);
}
}
void CryptomatteOperation::executePixel(float output[4],
int x,
int y,
void *data)
{
float input[4];
output[0] = output[1] = output[2] = output[3] = 0.0f;
for (size_t i = 0; i < inputs.size(); i++) {
inputs[i]->read(input, x, y, data);
if (i == 0) {
/* Write the frontmost object as false color for picking. */
output[0] = input[0];
uint32_t m3hash;
::memcpy(&m3hash, &input[0], sizeof(uint32_t));
/* Since the red channel is likely to be out of display range,
* setting green and blue gives more meaningful images. */
output[1] = ((float) ((m3hash << 8)) / (float) UINT32_MAX);
output[2] = ((float) ((m3hash << 16)) / (float) UINT32_MAX);
}
for(size_t i = 0; i < m_objectIndex.size(); i++) {
if (m_objectIndex[i] == input[0]) {
output[3] += input[1];
}
if (m_objectIndex[i] == input[2]) {
output[3] += input[3];
}
}
}
}

@ -0,0 +1,40 @@
/*
* Copyright 2018, Blender Foundation.
*
* 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.
*
* Contributor: Lukas Stockner, Stefan Werner
*/
#ifndef _COM_CryptomatteOperation_h
#define _COM_CryptomatteOperation_h
#include "COM_NodeOperation.h"
class CryptomatteOperation : public NodeOperation {
private:
std::vector<float> m_objectIndex;
public:
std::vector<SocketReader *> inputs;
CryptomatteOperation(size_t num_inputs = 6);
void initExecution();
void executePixel(float output[4], int x, int y, void *data);
void addObjectIndex(float objectIndex);
};
#endif

@ -1069,6 +1069,7 @@ void uiTemplateCurveMapping(
bool levels, bool brush, bool neg_slope);
void uiTemplateColorPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool value_slider, bool lock, bool lock_luminosity, bool cubic);
void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool color);
void uiTemplateCryptoPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
void uiTemplateLayers(
uiLayout *layout, struct PointerRNA *ptr, const char *propname,
PointerRNA *used_ptr, const char *used_propname, int active_layer);

@ -82,6 +82,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
/* assign to operators */
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color_crypto");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver");

@ -36,6 +36,7 @@
#include "DNA_screen_types.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BKE_context.h"
#include "BKE_main.h"
@ -72,6 +73,8 @@ typedef struct Eyedropper {
bool accum_start; /* has mouse been pressed */
float accum_col[3];
int accum_tot;
bool accumulate; /* Color picking for cryptomatte, without accumulation. */
} Eyedropper;
static bool eyedropper_init(bContext *C, wmOperator *op)
@ -80,6 +83,7 @@ static bool eyedropper_init(bContext *C, wmOperator *op)
Eyedropper *eye;
op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
eye->accumulate = !STREQ(op->type->idname, "UI_OT_eyedropper_color_crypto");
UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
@ -207,29 +211,30 @@ static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3
RNA_property_update(C, &eye->ptr, eye->prop);
}
/* set sample from accumulated values */
static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
{
float col[3];
mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
eyedropper_color_set(C, eye, col);
}
/* single point sample & set */
static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
{
/* Accumulate color. */
float col[3];
eyedropper_color_sample_fl(C, mx, my, col);
eyedropper_color_set(C, eye, col);
}
static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
{
float col[3];
eyedropper_color_sample_fl(C, mx, my, col);
/* delay linear conversion */
add_v3_v3(eye->accum_col, col);
eye->accum_tot++;
if (eye->accumulate) {
add_v3_v3(eye->accum_col, col);
eye->accum_tot++;
}
else {
copy_v3_v3(eye->accum_col, col);
eye->accum_tot = 1;
}
/* Apply to property. */
float accum_col[3];
if (eye->accum_tot > 1) {
mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot);
}
else {
copy_v3_v3(accum_col, eye->accum_col);
}
eyedropper_color_set(C, eye, accum_col);
}
static void eyedropper_cancel(bContext *C, wmOperator *op)
@ -254,29 +259,24 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (eye->accum_tot == 0) {
eyedropper_color_sample(C, eye, event->x, event->y);
}
else {
eyedropper_color_set_accum(C, eye);
}
eyedropper_exit(C, op);
return OPERATOR_FINISHED;
case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */
eye->accum_start = true;
eyedropper_color_sample_accum(C, eye, event->x, event->y);
eyedropper_color_sample(C, eye, event->x, event->y);
break;
case EYE_MODAL_SAMPLE_RESET:
eye->accum_tot = 0;
zero_v3(eye->accum_col);
eyedropper_color_sample_accum(C, eye, event->x, event->y);
eyedropper_color_set_accum(C, eye);
eyedropper_color_sample(C, eye, event->x, event->y);
break;
}
}
else if (event->type == MOUSEMOVE) {
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
eyedropper_color_sample_accum(C, eye, event->x, event->y);
eyedropper_color_set_accum(C, eye);
eyedropper_color_sample(C, eye, event->x, event->y);
}
}
@ -321,20 +321,9 @@ static int eyedropper_exec(bContext *C, wmOperator *op)
static bool eyedropper_poll(bContext *C)
{
PointerRNA ptr;
PropertyRNA *prop;
int index_dummy;
uiBut *but;
/* Only color buttons */
if ((CTX_wm_window(C) != NULL) &&
(but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
(but->type == UI_BTYPE_COLOR))
{
return 1;
}
return 0;
/* Actual test for active button happens later, since we don't
* know which one is active until mouse over. */
return (CTX_wm_window(C) != NULL);
}
void UI_OT_eyedropper_color(wmOperatorType *ot)
@ -353,6 +342,22 @@ void UI_OT_eyedropper_color(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* properties */
}
void UI_OT_eyedropper_color_crypto(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Cryptomatte Eyedropper";
ot->idname = "UI_OT_eyedropper_color_crypto";
ot->description = "Pick a color from Cryptomatte node Pick output image";
/* api callbacks */
ot->invoke = eyedropper_invoke;
ot->modal = eyedropper_modal;
ot->cancel = eyedropper_cancel;
ot->exec = eyedropper_exec;
ot->poll = eyedropper_poll;
/* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
}

@ -843,6 +843,7 @@ struct wmKeyMap *eyedropper_colorband_modal_keymap(struct wmKeyConfig *keyconf);
/* interface_eyedropper_color.c */
void UI_OT_eyedropper_color(struct wmOperatorType *ot);
void UI_OT_eyedropper_color_crypto(struct wmOperatorType *ot);
/* interface_eyedropper_colorband.c */
void UI_OT_eyedropper_colorband(struct wmOperatorType *ot);

@ -1359,6 +1359,7 @@ void ED_operatortypes_ui(void)
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
WM_operatortype_append(UI_OT_eyedropper_color_crypto);
WM_operatortype_append(UI_OT_eyedropper_colorband);
WM_operatortype_append(UI_OT_eyedropper_colorband_point);
WM_operatortype_append(UI_OT_eyedropper_id);

@ -3043,6 +3043,24 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname,
}
}
void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propname)
{
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
uiBlock *block;
uiBut *but;
if (!prop) {
RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
return;
}
block = uiLayoutGetBlock(layout);
but = uiDefIconTextButO(block, UI_BTYPE_BUT, "UI_OT_eyedropper_color_crypto", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, RNA_property_ui_name(prop), 0, 0, UI_UNIT_X, UI_UNIT_Y, RNA_property_ui_description(prop));
but->rnapoin = *ptr;
but->rnaprop = prop;
but->rnaindex = -1;
}
/********************* Layer Buttons Template ************************/

@ -1025,6 +1025,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;
@ -1207,6 +1212,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;
@ -2473,6 +2481,25 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "ray_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Matte Objects:"), ICON_NONE);
uiLayout *row = uiLayoutRow(col, true);
uiTemplateCryptoPicker(row, ptr, "add");
uiTemplateCryptoPicker(row, ptr, "remove");
uiItemR(col, ptr, "matte_id", 0, "", ICON_NONE);
}
static void node_composit_buts_cryptomatte_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr))
{
uiItemO(layout, IFACE_("Add Crypto Layer"), ICON_ZOOMIN, "NODE_OT_cryptomatte_layer_add");
uiItemO(layout, IFACE_("Remove Crypto Layer"), ICON_ZOOMOUT, "NODE_OT_cryptomatte_layer_remove");
}
static void node_composit_buts_brightcontrast(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "use_premultiply", 0, NULL, ICON_NONE);
@ -2705,6 +2732,10 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_SUNBEAMS:
ntype->draw_buttons = node_composit_buts_sunbeams;
break;
case CMP_NODE_CRYPTOMATTE:
ntype->draw_buttons = node_composit_buts_cryptomatte;
ntype->draw_buttons_ex = node_composit_buts_cryptomatte_ex;
break;
case CMP_NODE_BRIGHTCONTRAST:
ntype->draw_buttons = node_composit_buts_brightcontrast;
}

@ -2548,3 +2548,93 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ****************** Cryptomatte Add Socket ******************* */
static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNodeTree *ntree = NULL;
bNode *node = NULL;
if (ptr.data) {
node = ptr.data;
ntree = ptr.id.data;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
node = nodeGetActive(snode->edittree);
}
if (!node || node->type != CMP_NODE_CRYPTOMATTE) {
return OPERATOR_CANCELLED;
}
ntreeCompositCryptomatteAddSocket(ntree, node);
snode_notify(C, snode);
return OPERATOR_FINISHED;
}
void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Cryptomatte Socket";
ot->description = "Add a new input layer to a Cryptomatte node";
ot->idname = "NODE_OT_cryptomatte_layer_add";
/* callbacks */
ot->exec = node_cryptomatte_add_socket_exec;
ot->poll = composite_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ****************** Cryptomatte Remove Socket ******************* */
static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNodeTree *ntree = NULL;
bNode *node = NULL;
if (ptr.data) {
node = ptr.data;
ntree = ptr.id.data;
}
else if (snode && snode->edittree) {
ntree = snode->edittree;
node = nodeGetActive(snode->edittree);
}
if (!node || node->type != CMP_NODE_CRYPTOMATTE) {
return OPERATOR_CANCELLED;
}
if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) {
return OPERATOR_CANCELLED;
}
snode_notify(C, snode);
return OPERATOR_FINISHED;
}
void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Cryptomatte Socket";
ot->description = "Remove layer from a Crytpomatte node";
ot->idname = "NODE_OT_cryptomatte_layer_remove";
/* callbacks */
ot->exec = node_cryptomatte_remove_socket_exec;
ot->poll = composite_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

@ -229,6 +229,8 @@ void NODE_GGT_backdrop_crop(struct wmGizmoGroupType *gzgt);
void NODE_GGT_backdrop_sun_beams(struct wmGizmoGroupType *gzgt);
void NODE_GGT_backdrop_corner_pin(struct wmGizmoGroupType *gzgt);
void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot);
void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot);
extern const char *node_context_dir[];

@ -129,6 +129,9 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_tree_socket_add);
WM_operatortype_append(NODE_OT_tree_socket_remove);
WM_operatortype_append(NODE_OT_tree_socket_move);
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
}
void ED_operatormacros_node(void)

@ -905,6 +905,14 @@ typedef struct NodeSunBeams {
float ray_length;
} NodeSunBeams;
typedef struct NodeCryptomatte {
float add[3];
float remove[3];
char *matte_id;
int num_inputs;
int pad;
} NodeCryptomatte;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@ -953,6 +961,11 @@ typedef struct NodeSunBeams {
#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

@ -2874,6 +2874,48 @@ static void rna_NodeColorBalance_update_cdl(Main *bmain, Scene *scene, PointerRN
rna_Node_update(bmain, scene, ptr);
}
static void rna_NodeCryptomatte_matte_get(PointerRNA *ptr, char *value)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
strcpy(value, (nc->matte_id) ? nc->matte_id : "");
}
static int rna_NodeCryptomatte_matte_length(PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
return (nc->matte_id) ? strlen(nc->matte_id) : 0;
}
static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
if (nc->matte_id)
MEM_freeN(nc->matte_id);
if (value && value[0])
nc->matte_id = BLI_strdup(value);
else
nc->matte_id = NULL;
}
static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr)
{
ntreeCompositCryptomatteSyncFromAdd(ptr->id.data, ptr->data);
rna_Node_update(bmain, scene, ptr);
}
static void rna_NodeCryptomatte_update_remove(Main *bmain, Scene *scene, PointerRNA *ptr)
{
ntreeCompositCryptomatteSyncFromRemove(ptr->id.data, ptr->data);
rna_Node_update(bmain, scene, ptr);
}
/* ******** Node Socket Types ******** */
static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter)
@ -3297,6 +3339,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"},
@ -4295,6 +4344,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;
@ -6905,6 +6969,32 @@ static void def_cmp_sunbeams(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_cryptomatte(StructRNA *srna)
{
PropertyRNA *prop;
static float default_1[3] = {1.f, 1.f, 1.f};
RNA_def_struct_sdna_from(srna, "NodeCryptomatte", "storage");
prop = RNA_def_property(srna, "matte_id", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, "rna_NodeCryptomatte_matte_get", "rna_NodeCryptomatte_matte_length",
"rna_NodeCryptomatte_matte_set");
RNA_def_property_ui_text(prop, "Matte Objects", "List of object and material crypto IDs to include in matte");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "add", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_array_default(prop, default_1);
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Add", "Add object or material to matte, by picking a color from the Pick output");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeCryptomatte_update_add");
prop = RNA_def_property(srna, "remove", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_array_default(prop, default_1);
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Remove", "Remove object or material from matte, by picking a color from the Pick output");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeCryptomatte_update_remove");
}
/* -- Texture Nodes --------------------------------------------------------- */
static void def_tex_output(StructRNA *srna)

@ -60,6 +60,7 @@ set(SRC
composite/nodes/node_composite_composite.c
composite/nodes/node_composite_cornerpin.c
composite/nodes/node_composite_crop.c
composite/nodes/node_composite_cryptomatte.c
composite/nodes/node_composite_curves.c
composite/nodes/node_composite_despeckle.c
composite/nodes/node_composite_doubleEdgeMask.c
@ -168,6 +169,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

@ -109,6 +109,7 @@ void register_node_type_cmp_luma_matte(void);
void register_node_type_cmp_doubleedgemask(void);
void register_node_type_cmp_keyingscreen(void);
void register_node_type_cmp_keying(void);
void register_node_type_cmp_cryptomatte(void);
void register_node_type_cmp_translate(void);
void register_node_type_cmp_rotate(void);

@ -111,6 +111,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);

@ -85,6 +85,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", "" )
@ -215,6 +216,7 @@ DefNode( CompositorNode, CMP_NODE_PIXELATE, 0, "PIXEL
DefNode( CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" )
DefNode( CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" )
DefNode( CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" )
DefNode( CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTOMATTE", Cryptomatte, "Cryptomatte", "" )
DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )

@ -0,0 +1,302 @@
/*
* ***** 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, Stefan Werner
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/nodes/composite/nodes/node_composite_cryptomatte.c
* \ingroup cmpnodes
*/
#include "node_composite_util.h"
#include "BLI_assert.h"
#include "BLI_dynstr.h"
#include "BLI_hash_mm3.h"
#include "BLI_utildefines.h"
/* this is taken from the cryptomatte specification 1.0 */
static inline float hash_to_float(uint32_t hash)
{
uint32_t mantissa = hash & (( 1 << 23) - 1);
uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
exponent = MAX2(exponent, (uint32_t) 1);
exponent = MIN2(exponent, (uint32_t) 254);
exponent = exponent << 23;
uint32_t sign = (hash >> 31);
sign = sign << 31;
uint32_t float_bits = sign | exponent | mantissa;
float f;
/* Bit casting relies on equal size for both types. */
BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size")
memcpy(&f, &float_bits, sizeof(float));
return f;
}
static void cryptomatte_add(NodeCryptomatte* n, float f)
{
/* Turn the number into a string. */
char number[32];
BLI_snprintf(number, sizeof(number), "<%.9g>", f);
/* Search if we already have the number. */
if (n->matte_id && strlen(n->matte_id) != 0) {
size_t start = 0;
const size_t end = strlen(n->matte_id);
size_t token_len = 0;
while (start < end) {
/* Ignore leading whitespace. */
while (start < end && n->matte_id[start] == ' ') {
++start;
}
/* Find the next seprator. */
char* token_end = strchr(n->matte_id+start, ',');
if (token_end == NULL || token_end == n->matte_id+start) {
token_end = n->matte_id+end;
}
/* Be aware that token_len still contains any trailing white space. */
token_len = token_end - (n->matte_id + start);
/* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */
if (n->matte_id[start] == '<') {
if (strncmp(n->matte_id+start, number, strlen(number)) == 0) {
/* This number is already there, so continue. */
return;
}
}
else {
/* Remove trailing white space */
size_t name_len = token_len;
while (n->matte_id[start+name_len] == ' ' && name_len > 0) {
name_len--;
}
/* Calculate the hash of the token and compare. */
uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0);
if (f == hash_to_float(hash)) {
return;
}
}
start += token_len+1;
}
}
DynStr *new_matte = BLI_dynstr_new();
if (!new_matte) {
return;
}
if(n->matte_id) {
BLI_dynstr_append(new_matte, n->matte_id);
MEM_freeN(n->matte_id);
}
if(BLI_dynstr_get_len(new_matte) > 0) {
BLI_dynstr_append(new_matte, ",");
}
BLI_dynstr_append(new_matte, number);
n->matte_id = BLI_dynstr_get_cstring(new_matte);
BLI_dynstr_free(new_matte);
}
static void cryptomatte_remove(NodeCryptomatte*n, float f)
{
if (n->matte_id == NULL || strlen(n->matte_id) == 0) {
/* Empty string, nothing to remove. */
return;
}
/* This will be the new string without the removed key. */
DynStr *new_matte = BLI_dynstr_new();
if (!new_matte) {
return;
}
/* Turn the number into a string. */
static char number[32];
BLI_snprintf(number, sizeof(number), "<%.9g>", f);
/* Search if we already have the number. */
size_t start = 0;
const size_t end = strlen(n->matte_id);
size_t token_len = 0;
bool is_first = true;
while (start < end) {
bool skip = false;
/* Ignore leading whitespace or commas. */
while (start < end && ((n->matte_id[start] == ' ') || (n->matte_id[start] == ','))) {
++start;
}
/* Find the next seprator. */
char* token_end = strchr(n->matte_id+start+1, ',');
if (token_end == NULL || token_end == n->matte_id+start) {
token_end = n->matte_id+end;
}
/* Be aware that token_len still contains any trailing white space. */
token_len = token_end - (n->matte_id + start);
if (token_len == 1) {
skip = true;
}
/* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */
else if (n->matte_id[start] == '<') {
if (strncmp(n->matte_id+start, number, strlen(number)) == 0) {
/* This number is already there, so skip it. */
skip = true;
}
}
else {
/* Remove trailing white space */
size_t name_len = token_len;
while (n->matte_id[start+name_len] == ' ' && name_len > 0) {
name_len--;
}
/* Calculate the hash of the token and compare. */
uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0);
if (f == hash_to_float(hash)) {
skip = true;
}
}
if (!skip) {
if (is_first) {
is_first = false;
}
else {
BLI_dynstr_append(new_matte, ", ");
}
BLI_dynstr_nappend(new_matte, n->matte_id+start, token_len);
}
start += token_len+1;
}
if(n->matte_id) {
MEM_freeN(n->matte_id);
n->matte_id = NULL;
}
if(BLI_dynstr_get_len(new_matte) > 0) {
n->matte_id = BLI_dynstr_get_cstring(new_matte);
}
BLI_dynstr_free(new_matte);
}
static bNodeSocketTemplate outputs[] = {
{ SOCK_RGBA, 0, N_("Image")},
{ SOCK_FLOAT, 0, N_("Matte")},
{ SOCK_RGBA, 0, N_("Pick")},
{ -1, 0, "" }
};
void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeCryptomatte *n = node->storage;
if (n->add[0] != 0.0f) {
cryptomatte_add(n, n->add[0]);
n->add[0] = 0.0f;
n->add[1] = 0.0f;
n->add[2] = 0.0f;
}
}
void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeCryptomatte *n = node->storage;
if (n->remove[0] != 0.0f) {
cryptomatte_remove(n, n->remove[0]);
n->remove[0] = 0.0f;
n->remove[1] = 0.0f;
n->remove[2] = 0.0f;
}
}
bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node)
{
NodeCryptomatte *n = node->storage;
char sockname[32];
n->num_inputs++;
BLI_snprintf(sockname, sizeof(sockname), "Crypto %.2d", n->num_inputs-1);
bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, sockname);
return sock;
}
int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
{
NodeCryptomatte *n = node->storage;
if (n->num_inputs < 2) {
return 0;
}
bNodeSocket *sock = node->inputs.last;
nodeRemoveSocket(ntree, node, sock);
n->num_inputs--;
return 1;
}
static void init(bNodeTree *ntree, bNode *node)
{
NodeCryptomatte *user = MEM_callocN(sizeof(NodeCryptomatte), "cryptomatte user");
node->storage = user;
nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "image", "Image");
/* Add three inputs by default, as recommended by the Cryptomatte specification. */
ntreeCompositCryptomatteAddSocket(ntree, node);
ntreeCompositCryptomatteAddSocket(ntree, node);
ntreeCompositCryptomatteAddSocket(ntree, node);
}
static void node_free_cryptomatte(bNode *node)
{
NodeCryptomatte *nc = node->storage;
if (nc) {
if (nc->matte_id) {
MEM_freeN(nc->matte_id);
}
MEM_freeN(nc);
}
}
static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, bNode *src_node)
{
NodeCryptomatte *src_nc = src_node->storage;
NodeCryptomatte *dest_nc = MEM_dupallocN(src_nc);
if (src_nc->matte_id)
dest_nc->matte_id = MEM_dupallocN(src_nc->matte_id);
dest_node->storage = dest_nc;
}
void register_node_type_cmp_cryptomatte(void)
{
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE, "Cryptomatte", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, NULL, outputs);
node_type_init(&ntype, init);
node_type_storage(&ntype, "NodeCryptomatte", node_free_cryptomatte, node_copy_cryptomatte);
nodeRegisterType(&ntype);
}

@ -0,0 +1,132 @@
/*
* ***** 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_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);
}