Cycles: Remove MultiGGX code, replace with albedo scaling #107958

Merged
Lukas Stockner merged 8 commits from LukasStockner/blender:remove-multiggx into main 2023-06-05 02:21:03 +02:00
10 changed files with 87 additions and 1190 deletions
Showing only changes of commit 7432ecf55a - Show all commits

View File

@ -123,8 +123,6 @@ set(SRC_KERNEL_CLOSURE_HEADERS
closure/bsdf_diffuse.h
closure/bsdf_diffuse_ramp.h
closure/bsdf_microfacet.h
closure/bsdf_microfacet_multi.h
closure/bsdf_microfacet_multi_impl.h
closure/bsdf_oren_nayar.h
closure/bsdf_phong_ramp.h
closure/bsdf_toon.h

View File

@ -10,7 +10,6 @@
#include "kernel/closure/bsdf_phong_ramp.h"
#include "kernel/closure/bsdf_diffuse_ramp.h"
#include "kernel/closure/bsdf_microfacet.h"
#include "kernel/closure/bsdf_microfacet_multi.h"
#include "kernel/closure/bsdf_transparent.h"
#include "kernel/closure/bsdf_ashikhmin_shirley.h"
#include "kernel/closure/bsdf_toon.h"
@ -170,34 +169,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
label = bsdf_microfacet_ggx_sample(
sc, path_flag, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
label = bsdf_microfacet_multi_ggx_sample(kg,
sc,
Ng,
sd->wi,
rand.x,
rand.y,
eval,
wo,
pdf,
&sd->lcg_state,
sampled_roughness,
eta);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
label = bsdf_microfacet_multi_ggx_glass_sample(kg,
sc,
Ng,
sd->wi,
rand.x,
rand.y,
eval,
wo,
pdf,
&sd->lcg_state,
sampled_roughness,
eta);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
@ -339,13 +310,6 @@ ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
*eta = CLOSURE_IS_REFRACTIVE(bsdf->type) ? 1.0f / bsdf->ior : bsdf->ior;
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = bsdf->ior;
break;
}
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
@ -433,13 +397,11 @@ ccl_device_inline int bsdf_label(const KernelGlobals kg,
case CLOSURE_BSDF_SHARP_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: {
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
label = ((bsdf_is_transmission(sc, wo)) ? LABEL_TRANSMIT : LABEL_REFLECT) |
((bsdf->alpha_x * bsdf->alpha_y <= 1e-7f) ? LABEL_SINGULAR : LABEL_GLOSSY);
@ -542,12 +504,6 @@ ccl_device_inline
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
eval = bsdf_microfacet_ggx_eval(sc, sd->N, sd->wi, wo, pdf);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
eval = bsdf_microfacet_multi_ggx_eval(sc, sd->N, sd->wi, wo, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
eval = bsdf_microfacet_multi_ggx_glass_eval(sc, sd->wi, wo, pdf, &sd->lcg_state);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
@ -613,14 +569,11 @@ ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float
/* TODO: do we want to blur volume closures? */
#if defined(__SVM__) || defined(__OSL__)
switch (sc->type) {
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
bsdf_microfacet_multi_ggx_blur(sc, roughness);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
/* TODO: Recompute energy preservation after blur? */
bsdf_microfacet_ggx_blur(sc, roughness);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:

View File

@ -28,19 +28,8 @@ enum MicrofacetFresnel {
DIELECTRIC_TINT, /* used by the OSL MaterialX closures */
CONDUCTOR,
GENERALIZED_SCHLICK,
CONSTANT, /* only needed by MultiGGX */
PRINCIPLED_V1,
};
typedef struct FresnelPrincipledV1 {
Spectrum color; /* only needed by MultiGGX */
Spectrum cspec0;
} FresnelPrincipledV1;
typedef struct FresnelConstant {
Spectrum color;
} FresnelConstant;
typedef struct FresnelDielectricTint {
Spectrum reflection_tint;
Spectrum transmission_tint;
@ -53,7 +42,9 @@ typedef struct FresnelConductor {
typedef struct FresnelGeneralizedSchlick {
Spectrum reflection_tint;
Spectrum transmission_tint;
/* Reflectivity at perpendicular (F0) and glancing (F90) angles. */
Spectrum f0, f90;
/* Negative exponent signals a special case where the real Fresnel is remapped to F0...F90. */
float exponent;
} FresnelGeneralizedSchlick;
@ -218,12 +209,7 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
const float3 H,
const bool refraction)
{
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
kernel_assert(!refraction);
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
return interpolate_fresnel_color(wi, H, bsdf->ior, fresnel->cspec0);
}
else if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC) {
if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC) {
const float F = fresnel_dielectric_cos(dot(wi, H), bsdf->ior);
return make_spectrum(refraction ? 1.0f - F : F);
}
@ -241,18 +227,29 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) {
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
bsdf->fresnel;
float cosI = dot(wi, H);
if (bsdf->ior < 1.0f) {
/* When going from a higher to a lower IOR, we must use the transmitted angle. */
float sinT2 = (1.0f - sqr(cosI)) / sqr(bsdf->ior);
if (sinT2 >= 1.0f) {
/* Total internal reflection */
return refraction ? zero_spectrum() : fresnel->reflection_tint;
}
cosI = safe_sqrtf(1.0f - sinT2);
float s;
if (fresnel->exponent < 0.0f) {
/* Special case: Use real Fresnel curve to determine the interpolation between F0 and F90.
* Used by Principled v1. */
const float F_real = fresnel_dielectric_cos(dot(wi, H), bsdf->ior);
const float F0_real = F0_from_ior(bsdf->ior);
s = inverse_lerp(F0_real, 1.0f, F_real);
}
else {
/* Regular case: Generalized Schlick term. */
float cosI = dot(wi, H);
if (bsdf->ior < 1.0f) {
/* When going from a higher to a lower IOR, we must use the transmitted angle. */
float sinT2 = (1.0f - sqr(cosI)) / sqr(bsdf->ior);
if (sinT2 >= 1.0f) {
/* Total internal reflection */
return refraction ? zero_spectrum() : fresnel->reflection_tint;
}
cosI = safe_sqrtf(1.0f - sinT2);
}
/* TODO(lukas): Is a special case for exponent==5 worth it? */
s = powf(1.0f - cosI, fresnel->exponent);
}
/* TODO(lukas): Is a special case for exponent==5 worth it? */
const float s = powf(1.0f - cosI, fresnel->exponent);
const Spectrum F = mix(fresnel->f0, fresnel->f90, s);
if (refraction) {
return (one_spectrum() - F) * fresnel->transmission_tint;
@ -261,12 +258,6 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
return F * fresnel->reflection_tint;
}
}
else if (bsdf->fresnel_type == MicrofacetFresnel::CONSTANT) {
/* CONSTANT is only used my MultiGGX, which doesn't call this function.
* Therefore, this case only happens when determining the albedo of a MultiGGX closure.
* In that case, return 1.0 since the constant color is already baked into the weight. */
return one_spectrum();
}
else {
return one_spectrum();
}
@ -658,18 +649,6 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
/* Fresnel term setup functions. These get called after the distribution-specific setup functions
* like bsdf_microfacet_ggx_setup. */
ccl_device void bsdf_microfacet_setup_fresnel_principledv1(
ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd,
ccl_private FresnelPrincipledV1 *fresnel)
{
fresnel->cspec0 = saturate(fresnel->cspec0);
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
}
ccl_device void bsdf_microfacet_setup_fresnel_conductor(ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd,
ccl_private FresnelConductor *fresnel)

View File

@ -1,759 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/sample/lcg.h"
#include "kernel/sample/mapping.h"
CCL_NAMESPACE_BEGIN
/* Most of the code is based on the supplemental implementations from
* https://eheitzresearch.wordpress.com/240-2/. */
/* === GGX Microfacet distribution functions === */
/* Isotropic GGX microfacet distribution */
ccl_device_forceinline float D_ggx(float3 wm, float alpha)
{
wm.z *= wm.z;
alpha *= alpha;
float tmp = (1.0f - wm.z) + alpha * wm.z;
return alpha / max(M_PI_F * tmp * tmp, 1e-7f);
}
/* Anisotropic GGX microfacet distribution */
ccl_device_forceinline float D_ggx_aniso(const float3 wm, const float2 alpha)
{
float slope_x = -wm.x / alpha.x;
float slope_y = -wm.y / alpha.y;
float tmp = wm.z * wm.z + slope_x * slope_x + slope_y * slope_y;
return 1.0f / max(M_PI_F * tmp * tmp * alpha.x * alpha.y, 1e-7f);
}
/* Sample slope distribution (based on page 14 of the supplemental implementation). */
ccl_device_forceinline float2 mf_sampleP22_11(const float cosI,
const float randx,
const float randy)
{
if (cosI > 0.9999f || fabsf(cosI) < 1e-6f) {
const float r = sqrtf(randx / max(1.0f - randx, 1e-7f));
const float phi = M_2PI_F * randy;
return make_float2(r * cosf(phi), r * sinf(phi));
}
const float sinI = sin_from_cos(cosI);
const float tanI = sinI / cosI;
const float projA = 0.5f * (cosI + 1.0f);
if (projA < 0.0001f)
return make_float2(0.0f, 0.0f);
const float A = 2.0f * randx * projA / cosI - 1.0f;
float tmp = A * A - 1.0f;
if (fabsf(tmp) < 1e-7f)
return make_float2(0.0f, 0.0f);
tmp = 1.0f / tmp;
const float D = safe_sqrtf(tanI * tanI * tmp * tmp - (A * A - tanI * tanI) * tmp);
const float slopeX2 = tanI * tmp + D;
const float slopeX = (A < 0.0f || slopeX2 > 1.0f / tanI) ? (tanI * tmp - D) : slopeX2;
float U2;
if (randy >= 0.5f)
U2 = 2.0f * (randy - 0.5f);
else
U2 = 2.0f * (0.5f - randy);
const float z = (U2 * (U2 * (U2 * 0.27385f - 0.73369f) + 0.46341f)) /
(U2 * (U2 * (U2 * 0.093073f + 0.309420f) - 1.0f) + 0.597999f);
const float slopeY = z * sqrtf(1.0f + slopeX * slopeX);
if (randy >= 0.5f)
return make_float2(slopeX, slopeY);
else
return make_float2(slopeX, -slopeY);
}
/* Visible normal sampling for the GGX distribution
* (based on page 7 of the supplemental implementation). */
ccl_device_forceinline float3 mf_sample_vndf(const float3 wi,
const float2 alpha,
const float randx,
const float randy)
{
const float3 wi_11 = normalize(make_float3(alpha.x * wi.x, alpha.y * wi.y, wi.z));
const float2 slope_11 = mf_sampleP22_11(wi_11.z, randx, randy);
const float3 cossin_phi = safe_normalize(make_float3(wi_11.x, wi_11.y, 0.0f));
const float slope_x = alpha.x * (cossin_phi.x * slope_11.x - cossin_phi.y * slope_11.y);
const float slope_y = alpha.y * (cossin_phi.y * slope_11.x + cossin_phi.x * slope_11.y);
kernel_assert(isfinite(slope_x));
return normalize(make_float3(-slope_x, -slope_y, 1.0f));
}
/* === Phase functions: Glossy and Glass === */
/* Phase function for reflective materials. */
ccl_device_forceinline float3 mf_sample_phase_glossy(const float3 wi,
ccl_private Spectrum *weight,
const float3 wm)
{
return -wi + 2.0f * wm * dot(wi, wm);
}
ccl_device_forceinline Spectrum mf_eval_phase_glossy(const float3 w,
const float lambda,
const float3 wo,
const float2 alpha)
{
if (w.z > 0.9999f)
return zero_spectrum();
const float3 wh = normalize(wo - w);
if (wh.z < 0.0f)
return zero_spectrum();
float pArea = (w.z < -0.9999f) ? 1.0f : lambda * w.z;
const float dotW_WH = dot(-w, wh);
if (dotW_WH < 0.0f)
return zero_spectrum();
float phase = max(0.0f, dotW_WH) * 0.25f / max(pArea * dotW_WH, 1e-7f);
if (alpha.x == alpha.y)
phase *= D_ggx(wh, alpha.x);
else
phase *= D_ggx_aniso(wh, alpha);
return make_spectrum(phase);
}
/* Phase function for dielectric transmissive materials, including both reflection and refraction
* according to the dielectric fresnel term. */
ccl_device_forceinline float3 mf_sample_phase_glass(const float3 wi,
const float eta,
const float3 wm,
const float randV,
ccl_private bool *outside)
{
float cosI = dot(wi, wm);
float f = fresnel_dielectric_cos(cosI, eta);
if (randV < f) {
*outside = true;
return -wi + 2.0f * wm * cosI;
}
*outside = false;
float inv_eta = 1.0f / eta;
float cosT = -safe_sqrtf(1.0f - (1.0f - cosI * cosI) * inv_eta * inv_eta);
return normalize(wm * (cosI * inv_eta + cosT) - wi * inv_eta);
}
ccl_device_forceinline Spectrum mf_eval_phase_glass(const float3 w,
const float lambda,
const float3 wo,
const bool wo_outside,
const float2 alpha,
const float eta)
{
if (w.z > 0.9999f)
return zero_spectrum();
float pArea = (w.z < -0.9999f) ? 1.0f : lambda * w.z;
float v;
if (wo_outside) {
const float3 wh = normalize(wo - w);
if (wh.z < 0.0f)
return zero_spectrum();
const float dotW_WH = dot(-w, wh);
v = fresnel_dielectric_cos(dotW_WH, eta) * max(0.0f, dotW_WH) * D_ggx(wh, alpha.x) * 0.25f /
(pArea * dotW_WH);
}
else {
float3 wh = normalize(wo * eta - w);
if (wh.z < 0.0f)
wh = -wh;
const float dotW_WH = dot(-w, wh), dotWO_WH = dot(wo, wh);
if (dotW_WH < 0.0f)
return zero_spectrum();
float temp = dotW_WH + eta * dotWO_WH;
v = (1.0f - fresnel_dielectric_cos(dotW_WH, eta)) * max(0.0f, dotW_WH) * max(0.0f, -dotWO_WH) *
D_ggx(wh, alpha.x) / (pArea * temp * temp);
}
return make_spectrum(v);
}
/* === Utility functions for the random walks === */
/* Smith Lambda function for GGX (based on page 12 of the supplemental implementation). */
ccl_device_forceinline float mf_lambda(const float3 w, const float2 alpha)
{
if (w.z > 0.9999f)
return 0.0f;
else if (w.z < -0.9999f)
return -0.9999f;
const float inv_wz2 = 1.0f / max(w.z * w.z, 1e-7f);
const float2 wa = make_float2(w.x, w.y) * alpha;
float v = sqrtf(1.0f + dot(wa, wa) * inv_wz2);
if (w.z <= 0.0f)
v = -v;
return 0.5f * (v - 1.0f);
}
/* Height distribution CDF (based on page 4 of the supplemental implementation). */
ccl_device_forceinline float mf_invC1(const float h)
{
return 2.0f * saturatef(h) - 1.0f;
}
ccl_device_forceinline float mf_C1(const float h)
{
return saturatef(0.5f * (h + 1.0f));
}
/* Masking function (based on page 16 of the supplemental implementation). */
ccl_device_forceinline float mf_G1(const float3 w, const float C1, const float lambda)
{
if (w.z > 0.9999f)
return 1.0f;
if (w.z < 1e-5f)
return 0.0f;
return powf(C1, lambda);
}
/* Sampling from the visible height distribution (based on page 17 of the supplemental
* implementation). */
ccl_device_forceinline bool mf_sample_height(const float3 w,
ccl_private float *h,
ccl_private float *C1,
ccl_private float *G1,
ccl_private float *lambda,
const float U)
{
if (w.z > 0.9999f)
return false;
if (w.z < -0.9999f) {
*C1 *= U;
*h = mf_invC1(*C1);
*G1 = mf_G1(w, *C1, *lambda);
}
else if (fabsf(w.z) >= 0.0001f) {
if (U > 1.0f - *G1)
return false;
if (*lambda >= 0.0f) {
*C1 = 1.0f;
}
else {
*C1 *= powf(1.0f - U, -1.0f / *lambda);
}
*h = mf_invC1(*C1);
*G1 = mf_G1(w, *C1, *lambda);
}
return true;
}
/* === PDF approximations for the different phase functions. ===
* As explained in bsdf_microfacet_multi_impl.h, using approximations with MIS still produces an
* unbiased result. */
/* Approximation for the albedo of the single-scattering GGX distribution,
* the missing energy is then approximated as a diffuse reflection for the PDF. */
ccl_device_forceinline float mf_ggx_albedo(float r)
{
float albedo = 0.806495f * expf(-1.98712f * r * r) + 0.199531f;
albedo -= ((((((1.76741f * r - 8.43891f) * r + 15.784f) * r - 14.398f) * r + 6.45221f) * r -
1.19722f) *
r +
0.027803f) *
r +
0.00568739f;
return saturatef(albedo);
}
ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior)
{
if (ior < 1.0f) {
ior = 1.0f / ior;
}
a = saturatef(a);
ior = clamp(ior, 1.0f, 3.0f);
float I_1 = 0.0476898f * expf(-0.978352f * (ior - 0.65657f) * (ior - 0.65657f)) -
0.033756f * ior + 0.993261f;
float R_1 = (((0.116991f * a - 0.270369f) * a + 0.0501366f) * a - 0.00411511f) * a + 1.00008f;
float I_2 = (((-2.08704f * ior + 26.3298f) * ior - 127.906f) * ior + 292.958f) * ior - 287.946f +
199.803f / (ior * ior) - 101.668f / (ior * ior * ior);
float R_2 = ((((5.3725f * a - 24.9307f) * a + 22.7437f) * a - 3.40751f) * a + 0.0986325f) * a +
0.00493504f;
return saturatef(1.0f + I_2 * R_2 * 0.0019127f - (1.0f - I_1) * (1.0f - R_1) * 9.3205f);
}
ccl_device_forceinline float mf_ggx_pdf(const float3 wi, const float3 wo, const float alpha)
{
float D = D_ggx(normalize(wi + wo), alpha);
float lambda = mf_lambda(wi, make_float2(alpha, alpha));
float singlescatter = 0.25f * D / max((1.0f + lambda) * wi.z, 1e-7f);
float multiscatter = wo.z * M_1_PI_F;
float albedo = mf_ggx_albedo(alpha);
return albedo * singlescatter + (1.0f - albedo) * multiscatter;
}
ccl_device_forceinline float mf_ggx_aniso_pdf(const float3 wi, const float3 wo, const float2 alpha)
{
float D = D_ggx_aniso(normalize(wi + wo), alpha);
float lambda = mf_lambda(wi, alpha);
float singlescatter = 0.25f * D / max((1.0f + lambda) * wi.z, 1e-7f);
float multiscatter = wo.z * M_1_PI_F;
float albedo = mf_ggx_albedo(sqrtf(alpha.x * alpha.y));
return albedo * singlescatter + (1.0f - albedo) * multiscatter;
}
ccl_device_forceinline float mf_glass_pdf(const float3 wi,
const float3 wo,
const float alpha,
const float eta)
{
bool reflective = (wi.z * wo.z > 0.0f);
float wh_len;
float3 wh = normalize_len(wi + (reflective ? wo : (wo * eta)), &wh_len);
if (wh.z < 0.0f)
wh = -wh;
float3 r_wi = (wi.z < 0.0f) ? -wi : wi;
float lambda = mf_lambda(r_wi, make_float2(alpha, alpha));
float D = D_ggx(wh, alpha);
float fresnel = fresnel_dielectric_cos(dot(r_wi, wh), eta);
float multiscatter = fabsf(wo.z * M_1_PI_F);
if (reflective) {
float singlescatter = 0.25f * D / max((1.0f + lambda) * r_wi.z, 1e-7f);
float albedo = mf_ggx_albedo(alpha);
return fresnel * (albedo * singlescatter + (1.0f - albedo) * multiscatter);
}
else {
float singlescatter = fabsf(dot(r_wi, wh) * dot(wo, wh) * D * eta * eta /
max((1.0f + lambda) * r_wi.z * wh_len * wh_len, 1e-7f));
float albedo = mf_ggx_transmission_albedo(alpha, eta);
return (1.0f - fresnel) * (albedo * singlescatter + (1.0f - albedo) * multiscatter);
}
}
/* === Actual random walk implementations === */
/* One version of mf_eval and mf_sample per phase function. */
#define MF_NAME_JOIN(x, y) x##_##y
#define MF_NAME_EVAL(x, y) MF_NAME_JOIN(x, y)
#define MF_FUNCTION_FULL_NAME(prefix) MF_NAME_EVAL(prefix, MF_PHASE_FUNCTION)
#define MF_PHASE_FUNCTION glass
#define MF_MULTI_GLASS
#include "kernel/closure/bsdf_microfacet_multi_impl.h"
#define MF_PHASE_FUNCTION glossy
#define MF_MULTI_GLOSSY
#include "kernel/closure/bsdf_microfacet_multi_impl.h"
ccl_device void bsdf_microfacet_multi_ggx_blur(ccl_private ShaderClosure *sc, float roughness)
{
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc;
bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
/* === Closure implementations === */
/* Multi-scattering GGX Glossy closure */
ccl_device int bsdf_microfacet_multi_ggx_common_setup(ccl_private MicrofacetBsdf *bsdf)
{
bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f);
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
ccl_device int bsdf_microfacet_multi_ggx_setup(ccl_private MicrofacetBsdf *bsdf)
{
if (is_zero(bsdf->T))
bsdf->T = make_float3(1.0f, 0.0f, 0.0f);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
fresnel->color = saturate(fresnel->color);
bsdf->fresnel_type = MicrofacetFresnel::CONSTANT;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd)
{
if (is_zero(bsdf->T))
bsdf->T = make_float3(1.0f, 0.0f, 0.0f);
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
fresnel->color = saturate(fresnel->color);
fresnel->cspec0 = saturate(fresnel->cspec0);
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf)
{
bsdf->alpha_y = bsdf->alpha_x;
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
fresnel->color = saturate(fresnel->color);
bsdf->fresnel_type = MicrofacetFresnel::CONSTANT;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
ccl_device Spectrum bsdf_microfacet_multi_ggx_eval(ccl_private const ShaderClosure *sc,
const float3 Ng,
const float3 wi,
const float3 wo,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const float cosNgO = dot(Ng, wo);
if ((cosNgO < 0.0f) || bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
float3 X, Y, Z;
Z = bsdf->N;
/* Ensure that the both directions are on the outside w.r.t. the shading normal. */
if (dot(Z, wi) <= 0.0f || dot(Z, wo) <= 0.0f) {
*pdf = 0.0f;
return zero_spectrum();
}
Spectrum color, cspec0;
bool use_fresnel;
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
use_fresnel = true;
color = fresnel->color;
cspec0 = fresnel->cspec0;
}
else {
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::CONSTANT);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
use_fresnel = false;
color = fresnel->color;
cspec0 = zero_spectrum();
}
bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y);
if (is_aniso)
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
else
make_orthonormals(Z, &X, &Y);
float3 local_I = make_float3(dot(wi, X), dot(wi, Y), dot(wi, Z));
float3 local_O = make_float3(dot(wo, X), dot(wo, Y), dot(wo, Z));
if (is_aniso)
*pdf = mf_ggx_aniso_pdf(local_I, local_O, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
*pdf = mf_ggx_pdf(local_I, local_O, bsdf->alpha_x);
if (*pdf <= 0.f) {
*pdf = 0.f;
return make_float3(0.f, 0.f, 0.f);
}
return mf_eval_glossy(local_I,
local_O,
true,
color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
use_fresnel,
cspec0);
}
ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 wi,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *wo,
ccl_private float *pdf,
ccl_private uint *lcg_state,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float3 X, Y, Z;
Z = bsdf->N;
/* Ensure that the view direction is on the outside w.r.t. the shading normal. */
if (dot(Z, wi) <= 0.0f) {
*pdf = 0.0f;
return LABEL_NONE;
}
/* Special case: Extremely low roughness.
* Don't bother with microfacets, just do specular reflection. */
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
*wo = 2 * dot(Z, wi) * Z - wi;
if (dot(Ng, *wo) <= 0.0f) {
*pdf = 0.0f;
return LABEL_NONE;
}
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
return LABEL_REFLECT | LABEL_SINGULAR;
}
Spectrum color, cspec0;
bool use_fresnel;
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
use_fresnel = true;
color = fresnel->color;
cspec0 = fresnel->cspec0;
}
else {
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::CONSTANT);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
use_fresnel = false;
color = fresnel->color;
cspec0 = zero_spectrum();
}
*eta = bsdf->ior;
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y);
if (is_aniso)
make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
else
make_orthonormals(Z, &X, &Y);
float3 local_I = make_float3(dot(wi, X), dot(wi, Y), dot(wi, Z));
float3 local_O;
*eval = mf_sample_glossy(local_I,
&local_O,
color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
use_fresnel,
cspec0);
*wo = X * local_O.x + Y * local_O.y + Z * local_O.z;
/* Ensure that the light direction is on the outside w.r.t. the geometry normal. */
if (dot(Ng, *wo) <= 0.0f) {
*pdf = 0.0f;
return LABEL_NONE;
}
if (is_aniso)
*pdf = mf_ggx_aniso_pdf(local_I, local_O, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
*pdf = mf_ggx_pdf(local_I, local_O, bsdf->alpha_x);
*pdf = fmaxf(0.f, *pdf);
*eval *= *pdf;
return LABEL_REFLECT | LABEL_GLOSSY;
}
/* Multi-scattering GGX Glass closure */
ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf *bsdf)
{
bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
bsdf->alpha_y = bsdf->alpha_x;
bsdf->ior = max(0.0f, bsdf->ior);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
fresnel->color = saturate(fresnel->color);
bsdf->fresnel_type = MicrofacetFresnel::CONSTANT;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION;
}
ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private MicrofacetBsdf *bsdf,
ccl_private const ShaderData *sd)
{
bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
bsdf->alpha_y = bsdf->alpha_x;
bsdf->ior = max(0.0f, bsdf->ior);
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
fresnel->color = saturate(fresnel->color);
fresnel->cspec0 = saturate(fresnel->cspec0);
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
ccl_device Spectrum bsdf_microfacet_multi_ggx_glass_eval(ccl_private const ShaderClosure *sc,
const float3 wi,
const float3 wo,
ccl_private float *pdf,
ccl_private uint *lcg_state)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
*pdf = 0.0f;
return zero_spectrum();
}
float3 X, Y, Z;
Z = bsdf->N;
make_orthonormals(Z, &X, &Y);
float3 local_I = make_float3(dot(wi, X), dot(wi, Y), dot(wi, Z));
float3 local_O = make_float3(dot(wo, X), dot(wo, Y), dot(wo, Z));
const bool is_transmission = local_O.z < 0.0f;
Spectrum color, cspec0;
bool use_fresnel;
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
use_fresnel = true;
color = fresnel->color;
cspec0 = is_transmission ? fresnel->color : fresnel->cspec0;
}
else {
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::CONSTANT);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
use_fresnel = false;
color = fresnel->color;
cspec0 = zero_spectrum();
}
*pdf = mf_glass_pdf(local_I, local_O, bsdf->alpha_x, bsdf->ior);
kernel_assert(*pdf >= 0.f);
return mf_eval_glass(local_I,
local_O,
!is_transmission,
color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
!is_transmission && use_fresnel,
cspec0);
}
ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg,
ccl_private const ShaderClosure *sc,
float3 Ng,
float3 wi,
float randu,
float randv,
ccl_private Spectrum *eval,
ccl_private float3 *wo,
ccl_private float *pdf,
ccl_private uint *lcg_state,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
float3 X, Y, Z;
Z = bsdf->N;
*eta = bsdf->ior;
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) {
float3 T;
bool inside;
float fresnel = fresnel_dielectric(bsdf->ior, Z, wi, &T, &inside);
*pdf = 1e6f;
*eval = make_spectrum(1e6f);
if (randu < fresnel) {
*wo = 2 * dot(Z, wi) * Z - wi;
return LABEL_REFLECT | LABEL_SINGULAR;
}
else {
*wo = T;
return LABEL_TRANSMIT | LABEL_SINGULAR;
}
}
Spectrum color, cspec0;
bool use_fresnel;
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
use_fresnel = true;
color = fresnel->color;
cspec0 = fresnel->cspec0;
}
else {
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::CONSTANT);
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)bsdf->fresnel;
use_fresnel = false;
color = fresnel->color;
cspec0 = zero_spectrum();
}
make_orthonormals(Z, &X, &Y);
float3 local_I = make_float3(dot(wi, X), dot(wi, Y), dot(wi, Z));
float3 local_O;
*eval = mf_sample_glass(local_I,
&local_O,
color,
bsdf->alpha_x,
bsdf->alpha_y,
lcg_state,
bsdf->ior,
use_fresnel,
cspec0);
*pdf = mf_glass_pdf(local_I, local_O, bsdf->alpha_x, bsdf->ior);
kernel_assert(*pdf >= 0.f);
*eval *= *pdf;
*wo = X * local_O.x + Y * local_O.y + Z * local_O.z;
if (local_O.z * local_I.z > 0.0f) {
return LABEL_REFLECT | LABEL_GLOSSY;
}
else {
return LABEL_TRANSMIT | LABEL_GLOSSY;
}
}
CCL_NAMESPACE_END

