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

Merged
Weizhen Huang merged 114 commits from weizhen/blender:microfacet_hair into main 2023-08-18 12:46:20 +02:00
Showing only changes of commit 63d51d3305 - Show all commits

View File

@ -182,8 +182,8 @@ ccl_device float3 sphg_dir(float theta, float gamma, float a, float b)
float cos_gamma = cosf(gamma);
float tan_gamma = sin_gamma / cos_gamma;
float tan_phi = b / a * tan_gamma;
float cos_phi = 1.f / sqrtf(sqr(tan_phi) + 1.f);
if (cos_gamma < 0.f)
float cos_phi = 1.0f / sqrtf(sqr(tan_phi) + 1.0f);
if (cos_gamma < 0.0f)
cos_phi = -cos_phi;
float sin_phi = cos_phi * tan_phi;
return make_float3(sin_phi * cos_theta, sin_theta, cos_phi * cos_theta);
@ -217,7 +217,7 @@ ccl_device_inline float3 sample_wh(KernelGlobals kg,
/* Check micronormal/mesonormal direct visiblity from v */
ccl_device_inline bool microfacet_visible(const float3 v, const float3 m, const float3 h)
{
return (dot(v, h) > 0.f && dot(v, m) > 0.f);
return (dot(v, h) > 0.0f && dot(v, m) > 0.0f);
}
/* Check micronormal/mesonormal direct visiblity from wi and wo */
@ -237,11 +237,11 @@ ccl_device_inline float smith_g1(
/* Assume consistent orientation (can't see the back of the microfacet from the front and vice
* versa) */
float cos_vm = dot(v, m);
if (dot(v, h) <= 0.f || cos_vm <= 0.f)
return 0.f;
if (dot(v, h) <= 0.0f || cos_vm <= 0.0f)
return 0.0f;
return beckmann ? bsdf_beckmann_G1(roughness, cos_vm) :
2.f / (1.f + sqrtf(1.0f + sqr(roughness) * (1.0f / sqr(cos_vm) - 1.0f)));
2.0f / (1.0f + sqrtf(1.0f + sqr(roughness) * (1.0f / sqr(cos_vm) - 1.0f)));
}
/* Smith's separable shadowing-masking approximation */
@ -265,14 +265,15 @@ ccl_device float D(const bool beckmann, const float roughness, const float3 m, c
const float cos_theta2 = sqr(cos_theta);
if (beckmann) {
result = expf((1.f - 1.f / cos_theta2) / roughness2) / (M_PI_F * roughness2 * sqr(cos_theta2));
result = expf((1.0f - 1.0f / cos_theta2) / roughness2) /
(M_PI_F * roughness2 * sqr(cos_theta2));
}
else { /* GGX */
result = roughness2 / (M_PI_F * sqr(1.f + (roughness2 - 1.f) * cos_theta2));
result = roughness2 / (M_PI_F * sqr(1.0f + (roughness2 - 1.0f) * cos_theta2));
}
/* Prevent potential numerical issues in other stages of the model */
return (result * cos_theta > 1e-20f) ? result : 0.f;
return (result * cos_theta > 1e-20f) ? result : 0.0f;
}
/* Compute fresnel reflection. Also return the dot product of the refracted ray and the normal as
@ -282,18 +283,18 @@ ccl_device float fresnel(float cos_theta_i, float eta, ccl_private float *cos_th
kernel_assert(!isnan_safe(cos_theta_i));
/* Special cases. */
if (eta == 1.f) {
return 0.f;
if (eta == 1.0f) {
return 0.0f;
}
if (cos_theta_i == 0.f) {
return 1.f;
if (cos_theta_i == 0.0f) {
return 1.0f;
}
cos_theta_i = fabsf(cos_theta_i);
/* Using Snell's law, calculate the squared cosine of the angle between the surface normal and
* the transmitted ray. */
float cos_theta_t_sqr = 1.f - (1.f - cos_theta_i * cos_theta_i) / (eta * eta);
float cos_theta_t_sqr = 1.0f - (1.0f - cos_theta_i * cos_theta_i) / (eta * eta);
*cos_theta_t = safe_sqrtf(cos_theta_t_sqr);
if (cos_theta_t_sqr <= 0) {
@ -305,7 +306,7 @@ ccl_device float fresnel(float cos_theta_i, float eta, ccl_private float *cos_th
float a_s = (cos_theta_i - eta * (*cos_theta_t)) / (cos_theta_i + eta * (*cos_theta_t));
float a_p = (*cos_theta_t - eta * cos_theta_i) / (*cos_theta_t + eta * cos_theta_i);
float r = .5f * (sqr(a_s) + sqr(a_p));
float r = 0.5f * (sqr(a_s) + sqr(a_p));
/* Adjust the sign of the transmitted direction to be relative to the surface normal. */
*cos_theta_t = -(*cos_theta_t);
@ -333,7 +334,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
const bool analytical_ggx = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_GGX_ANALYTIC);
float3 R = zero_float3();
if (bsdf->extra->R <= 0.f)
if (bsdf->extra->R <= 0.0f)
return R;
const float3 wh = normalize(wi + wo);
@ -341,23 +342,23 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
/* dot(wi, wmi) > 0 */
const float tan_tilt = tanf(tilt);
float phi_m_max1 = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.f));
float phi_m_max1 = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.0f));
if (isnan_safe(phi_m_max1))
return R;
const float phi_m_min1 = -phi_m_max1;
/* dot(wo, wmi) > 0 */
const float phi_m_max2 = acosf(fmaxf(-tan_tilt * tan_theta(wo), 0.f)) + phi_o;
const float phi_m_max2 = acosf(fmaxf(-tan_tilt * tan_theta(wo), 0.0f)) + phi_o;
if (isnan_safe(phi_m_max2))
return R;
const float phi_m_min2 = -phi_m_max2 + 2.f * phi_o;
const float phi_m_min2 = -phi_m_max2 + 2.0f * phi_o;
const float phi_m_min = fmaxf(phi_m_min1, phi_m_min2) + 1e-5f;
const float phi_m_max = fminf(phi_m_max1, phi_m_max2) - 1e-5f;
if (phi_m_min > phi_m_max)
return R;
float integral = 0.f;
float integral = 0.0f;
/* analytical for ggx (no masking term) */
if (analytical_ggx) {
@ -370,37 +371,39 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
const float sm = sinf(tilt);
const float cm = cosf(tilt);
const float C = sqrtf(1.f - roughness_squared);
const float C = sqrtf(1.0f - roughness_squared);
const float A = cm * cos_theta(wh) * C;
const float B = sm * sin_theta(wh) * C;
const float A2 = sqr(A);
const float B2 = sqr(B);
const float tmp1 = 1.f / sqrtf(sqr(B - 1.f) - A2);
const float tmp2 = 1.f / sqrtf(sqr(B + 1.f) - A2);
const float tmp1 = 1.0f / sqrtf(sqr(B - 1.0f) - A2);
const float tmp2 = 1.0f / sqrtf(sqr(B + 1.0f) - A2);
const float smax = sinf(d_max);
const float cmax = cosf(d_max);
const float smin = sinf(d_min);
const float cmin = cosf(d_min);
const float tmax = smax / (1.f + cmax);
const float tmin = smin / (1.f + cmin);
const float tmax = smax / (1.0f + cmax);
const float tmin = smin / (1.0f + cmin);
const float temp1 = 2.f * (A2 - B2 + 3.f * B - 2) * sqr(tmp1) * tmp1 *
(atanf((A - B + 1.f) * tmp1 * tmax) - atanf((A - B + 1.f) * tmp1 * tmin));
const float temp2 = 2.f * (A2 - B2 - 3.f * B - 2) * sqr(tmp2) * tmp2 *
(atanf((B - A + 1.f) * tmp2 * tmax) - atanf((B - A + 1.f) * tmp2 * tmin));
const float temp1 = 2.0f * (A2 - B2 + 3.0f * B - 2) * sqr(tmp1) * tmp1 *
(atanf((A - B + 1.0f) * tmp1 * tmax) -
atanf((A - B + 1.0f) * tmp1 * tmin));
const float temp2 = 2.0f * (A2 - B2 - 3.0f * B - 2) * sqr(tmp2) * tmp2 *
(atanf((B - A + 1.0f) * tmp2 * tmax) -
atanf((B - A + 1.0f) * tmp2 * tmin));
const float temp3 = A * sqr(tmp1) *
(smax / (A * cmax + B - 1.f) - smin / (A * cmin + B - 1.f));
(smax / (A * cmax + B - 1.0f) - smin / (A * cmin + B - 1.0f));
const float temp4 = A * sqr(tmp2) *
(smax / (A * cmax + B + 1.f) - smin / (A * cmin + B + 1.f));
(smax / (A * cmax + B + 1.0f) - smin / (A * cmin + B + 1.0f));
integral = roughness_squared * M_1_PI_F * 0.5f * (temp1 + temp2 + temp3 + temp4);
}
else { /* falls back to numerical integration */
/* initial sample resolution */
float res = roughness * .7f;
const float scale = (phi_m_max - phi_m_min) * .5f;
float res = roughness * 0.7f;
const float scale = (phi_m_max - phi_m_min) * 0.5f;
size_t intervals = 2 * (size_t)ceilf(scale / res) + 1;
/* modified resolution based on integral domain */
@ -412,18 +415,18 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_circular(ccl_private const ShaderC
const float phi_m = phi_m_min + i * res;
const float3 wm = sph_dir(tilt, phi_m);
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.f, wm.z), wh)) {
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals - 1) ? 0.5f : (i % 2 + 1);
integral += weight * D(beckmann, roughness, wm, wh) *
G(beckmann, roughness, wi, wo, wm, wh);
}
}
integral *= (2.f / 3.f * res);
integral *= (2.0f / 3.0f * res);
}
const float F = fresnel_dielectric_cos(dot(wi, wh), eta);
R = make_float3(bsdf->extra->R * 0.125f * F * fmaxf(0.f, integral));
R = make_float3(bsdf->extra->R * 0.125f * F * fmaxf(0.0f, integral));
return R;
}
@ -439,26 +442,26 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_CIRCULAR_BECKMANN);
if (bsdf->extra->TT <= 0.f && bsdf->extra->TRT <= 0.f)
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f)
return zero_float3();
/* dot(wi, wmi) > 0 */
const float tan_tilt = tanf(tilt);
const float phi_m_max = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.f)) - 1e-3f;
const float phi_m_max = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.0f)) - 1e-3f;
if (isnan_safe(phi_m_max))
return zero_float3();
const float phi_m_min = -phi_m_max;
/* dot(wo, wmo) < 0 */
float tmp1 = acosf(fminf(tan_tilt * tan_theta(wo), 0.f));
float tmp1 = acosf(fminf(tan_tilt * tan_theta(wo), 0.0f));
if (isnan_safe(tmp1))
return zero_float3();
const float3 mu_a = bsdf->sigma;
const float inv_eta = 1.f / eta;
const float inv_eta = 1.0f / eta;
float res = roughness * .8f;
const float scale = (phi_m_max - phi_m_min) * .5f;
const float scale = (phi_m_max - phi_m_min) * 0.5f;
size_t intervals = 2 * (size_t)ceilf(scale / res) + 1;
res = (phi_m_max - phi_m_min) / intervals;
@ -480,39 +483,39 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
}
float cos_theta_t1;
const float T1 = 1.f - fresnel(dot_wi_wh1, eta, &cos_theta_t1);
const float T1 = 1.0f - fresnel(dot_wi_wh1, eta, &cos_theta_t1);
/* refraction at the first interface */
const float3 wt = -refract_angle(wi, wh1, cos_theta_t1, inv_eta);
const float phi_t = dir_phi(wt);
const float phi_mt = 2.f * phi_t - phi_mi;
const float phi_mt = 2.0f * phi_t - phi_mi;
const float3 wmt = sph_dir(-tilt, phi_mt);
const float G1 = G(beckmann, roughness, wi, -wt, wmi, wh1);
if (G1 == 0.f || !microfacet_visible(wi, -wt, make_float3(wmi.x, 0.f, wmi.z), wh1))
if (G1 == 0.0f || !microfacet_visible(wi, -wt, make_float3(wmi.x, 0.0f, wmi.z), wh1))
continue;
/* Simpson's rule weight */
float weight = (i == 0 || i == intervals - 1) ? 0.5f : (i % 2 + 1);
float3 A_t = exp(mu_a * 2.f * cosf(phi_t - phi_mi) / cos_theta(wt));
float3 A_t = exp(mu_a * 2.0f * cosf(phi_t - phi_mi) / cos_theta(wt));
/* TT */
if (bsdf->extra->TT > 0.f) {
if (bsdf->extra->TT > 0.0f) {
/* total internal reflection */
if (dot(wo, wt) >= inv_eta - 1e-5f) {
/* microfacet visiblity from macronormal */
float3 wh2 = -wt + inv_eta * wo;
if (dot(wmt, wh2) >= 0.f) {
if (dot(wmt, wh2) >= 0.0f) {
const float rcp_norm_wh2 = 1.f / len(wh2);
const float rcp_norm_wh2 = 1.0f / len(wh2);
wh2 *= rcp_norm_wh2;
float dot_wt_wh2 = dot(-wt, wh2);
const float T2 = 1.f - fresnel_dielectric_cos(dot_wt_wh2, inv_eta);
const float T2 = 1.0f - fresnel_dielectric_cos(dot_wt_wh2, inv_eta);
float D2 = D(beckmann, roughness, wh2, wmt) * G(beckmann, roughness, -wt, -wo, wmt, wh2);
const float3 result = T1 * T2 * D2 * A_t * dot_wt_wh2 * dot(wo, wh2) *
@ -526,7 +529,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
}
/* TRT */
if (bsdf->extra->TRT > 0.f) {
if (bsdf->extra->TRT > 0.0f) {
/* sample wh2 */
const float2 sample2 = make_float2(lcg_step_float(&rng_quadrature),
@ -543,7 +546,8 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
float3 wtr = -reflect(wt, wh2);
float G2 = G(beckmann, roughness, -wt, -wtr, wmt, wh2);
if (G2 == 0.f || microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.f, wmt.z), wh2) == 0.f)
if (G2 == 0.0f ||
microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.0f, wmt.z), wh2) == 0.0f)
continue;
/* total internal reflection */
@ -551,23 +555,23 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
continue;
float phi_tr = dir_phi(wtr);
float phi_mtr = phi_mi - 2.f * (phi_t - phi_tr) + M_PI_F;
float phi_mtr = phi_mi - 2.0f * (phi_t - phi_tr) + M_PI_F;
const float3 wmtr = sph_dir(-tilt, phi_mtr);
float3 wh3 = wtr + inv_eta * wo;
float G3 = G(beckmann, roughness, wtr, -wo, wmtr, wh3);
if (dot(wmtr, wh3) < 0.f || G3 == 0.f ||
!microfacet_visible(wtr, -wo, make_float3(wmtr.x, 0.f, wmtr.z), wh3))
if (dot(wmtr, wh3) < 0.0f || G3 == 0.0f ||
!microfacet_visible(wtr, -wo, make_float3(wmtr.x, 0.0f, wmtr.z), wh3))
continue;
float rcp_norm_wh3 = 1.f / len(wh3);
float rcp_norm_wh3 = 1.0f / len(wh3);
wh3 *= rcp_norm_wh3;
const float cos_trh3 = dot(wh3, wtr);
const float T3 = 1.f - fresnel_dielectric_cos(cos_trh3, inv_eta);
const float T3 = 1.0f - fresnel_dielectric_cos(cos_trh3, inv_eta);
const float D3 = D(beckmann, roughness, wh3, wmtr) * G3;
const float3 A_tr = exp(mu_a * 2.f * cosf(phi_tr - phi_mt) / cos_theta(wtr));
const float3 A_tr = exp(mu_a * 2.0f * cosf(phi_tr - phi_mt) / cos_theta(wtr));
const float3 result = T1 * R2 * T3 * D3 * cos_trh3 * dot(wh3, wo) * sqr(rcp_norm_wh3) * A_t *
A_tr * weight / (dot(wt, wmi) * dot(wtr, wmt)) *
@ -580,7 +584,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_circular(KernelGlobals kg,
}
}
return (S_tt + S_trt) * 1.f / 3.f * res * sqr(inv_eta);
return (S_tt + S_trt) / 3.0f * res * sqr(inv_eta);
}
ccl_device Spectrum bsdf_microfacet_hair_eval_circular(KernelGlobals kg,
@ -607,7 +611,7 @@ ccl_device Spectrum bsdf_microfacet_hair_eval_circular(KernelGlobals kg,
bsdf_microfacet_hair_eval_tt_trt_circular(kg, sc, wi, wo, sd->lcg_state);
weizhen marked this conversation as resolved Outdated

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

Since both of these are already known in the setup function at shader evaluation time, we might want to implement the "default to transparent" logic there?
/* TODO: better estimation of the pdf */
*pdf = 1.f;
*pdf = 1.0f;
return rgb_to_spectrum(R / cos_theta(wi));
}
@ -626,11 +630,11 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)sc;
*sampled_roughness = make_float2(bsdf->roughness, bsdf->roughness);
*eta = bsdf->eta;
const float inv_eta = 1.f / *eta;
const float inv_eta = 1.0f / *eta;
if (bsdf->extra->R <= 0.f && bsdf->extra->TT <= 0.f && bsdf->extra->TRT <= 0.f) {
if (bsdf->extra->R <= 0.0f && bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
/* early out for inactive lobe */
*pdf = 0.f;
*pdf = 0.0f;
return LABEL_NONE;
}
@ -660,18 +664,18 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
lcg_step_float(&sd->lcg_state));
/* sample offset h = -sin(phi_m) */
const float sin_phi_mi = sample_h * 2.f - 1.f;
const float cos_phi_mi = safe_sqrtf(1.f - sqr(sin_phi_mi));
const float sin_phi_mi = sample_h * 2.0f - 1.0f;
const float cos_phi_mi = safe_sqrtf(1.0f - sqr(sin_phi_mi));
const float st = sinf(tilt);
const float ct = cosf(tilt);
const float3 wmi = make_float3(sin_phi_mi * ct, st, cos_phi_mi * ct); /* mesonormal */
const float3 wmi_ = make_float3(sin_phi_mi, 0.f, cos_phi_mi); /* macronormal */
const float3 wmi_ = make_float3(sin_phi_mi, 0.0f, cos_phi_mi); /* macronormal */
if (dot(wmi, wi) < 0.f || dot(wmi_, wi) < 0.f) {
if (dot(wmi, wi) < 0.0f || dot(wmi_, wi) < 0.0f) {
/* macro/mesonormal invisible */
*pdf = 0.f;
*pdf = 0.0f;
return LABEL_NONE;
}
@ -680,8 +684,8 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
const float3 wr = -reflect(wi, wh1);
/* ensure that this is a valid sample */
if (!(dot(wr, wh1) > 0.f) || !(dot(wr, wmi) > 0.f) || !microfacet_visible(wi, wr, wmi_, wh1)) {
*pdf = 0.f;
if (!(dot(wr, wh1) > 0.0f) || !(dot(wr, wmi) > 0.0f) || !microfacet_visible(wi, wr, wmi_, wh1)) {
*pdf = 0.0f;
return LABEL_NONE;
}
@ -697,10 +701,10 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
const float phi_t = dir_phi(wt);
float phi_mi = atan2f(sin_phi_mi, cos_phi_mi);
float phi_mt = 2.f * phi_t - phi_mi;
float phi_mt = 2.0f * phi_t - phi_mi;
const float3 wmt = sph_dir(-tilt, phi_mt);
const float3 wmt_ = sph_dir(0.f, phi_mt);
const float3 wmt_ = sph_dir(0.0f, phi_mt);
const float3 wh2 = sample_wh(kg, beckmann, roughness, -wt, wmt, sample_h2);
const float3 wtr = -reflect(wt, wh2);
@ -709,29 +713,29 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
float3 wtt, wtrt;
float3 wmtr, wmtr_;
if (dot(wt, wh2) < 0.f && dot(wmt, wt) < 0.f &&
microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.f, wmt.z), wh2)) {
if (dot(wt, wh2) < 0.0f && dot(wmt, wt) < 0.0f &&
microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.0f, wmt.z), wh2)) {
const float3 mu_a = bsdf->sigma;
const float cos_gamma_t = -cosf(phi_t - phi_mi);
const float cos_theta_wt = sqrtf(1.f - sqr(wt.y));
const float3 A_t = exp(-mu_a * (2.f * cos_gamma_t / cos_theta_wt));
const float cos_theta_wt = sqrtf(1.0f - sqr(wt.y));
const float3 A_t = exp(-mu_a * (2.0f * cos_gamma_t / cos_theta_wt));
float cos_theta_t2;
const float R2 = fresnel(dot(-wt, wh2), inv_eta, &cos_theta_t2);
const float3 T1 = make_float3(1.f - R1);
const float3 T2 = make_float3(1.f - R2);
const float3 T1 = make_float3(1.0f - R1);
const float3 T2 = make_float3(1.0f - R2);
wtt = -refract_angle(-wt, wh2, cos_theta_t2, *eta);
if (dot(wtt, wmt) < 0.f && cos_theta_t2 != 0.f) /* total internal reflection */
if (dot(wtt, wmt) < 0.0f && cos_theta_t2 != 0.0f) /* total internal reflection */
TT = bsdf->extra->TT * T1 * A_t * T2;
/* sample TRT lobe */
const float phi_tr = dir_phi(wtr);
const float phi_mtr = phi_mi - 2.f * (phi_t - phi_tr) + M_PI_F;
const float phi_mtr = phi_mi - 2.0f * (phi_t - phi_tr) + M_PI_F;
wmtr = sph_dir(-tilt, phi_mtr);
wmtr_ = sph_dir(0.f, phi_mtr);
wmtr_ = sph_dir(0.0f, phi_mtr);
wh3 = sample_wh(kg, beckmann, roughness, wtr, wmtr, sample_h3);
@ -740,14 +744,14 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
wtrt = -refract_angle(wtr, wh3, cos_theta_t3, *eta);
if (cos_theta_t3 != 0.f && dot(wtr, wh3) > 0.f && dot(wmtr, wtr) > 0.f &&
dot(wtrt, wmtr) < 0.f &&
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.f, wmtr.z), wh3)) {
if (cos_theta_t3 != 0.0f && dot(wtr, wh3) > 0.0f && dot(wmtr, wtr) > 0.0f &&
dot(wtrt, wmtr) < 0.0f &&
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3)) {
const float3 T3 = make_float3(1.f - R3);
const float3 T3 = make_float3(1.0f - R3);
const float cos_gamma_t2 = -cos(phi_tr - phi_mt);
const float cos_theta_wtr = sqrtf(1.f - sqr(wtr.y));
const float3 A_tr = exp(-mu_a * (2.f * cos_gamma_t2 / cos_theta_wtr));
const float cos_theta_wtr = sqrtf(1.0f - sqr(wtr.y));
const float3 A_tr = exp(-mu_a * (2.0f * cos_gamma_t2 / cos_theta_wtr));
TRT = bsdf->extra->TRT * T1 * R2 * T3 * A_t * A_tr;
}
@ -759,13 +763,13 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
const float trt = average(TRT);
const float total_energy = r + tt + trt;
if (total_energy <= 0.f) {
*pdf = 0.f;
if (total_energy <= 0.0f) {
*pdf = 0.0f;
return LABEL_NONE;
}
float3 wo;
float visibility = 0.f;
float visibility = 0.0f;
int label = LABEL_GLOSSY;

I think this is only used if we actually sample TRRT? The compiler should probably optimize that by itself though I guess.

I think this is only used if we actually sample TRRT? The compiler should probably optimize that by itself though I guess.
sample_lobe *= total_energy;
@ -810,7 +814,7 @@ ccl_device int bsdf_microfacet_hair_sample_circular(const KernelGlobals kg,
/* ensure the same pdf is returned for BSDF and emitter sampling. The importance sampling pdf is
* already factored in the value so this value is only used for mis */
*pdf = 1.f;
*pdf = 1.0f;
return label;
}
@ -828,14 +832,14 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_elliptic(ccl_private const ShaderC
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
float3 R = zero_float3();
if (bsdf->extra->R <= 0.f)
if (bsdf->extra->R <= 0.0f)
return R;
/* get elliptical cross section characteristic */
const float a = 1.f;
const float a = 1.0f;
/* TODO: rename this as aspect ratio? e is in [0, 0.85], b is in [0.52, 1]. */
const float b = bsdf->extra->eccentricity;
const float e2 = 1.f - sqr(b / a);
const float e2 = 1.0f - sqr(b / a);
/* this follows blender's convention (unlike the circular case?) */
const float3 wo = wi_;
@ -847,16 +851,16 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_elliptic(ccl_private const ShaderC
/* dot(wi, wmi) > 0 */
const float tan_tilt = tanf(tilt);
float phi_m_max1 = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.f)) + phi_i;
float phi_m_max1 = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.0f)) + phi_i;
if (isnan_safe(phi_m_max1))
return R;
weizhen marked this conversation as resolved Outdated

