Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600
|
@ -15,15 +15,11 @@
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
typedef struct MicrofacetHairExtra {
|
||||
/* TODO: is this necessary? */
|
||||
typedef struct MicrofacetHairLobeFactor {
|
||||
float R;
|
||||
weizhen marked this conversation as resolved
Outdated
|
||||
float TT;
|
||||
float TRT;
|
||||
|
||||
/* Geometry data. */
|
||||
float4 geom;
|
||||
} MicrofacetHairExtra;
|
||||
} MicrofacetHairLobeFactor;
|
||||
|
||||
typedef struct MicrofacetHairBSDF {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
@ -43,14 +39,17 @@ typedef struct MicrofacetHairBSDF {
|
|||
/* The ratio of the minor axis to the major axis. */
|
||||
float aspect_ratio;
|
||||
|
||||
/* Extra closure. */
|
||||
ccl_private MicrofacetHairExtra *extra;
|
||||
/* Azimuthal offset. */
|
||||
float h;
|
||||
|
||||
/* Extra closure for optional modulation factors. */
|
||||
ccl_private MicrofacetHairLobeFactor *factor;
|
||||
} MicrofacetHairBSDF;
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetHairBSDF),
|
||||
"MicrofacetHairBSDF is too large!");
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetHairExtra),
|
||||
"MicrofacetHairExtra is too large!");
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetHairLobeFactor),
|
||||
"MicrofacetHairLobeFactor is too large!");
|
||||
|
||||
#ifdef __HAIR__
|
||||
/* Set up the hair closure. */
|
||||
|
@ -69,32 +68,27 @@ ccl_device int bsdf_microfacet_hair_setup(ccl_private ShaderData *sd,
|
|||
|
||||
/* h -1..0..1 means the rays goes from grazing the hair, to hitting it at the center, to grazing
|
||||
Lukas Stockner
commented
This is probably fine, but one consideration here is that all functions in the kernel share a namespace, so e.g. This is probably fine, but one consideration here is that all functions in the kernel share a namespace, so e.g. `sin_theta` might easily conflict with something else in the future.
Then again, this is probably general enough that we could just move it to `util/` if that happens.
|
||||
* the other edge. This is the cosine of the angle between sd->N and X. */
|
||||
const float h = (sd->type & PRIMITIVE_CURVE_RIBBON) ? -sd->v : -dot(X, sd->N);
|
||||
bsdf->h = (sd->type & PRIMITIVE_CURVE_RIBBON) ? -sd->v : -dot(X, sd->N);
|
||||
|
||||
kernel_assert(fabsf(h) < 1.0f + 1e-4f);
|
||||
kernel_assert(isfinite_safe(X));
|
||||
kernel_assert(isfinite_safe(h));
|
||||
kernel_assert(fabsf(bsdf->h) < 1.0f + 1e-4f);
|
||||
kernel_assert(isfinite_safe(bsdf->h));
|
||||
|
||||
if (bsdf->aspect_ratio != 1.0f && !(sd->type & PRIMITIVE_TRIANGLE)) {
|
||||
if (bsdf->aspect_ratio > 1.0f) {
|
||||
bsdf->aspect_ratio = 1.0f / bsdf->aspect_ratio;
|
||||
|
||||
/* Switch major and minor axis. */
|
||||
const float3 minor_axis = safe_normalize(cross(
|
||||
sd->dPdu, make_float3(bsdf->extra->geom.x, bsdf->extra->geom.y, bsdf->extra->geom.z)));
|
||||
const float3 major_axis = safe_normalize(cross(minor_axis, sd->dPdu));
|
||||
|
||||
bsdf->extra->geom = make_float4(major_axis.x, major_axis.y, major_axis.z, h);
|
||||
}
|
||||
else {
|
||||
bsdf->extra->geom.w = h;
|
||||
const float3 minor_axis = safe_normalize(cross(sd->dPdu, bsdf->N));
|
||||
bsdf->N = safe_normalize(cross(minor_axis, sd->dPdu));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Align local frame with the ray direction so that `phi_i == 0`. */
|
||||
bsdf->extra->geom = make_float4(X.x, X.y, X.z, h);
|
||||
bsdf->N = X;
|
||||
}
|
||||
|
||||
kernel_assert(!is_zero(bsdf->N) && isfinite_safe(bsdf->N));
|
||||
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION;
|
||||
}
|
||||
|
||||
|
@ -310,7 +304,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_r(ccl_private const ShaderClosure *s
|
|||
const float roughness2 = sqr(roughness);
|
||||
const float eta = bsdf->eta;
|
||||
|
||||
if (bsdf->extra->R <= 0.0f) {
|
||||
if (bsdf->factor->R <= 0.0f) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
|
@ -389,7 +383,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_r(ccl_private const ShaderClosure *s
|
|||
|
||||
const float F = fresnel_dielectric_cos(dot(wi, wh), eta);
|
||||
|
||||
return make_float3(bsdf->extra->R * 0.125f * F * integral / projected_radius(e2, sinf(phi_i)));
|
||||
return make_float3(bsdf->factor->R * 0.125f * F * integral / projected_radius(e2, sinf(phi_i)));
|
||||
}
|
||||
|
||||
template<MicrofacetType m_type>
|
||||
|
@ -405,7 +399,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
|
|||
const float roughness2 = sqr(roughness);
|
||||
const float eta = bsdf->eta;
|
||||
|
||||
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
|
||||
if (bsdf->factor->TT <= 0.0f && bsdf->factor->TRT <= 0.0f) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
|
@ -487,7 +481,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
|
|||
-len(to_point(gamma_mi, b) - to_point(gamma_mt + M_PI_F, b))));
|
||||
|
||||
Lukas Stockner
commented
This appears 4 times I think, can we split it into a helper function? This appears 4 times I think, can we split it into a helper function?
|
||||
/* TT */
|
||||
if (bsdf->extra->TT > 0.0f) {
|
||||
if (bsdf->factor->TT > 0.0f) {
|
||||
if (dot(wo, wt) >= inv_eta - 1e-5f) { /* Total internal reflection otherwise. */
|
||||
float3 wh2 = -wt + inv_eta * wo;
|
||||
const float rcp_norm_wh2 = 1.0f / len(wh2);
|
||||
|
@ -507,14 +501,14 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
|
|||
cos_hi2 * cos_ho2 * sqr(rcp_norm_wh2);
|
||||
|
||||
if (isfinite_safe(result)) {
|
||||
S_tt += bsdf->extra->TT * result * arc_length(e2, gamma_mt);
|
||||
S_tt += bsdf->factor->TT * result * arc_length(e2, gamma_mt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TRT */
|
||||
if (bsdf->extra->TRT > 0.0f) {
|
||||
if (bsdf->factor->TRT > 0.0f) {
|
||||
/* Sample wh2. */
|
||||
const float2 sample2 = make_float2(lcg_step_float(&rng_quadrature),
|
||||
lcg_step_float(&rng_quadrature));
|
||||
|
@ -570,7 +564,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt(KernelGlobals kg,
|
|||
sqr(rcp_norm_wh3);
|
||||
|
||||
if (isfinite_safe(result)) {
|
||||
S_trt += bsdf->extra->TRT * result * arc_length(e2, gamma_mtr);
|
||||
S_trt += bsdf->factor->TRT * result * arc_length(e2, gamma_mtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -595,7 +589,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
*eta = bsdf->eta;
|
||||
const float inv_eta = 1.0f / *eta;
|
||||
|
||||
if (bsdf->extra->R <= 0.0f && bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
|
||||
if (bsdf->factor->R <= 0.0f && bsdf->factor->TT <= 0.0f && bsdf->factor->TRT <= 0.0f) {
|
||||
/* Early out for inactive lobe. */
|
||||
*pdf = 0.0f;
|
||||
return LABEL_NONE;
|
||||
|
@ -605,7 +599,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
* . X major axis.
|
||||
* . Y along the fiber tangent.
|
||||
* . Z minor axis. */
|
||||
const float3 X = float4_to_float3(bsdf->extra->geom);
|
||||
const float3 X = bsdf->N;
|
||||
const float3 Z = safe_normalize(cross(X, sd->dPdu));
|
||||
const float3 Y = safe_normalize(cross(Z, X));
|
||||
|
||||
|
@ -624,7 +618,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
const float d_i = projected_radius(e2, sin_phi_i);
|
||||
|
||||
weizhen marked this conversation as resolved
Outdated
|
||||
/* Treat as transparent material if intersection lies outside of the projected radius. */
|
||||
if (fabsf(bsdf->extra->geom.w) > d_i) {
|
||||
if (fabsf(bsdf->h) > d_i) {
|
||||
*wo = -sd->wi;
|
||||
*pdf = 1;
|
||||
*eval = one_spectrum();
|
||||
|
@ -675,7 +669,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
|
||||
float cos_theta_t1;
|
||||
const float R1 = fresnel(dot(wi, wh1), *eta, &cos_theta_t1);
|
||||
const float3 R = make_float3(bsdf->extra->R * R1);
|
||||
const float3 R = make_float3(bsdf->factor->R * R1);
|
||||
|
||||
/* Sample TT lobe. */
|
||||
const float3 wt = -refract_angle(wi, wh1, cos_theta_t1, inv_eta);
|
||||
|
@ -710,7 +704,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
wtt = -refract_angle(-wt, wh2, cos_theta_t2, *eta);
|
||||
|
||||
if (dot(wmt, -wtt) > 0.0f && cos_theta_t2 != 0.0f) {
|
||||
TT = bsdf->extra->TT * T1 * A_t * T2;
|
||||
TT = bsdf->factor->TT * T1 * A_t * T2;
|
||||
}
|
||||
|
||||
/* Sample TRT lobe. */
|
||||
|
@ -735,7 +729,7 @@ ccl_device int bsdf_microfacet_hair_sample(const KernelGlobals kg,
|
|||
2.0f * cos(phi_tr - gamma_mt) :
|
||||
-len(to_point(gamma_mt, b) - to_point(gamma_mtr, b))));
|
||||
|
||||
TRT = bsdf->extra->TRT * T1 * R2 * T3 * A_t * A_tr;
|
||||
TRT = bsdf->factor->TRT * T1 * R2 * T3 * A_t * A_tr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,7 +809,7 @@ ccl_device Spectrum bsdf_microfacet_hair_eval(KernelGlobals kg,
|
|||
* . X major axis.
|
||||
* . Y along the fiber tangent.
|
||||
* . Z minor axis. */
|
||||
const float3 X = float4_to_float3(bsdf->extra->geom);
|
||||
const float3 X = bsdf->N;
|
||||
const float3 Z = safe_normalize(cross(X, sd->dPdu));
|
||||
const float3 Y = safe_normalize(cross(Z, X));
|
||||
|
||||
|
@ -825,7 +819,7 @@ ccl_device Spectrum bsdf_microfacet_hair_eval(KernelGlobals kg,
|
|||
|
||||
/* Treat as transparent material if intersection lies outside of the projected radius. */
|
||||
const float e2 = 1.0f - sqr(bsdf->aspect_ratio);
|
||||
if (fabsf(bsdf->extra->geom.w) > projected_radius(e2, sin_phi(local_I))) {
|
||||
if (fabsf(bsdf->h) > projected_radius(e2, sin_phi(local_I))) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
|
|
@ -1300,13 +1300,13 @@ ccl_device void osl_closure_microfacet_hair_setup(KernelGlobals kg,
|
|||
return;
|
||||
}
|
||||
|
||||
ccl_private MicrofacetHairExtra *extra = (ccl_private MicrofacetHairExtra *)closure_alloc_extra(
|
||||
sd, sizeof(MicrofacetHairExtra));
|
||||
if (!extra) {
|
||||
ccl_private MicrofacetHairLobeFactor *factor = (ccl_private MicrofacetHairLobeFactor *)
|
||||
closure_alloc_extra(sd, sizeof(MicrofacetHairLobeFactor));
|
||||
if (!factor) {
|
||||
return;
|
||||
}
|
||||
|
||||
bsdf->N = ensure_valid_specular_reflection(sd->Ng, sd->wi, closure->N);
|
||||
bsdf->N = closure->N;
|
||||
bsdf->sigma = closure->sigma;
|
||||
bsdf->roughness = closure->roughness;
|
||||
bsdf->tilt = closure->tilt;
|
||||
|
@ -1314,12 +1314,10 @@ ccl_device void osl_closure_microfacet_hair_setup(KernelGlobals kg,
|
|||
bsdf->distribution_type = closure->distribution_type;
|
||||
bsdf->aspect_ratio = closure->aspect_ratio;
|
||||
|
||||
bsdf->extra = extra;
|
||||
bsdf->extra->R = closure->reflection;
|
||||
bsdf->extra->TT = closure->transmission;
|
||||
bsdf->extra->TRT = closure->secondary_reflection;
|
||||
bsdf->extra->geom = make_float4(
|
||||
closure->major_axis.x, closure->major_axis.y, closure->major_axis.z, 0.0f);
|
||||
bsdf->factor = factor;
|
||||
bsdf->factor->R = closure->reflection;
|
||||
bsdf->factor->TT = closure->transmission;
|
||||
bsdf->factor->TRT = closure->secondary_reflection;
|
||||
|
||||
sd->flag |= bsdf_microfacet_hair_setup(sd, bsdf);
|
||||
#endif
|
||||
|
|
|
@ -290,7 +290,6 @@ OSL_CLOSURE_STRUCT_BEGIN(MicrofacetHair, microfacet_hair)
|
|||
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetHair, FLOAT, float, reflection, NULL)
|
||||
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetHair, FLOAT, float, transmission, NULL)
|
||||
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetHair, FLOAT, float, secondary_reflection, NULL)
|
||||
OSL_CLOSURE_STRUCT_MEMBER(MicrofacetHair, VECTOR, packed_float3, major_axis, NULL)
|
||||
OSL_CLOSURE_STRUCT_END(MicrofacetHair, microfacet_hair)
|
||||
|
||||
OSL_CLOSURE_STRUCT_BEGIN(VolumeAbsorption, absorption)
|
||||
|
|
|
@ -41,7 +41,6 @@ shader node_microfacet_hair_bsdf(color Color = color(0.017513, 0.005763, 0.00205
|
|||
float RandomColor = 0.0,
|
||||
color Tint = 1.0,
|
||||
color AbsorptionCoefficient = color(0.245531, 0.52, 1.365),
|
||||
normal Normal = Ng,
|
||||
string parametrization = "Direct Coloring",
|
||||
string distribution_type = "GGX",
|
||||
float Offset = radians(2),
|
||||
|
@ -103,13 +102,13 @@ shader node_microfacet_hair_bsdf(color Color = color(0.017513, 0.005763, 0.00205
|
|||
}
|
||||
|
||||
int distribution_type_enum = (distribution_type == "GGX") ? 0 : 1;
|
||||
normal major_axis = 0.0;
|
||||
|
||||
normal major_axis = 0.0;
|
||||
if (AspectRatio != 1.0) {
|
||||
getattribute("geom:N", major_axis);
|
||||
}
|
||||
|
||||
BSDF = microfacet_hair(Normal,
|
||||
BSDF = microfacet_hair(major_axis,
|
||||
sigma,
|
||||
roughness,
|
||||
Offset,
|
||||
|
@ -118,6 +117,5 @@ shader node_microfacet_hair_bsdf(color Color = color(0.017513, 0.005763, 0.00205
|
|||
AspectRatio,
|
||||
Reflection,
|
||||
Transmission,
|
||||
SecondaryReflection,
|
||||
major_axis);
|
||||
SecondaryReflection);
|
||||
}
|
||||
|
|
|
@ -69,8 +69,7 @@ closure color microfacet_hair(normal N,
|
|||
float aspect_ratio,
|
||||
float reflection,
|
||||
float transmission,
|
||||
float secondary_reflection,
|
||||
normal major_axis) BUILTIN;
|
||||
float secondary_reflection) BUILTIN;
|
||||
|
||||
// Volume
|
||||
closure color henyey_greenstein(float g) BUILTIN;
|
||||
|
|
|
@ -848,26 +848,24 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
|
|||
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)bsdf_alloc(
|
||||
sd, sizeof(MicrofacetHairBSDF), weight);
|
||||
if (bsdf) {
|
||||
ccl_private MicrofacetHairExtra *extra = (ccl_private MicrofacetHairExtra *)
|
||||
closure_alloc_extra(sd, sizeof(MicrofacetHairExtra));
|
||||
ccl_private MicrofacetHairLobeFactor *factor = (ccl_private MicrofacetHairLobeFactor *)
|
||||
closure_alloc_extra(sd, sizeof(MicrofacetHairLobeFactor));
|
||||
|
||||
if (!extra) {
|
||||
if (!factor) {
|
||||
break;
|
||||
}
|
||||
|
||||
bsdf->extra = extra;
|
||||
bsdf->extra->R = fmaxf(0.0f, R);
|
||||
bsdf->extra->TT = fmaxf(0.0f, TT);
|
||||
bsdf->extra->TRT = fmaxf(0.0f, TRT);
|
||||
bsdf->factor = factor;
|
||||
bsdf->factor->R = fmaxf(0.0f, R);
|
||||
bsdf->factor->TT = fmaxf(0.0f, TT);
|
||||
bsdf->factor->TRT = fmaxf(0.0f, TRT);
|
||||
|
||||
bsdf->aspect_ratio = stack_load_float_default(stack, aspect_ratio_ofs, data_node2.y);
|
||||
|
||||
if (bsdf->aspect_ratio != 1.0f) {
|
||||
const AttributeDescriptor attr_descr_normal = find_attribute(kg, sd, attr_normal);
|
||||
const float3 major_axis = curve_attribute_float3(kg, sd, attr_descr_normal, NULL, NULL);
|
||||
|
||||
/* Align ellipse major axis with the curve normal direction. */
|
||||
bsdf->extra->geom = make_float4(major_axis.x, major_axis.y, major_axis.z, 0.0f);
|
||||
const AttributeDescriptor attr_descr_normal = find_attribute(kg, sd, attr_normal);
|
||||
bsdf->N = curve_attribute_float3(kg, sd, attr_descr_normal, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Random factors range: [-randomization/2, +randomization/2]. */
|
||||
|
@ -878,7 +876,6 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
|
|||
bsdf->distribution_type = clamp(
|
||||
distribution_type, NODE_MICROFACET_HAIR_GGX, NODE_MICROFACET_HAIR_BECKMANN);
|
||||
|
||||
bsdf->N = N;
|
||||
bsdf->roughness = roughness;
|
||||
bsdf->tilt = tilt;
|
||||
bsdf->eta = ior;
|
||||
|
|
Loading…
Reference in New Issue
Do you think it could make sense to support colors here? It would increase the space usage, so we should only do it if there's a realistic use case I guess.
Currently if the parametrization is set to Melanin Concentration, an extra Tint socket is enabled, which corresponds to the realistic case of dyed hair (there is melanin present in natural hair, extra pigments from the dye is added). The R, TT, TRT are non-physical modulation factors, I thinking adding colors there would make the hair appearance quite difficult to control, using Tint should be a more proper way.