View File

@ -1,250 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
/* Evaluate the BSDF from wi to wo.
* Evaluation is split into the analytical single-scattering BSDF and the multi-scattering BSDF,
* which is evaluated stochastically through a random walk. At each bounce (except for the first
* one), the amount of reflection from here towards wo is evaluated before bouncing again.
*
* Because of the random walk, the evaluation is not deterministic, but its expected value is equal
* to the correct BSDF, which is enough for Monte-Carlo rendering. The PDF also can't be determined
* analytically, so the single-scattering PDF plus a diffuse term to account for the
* multi-scattered energy is used. In combination with MIS, that is enough to produce an unbiased
* result, although the balance heuristic isn't necessarily optimal anymore.
*/
ccl_device_forceinline Spectrum MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi,
float3 wo,
const bool wo_outside,
const Spectrum color,
const float alpha_x,
const float alpha_y,
ccl_private uint *lcg_state,
const float eta,
bool use_fresnel,
const Spectrum cspec0)
{
/* Evaluating for a shallower incoming direction produces less noise, and the properties of the
* BSDF guarantee reciprocity. */
bool swapped = false;
#ifdef MF_MULTI_GLASS
if (wi.z * wo.z < 0.0f) {
/* Glass transmission is a special case and requires the directions to change hemisphere. */
if (-wo.z < wi.z) {
swapped = true;
float3 tmp = -wo;
wo = -wi;
wi = tmp;
}
}
else
#endif
if (wo.z < wi.z)
{
swapped = true;
float3 tmp = wo;
wo = wi;
wi = tmp;
}
if (wi.z < 1e-5f || (wo.z < 1e-5f && wo_outside) || (wo.z > -1e-5f && !wo_outside))
return zero_spectrum();
const float2 alpha = make_float2(alpha_x, alpha_y);
float lambda_r = mf_lambda(-wi, alpha);
float shadowing_lambda = mf_lambda(wo_outside ? wo : -wo, alpha);
/* Analytically compute single scattering for lower noise. */
Spectrum eval;
Spectrum throughput = one_spectrum();
const float3 wh = normalize(wi + wo);
#ifdef MF_MULTI_GLASS
eval = mf_eval_phase_glass(-wi, lambda_r, wo, wo_outside, alpha, eta);
if (wo_outside)
eval *= -lambda_r / (shadowing_lambda - lambda_r);
else
eval *= -lambda_r * beta(-lambda_r, shadowing_lambda + 1.0f);
#else /* MF_MULTI_GLOSSY */
const float G2 = 1.0f / (1.0f - (lambda_r + 1.0f) + shadowing_lambda);
float val = G2 * 0.25f / wi.z;
if (alpha.x == alpha.y)
val *= D_ggx(wh, alpha.x);
else
val *= D_ggx_aniso(wh, alpha);
eval = make_spectrum(val);
#endif
if (use_fresnel) {
throughput = interpolate_fresnel_color(wi, wh, eta, cspec0);
eval *= throughput;
}
float3 wr = -wi;
float hr = 1.0f;
float C1_r = 1.0f;
float G1_r = 0.0f;
bool outside = true;
for (int order = 0; order < 10; order++) {
/* Sample microfacet height. */
float height_rand = lcg_step_float(lcg_state);
if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand))
break;
/* Sample microfacet normal. */
float vndf_rand_y = lcg_step_float(lcg_state);
float vndf_rand_x = lcg_step_float(lcg_state);
float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y);
#ifdef MF_MULTI_GLASS
if (order == 0 && use_fresnel) {
/* Evaluate amount of scattering towards wo on this microfacet. */
Spectrum phase;
if (outside)
phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta);
else
phase = mf_eval_phase_glass(wr, lambda_r, -wo, !wo_outside, alpha, 1.0f / eta);
eval = throughput * phase *
mf_G1(wo_outside ? wo : -wo,
mf_C1((outside == wo_outside) ? hr : -hr),
shadowing_lambda);
}
#endif
if (order > 0) {
/* Evaluate amount of scattering towards wo on this microfacet. */
Spectrum phase;
#ifdef MF_MULTI_GLASS
if (outside)
phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta);
else
phase = mf_eval_phase_glass(wr, lambda_r, -wo, !wo_outside, alpha, 1.0f / eta);
#else /* MF_MULTI_GLOSSY */
phase = mf_eval_phase_glossy(wr, lambda_r, wo, alpha) * throughput;
#endif
eval += throughput * phase *
mf_G1(wo_outside ? wo : -wo,
mf_C1((outside == wo_outside) ? hr : -hr),
shadowing_lambda);
}
if (order + 1 < 10) {
/* Bounce from the microfacet. */
#ifdef MF_MULTI_GLASS
bool next_outside;
float3 wi_prev = -wr;
float phase_rand = lcg_step_float(lcg_state);
wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside);
if (!next_outside) {
outside = !outside;
wr = -wr;
hr = -hr;
}
if (use_fresnel && !next_outside) {
throughput *= color;
}
else if (use_fresnel && order > 0) {
throughput *= interpolate_fresnel_color(wi_prev, wm, eta, cspec0);
}
#else /* MF_MULTI_GLOSSY */
if (use_fresnel && order > 0) {
throughput *= interpolate_fresnel_color(-wr, wm, eta, cspec0);
}
wr = mf_sample_phase_glossy(-wr, &throughput, wm);
#endif
lambda_r = mf_lambda(wr, alpha);
if (!use_fresnel)
throughput *= color;
C1_r = mf_C1(hr);
G1_r = mf_G1(wr, C1_r, lambda_r);
}
}
if (swapped)
eval *= fabsf(wi.z / wo.z);
return eval;
}
/* Perform a random walk on the microsurface starting from wi, returning the direction in which the
* walk escaped the surface in wo. The function returns the throughput between wi and wo. Without
* reflection losses due to coloring or fresnel absorption in conductors, the sampling is optimal.
*/
ccl_device_forceinline Spectrum MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi,
ccl_private float3 *wo,
const Spectrum color,
const float alpha_x,
const float alpha_y,
ccl_private uint *lcg_state,
const float eta,
bool use_fresnel,
const Spectrum cspec0)
{
const float2 alpha = make_float2(alpha_x, alpha_y);
Spectrum throughput = one_spectrum();
float3 wr = -wi;
float lambda_r = mf_lambda(wr, alpha);
float hr = 1.0f;
float C1_r = 1.0f;
float G1_r = 0.0f;
bool outside = true;
int order;
for (order = 0; order < 10; order++) {
/* Sample microfacet height. */
float height_rand = lcg_step_float(lcg_state);
if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) {
/* The random walk has left the surface. */
*wo = outside ? wr : -wr;
return throughput;
}
/* Sample microfacet normal. */
float vndf_rand_y = lcg_step_float(lcg_state);
float vndf_rand_x = lcg_step_float(lcg_state);
float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y);
/* First-bounce color is already accounted for in mix weight. */
if (!use_fresnel && order > 0)
throughput *= color;
/* Bounce from the microfacet. */
#ifdef MF_MULTI_GLASS
bool next_outside;
float3 wi_prev = -wr;
float phase_rand = lcg_step_float(lcg_state);
wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside);
if (!next_outside) {
hr = -hr;
wr = -wr;
outside = !outside;
}
if (use_fresnel) {
if (!next_outside) {
throughput *= color;
}
else {
throughput *= interpolate_fresnel_color(wi_prev, wm, eta, cspec0);
}
}
#else /* MF_MULTI_GLOSSY */
if (use_fresnel) {
throughput *= interpolate_fresnel_color(-wr, wm, eta, cspec0);
}
wr = mf_sample_phase_glossy(-wr, &throughput, wm);
#endif
/* Update random walk parameters. */
lambda_r = mf_lambda(wr, alpha);
G1_r = mf_G1(wr, C1_r, lambda_r);
}
*wo = make_float3(0.0f, 0.0f, 1.0f);
return zero_spectrum();
}
#undef MF_MULTI_GLASS
#undef MF_MULTI_GLOSSY
#undef MF_PHASE_FUNCTION