Does this TODO refer to the PR, or it more general?
I think it's fine to keep it like this for now, just want to make sure it's not overlooked.

Does this TODO refer to the PR, or it more general? I think it's fine to keep it like this for now, just want to make sure it's not overlooked.

In the original implementation the pdf involves sampling microfacets, it is as costly as evaluating the BSDF itself, and it is also just an approximation, so not sure if it's worth it and how to do it better. As pdf is only used for MIS, it doesn't influence the correctness of the model, so left as a TODO as a future improvement.

In the original implementation the pdf involves sampling microfacets, it is as costly as evaluating the BSDF itself, and it is also just an approximation, so not sure if it's worth it and how to do it better. As pdf is only used for MIS, it doesn't influence the correctness of the model, so left as a TODO as a future improvement.
float phi_m_min1 = -phi_m_max1 + 2.f * phi_i;
float phi_m_min1 = -phi_m_max1 + 2.0f * phi_i;
/* dot(wo, wmi) > 0 */
float phi_m_max2 = acosf(fmaxf(-tan_tilt * tan_theta(wo), 0.f)) + phi_o;
float phi_m_max2 = acosf(fmaxf(-tan_tilt * tan_theta(wo), 0.0f)) + phi_o;
if (isnan_safe(phi_m_max2))
return R;
float phi_m_min2 = -phi_m_max2 + 2.f * phi_o;
float phi_m_min2 = -phi_m_max2 + 2.0f * phi_o;
/* try to wrap range */
if ((phi_m_max2 - phi_m_min1) > M_2PI_F) {
@ -880,32 +884,32 @@ ccl_device float3 bsdf_microfacet_hair_eval_r_elliptic(ccl_private const ShaderC
/* initial sample resolution */
float res = roughness * .7f;
const float scale = (gamma_m_max - gamma_m_min) * .5f;
const float scale = (gamma_m_max - gamma_m_min) * 0.5f;
size_t intervals = 2 * (size_t)ceilf(scale / res) + 1;
/* modified resolution based on integral domain */
res = (gamma_m_max - gamma_m_min) / float(intervals);
/* integrate using Simpson's rule */
float integral = 0.f;
float integral = 0.0f;
for (size_t i = 0; i < intervals; i++) {
const float gamma_m = gamma_m_min + i * res;
const float3 wm = sphg_dir(tilt, gamma_m, a, b);
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals - 1) ? .5f : (i % 2 + 1);
const float arc_length = sqrtf(1.f - e2 * sqr(sinf(gamma_m)));
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
const float weight = (i == 0 || i == intervals - 1) ? 0.5f : (i % 2 + 1);
const float arc_length = sqrtf(1.0f - e2 * sqr(sinf(gamma_m)));
integral += weight * D(beckmann, roughness, wm, wh) *
G(beckmann, roughness, wi, wo, wm, wh) * arc_length;
}
}
integral *= (2.f / 3.f * res);
integral *= (2.0f / 3.0f * res);
const float F = fresnel_dielectric_cos(dot(wi, wh), eta);
const float d_o_inv = 1.f / sqrtf(1.f - e2 * sqr(sinf(phi_o)));
const float d_o_inv = 1.0f / sqrtf(1.0f - e2 * sqr(sinf(phi_o)));
R = make_float3(bsdf->extra->R * 0.125f * F * integral * d_o_inv);
return R;
@ -923,7 +927,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
const float eta = bsdf->eta;
const bool beckmann = (bsdf->model_type == NODE_MICROFACET_HAIR_ELLIPTIC_BECKMANN);
if (bsdf->extra->TT <= 0.f && bsdf->extra->TRT <= 0.f)
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f)
return zero_float3();
/* this follows blender's convention (unlike the circular case?) */
@ -935,31 +939,31 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
/* dot(wi, wmi) > 0 */
const float tan_tilt = tanf(tilt);
float phi_m_max = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.f)) + phi_i;
float phi_m_max = acosf(fmaxf(-tan_tilt * tan_theta(wi), 0.0f)) + phi_i;
if (isnan_safe(phi_m_max))
return zero_float3();
float phi_m_min = -phi_m_max + 2.f * phi_i;
float phi_m_min = -phi_m_max + 2.0f * phi_i;
/* dot(wo, wmo) < 0 */
float tmp1 = acosf(fminf(tan_tilt * tan_theta(wo), 0.f));
float tmp1 = acosf(fminf(tan_tilt * tan_theta(wo), 0.0f));
if (isnan_safe(tmp1))
return zero_float3();
const float3 mu_a = bsdf->sigma;
const float inv_eta = 1.f / eta;
const float inv_eta = 1.0f / eta;
/* get elliptical cross section characteristic */
const float a = 1.f;
const float a = 1.0f;
const float b = bsdf->extra->eccentricity;
const float e2 = 1.f - sqr(b / a);
const float e2 = 1.0f - sqr(b / a);
float gamma_m_min = to_gamma(phi_m_min, a, b) + 1e-3f;
float gamma_m_max = to_gamma(phi_m_max, a, b) - 1e-3f;
if (gamma_m_max < gamma_m_min)
gamma_m_max += M_2PI_F;
float res = roughness * .8f;
const float scale = (gamma_m_max - gamma_m_min) * .5f;
float res = roughness * 0.8f;
const float scale = (gamma_m_max - gamma_m_min) * 0.5f;
size_t intervals = 2 * (size_t)ceilf(scale / res) + 1;
res = (gamma_m_max - gamma_m_min) / intervals;
@ -970,7 +974,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
const float gamma_mi = gamma_m_min + i * res;
const float3 wmi = sphg_dir(tilt, gamma_mi, a, b);
const float3 wmi_ = sphg_dir(0.f, gamma_mi, a, b);
const float3 wmi_ = sphg_dir(0.0f, gamma_mi, a, b);
/* sample wh1 */
const float2 sample1 = make_float2(lcg_step_float(&rng_quadrature),
@ -983,17 +987,17 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
}
float cos_theta_t1;
const float T1 = 1.f - fresnel(dot_wi_wh1, eta, &cos_theta_t1);
const float T1 = 1.0f - fresnel(dot_wi_wh1, eta, &cos_theta_t1);
/* refraction at the first interface */
const float3 wt = -refract_angle(wi, wh1, cos_theta_t1, inv_eta);
const float phi_t = dir_phi(wt);
const float gamma_mt = 2.f * to_phi(phi_t, a, b) - gamma_mi;
const float gamma_mt = 2.0f * to_phi(phi_t, a, b) - gamma_mi;
const float3 wmt = sphg_dir(-tilt, gamma_mt, a, b);
const float3 wmt_ = sphg_dir(0.f, gamma_mt, a, b);
const float3 wmt_ = sphg_dir(0.0f, gamma_mt, a, b);
const float G1 = G(beckmann, roughness, wi, -wt, wmi, wh1);
if (G1 == 0.f || !microfacet_visible(wi, -wt, wmi_, wh1))
if (G1 == 0.0f || !microfacet_visible(wi, -wt, wmi_, wh1))
continue;
/* Simpson's rule weight */
@ -1006,21 +1010,21 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
const float3 A_t = exp(-mu_a * len(pi - pt) / cos_theta(wt));
/* TT */
if (bsdf->extra->TT > 0.f) {
if (bsdf->extra->TT > 0.0f) {
/* Total internal reflection otherwise. */
if (dot(wo, wt) >= inv_eta - 1e-5f) {
/* microfacet visiblity from macronormal */
float3 wh2 = -wt + inv_eta * wo;
if (dot(wmt, wh2) >= 0.f) {
if (dot(wmt, wh2) >= 0.0f) {
const float rcp_norm_wh2 = 1.f / len(wh2);
const float rcp_norm_wh2 = 1.0f / len(wh2);
wh2 *= rcp_norm_wh2;
const float dot_wt_wh2 = dot(-wt, wh2);
const float T2 = 1.f - fresnel_dielectric_cos(dot_wt_wh2, inv_eta);
const float T2 = 1.0f - fresnel_dielectric_cos(dot_wt_wh2, inv_eta);
const float D2 = D(beckmann, roughness, wh2, wmt) *
G(beckmann, roughness, -wt, -wo, wmt, wh2);
@ -1029,7 +1033,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
smith_g1(beckmann, roughness, -wt, wmi, wh1) * dot(wi, wmi);
if (isfinite_safe(result)) {
const float arc_length = sqrtf(1.f - e2 * sqr(sinf(gamma_mt)));
const float arc_length = sqrtf(1.0f - e2 * sqr(sinf(gamma_mt)));
S_tt += bsdf->extra->TT * result * arc_length;
}
}
@ -1037,7 +1041,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
}
/* TRT */
if (bsdf->extra->TRT > 0.f) {
if (bsdf->extra->TRT > 0.0f) {
/* sample wh2 */
const float2 sample2 = make_float2(lcg_step_float(&rng_quadrature),
@ -1054,7 +1058,7 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
const float3 wtr = -reflect(wt, wh2);
const float G2 = G(beckmann, roughness, -wt, -wtr, wmt, wh2);
if (G2 == 0.f || !microfacet_visible(-wt, -wtr, wmt_, wh2))
if (G2 == 0.0f || !microfacet_visible(-wt, -wtr, wmt_, wh2))
continue;
/* total internal reflection */
@ -1062,22 +1066,22 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
continue;
const float phi_tr = dir_phi(wtr);
const float gamma_mtr = gamma_mi - 2.f * (to_phi(phi_t, a, b) - to_phi(phi_tr, a, b)) +
const float gamma_mtr = gamma_mi - 2.0f * (to_phi(phi_t, a, b) - to_phi(phi_tr, a, b)) +
M_PI_F;
const float3 wmtr = sphg_dir(-tilt, gamma_mtr, a, b);
const float3 wmtr_ = sphg_dir(0.f, gamma_mtr, a, b);
const float3 wmtr_ = sphg_dir(0.0f, gamma_mtr, a, b);
float3 wh3 = wtr + inv_eta * wo;
const float G3 = G(beckmann, roughness, wtr, -wo, wmtr, wh3);
if (dot(wmtr, wh3) < 0.f || G3 == 0.f || !microfacet_visible(wtr, -wo, wmtr_, wh3))
if (dot(wmtr, wh3) < 0.0f || G3 == 0.0f || !microfacet_visible(wtr, -wo, wmtr_, wh3))
continue;
const float rcp_norm_wh3 = 1.f / len(wh3);
const float rcp_norm_wh3 = 1.0f / len(wh3);
wh3 *= rcp_norm_wh3;
const float cos_trh3 = dot(wh3, wtr);
const float T3 = 1.f - fresnel_dielectric_cos(cos_trh3, inv_eta);
const float T3 = 1.0f - fresnel_dielectric_cos(cos_trh3, inv_eta);
const float D3 = D(beckmann, roughness, wh3, wmtr) * G3;
@ -1091,14 +1095,14 @@ ccl_device float3 bsdf_microfacet_hair_eval_tt_trt_elliptic(KernelGlobals kg,
dot(wt, wmt);
if (isfinite_safe(result)) {
const float arc_length = sqrtf(1.f - e2 * sqr(sin(gamma_mtr)));
const float arc_length = sqrtf(1.0f - e2 * sqr(sin(gamma_mtr)));
S_trt += bsdf->extra->TRT * result * arc_length;
}
}
}
const float d_o_inv = 1.f / sqrtf(1.f - e2 * sqr(sin(phi_o)));
return (S_tt + S_trt) * 1.f / 3.f * res * sqr(inv_eta) * d_o_inv;
const float d_o_inv = 1.0f / sqrtf(1.0f - e2 * sqr(sin(phi_o)));
return (S_tt + S_trt) / 3.0f * res * sqr(inv_eta) * d_o_inv;
}
ccl_device Spectrum bsdf_microfacet_hair_eval_elliptic(KernelGlobals kg,
@ -1134,7 +1138,7 @@ ccl_device Spectrum bsdf_microfacet_hair_eval_elliptic(KernelGlobals kg,
const float3 R = bsdf_microfacet_hair_eval_r_elliptic(sc, wi, wo) +
bsdf_microfacet_hair_eval_tt_trt_elliptic(kg, sc, wi, wo, sd->lcg_state);
*pdf = 1.f;
*pdf = 1.0f;
return rgb_to_spectrum(R / cos_theta(wi));
}
@ -1153,11 +1157,11 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
ccl_private MicrofacetHairBSDF *bsdf = (ccl_private MicrofacetHairBSDF *)sc;
*sampled_roughness = make_float2(bsdf->roughness, bsdf->roughness);
*eta = bsdf->eta;
const float inv_eta = 1.f / *eta;
const float inv_eta = 1.0f / *eta;
if (bsdf->extra->R <= 0.f && bsdf->extra->TT <= 0.f && bsdf->extra->TRT <= 0.f) {
if (bsdf->extra->R <= 0.0f && bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
/* early out for inactive lobe */
*pdf = 0.f;
*pdf = 0.0f;
return LABEL_NONE;
}
@ -1194,30 +1198,30 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
lcg_step_float(&sd->lcg_state));
/* get elliptical cross section characteristic */
const float a = 1.f;
const float a = 1.0f;
const float b = bsdf->extra->eccentricity;
const float e2 = 1.f - sqr(b / a);
const float e2 = 1.0f - sqr(b / a);
/* macronormal */
const float2 sincos_phi_i = sincos_phi(wi);
const float sin_phi_i = sincos_phi_i.x;
const float cos_phi_i = sincos_phi_i.y;
const float d_i = sqrtf(1.f - e2 * sqr(sin_phi_i));
const float h = d_i * (sample_h * 2.f - 1.f);
const float d_i = sqrtf(1.0f - e2 * sqr(sin_phi_i));
const float h = d_i * (sample_h * 2.0f - 1.0f);
const float gamma_mi = atan2f(cos_phi_i, -b / a * sin_phi_i) -
acosf(h / sqrtf(sqr(cos_phi_i) + sqr(b / a * sin_phi_i)));
const float sin_gamma_mi = sinf(gamma_mi);
const float cos_gamma_mi = cosf(gamma_mi);
const float3 wmi_ = normalize(make_float3(b * sin_gamma_mi, 0.f, a * cos_gamma_mi));
const float3 wmi_ = normalize(make_float3(b * sin_gamma_mi, 0.0f, a * cos_gamma_mi));
/* mesonormal */
const float st = sinf(tilt);
const float ct = cosf(tilt);
const float3 wmi = make_float3(wmi_.x * ct, st, wmi_.z * ct);
if (dot(wmi, wi) < 0.f || dot(wmi_, wi) < 0.f) {
if (dot(wmi, wi) < 0.0f || dot(wmi_, wi) < 0.0f) {
/* macro/mesonormal invisible */
*pdf = 0.f;
*pdf = 0.0f;
return LABEL_NONE;
}
@ -1227,8 +1231,8 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
const float3 wr = -reflect(wi, wh1);
/* ensure that this is a valid sample */
if (!(dot(wr, wh1) > 0.f) || !(dot(wr, wmi) > 0.f) || !microfacet_visible(wi, wr, wmi_, wh1)) {
*pdf = 0.f;
if (!(dot(wr, wh1) > 0.0f) || !(dot(wr, wmi) > 0.0f) || !microfacet_visible(wi, wr, wmi_, wh1)) {
*pdf = 0.0f;
return LABEL_NONE;
}
@ -1243,9 +1247,9 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
const float3 wt = -refract_angle(wi, wh1, cos_theta_t1, inv_eta);
const float phi_t = dir_phi(wt);
const float gamma_mt = 2.f * to_phi(phi_t, a, b) - gamma_mi;
const float gamma_mt = 2.0f * to_phi(phi_t, a, b) - gamma_mi;
const float3 wmt = sphg_dir(-tilt, gamma_mt, a, b);
const float3 wmt_ = sphg_dir(0.f, gamma_mt, a, b);
const float3 wmt_ = sphg_dir(0.0f, gamma_mt, a, b);
const float3 wh2 = sample_wh(kg, beckmann, roughness, -wt, wmt, sample_h2);
@ -1255,8 +1259,8 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
float3 wtt, wtrt;
float3 wmtr, wmtr_;
if (dot(wt, wh2) < 0.f && dot(wmt, wt) < 0.f &&
microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.f, wmt.z), wh2)) {
if (dot(wt, wh2) < 0.0f && dot(wmt, wt) < 0.0f &&
microfacet_visible(-wt, -wtr, make_float3(wmt.x, 0.0f, wmt.z), wh2)) {
const float3 mu_a = bsdf->sigma;
const float2 pi = to_point(gamma_mi, a, b);
@ -1265,20 +1269,20 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
float cos_theta_t2;
const float R2 = fresnel(dot(-wt, wh2), inv_eta, &cos_theta_t2);
const float3 T1 = make_float3(1.f - R1);
const float3 T2 = make_float3(1.f - R2);
const float3 T1 = make_float3(1.0f - R1);
const float3 T2 = make_float3(1.0f - R2);
wtt = -refract_angle(-wt, wh2, cos_theta_t2, *eta);
if (dot(wtt, wmt) < 0.f && cos_theta_t2 != 0.f) {
if (dot(wtt, wmt) < 0.0f && cos_theta_t2 != 0.0f) {
TT = bsdf->extra->TT * T1 * A_t * T2;
}
/* sample TRT lobe */
const float phi_tr = dir_phi(wtr);
float gamma_mtr = gamma_mi - 2.f * (to_phi(phi_t, a, b) - to_phi(phi_tr, a, b)) + M_PI_F;
float gamma_mtr = gamma_mi - 2.0f * (to_phi(phi_t, a, b) - to_phi(phi_tr, a, b)) + M_PI_F;
wmtr = sphg_dir(-tilt, gamma_mtr, a, b);
wmtr_ = sphg_dir(0.f, gamma_mtr, a, b);
wmtr_ = sphg_dir(0.0f, gamma_mtr, a, b);
wh3 = sample_wh(kg, beckmann, roughness, wtr, wmtr, sample_h3);
@ -1287,11 +1291,11 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
wtrt = -refract_angle(wtr, wh3, cos_theta_t3, *eta);
if (cos_theta_t3 != 0.f && dot(wtr, wh3) > 0.f && dot(wmtr, wtr) > 0.f &&
dot(wtrt, wmtr) < 0.f &&
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.f, wmtr.z), wh3)) {
if (cos_theta_t3 != 0.0f && dot(wtr, wh3) > 0.0f && dot(wmtr, wtr) > 0.0f &&
dot(wtrt, wmtr) < 0.0f &&
microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3)) {
const float3 T3 = make_float3(1.f - R3);
const float3 T3 = make_float3(1.0f - R3);
const float2 ptr = to_point(gamma_mtr + M_PI_F, a, b);
const float3 A_tr = exp(-mu_a * len(pt - ptr) / cos_theta(wtr));
@ -1306,13 +1310,13 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
const float trt = average(TRT);
const float total_energy = r + tt + trt;
if (total_energy == 0.f) {
*pdf = 0.f;
if (total_energy == 0.0f) {
*pdf = 0.0f;
return LABEL_NONE;
}
float3 wo;
float visibility = 0.f;
float visibility = 0.0f;
int label = LABEL_GLOSSY;
sample_lobe *= total_energy;
@ -1355,7 +1359,7 @@ ccl_device int bsdf_microfacet_hair_sample_elliptic(const KernelGlobals kg,
*eval *= dot(wi, wmi) / dot(wi, wmi_); */
/* ensure the same pdf is returned for BSDF and emitter sampling */
*pdf = 1.f;
*pdf = 1.0f;
return label;
}