View File

@ -81,6 +81,11 @@ ccl_device float ior_from_F0(Spectrum f0)
return (1.0f + sqrt_f0) / (1.0f - sqrt_f0);
}
ccl_device float F0_from_ior(float ior)
{
return sqr((ior - 1.0f) / (ior + 1.0f));
}
ccl_device float schlick_fresnel(float u)
{
float m = clamp(1.0f - u, 0.0f, 1.0f);

View File

@ -14,7 +14,6 @@
#include "kernel/closure/bsdf_ashikhmin_velvet.h"
#include "kernel/closure/bsdf_diffuse.h"
#include "kernel/closure/bsdf_microfacet.h"
#include "kernel/closure/bsdf_microfacet_multi.h"
#include "kernel/closure/bsdf_oren_nayar.h"
#include "kernel/closure/bsdf_transparent.h"
#include "kernel/closure/bsdf_ashikhmin_shirley.h"

View File

@ -261,9 +261,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), spec_weight);
ccl_private FresnelPrincipledV1 *fresnel =
(bsdf != NULL) ? (ccl_private FresnelPrincipledV1 *)closure_alloc_extra(
sd, sizeof(FresnelPrincipledV1)) :
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
@ -283,21 +283,18 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
one_float3(); // normalize lum. to isolate hue+sat
float3 tmp_col = make_float3(1.0f - specular_tint) + m_ctint * specular_tint;
fresnel->cspec0 = rgb_to_spectrum((specular * 0.08f * tmp_col) * (1.0f - metallic) +
base_color * metallic);
fresnel->color = rgb_to_spectrum(base_color);
fresnel->f0 = rgb_to_spectrum((specular * 0.08f * tmp_col) * (1.0f - metallic) +
base_color * metallic);
fresnel->f90 = one_spectrum();
fresnel->exponent = -1.0f;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
/* setup bsdf */
/* Use single-scatter GGX. */
if (distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID || roughness <= 0.075f) {
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
bsdf_microfacet_setup_fresnel_principledv1(bsdf, sd, fresnel);
} /* Use multi-scatter GGX. */
else {
bsdf->fresnel = fresnel;
sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd);
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
bsdf_microfacet_setup_fresnel_generalized_schlick(bsdf, sd, fresnel);
if (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) {
/* TODO: Energy preservation */
}
}
}
@ -327,9 +324,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
{
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), glass_weight * fresnel);
ccl_private FresnelPrincipledV1 *fresnel =
(bsdf != NULL) ? (ccl_private FresnelPrincipledV1 *)closure_alloc_extra(
sd, sizeof(FresnelPrincipledV1)) :
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
@ -344,9 +341,13 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
fresnel->color = rgb_to_spectrum(base_color);
fresnel->cspec0 = rgb_to_spectrum(cspec0);
bsdf_microfacet_setup_fresnel_principledv1(bsdf, sd, fresnel);
fresnel->f0 = cspec0;
fresnel->f90 = one_spectrum();
fresnel->exponent = -1.0f;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
bsdf_microfacet_setup_fresnel_generalized_schlick(bsdf, sd, fresnel);
}
}
@ -384,9 +385,9 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
else {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), glass_weight);
ccl_private FresnelPrincipledV1 *fresnel =
(bsdf != NULL) ? (ccl_private FresnelPrincipledV1 *)closure_alloc_extra(
sd, sizeof(FresnelPrincipledV1)) :
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
@ -398,11 +399,16 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
bsdf->alpha_y = roughness * roughness;
bsdf->ior = ior;
fresnel->color = rgb_to_spectrum(base_color);
fresnel->cspec0 = rgb_to_spectrum(cspec0);
fresnel->f0 = make_spectrum(F0_from_ior(ior));
fresnel->f90 = one_spectrum();
fresnel->exponent = -1.0f;
fresnel->reflection_tint = cspec0;
fresnel->transmission_tint = base_color;
/* setup bsdf */
sd->flag |= bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf, sd);
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
bsdf_microfacet_setup_fresnel_generalized_schlick(bsdf, sd, fresnel);
/* TODO: Energy preservation */
}
}
}
@ -525,20 +531,15 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
sd->flag |= bsdf_reflection_setup(bsdf);
else if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_ID)
sd->flag |= bsdf_microfacet_beckmann_setup(bsdf);
else if (type == CLOSURE_BSDF_MICROFACET_GGX_ID)
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
else if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) {
kernel_assert(stack_valid(data_node.w));
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)closure_alloc_extra(
sd, sizeof(FresnelConstant));
if (fresnel) {
bsdf->fresnel = fresnel;
fresnel->color = rgb_to_spectrum(stack_load_float3(stack, data_node.w));
sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf);
}
else if (type == CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) {
sd->flag |= bsdf_ashikhmin_shirley_setup(bsdf);
}
else {
sd->flag |= bsdf_ashikhmin_shirley_setup(bsdf);
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) {
kernel_assert(stack_valid(data_node.w));
/* TODO: Color multiplier and energy preservation */
}
}
break;
@ -587,7 +588,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
}
case CLOSURE_BSDF_SHARP_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: {
#ifdef __CAUSTICS_TRICKS__
if (!kernel_data.integrator.caustics_reflective &&
!kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE))
@ -621,49 +623,18 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg,
if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID)
sd->flag |= bsdf_microfacet_beckmann_glass_setup(bsdf);
else
else {
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) {
kernel_assert(stack_valid(data_node.z));
/* TODO: Color multiplier and energy preservation */
}
}
}
}
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: {
#ifdef __CAUSTICS_TRICKS__
if (!kernel_data.integrator.caustics_reflective &&
!kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE))
break;
#endif
Spectrum weight = sd->svm_closure_weight * mix_weight;
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), weight);
if (!bsdf) {
break;
}
ccl_private FresnelConstant *fresnel = (ccl_private FresnelConstant *)closure_alloc_extra(
sd, sizeof(FresnelConstant));
if (!fresnel) {
break;
}
bsdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bsdf->fresnel = fresnel;
bsdf->T = zero_float3();
float roughness = sqr(param1);
bsdf->alpha_x = roughness;
bsdf->alpha_y = roughness;
float eta = fmaxf(param2, 1e-5f);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta;
kernel_assert(stack_valid(data_node.z));
fresnel->color = rgb_to_spectrum(stack_load_float3(stack, data_node.z));
/* setup bsdf */
sd->flag |= bsdf_microfacet_multi_ggx_glass_setup(bsdf);
break;
}
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: {
Spectrum weight = sd->svm_closure_weight * mix_weight;
ccl_private VelvetBsdf *bsdf = (ccl_private VelvetBsdf *)bsdf_alloc(

View File

@ -423,7 +423,7 @@ typedef enum ClosureType {
CLOSURE_BSDF_MICROFACET_GGX_ID,
CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, /* virtual closure */
CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID,
CLOSURE_BSDF_ASHIKHMIN_VELVET_ID,
CLOSURE_BSDF_PHONG_RAMP_ID,
@ -437,7 +437,7 @@ typedef enum ClosureType {
CLOSURE_BSDF_SHARP_GLASS_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID,
CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, /* virtual closure */
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,

View File

@ -2350,7 +2350,7 @@ NODE_DEFINE(GlossyBsdfNode)
distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_ID);
distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID);
distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ID);
SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT);
@ -2444,6 +2444,7 @@ void GlossyBsdfNode::compile(SVMCompiler &compiler)
if (closure == CLOSURE_BSDF_REFLECTION_ID)
BsdfNode::compile(compiler, NULL, NULL);
/* TODO: Just use weight for legacy MultiGGX? Would also simplify OSL. */
else if (closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID)
BsdfNode::compile(
compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), input("Color"));
@ -2471,7 +2472,7 @@ NODE_DEFINE(GlassBsdfNode)
distribution_enum.insert("sharp", CLOSURE_BSDF_SHARP_GLASS_ID);
distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID);
distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID);
distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
SOCKET_ENUM(
distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID);
SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f);
@ -2750,7 +2751,7 @@ NODE_DEFINE(PrincipledBsdfNode)
static NodeEnum distribution_enum;
distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID);
distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
SOCKET_ENUM(
distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);