mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Fixes the following misspellings: "indicies" -> "indices" "Remapp" -> "Remap" "Consine" -> "Cosine" "Renderman" -> "RenderMan" "Compution" -> "Compute" Pull Request: https://projects.blender.org/blender/blender/pulls/164180
1615 lines
63 KiB
C++
1615 lines
63 KiB
C++
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
|
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Adapted code from Open Shading Language. */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/closure/bsdf_transparent.h"
|
|
#include "kernel/closure/bsdf_util.h"
|
|
#include "kernel/constants.h"
|
|
#include "kernel/sample/mapping.h"
|
|
#include "kernel/util/lookup_table.h"
|
|
|
|
#include "util/math_fast.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
enum MicrofacetType {
|
|
BECKMANN,
|
|
GGX,
|
|
};
|
|
|
|
enum MicrofacetFresnel {
|
|
NONE = 0,
|
|
DIELECTRIC,
|
|
DIELECTRIC_TINT, /* used by the OSL MaterialX closures */
|
|
CONDUCTOR,
|
|
GENERALIZED_SCHLICK,
|
|
F82_TINT,
|
|
};
|
|
|
|
struct FresnelCoeff {
|
|
Spectrum reflectance;
|
|
Spectrum transmittance;
|
|
|
|
FresnelCoeff() = default;
|
|
|
|
ccl_device_inline_method FresnelCoeff(Spectrum r_, Spectrum t_)
|
|
: reflectance(r_), transmittance(t_)
|
|
{
|
|
}
|
|
|
|
ccl_device_inline_method FresnelCoeff(bool reflective, bool refractive)
|
|
: reflectance(reflective ? one_spectrum() : zero_spectrum()),
|
|
transmittance(refractive ? one_spectrum() : zero_spectrum())
|
|
{
|
|
}
|
|
|
|
ccl_device_inline_method FresnelCoeff operator*(ccl_private const FresnelCoeff &other) const
|
|
{
|
|
return {this->reflectance * other.reflectance, this->transmittance * other.transmittance};
|
|
}
|
|
|
|
template<class T> ccl_device_inline_method FresnelCoeff operator*(const T reflectance_) const
|
|
{
|
|
return {this->reflectance * reflectance_, this->transmittance * (1.0f - reflectance_)};
|
|
}
|
|
|
|
template<class T> ccl_device_inline_method FresnelCoeff operator*=(const T other)
|
|
{
|
|
return *this = *this * other;
|
|
}
|
|
|
|
ccl_device_inline_method Spectrum sum() const
|
|
{
|
|
return reflectance + transmittance;
|
|
}
|
|
};
|
|
|
|
ccl_device_inline bool iszero(ccl_private const FresnelCoeff &coeff)
|
|
{
|
|
return is_zero(coeff.reflectance) && is_zero(coeff.transmittance);
|
|
}
|
|
|
|
struct FresnelDielectricTint {
|
|
FresnelThinFilm thin_film;
|
|
FresnelCoeff tint;
|
|
};
|
|
|
|
struct FresnelConductor {
|
|
FresnelThinFilm thin_film;
|
|
complex<Spectrum> ior;
|
|
};
|
|
|
|
struct FresnelGeneralizedSchlick {
|
|
FresnelThinFilm thin_film;
|
|
|
|
FresnelCoeff 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;
|
|
};
|
|
|
|
struct FresnelF82Tint {
|
|
FresnelThinFilm thin_film;
|
|
|
|
/* Perpendicular reflectivity. */
|
|
Spectrum f0;
|
|
/* Precomputed (1-cos)^6 factor for edge tint. */
|
|
Spectrum b;
|
|
};
|
|
|
|
struct MicrofacetBsdf {
|
|
SHADER_CLOSURE_BASE;
|
|
|
|
float alpha_x, alpha_y, ior;
|
|
|
|
/* Used to account for missing energy due to the single-scattering microfacet model.
|
|
* This could be included in bsdf->weight as well, but there it would mess up the color
|
|
* channels.
|
|
* Note that this is currently only used by GGX. */
|
|
float energy_scale;
|
|
|
|
/* Fresnel model to apply, as well as the extra data for it.
|
|
* For NONE and DIELECTRIC, no extra storage is needed, so the pointer is nullptr for them. */
|
|
int fresnel_type;
|
|
ccl_private void *fresnel;
|
|
|
|
float3 T;
|
|
};
|
|
|
|
static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetBsdf), "MicrofacetBsdf is too large!");
|
|
|
|
struct Coat {
|
|
PackedSpectrum tint;
|
|
float ior;
|
|
packed_float3 N;
|
|
float roughness;
|
|
Spectrum weight;
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name GGX LUT Index Mapping Functions
|
|
*
|
|
* This section contains functions to map from GGX alphas, IOR, and mu to the indices of
|
|
* 2D and 3D LUTs (e.g., for multi-scatter GGX energy compensation or scattering albedos).
|
|
* \{ */
|
|
|
|
ccl_device_forceinline float alpha_to_roughness_index(const float alpha_x, const float alpha_y)
|
|
{
|
|
return sqrtf(sqrtf(alpha_x * alpha_y));
|
|
}
|
|
|
|
ccl_device_forceinline float ior_to_z_index(const float ior)
|
|
{
|
|
return sqrtf(fabsf((ior - 1.0f) / (ior + 1.0f)));
|
|
}
|
|
|
|
ccl_device_forceinline float mu_to_y_index(const float mu)
|
|
{
|
|
return safe_sqrtf(mu);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
ccl_device_inline void adjust_thin_film_ior_at_backface(ccl_private float &film_ior,
|
|
const float inv_bulk_ior)
|
|
{
|
|
/* The IOR configuration at the backface is
|
|
*
|
|
* bulk_ior
|
|
* ------------
|
|
* film_ior
|
|
* ------------
|
|
* 1
|
|
*
|
|
* this is equivalent to
|
|
*
|
|
* 1
|
|
* ------------
|
|
* film_ior / bulk_ior
|
|
* ------------
|
|
* 1 / bulk_ior
|
|
*
|
|
* Therefore, we divide `film_ior` by `bulk_ior` to have the correct configuration. */
|
|
|
|
film_ior *= inv_bulk_ior;
|
|
}
|
|
|
|
ccl_device_forceinline FresnelGeneralizedSchlick
|
|
generalized_schlick_setup(const float ior,
|
|
const bool reflective_caustics,
|
|
const bool refractive_caustics,
|
|
const Spectrum reflection_tint,
|
|
const Spectrum transmission_tint,
|
|
FresnelThinFilm thinfilm)
|
|
{
|
|
FresnelCoeff tint(reflective_caustics, refractive_caustics);
|
|
tint.transmittance *= transmission_tint;
|
|
return {/* .thin_film = */ thinfilm,
|
|
/* .tint = */ tint,
|
|
/* .f0 = */ F0_from_ior(ior) * reflection_tint,
|
|
/* .f90 = */ one_spectrum(),
|
|
/* .exponent = */ -ior};
|
|
}
|
|
|
|
/* Beckmann VNDF importance sampling algorithm from:
|
|
* Importance Sampling Microfacet-Based BSDFs using the Distribution of Visible Normals.
|
|
* Eric Heitz and Eugene d'Eon, EGSR 2014.
|
|
* https://hal.inria.fr/hal-00996995v2/document */
|
|
|
|
ccl_device_forceinline float3 microfacet_beckmann_sample_vndf(const float3 wi,
|
|
const float alpha_x,
|
|
const float alpha_y,
|
|
const float2 rand)
|
|
{
|
|
/* 1. stretch wi */
|
|
float3 wi_ = make_float3(alpha_x * wi.x, alpha_y * wi.y, wi.z);
|
|
wi_ = normalize(wi_);
|
|
|
|
/* 2. sample P22_{wi}(x_slope, y_slope, 1, 1) */
|
|
float2 slope;
|
|
float cos_phi_i = 1.0f;
|
|
float sin_phi_i = 0.0f;
|
|
|
|
if (wi_.z >= 0.99999f) {
|
|
/* Special case (normal incidence). */
|
|
const float r = sqrtf(-logf(rand.x));
|
|
const float phi = M_2PI_F * rand.y;
|
|
slope = polar_to_cartesian(r, phi);
|
|
}
|
|
else {
|
|
/* Precomputations. */
|
|
const float cos_theta_i = wi_.z;
|
|
const float sin_theta_i = sin_from_cos(cos_theta_i);
|
|
const float tan_theta_i = sin_theta_i / cos_theta_i;
|
|
const float cot_theta_i = 1.0f / tan_theta_i;
|
|
const float erf_a = fast_erff(cot_theta_i);
|
|
const float exp_a2 = expf(-cot_theta_i * cot_theta_i);
|
|
const float SQRT_PI_INV = 0.56418958354f;
|
|
|
|
const float invlen = 1.0f / sin_theta_i;
|
|
cos_phi_i = wi_.x * invlen;
|
|
sin_phi_i = wi_.y * invlen;
|
|
|
|
/* Based on paper from Wenzel Jakob
|
|
* An Improved Visible Normal Sampling Routine for the Beckmann Distribution
|
|
*
|
|
* http://www.mitsuba-renderer.org/~wenzel/files/visnormal.pdf
|
|
*
|
|
* Reformulation from OpenShadingLanguage which avoids using inverse
|
|
* trigonometric functions.
|
|
*/
|
|
|
|
/* Sample slope X.
|
|
*
|
|
* Compute a coarse approximation using the approximation:
|
|
* `exp(-ierf(x)^2) ~= 1 - x * x`
|
|
* `solve y = 1 + b + K * (1 - b * b)`
|
|
*/
|
|
const float K = tan_theta_i * SQRT_PI_INV;
|
|
const float y_approx = rand.x * (1.0f + erf_a + K * (1 - erf_a * erf_a));
|
|
const float y_exact = rand.x * (1.0f + erf_a + K * exp_a2);
|
|
const float b = K > 0 ? (0.5f - sqrtf(K * (K - y_approx + 1.0f) + 0.25f)) / K :
|
|
y_approx - 1.0f;
|
|
|
|
float inv_erf = fast_ierff(b);
|
|
float2 begin = make_float2(-1.0f, -y_exact);
|
|
float2 end = make_float2(erf_a, 1.0f + erf_a + K * exp_a2 - y_exact);
|
|
float2 current = make_float2(b, 1.0f + b + K * expf(-sqr(inv_erf)) - y_exact);
|
|
|
|
/* Find root in a monotonic interval using newton method, under given precision and maximal
|
|
* iterations. Falls back to bisection if newton step produces results outside of the valid
|
|
* interval. */
|
|
const float precision = 1e-6f;
|
|
const int max_iter = 3;
|
|
int iter = 0;
|
|
while (fabsf(current.y) > precision && iter++ < max_iter) {
|
|
if (signf(begin.y) == signf(current.y)) {
|
|
begin.x = current.x;
|
|
begin.y = current.y;
|
|
}
|
|
else {
|
|
end.x = current.x;
|
|
}
|
|
const float newton_x = current.x - current.y / (1.0f - inv_erf * tan_theta_i);
|
|
current.x = (newton_x >= begin.x && newton_x <= end.x) ? newton_x : 0.5f * (begin.x + end.x);
|
|
inv_erf = fast_ierff(current.x);
|
|
current.y = 1.0f + current.x + K * expf(-sqr(inv_erf)) - y_exact;
|
|
}
|
|
|
|
slope.x = inv_erf;
|
|
slope.y = fast_ierff(2.0f * rand.y - 1.0f);
|
|
}
|
|
|
|
/* 3. rotate */
|
|
slope = make_float2(cos_phi_i * slope.x - sin_phi_i * slope.y,
|
|
sin_phi_i * slope.x + cos_phi_i * slope.y);
|
|
|
|
/* 4. unstretch */
|
|
slope *= make_float2(alpha_x, alpha_y);
|
|
|
|
/* 5. compute normal */
|
|
return normalize(make_float3(-slope.x, -slope.y, 1.0f));
|
|
}
|
|
|
|
/* GGX VNDF importance sampling algorithm from:
|
|
* Sampling the GGX Distribution of Visible Normals.
|
|
* Eric Heitz, JCGT Vol. 7, No. 4, 2018.
|
|
* https://jcgt.org/published/0007/04/01/ */
|
|
ccl_device_forceinline float3 microfacet_ggx_sample_vndf(const float3 wi,
|
|
const float alpha_x,
|
|
const float alpha_y,
|
|
const float2 rand)
|
|
{
|
|
/* Section 3.2: Transforming the view direction to the hemisphere configuration. */
|
|
const float3 wi_ = normalize(make_float3(alpha_x * wi.x, alpha_y * wi.y, wi.z));
|
|
|
|
/* Section 4.1: Orthonormal basis. */
|
|
const float lensq = sqr(wi_.x) + sqr(wi_.y);
|
|
float3 T1;
|
|
float3 T2;
|
|
if (lensq > 1e-7f) {
|
|
T1 = make_float3(-wi_.y, wi_.x, 0.0f) * inversesqrtf(lensq);
|
|
T2 = cross(wi_, T1);
|
|
}
|
|
else {
|
|
/* Normal incidence, any basis is fine. */
|
|
T1 = make_float3(1.0f, 0.0f, 0.0f);
|
|
T2 = make_float3(0.0f, 1.0f, 0.0f);
|
|
}
|
|
|
|
/* Section 4.2: Parameterization of the projected area. */
|
|
float2 t = sample_uniform_disk(rand);
|
|
t.y = mix(safe_sqrtf(1.0f - sqr(t.x)), t.y, 0.5f * (1.0f + wi_.z));
|
|
|
|
/* Section 4.3: Reprojection onto hemisphere. */
|
|
const float3 H_ = to_global(disk_to_hemisphere(t), T1, T2, wi_);
|
|
|
|
/* Section 3.4: Transforming the normal back to the ellipsoid configuration. */
|
|
return normalize(make_float3(alpha_x * H_.x, alpha_y * H_.y, max(0.0f, H_.z)));
|
|
}
|
|
|
|
/* Calculate ior of potentially dispersive materials, using Cauchy empirical formula. Input ior is
|
|
* the ior at the Fraunhofer d spectral line, after applying backface flipping. */
|
|
ccl_device_inline float bsdf_glass_ior(ccl_private ShaderData *sd, float ior, const float inv_abbe)
|
|
{
|
|
#ifdef __SPECTRAL__
|
|
if (!(sd->shader_flag & SD_REQUIRES_WAVELENGTH) || (inv_abbe == 0.0f)) {
|
|
return ior;
|
|
}
|
|
|
|
const bool backfacing = sd->runtime_flag & SR_BACKFACING;
|
|
|
|
sd->runtime_flag |= SR_BSDF_HAS_DISPERSION;
|
|
ior = backfacing ? 1.0f / ior : ior;
|
|
|
|
/* Wavelengths of the Fraunhofer spectral lines in um. */
|
|
constexpr float lambda_d = 0.5876f;
|
|
constexpr float lambda_C = 0.6563f;
|
|
constexpr float lambda_F = 0.4861f;
|
|
|
|
constexpr float fac = 1.0f / (1.0f / (lambda_F * lambda_F) - 1.0f / (lambda_C * lambda_C));
|
|
constexpr float inv_lambda_d_sq = 1.0f / (lambda_d * lambda_d);
|
|
|
|
/* OpenPBR Surface specification v1.1.1, Eq. (56). */
|
|
const float B = (ior - 1.0f) * inv_abbe * fac;
|
|
const float A = ior - B * inv_lambda_d_sq;
|
|
|
|
const float wavelength = sample_wavelength(sd->rand_wavelength);
|
|
|
|
/* OpenPBR Surface specification v1.1.1, Eq. (55). */
|
|
ior = A + B / sqr(wavelength);
|
|
return backfacing ? 1.0f / ior : ior;
|
|
#else
|
|
(void)sd;
|
|
(void)inv_abbe;
|
|
return ior;
|
|
#endif
|
|
}
|
|
|
|
/* Computes Fresnel reflectance and transmittance of the Generalized Schlick Model. */
|
|
ccl_device_forceinline FresnelCoeff
|
|
generalized_schlick_fresnel(KernelGlobals kg,
|
|
const ccl_private FresnelGeneralizedSchlick *fresnel,
|
|
const float ior,
|
|
const float cos_theta_i,
|
|
ccl_private float *r_cos_theta_t)
|
|
{
|
|
Spectrum F;
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
/* Iridescence doesn't combine well with the general case. We only expose it through the
|
|
* Principled BSDF for now, so it's fine to not support custom exponents and F90. */
|
|
kernel_assert(fresnel->exponent < 0.0f);
|
|
kernel_assert(fresnel->f90 == one_spectrum());
|
|
/* One channel at a time to reduce GPU register pressure. */
|
|
FOREACH_SPECTRUM_CHANNEL (i) {
|
|
GET_SPECTRUM_CHANNEL(F, i) = fresnel_iridescence_channel<false>(
|
|
kg, i, 1.0f, fresnel->thin_film, ior, 0.0f, -1.0f, cos_theta_i, r_cos_theta_t);
|
|
}
|
|
/* Apply F0 scaling (here per-channel, since iridescence produces colored output).
|
|
* Note that the usual approach (as used below) cannot be used here, since F may be below
|
|
* F0_real. Therefore, use a different approach: Scale the result by (F0 / F0_real), with the
|
|
* strength of the scaling depending on how close F is to F0_real.
|
|
* There isn't one single "correct" way to do this, it's just for artistic control anyways.
|
|
*/
|
|
const float F0_real = F0_from_ior(ior);
|
|
if (F0_real > 1e-5f && !isequal(F, one_spectrum())) {
|
|
FOREACH_SPECTRUM_CHANNEL (i) {
|
|
const float s = saturatef(inverse_lerp(1.0f, F0_real, GET_SPECTRUM_CHANNEL(F, i)));
|
|
const float factor = GET_SPECTRUM_CHANNEL(fresnel->f0, i) / F0_real;
|
|
GET_SPECTRUM_CHANNEL(F, i) *= mix(1.0f, factor, s);
|
|
}
|
|
}
|
|
}
|
|
else if (fresnel->exponent < 0.0f) {
|
|
/* Special case: Use real Fresnel curve to determine the interpolation between F0 and F90.
|
|
* Used by Principled BSDF. */
|
|
const float F_real = fresnel_dielectric(cos_theta_i, ior, r_cos_theta_t);
|
|
const float F0_real = F0_from_ior(ior);
|
|
const float s = saturatef(inverse_lerp(F0_real, 1.0f, F_real));
|
|
F = mix(fresnel->f0, fresnel->f90, s);
|
|
}
|
|
else {
|
|
/* Regular case: Generalized Schlick term. */
|
|
const float cos_theta_t_sq = 1.0f - (1.0f - sqr(cos_theta_i)) / sqr(ior);
|
|
if (cos_theta_t_sq <= 0.0f) {
|
|
/* Total internal reflection */
|
|
return {fresnel->tint.reflectance, zero_spectrum()};
|
|
}
|
|
const float cos_theta_t = sqrtf(cos_theta_t_sq);
|
|
if (r_cos_theta_t) {
|
|
*r_cos_theta_t = cos_theta_t;
|
|
}
|
|
|
|
/* TODO(lukas): Is a special case for exponent==5 worth it? */
|
|
/* When going from a higher to a lower IOR, we must use the transmitted angle. */
|
|
const float fresnel_angle = (ior < 1.0f) ? cos_theta_t : cos_theta_i;
|
|
const float s = powf(1.0f - fresnel_angle, fresnel->exponent);
|
|
F = mix(fresnel->f0, fresnel->f90, s);
|
|
}
|
|
return fresnel->tint * F;
|
|
}
|
|
|
|
/* Computes the Fresnel reflectance and transmittance given the Microfacet BSDF and the cosine of
|
|
* the incoming angle `cos_theta_i`.
|
|
* Also returns the cosine of the angle between the normal and the refracted ray as `r_cos_theta_t`
|
|
* if provided. */
|
|
ccl_device_forceinline FresnelCoeff microfacet_fresnel(KernelGlobals kg,
|
|
const ccl_private MicrofacetBsdf *bsdf,
|
|
const float cos_theta_i,
|
|
ccl_private float *r_cos_theta_t)
|
|
{
|
|
/* Whether the closure has reflective or transmissive lobes. */
|
|
const bool has_reflection = !CLOSURE_IS_REFRACTION(bsdf->type);
|
|
const bool has_transmission = CLOSURE_IS_GLASS(bsdf->type) || !has_reflection;
|
|
|
|
FresnelCoeff coeff(has_reflection, has_transmission);
|
|
|
|
if (bsdf->fresnel_type == MicrofacetFresnel::CONDUCTOR) {
|
|
if (!has_reflection) {
|
|
return {zero_spectrum(), zero_spectrum()};
|
|
}
|
|
ccl_private FresnelConductor *fresnel = (ccl_private FresnelConductor *)bsdf->fresnel;
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
/* One channel at a time to reduce GPU register pressure. */
|
|
FOREACH_SPECTRUM_CHANNEL (i) {
|
|
GET_SPECTRUM_CHANNEL(coeff.reflectance, i) = fresnel_iridescence_channel<true>(
|
|
kg,
|
|
i,
|
|
1.0f,
|
|
fresnel->thin_film,
|
|
GET_SPECTRUM_CHANNEL(fresnel->ior.re, i),
|
|
GET_SPECTRUM_CHANNEL(fresnel->ior.im, i),
|
|
-1.0f,
|
|
cos_theta_i,
|
|
r_cos_theta_t);
|
|
}
|
|
}
|
|
else {
|
|
coeff.reflectance = fresnel_conductor(cos_theta_i, fresnel->ior);
|
|
}
|
|
}
|
|
else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) {
|
|
if (!has_reflection) {
|
|
return {zero_spectrum(), zero_spectrum()};
|
|
}
|
|
/* F82-Tint model, described in "Novel aspects of the Adobe Standard Material" by Kutz et al.
|
|
* Essentially, this is the usual Schlick Fresnel with an additional cosI*(1-cosI)^6
|
|
* term which modulates the reflectivity around acos(1/7) degrees (ca. 82°). */
|
|
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel;
|
|
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
/* Estimate n and k by reinterpreting F0 and F82 as r and g from "Artist Friendly Metallic
|
|
* Fresnel" by Ole Gulbrandsen.
|
|
* One channel at a time to reduce GPU register pressure. */
|
|
FOREACH_SPECTRUM_CHANNEL (i) {
|
|
const float f0 = GET_SPECTRUM_CHANNEL(fresnel->f0, i);
|
|
const float r = min(f0, 0.999f);
|
|
const float g = fresnel_f82(1.0f / 7.0f, f0, GET_SPECTRUM_CHANNEL(fresnel->b, i));
|
|
|
|
const float sqrt_r = sqrtf(r);
|
|
const float n = mix((1.0f + sqrt_r) / (1.0f - sqrt_r), (1.0f - r) / (1.0f + r), g);
|
|
const float k = safe_sqrtf((r * sqr(n + 1.0f) - sqr(n - 1.0f)) / (1.0f - r));
|
|
|
|
GET_SPECTRUM_CHANNEL(coeff.reflectance, i) = fresnel_iridescence_channel<true>(
|
|
kg, i, 1.0f, fresnel->thin_film, n, k, g, cos_theta_i, r_cos_theta_t);
|
|
}
|
|
}
|
|
else {
|
|
coeff.reflectance = fresnel_f82(cos_theta_i, fresnel->f0, fresnel->b);
|
|
}
|
|
}
|
|
else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) {
|
|
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
|
|
bsdf->fresnel;
|
|
coeff *= generalized_schlick_fresnel(kg, fresnel, bsdf->ior, cos_theta_i, r_cos_theta_t);
|
|
}
|
|
else if (bsdf->fresnel_type == MicrofacetFresnel::NONE) {
|
|
/* No Fresnel used, this is either purely reflective or purely refractive closure. */
|
|
/* Exclude total internal reflection. */
|
|
if (has_transmission && fresnel_dielectric(cos_theta_i, bsdf->ior, r_cos_theta_t) == 1.0f) {
|
|
coeff.transmittance = zero_spectrum();
|
|
}
|
|
}
|
|
else if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC_TINT) {
|
|
Spectrum F;
|
|
ccl_private FresnelDielectricTint *fresnel = (ccl_private FresnelDielectricTint *)
|
|
bsdf->fresnel;
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
FOREACH_SPECTRUM_CHANNEL (i) {
|
|
GET_SPECTRUM_CHANNEL(F, i) = fresnel_iridescence_channel<false>(
|
|
kg, i, 1.0f, fresnel->thin_film, bsdf->ior, 0.0f, -1.0f, cos_theta_i, r_cos_theta_t);
|
|
}
|
|
}
|
|
else {
|
|
F = make_spectrum(fresnel_dielectric(cos_theta_i, bsdf->ior, r_cos_theta_t));
|
|
}
|
|
coeff *= F;
|
|
coeff *= ((ccl_private FresnelDielectricTint *)bsdf->fresnel)->tint;
|
|
}
|
|
else {
|
|
/* Transmissive dielectric. */
|
|
coeff *= fresnel_dielectric(cos_theta_i, bsdf->ior, r_cos_theta_t);
|
|
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC);
|
|
}
|
|
|
|
return coeff;
|
|
}
|
|
|
|
ccl_device_inline void microfacet_ggx_preserve_energy(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi,
|
|
const Spectrum Fss)
|
|
{
|
|
const float mu = dot(wi, bsdf->N);
|
|
const float y = mu_to_y_index(mu);
|
|
const float rough = alpha_to_roughness_index(bsdf->alpha_x, bsdf->alpha_y);
|
|
|
|
float E;
|
|
float E_avg;
|
|
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_ID ||
|
|
bsdf->type == CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID)
|
|
{
|
|
E = lookup_table_read_2D(
|
|
kg, rough, y, kernel_data.tables.ggx_E, GGX_E_RES_ROUGH, GGX_E_RES_MU);
|
|
E_avg = lookup_table_read(kg, rough, kernel_data.tables.ggx_Eavg, GGX_E_RES_ROUGH);
|
|
}
|
|
else if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID) {
|
|
int ofs = kernel_data.tables.ggx_glass_E;
|
|
int avg_ofs = kernel_data.tables.ggx_glass_Eavg;
|
|
float ior = bsdf->ior;
|
|
if (ior < 1.0f) {
|
|
ior = 1.0f / ior;
|
|
ofs = kernel_data.tables.ggx_glass_inv_E;
|
|
avg_ofs = kernel_data.tables.ggx_glass_inv_Eavg;
|
|
}
|
|
const float z = ior_to_z_index(ior);
|
|
E = lookup_table_read_3D(
|
|
kg, rough, y, z, ofs, GGX_GLASS_E_RES_ROUGH, GGX_GLASS_E_RES_MU, GGX_GLASS_E_RES_IOR);
|
|
E_avg = lookup_table_read_2D(
|
|
kg, rough, z, avg_ofs, GGX_GLASS_E_RES_ROUGH, GGX_GLASS_E_RES_IOR);
|
|
}
|
|
else {
|
|
kernel_assert(false);
|
|
E = 1.0f;
|
|
E_avg = 1.0f;
|
|
}
|
|
|
|
const float missing_factor = ((1.0f - E) / E);
|
|
bsdf->energy_scale = 1.0f + missing_factor;
|
|
|
|
/* Check if we need to account for extra darkening/saturation due to multi-bounce Fresnel. */
|
|
if (!isequal(Fss, one_spectrum())) {
|
|
/* Fms here is based on the appendix of "Revisiting Physically Based Shading at Imageworks"
|
|
* by Christopher Kulla and Alejandro Conty,
|
|
* with one Fss cancelled out since this is just a multiplier on top of
|
|
* the single-scattering BSDF, which already contains one bounce of Fresnel. */
|
|
const Spectrum Fms = Fss * E_avg / (one_spectrum() - Fss * (1.0f - E_avg));
|
|
/* Since we already include the energy compensation in bsdf->energy_scale,
|
|
* this term is what's needed to make the full BSDF * weight * energy_scale
|
|
* computation work out to the correct value. */
|
|
const Spectrum darkening = (one_spectrum() + Fms * missing_factor) / bsdf->energy_scale;
|
|
bsdf->weight *= darkening;
|
|
bsdf->sample_weight *= average(darkening);
|
|
}
|
|
}
|
|
|
|
/* This function estimates the albedo of the BSDF (NOT including the bsdf->weight) as caused by
|
|
* the applied Fresnel model for the given view direction.
|
|
* The base microfacet model is assumed to have an albedo of 1 (we have the energy preservation
|
|
* code for that), but e.g. a reflection-only closure with Fresnel applied can end up having
|
|
* a very low overall albedo.
|
|
* This is used to adjust the sample weight, as well as for the Diff/Gloss/Trans Color pass
|
|
* and the Denoising Albedo pass.
|
|
*
|
|
* TODO: The Schlick LUT seems to assume energy preservation, which is not true for GGX. if
|
|
* energy-preserving then transmission should just be `1 - reflection`. For dielectric we could
|
|
* probably split the LUT for multiGGX if smooth assumption is not good enough. */
|
|
ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg,
|
|
const float3 wi,
|
|
const ccl_private MicrofacetBsdf *bsdf,
|
|
const bool eval_reflection,
|
|
const bool eval_transmission)
|
|
{
|
|
const float cos_NI = dot(wi, bsdf->N);
|
|
FresnelCoeff coeff(eval_reflection, eval_transmission);
|
|
|
|
/* Use lookup tables for generalized Schlick reflection, otherwise assume smooth surface.
|
|
* Note that even if the reflectance or transmittance would evaluate to zero for
|
|
* #microfacet_fresnel, we still compute it because this contributes to albedo passes. */
|
|
if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) {
|
|
ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *)
|
|
bsdf->fresnel;
|
|
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
/* Precomputing LUTs for thin-film iridescence isn't viable, so fall back to the specular
|
|
* reflection approximation from the microfacet_fresnel call above in that case. */
|
|
}
|
|
else {
|
|
const float rough = alpha_to_roughness_index(bsdf->alpha_x, bsdf->alpha_y);
|
|
float s;
|
|
if (fresnel->exponent < 0.0f) {
|
|
const float z = ior_to_z_index(bsdf->ior);
|
|
const float y = mu_to_y_index(cos_NI);
|
|
s = lookup_table_read_3D(kg,
|
|
rough,
|
|
y,
|
|
z,
|
|
kernel_data.tables.ggx_gen_schlick_ior_s,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_ROUGH,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_MU,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_IOR);
|
|
}
|
|
else {
|
|
const float z = 1.0f / (0.2f * fresnel->exponent + 1.0f);
|
|
const float y = mu_to_y_index(cos_NI);
|
|
s = lookup_table_read_3D(kg,
|
|
rough,
|
|
y,
|
|
z,
|
|
kernel_data.tables.ggx_gen_schlick_s,
|
|
GGX_GEN_SCHLICK_S_RES_ROUGH,
|
|
GGX_GEN_SCHLICK_S_RES_MU,
|
|
GGX_GEN_SCHLICK_S_RES_IOR);
|
|
}
|
|
coeff *= fresnel->tint * mix(fresnel->f0, fresnel->f90, s);
|
|
return coeff.sum();
|
|
}
|
|
}
|
|
else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) {
|
|
ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel;
|
|
|
|
if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
/* Precomputing LUTs for thin-film iridescence isn't viable, so fall back to the specular
|
|
* reflection approximation from the microfacet_fresnel call above in that case. */
|
|
}
|
|
else {
|
|
const float rough = alpha_to_roughness_index(bsdf->alpha_x, bsdf->alpha_y);
|
|
const float y = mu_to_y_index(cos_NI);
|
|
const float s = lookup_table_read_3D(kg,
|
|
rough,
|
|
y,
|
|
0.5f,
|
|
kernel_data.tables.ggx_gen_schlick_s,
|
|
GGX_GEN_SCHLICK_S_RES_ROUGH,
|
|
GGX_GEN_SCHLICK_S_RES_MU,
|
|
GGX_GEN_SCHLICK_S_RES_IOR);
|
|
/* TODO: Precompute B factor term and account for it here. */
|
|
const Spectrum reflectance = mix(fresnel->f0, one_spectrum(), s) * float(eval_reflection);
|
|
return reflectance;
|
|
}
|
|
}
|
|
if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC_TINT &&
|
|
((ccl_private FresnelDielectricTint *)bsdf->fresnel)->thin_film.thickness >
|
|
THINFILM_THICKNESS_CUTOFF)
|
|
{
|
|
/* Precomputing LUTs for thin-film iridescence isn't viable, so fall back to the specular
|
|
* reflection approximation from the microfacet_fresnel call above in that case. */
|
|
}
|
|
else if ((bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC ||
|
|
bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC_TINT) &&
|
|
bsdf->ior > 1.0f)
|
|
{
|
|
/* We can re-use the ggx_gen_schlick_ior_s table here, since it's already precomputed for our
|
|
* exponent<0 corner case where we use the real dielectric Fresnel. */
|
|
const float rough = alpha_to_roughness_index(bsdf->alpha_x, bsdf->alpha_y);
|
|
const float z = ior_to_z_index(bsdf->ior);
|
|
const float y = mu_to_y_index(cos_NI);
|
|
const float s = lookup_table_read_3D(kg,
|
|
rough,
|
|
y,
|
|
z,
|
|
kernel_data.tables.ggx_gen_schlick_ior_s,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_ROUGH,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_MU,
|
|
GGX_GEN_SCHLICK_IOR_S_RES_IOR);
|
|
|
|
#if defined(__KERNEL_HIP__)
|
|
/* Temporary workaround for a HIP compiler bug under Windows in combination with AMD 6800 XT
|
|
* GPUs */
|
|
const float F = mix(F0_from_ior(bsdf->ior), 1.0f, s);
|
|
coeff.reflectance = make_spectrum(F) * float(eval_reflection);
|
|
coeff.transmittance = make_spectrum(1.0f - F) * float(eval_transmission);
|
|
#else
|
|
coeff *= mix(F0_from_ior(bsdf->ior), 1.0f, s);
|
|
#endif
|
|
|
|
/* Tinted dielectric. */
|
|
if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC_TINT) {
|
|
coeff *= ((ccl_private FresnelDielectricTint *)bsdf->fresnel)->tint;
|
|
return coeff.sum();
|
|
}
|
|
|
|
/* Untinted dielectric. */
|
|
if (CLOSURE_IS_GLASS(bsdf->type)) {
|
|
return coeff.sum();
|
|
}
|
|
return coeff.reflectance;
|
|
}
|
|
else if (bsdf->type == CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID) {
|
|
return eval_transmission ? one_spectrum() : zero_spectrum();
|
|
}
|
|
|
|
/* Simpler case without the lookup tables. */
|
|
coeff *= microfacet_fresnel(kg, bsdf, cos_NI, nullptr);
|
|
|
|
return coeff.sum();
|
|
}
|
|
|
|
/* Smith shadowing-masking term, here in the non-separable form.
|
|
* For details, see:
|
|
* Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs.
|
|
* Eric Heitz, JCGT Vol. 3, No. 2, 2014.
|
|
* https://jcgt.org/published/0003/02/03/ */
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_lambda_from_sqr_alpha_tan_n(const float sqr_alpha_tan_n)
|
|
{
|
|
if (m_type == MicrofacetType::GGX) {
|
|
/* Equation 72. */
|
|
return 0.5f * (sqrtf(1.0f + sqr_alpha_tan_n) - 1.0f);
|
|
}
|
|
kernel_assert(m_type == MicrofacetType::BECKMANN);
|
|
/* Approximation from below Equation 69. */
|
|
if (sqr_alpha_tan_n < 0.39f) {
|
|
/* Equivalent to a >= 1.6f, but also handles sqr_alpha_tan_n == 0.0f cleanly. */
|
|
return 0.0f;
|
|
}
|
|
|
|
const float a = inversesqrtf(sqr_alpha_tan_n);
|
|
return ((0.396f * a - 1.259f) * a + 1.0f) / ((2.181f * a + 3.535f) * a);
|
|
}
|
|
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_lambda(const float alpha2, const float cos_N)
|
|
{
|
|
return bsdf_lambda_from_sqr_alpha_tan_n<m_type>(alpha2 * fmaxf(1.0f / sqr(cos_N) - 1.0f, 0.0f));
|
|
}
|
|
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_aniso_lambda(const float alpha_x, const float alpha_y, const float3 V)
|
|
{
|
|
const float sqr_alpha_tan_n = (sqr(alpha_x * V.x) + sqr(alpha_y * V.y)) / sqr(V.z);
|
|
return bsdf_lambda_from_sqr_alpha_tan_n<m_type>(sqr_alpha_tan_n);
|
|
}
|
|
|
|
/* Mono-directional shadowing-masking term. */
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_G(const float alpha2, const float cos_N)
|
|
{
|
|
return 1.0f / (1.0f + bsdf_lambda<m_type>(alpha2, cos_N));
|
|
}
|
|
|
|
/* Combined shadowing-masking term. */
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_G(const float alpha2, const float cos_NI, const float cos_NO)
|
|
{
|
|
return 1.0f / (1.0f + bsdf_lambda<m_type>(alpha2, cos_NI) + bsdf_lambda<m_type>(alpha2, cos_NO));
|
|
}
|
|
|
|
/* Normal distribution function. */
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_D(const float alpha2, const float cos_NH)
|
|
{
|
|
const float cos_NH2 = min(sqr(cos_NH), 1.0f);
|
|
const float one_minus_cos_NH2 = 1.0f - cos_NH2;
|
|
|
|
if (m_type == MicrofacetType::BECKMANN) {
|
|
return 1.0f / (expf(one_minus_cos_NH2 / (cos_NH2 * alpha2)) * M_PI_F * alpha2 * sqr(cos_NH2));
|
|
}
|
|
kernel_assert(m_type == MicrofacetType::GGX);
|
|
return alpha2 / (M_PI_F * sqr(one_minus_cos_NH2 + alpha2 * cos_NH2));
|
|
}
|
|
|
|
template<MicrofacetType m_type>
|
|
ccl_device_inline float bsdf_aniso_D(const float alpha_x, const float alpha_y, float3 H)
|
|
{
|
|
H /= make_float3(alpha_x, alpha_y, 1.0f);
|
|
|
|
const float cos_NH2 = sqr(H.z);
|
|
const float alpha2 = alpha_x * alpha_y;
|
|
|
|
if (m_type == MicrofacetType::BECKMANN) {
|
|
return expf(-(sqr(H.x) + sqr(H.y)) / cos_NH2) / (M_PI_F * alpha2 * sqr(cos_NH2));
|
|
}
|
|
kernel_assert(m_type == MicrofacetType::GGX);
|
|
return M_1_PI_F / (alpha2 * sqr(len_squared(H)));
|
|
}
|
|
|
|
/* Below certain roughness threshold we can treat closures as perfectly specular. */
|
|
ccl_device_forceinline bool roughness_is_almost_specular(const float alpha_x, const float alpha_y)
|
|
{
|
|
return (alpha_x * alpha_y) <= 2e-10f;
|
|
}
|
|
|
|
/* A specular BSDF has no eval. */
|
|
ccl_device_forceinline int bsdf_microfacet_eval_flag(const ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
return roughness_is_almost_specular(bsdf->alpha_x, bsdf->alpha_y) ? 0 : SR_BSDF_HAS_EVAL;
|
|
}
|
|
|
|
template<MicrofacetType m_type>
|
|
ccl_device Spectrum bsdf_microfacet_eval(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 wi,
|
|
const float3 wo,
|
|
ccl_private float *pdf)
|
|
{
|
|
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
|
|
|
/* Whether the closure has reflective or transmissive lobes. */
|
|
const bool has_reflection = !CLOSURE_IS_REFRACTION(bsdf->type);
|
|
const bool has_transmission = CLOSURE_IS_GLASS(bsdf->type) || !has_reflection;
|
|
|
|
const float3 N = bsdf->N;
|
|
const float cos_NI = dot(N, wi);
|
|
const float cos_NO = dot(N, wo);
|
|
|
|
const float alpha_x = bsdf->alpha_x;
|
|
const float alpha_y = bsdf->alpha_y;
|
|
|
|
const bool is_transmission = (cos_NO < 0.0f);
|
|
|
|
/* Check whether the pair of directions is valid for evaluation:
|
|
* - Incoming direction has to be in the upper hemisphere (Cycles convention)
|
|
* - Specular cases can't be evaluated, only sampled.
|
|
* - Purely reflective closures can't have refraction.
|
|
* - Purely refractive closures can't have reflection.
|
|
*/
|
|
if ((cos_NI <= 0) || !bsdf_microfacet_eval_flag(bsdf) ||
|
|
(is_transmission && !has_transmission) || (!is_transmission && !has_reflection))
|
|
{
|
|
return zero_spectrum();
|
|
}
|
|
|
|
/* Compute half vector. */
|
|
/* TODO: deal with the case when `bsdf->ior` is close to one. */
|
|
/* TODO: check if the refraction configuration is valid. See `btdf_ggx()` in
|
|
* `eevee_bxdf_lib.glsl`. */
|
|
float3 H = is_transmission ? -(bsdf->ior * wo + wi) : (wi + wo);
|
|
const float inv_len_H = safe_divide(1.0f, len(H));
|
|
H *= inv_len_H;
|
|
|
|
/* Compute Fresnel coefficients. */
|
|
const float cos_HI = dot(H, wi);
|
|
const FresnelCoeff coeff = microfacet_fresnel(kg, bsdf, cos_HI, nullptr);
|
|
|
|
if (iszero(coeff)) {
|
|
return zero_spectrum();
|
|
}
|
|
|
|
const float cos_NH = dot(N, H);
|
|
float D;
|
|
float lambdaI;
|
|
float lambdaO;
|
|
|
|
if (alpha_x == alpha_y) {
|
|
/* Isotropic. */
|
|
const float alpha2 = alpha_x * alpha_y;
|
|
D = bsdf_D<m_type>(alpha2, cos_NH);
|
|
lambdaI = bsdf_lambda<m_type>(alpha2, cos_NI);
|
|
lambdaO = bsdf_lambda<m_type>(alpha2, cos_NO);
|
|
}
|
|
else {
|
|
/* Anisotropic. */
|
|
float3 X;
|
|
float3 Y;
|
|
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
|
|
|
|
const float3 local_H = make_float3(dot(X, H), dot(Y, H), cos_NH);
|
|
const float3 local_I = make_float3(dot(X, wi), dot(Y, wi), cos_NI);
|
|
const float3 local_O = make_float3(dot(X, wo), dot(Y, wo), cos_NO);
|
|
|
|
D = bsdf_aniso_D<m_type>(alpha_x, alpha_y, local_H);
|
|
|
|
lambdaI = bsdf_aniso_lambda<m_type>(alpha_x, alpha_y, local_I);
|
|
lambdaO = bsdf_aniso_lambda<m_type>(alpha_x, alpha_y, local_O);
|
|
}
|
|
|
|
const float common = D / cos_NI *
|
|
(is_transmission ? sqr(bsdf->ior * inv_len_H) * fabsf(cos_HI * dot(H, wo)) :
|
|
0.25f);
|
|
|
|
const float pdf_reflect = average(coeff.reflectance) / average(coeff.sum());
|
|
const float lobe_pdf = is_transmission ? 1.0f - pdf_reflect : pdf_reflect;
|
|
|
|
*pdf = common * lobe_pdf / (1.0f + lambdaI);
|
|
return (is_transmission ? coeff.transmittance : coeff.reflectance) * common /
|
|
(1.0f + lambdaO + lambdaI);
|
|
}
|
|
|
|
template<MicrofacetType m_type>
|
|
ccl_device int bsdf_microfacet_sample(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 Ng,
|
|
const float3 wi,
|
|
const float3 rand,
|
|
ccl_private Spectrum *eval,
|
|
ccl_private float3 *wo,
|
|
ccl_private float *pdf,
|
|
ccl_private float2 *sampled_roughness,
|
|
ccl_private float *eta)
|
|
{
|
|
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
|
|
|
const float3 N = bsdf->N;
|
|
const float cos_NI = dot(N, wi);
|
|
if (cos_NI <= 0) {
|
|
/* Incident angle from the lower hemisphere is invalid. */
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
const float m_eta = bsdf->ior;
|
|
const float m_inv_eta = safe_divide(1.0f, bsdf->ior);
|
|
const float alpha_x = bsdf->alpha_x;
|
|
const float alpha_y = bsdf->alpha_y;
|
|
bool m_singular = roughness_is_almost_specular(alpha_x, alpha_y);
|
|
|
|
/* Half vector. */
|
|
float3 H;
|
|
/* Needed for anisotropic microfacets later. */
|
|
float3 local_H, local_I, X, Y;
|
|
if (m_singular) {
|
|
H = N;
|
|
}
|
|
else {
|
|
if (alpha_x == alpha_y) {
|
|
make_orthonormals(N, &X, &Y);
|
|
}
|
|
else {
|
|
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
|
|
}
|
|
|
|
/* Importance sampling with distribution of visible normals. Vectors are transformed to local
|
|
* space before and after sampling. */
|
|
local_I = make_float3(dot(X, wi), dot(Y, wi), cos_NI);
|
|
if (m_type == MicrofacetType::GGX) {
|
|
local_H = microfacet_ggx_sample_vndf(local_I, alpha_x, alpha_y, make_float2(rand));
|
|
}
|
|
else {
|
|
/* m_type == MicrofacetType::BECKMANN */
|
|
local_H = microfacet_beckmann_sample_vndf(local_I, alpha_x, alpha_y, make_float2(rand));
|
|
}
|
|
|
|
H = to_global(local_H, X, Y, N);
|
|
}
|
|
const float cos_HI = dot(H, wi);
|
|
|
|
/* The angle between the half vector and the refracted ray. Not used when sampling reflection. */
|
|
float cos_HO;
|
|
/* Compute Fresnel coefficients. */
|
|
const FresnelCoeff coeff = microfacet_fresnel(kg, bsdf, cos_HI, &cos_HO);
|
|
|
|
if (iszero(coeff)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
/* Decide between refraction and reflection based on the energy. */
|
|
const float pdf_reflect = average(coeff.reflectance) / average(coeff.sum());
|
|
const bool do_refract = (rand.z >= pdf_reflect);
|
|
|
|
/* Compute actual reflected or refracted direction. */
|
|
*wo = do_refract ? refract_angle(wi, H, cos_HO, m_inv_eta) : 2.0f * cos_HI * H - wi;
|
|
|
|
/* Ensure that the sampled direction lies in the correct hemisphere.
|
|
* Note that the check against Ng is only performed in the sampling code, not the evaluation.
|
|
* This is technically inconsistent, but required in order to avoid shadow terminator artifacts
|
|
* on smooth geometry (which we'd get if we checked Ng in evaluation) while ensuring that
|
|
* sampling doesn't return supposed reflection rays going into the geometry and vice versa.
|
|
* The same is done for other closures as well. */
|
|
const float cos_NO = dot(N, *wo);
|
|
const float cos_NgO = dot(Ng, *wo);
|
|
if ((cos_NgO < 0) != do_refract || (cos_NO < 0) != do_refract) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
if (do_refract) {
|
|
*eval = coeff.transmittance;
|
|
*pdf = 1.0f - pdf_reflect;
|
|
/* If the IOR is close enough to 1.0, just treat the interaction as specular. */
|
|
m_singular = m_singular || (fabsf(m_eta - 1.0f) < 1e-4f);
|
|
}
|
|
else {
|
|
*eval = coeff.reflectance;
|
|
*pdf = pdf_reflect;
|
|
}
|
|
|
|
if (m_singular) {
|
|
/* Some high number for MIS. */
|
|
*pdf *= 1e6f;
|
|
*eval *= 1e6f;
|
|
}
|
|
else {
|
|
float D;
|
|
float lambdaI;
|
|
float lambdaO;
|
|
|
|
if (alpha_x == alpha_y) {
|
|
/* Isotropic. */
|
|
const float alpha2 = alpha_x * alpha_y;
|
|
const float cos_NH = local_H.z;
|
|
const float cos_NO = dot(N, *wo);
|
|
|
|
D = bsdf_D<m_type>(alpha2, cos_NH);
|
|
lambdaO = bsdf_lambda<m_type>(alpha2, cos_NO);
|
|
lambdaI = bsdf_lambda<m_type>(alpha2, cos_NI);
|
|
}
|
|
else {
|
|
/* Anisotropic. */
|
|
const float3 local_O = make_float3(dot(X, *wo), dot(Y, *wo), cos_NO);
|
|
|
|
D = bsdf_aniso_D<m_type>(alpha_x, alpha_y, local_H);
|
|
|
|
lambdaO = bsdf_aniso_lambda<m_type>(alpha_x, alpha_y, local_O);
|
|
lambdaI = bsdf_aniso_lambda<m_type>(alpha_x, alpha_y, local_I);
|
|
}
|
|
|
|
const float common = D / cos_NI *
|
|
(do_refract ? fabsf(cos_HI * cos_HO) / sqr(cos_HO + cos_HI * m_inv_eta) :
|
|
0.25f);
|
|
|
|
*pdf *= common / (1.0f + lambdaI);
|
|
*eval *= common / (1.0f + lambdaI + lambdaO);
|
|
}
|
|
|
|
*sampled_roughness = make_float2(alpha_x, alpha_y);
|
|
*eta = do_refract ? m_eta : 1.0f;
|
|
|
|
return (do_refract ? LABEL_TRANSMIT : LABEL_REFLECT) |
|
|
(m_singular ? LABEL_SINGULAR : LABEL_GLOSSY);
|
|
}
|
|
|
|
/* 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_conductor(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi,
|
|
ccl_private FresnelConductor *fresnel,
|
|
const bool preserve_energy)
|
|
{
|
|
bsdf->fresnel_type = MicrofacetFresnel::CONDUCTOR;
|
|
bsdf->fresnel = fresnel;
|
|
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, wi, bsdf, true, true));
|
|
|
|
if (preserve_energy) {
|
|
microfacet_ggx_preserve_energy(kg, bsdf, wi, fresnel_conductor_Fss(fresnel->ior));
|
|
}
|
|
}
|
|
|
|
ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
|
|
KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi,
|
|
ccl_private FresnelGeneralizedSchlick *fresnel,
|
|
const bool preserve_energy)
|
|
{
|
|
fresnel->f0 = saturate(fresnel->f0);
|
|
bsdf->fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
|
|
bsdf->fresnel = fresnel;
|
|
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, wi, bsdf, true, true));
|
|
|
|
if (preserve_energy) {
|
|
Spectrum Fss = one_spectrum();
|
|
/* Multi-bounce Fresnel is only supported for reflective lobes here. */
|
|
if (is_zero(fresnel->tint.transmittance)) {
|
|
float s;
|
|
if (fresnel->exponent < 0.0f) {
|
|
const float F0 = F0_from_ior(bsdf->ior);
|
|
const float Fss = fresnel_dielectric_Fss(bsdf->ior);
|
|
s = saturatef(inverse_lerp(F0, 1.0f, Fss));
|
|
}
|
|
else {
|
|
/* Integral of 2*cosI * (1 - cosI)^exponent over 0...1. */
|
|
s = 2.0f / ((fresnel->exponent + 3.0f) * fresnel->exponent + 2.0f);
|
|
}
|
|
/* Due to the linearity of the generalized model, this ends up working. */
|
|
Fss = fresnel->tint.reflectance * mix(fresnel->f0, fresnel->f90, s);
|
|
}
|
|
else {
|
|
/* For transmissive BSDFs, assume that the transmissive tint makes up most of the overall
|
|
* color. */
|
|
Fss = fresnel->tint.transmittance;
|
|
}
|
|
|
|
microfacet_ggx_preserve_energy(kg, bsdf, wi, Fss);
|
|
}
|
|
}
|
|
|
|
ccl_device void bsdf_microfacet_setup_fresnel_f82_tint(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi,
|
|
ccl_private FresnelF82Tint *fresnel,
|
|
const Spectrum f82_tint,
|
|
const bool preserve_energy)
|
|
{
|
|
if (isequal(f82_tint, one_spectrum())) {
|
|
fresnel->b = zero_spectrum();
|
|
}
|
|
else {
|
|
fresnel->b = fresnel_f82tint_B(fresnel->f0, f82_tint);
|
|
}
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::F82_TINT;
|
|
bsdf->fresnel = fresnel;
|
|
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, wi, bsdf, true, true));
|
|
|
|
if (preserve_energy) {
|
|
microfacet_ggx_preserve_energy(kg, bsdf, wi, fresnel_f82_Fss(fresnel->f0, fresnel->b));
|
|
}
|
|
}
|
|
|
|
ccl_device void bsdf_microfacet_setup_fresnel_constant(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi,
|
|
const Spectrum color)
|
|
{
|
|
/* Constant Fresnel is a special case - the color is already baked into the closure's
|
|
* weight, so we just need to perform the energy preservation. */
|
|
kernel_assert(bsdf->fresnel_type == MicrofacetFresnel::NONE ||
|
|
bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC);
|
|
|
|
microfacet_ggx_preserve_energy(kg, bsdf, wi, color);
|
|
}
|
|
|
|
ccl_device void bsdf_microfacet_setup_fresnel_dielectric(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
const float3 wi)
|
|
{
|
|
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC;
|
|
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, wi, bsdf, true, true));
|
|
|
|
const float Fss = fresnel_dielectric_Fss(bsdf->ior);
|
|
microfacet_ggx_preserve_energy(kg, bsdf, wi, make_spectrum(Fss));
|
|
}
|
|
|
|
/* GGX microfacet with Smith shadow-masking from:
|
|
*
|
|
* Microfacet Models for Refraction through Rough Surfaces
|
|
* B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007
|
|
*
|
|
* Anisotropic from:
|
|
*
|
|
* Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs
|
|
* E. Heitz, Research Report 2014
|
|
*/
|
|
|
|
ccl_device int bsdf_microfacet_ggx_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
|
bsdf->energy_scale = 1.0f;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ID;
|
|
|
|
return SR_BSDF | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
|
bsdf->energy_scale = 1.0f;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
|
|
|
|
return SR_BSDF | SR_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_ggx_glass_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC;
|
|
bsdf->energy_scale = 1.0f;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID;
|
|
|
|
return SR_BSDF | SR_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device void bsdf_microfacet_blur(ccl_private ShaderClosure *sc, const 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);
|
|
}
|
|
|
|
ccl_device Spectrum bsdf_microfacet_ggx_eval(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 wi,
|
|
const float3 wo,
|
|
ccl_private float *pdf)
|
|
{
|
|
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
|
return bsdf->energy_scale * bsdf_microfacet_eval<MicrofacetType::GGX>(kg, sc, wi, wo, pdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 Ng,
|
|
const float3 wi,
|
|
const float3 rand,
|
|
ccl_private Spectrum *eval,
|
|
ccl_private float3 *wo,
|
|
ccl_private float *pdf,
|
|
ccl_private float2 *sampled_roughness,
|
|
ccl_private float *eta)
|
|
{
|
|
const int label = bsdf_microfacet_sample<MicrofacetType::GGX>(
|
|
kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
|
|
*eval *= ((const ccl_private MicrofacetBsdf *)sc)->energy_scale;
|
|
return label;
|
|
}
|
|
|
|
/* Beckmann microfacet with Smith shadow-masking from:
|
|
*
|
|
* Microfacet Models for Refraction through Rough Surfaces
|
|
* B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007 */
|
|
|
|
ccl_device int bsdf_microfacet_beckmann_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID;
|
|
|
|
return SR_BSDF | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
|
|
|
|
return SR_BSDF | SR_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_beckmann_glass_setup(ccl_private MicrofacetBsdf *bsdf)
|
|
{
|
|
bsdf->alpha_x = saturatef(bsdf->alpha_x);
|
|
bsdf->alpha_y = saturatef(bsdf->alpha_y);
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC;
|
|
bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID;
|
|
|
|
return SR_BSDF | SR_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf);
|
|
}
|
|
|
|
ccl_device Spectrum bsdf_microfacet_beckmann_eval(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 wi,
|
|
const float3 wo,
|
|
ccl_private float *pdf)
|
|
{
|
|
return bsdf_microfacet_eval<MicrofacetType::BECKMANN>(kg, sc, wi, wo, pdf);
|
|
}
|
|
|
|
ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 Ng,
|
|
const float3 wi,
|
|
const float3 rand,
|
|
ccl_private Spectrum *eval,
|
|
ccl_private float3 *wo,
|
|
ccl_private float *pdf,
|
|
ccl_private float2 *sampled_roughness,
|
|
ccl_private float *eta)
|
|
{
|
|
return bsdf_microfacet_sample<MicrofacetType::BECKMANN>(
|
|
kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta);
|
|
}
|
|
|
|
ccl_device void bsdf_dielectric_tint_setup(KernelGlobals kg,
|
|
ccl_private MicrofacetBsdf *bsdf,
|
|
ccl_private ShaderData *sd,
|
|
ccl_private FresnelDielectricTint *fresnel,
|
|
const bool is_beckmann = false,
|
|
const bool preserve_energy = true)
|
|
{
|
|
const bool has_reflection = !is_zero(fresnel->tint.reflectance);
|
|
const bool has_transmission = !is_zero(fresnel->tint.transmittance);
|
|
|
|
if (has_reflection && has_transmission) {
|
|
sd->runtime_flag |= is_beckmann ? bsdf_microfacet_beckmann_glass_setup(bsdf) :
|
|
bsdf_microfacet_ggx_glass_setup(bsdf);
|
|
}
|
|
else if (has_transmission) {
|
|
sd->runtime_flag |= is_beckmann ? bsdf_microfacet_beckmann_refraction_setup(bsdf) :
|
|
bsdf_microfacet_ggx_refraction_setup(bsdf);
|
|
}
|
|
else {
|
|
sd->runtime_flag |= is_beckmann ? bsdf_microfacet_beckmann_setup(bsdf) :
|
|
bsdf_microfacet_ggx_setup(bsdf);
|
|
}
|
|
|
|
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC_TINT;
|
|
bsdf->fresnel = fresnel;
|
|
bsdf->sample_weight *= average(bsdf_microfacet_estimate_albedo(kg, sd->wi, bsdf, true, true));
|
|
|
|
if (!preserve_energy) {
|
|
return;
|
|
}
|
|
|
|
Spectrum Fss;
|
|
if (has_transmission) {
|
|
/* Assume that the transmissive tint makes up most of the overall color. */
|
|
Fss = fresnel->tint.transmittance;
|
|
}
|
|
else {
|
|
/* For purely reflective closures, use the reflection component. */
|
|
Fss = fresnel_dielectric_Fss(bsdf->ior) * fresnel->tint.reflectance;
|
|
}
|
|
|
|
microfacet_ggx_preserve_energy(kg, bsdf, sd->wi, Fss);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------ */
|
|
/** \name Dielectric in thin-walled mode
|
|
*
|
|
* An infinitesimally thin sheet of dielectric, following OpenPBR spec
|
|
* https://academysoftwarefoundation.github.io/OpenPBR/#model/thin-walledcase
|
|
*
|
|
* It is approximated by a reflected lobe and a transmitted lobe, the respective weights of both
|
|
* lobes are analytically computed by summing up infinite geometric series that account for
|
|
* internal reflections.
|
|
*
|
|
* The reflected lobe is a glossy lobe with given roughness.
|
|
* The transmitted lobe is modeled as a mirrored reflection with modified roughness.
|
|
*
|
|
* \{ */
|
|
|
|
ccl_device_inline FresnelCoeff bsdf_thin_glass_fresnel(KernelGlobals kg,
|
|
const bool reflective,
|
|
const bool refractive,
|
|
const FresnelCoeff tint,
|
|
FresnelThinFilm thinfilm,
|
|
const float ior,
|
|
const float cos_theta_i)
|
|
{
|
|
/* Fresnel coefficients at the front side. */
|
|
float cos_theta_t;
|
|
const FresnelGeneralizedSchlick fresnel = generalized_schlick_setup(
|
|
ior, reflective, refractive, tint.reflectance, one_spectrum(), thinfilm);
|
|
const FresnelCoeff front = generalized_schlick_fresnel(
|
|
kg, &fresnel, ior, cos_theta_i, &cos_theta_t);
|
|
if (iszero(front)) {
|
|
return front;
|
|
}
|
|
|
|
/* transmission_tint gives the transmittance through the thin glass at normal incidence due to
|
|
* absorption. Compute the transmittance at oblique angle according to Beer-Lambert law. */
|
|
const Spectrum color = is_zero(cos_theta_t) ? zero_spectrum() :
|
|
power(tint.transmittance, -1.0f / cos_theta_t);
|
|
|
|
/* Fresnel coefficients at the back side. */
|
|
FresnelCoeff back;
|
|
if (thinfilm.thickness > THINFILM_THICKNESS_CUTOFF) {
|
|
const float inv_ior = 1.0f / ior;
|
|
adjust_thin_film_ior_at_backface(thinfilm.ior, inv_ior);
|
|
const FresnelGeneralizedSchlick fresnel2 = generalized_schlick_setup(
|
|
ior, reflective, refractive, tint.reflectance, one_spectrum(), thinfilm);
|
|
back = generalized_schlick_fresnel(kg, &fresnel2, inv_ior, -cos_theta_t, &cos_theta_t);
|
|
}
|
|
else {
|
|
back = front;
|
|
}
|
|
|
|
/* Account for internal reflections, t' = ct1t2 + ct1(r2c)^2t2 + ct1(r2c)^4t2 + ... */
|
|
const Spectrum transmittance = safe_divide(color * front.transmittance * back.transmittance,
|
|
1.0f - sqr(back.reflectance * color));
|
|
/* r' = r1 + ct1r2ct2 + ct1(r2c)^3t2 + ... */
|
|
const Spectrum reflectance = front.reflectance + transmittance * back.reflectance * color;
|
|
|
|
return {reflectance, transmittance};
|
|
}
|
|
|
|
ccl_device_inline void bsdf_thin_glass_reflection_setup(KernelGlobals kg,
|
|
ccl_private ShaderData *sd,
|
|
const Spectrum color,
|
|
const Spectrum weight,
|
|
const float3 N,
|
|
const float roughness)
|
|
{
|
|
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
|
|
sd, sizeof(MicrofacetBsdf), weight);
|
|
if (bsdf) {
|
|
bsdf->N = N;
|
|
bsdf->T = zero_float3();
|
|
bsdf->alpha_x = bsdf->alpha_y = roughness;
|
|
bsdf->ior = 1.0f;
|
|
sd->runtime_flag |= bsdf_microfacet_ggx_setup(bsdf);
|
|
bsdf_microfacet_setup_fresnel_constant(kg, bsdf, sd->wi, color);
|
|
}
|
|
}
|
|
|
|
/* Roughen the transmission lobe since it undergoes two refraction events.
|
|
* From "Revisiting Physically Based Shading at Imageworks" by Christopher Kulla and Alejandro
|
|
* Conty, Page 40.
|
|
* Note that the number 3.7 in the slides is a typo, it should be 1.7 * 2, which is 3.4. */
|
|
ccl_device_inline float bsdf_thin_glass_transmission_roughness(const float alpha, const float eta)
|
|
{
|
|
return saturatef(alpha * sqrtf(3.4f * (eta - 1.0f) * sqr(eta - 0.5f) / (sqr(eta) * eta)));
|
|
}
|
|
|
|
ccl_device_inline void bsdf_thin_glass_transmission_setup(KernelGlobals kg,
|
|
ccl_private ShaderData *sd,
|
|
const Spectrum weight,
|
|
const float3 N,
|
|
const float roughness,
|
|
const float ior,
|
|
const PathRayVisibility ray_visibility,
|
|
const uint32_t path_flag)
|
|
{
|
|
const float transmission_roughness = bsdf_thin_glass_transmission_roughness(roughness, ior);
|
|
if (!(ray_visibility & PATH_RAY_VISIBILITY_CAMERA) &&
|
|
roughness_is_almost_specular(transmission_roughness, transmission_roughness))
|
|
{
|
|
/* Smooth thin glass does not bend the ray and is effectively transparent, allocate transparent
|
|
* closures for non-camera rays to improve sampling and keep render passes intact. */
|
|
bsdf_transparent_setup(sd, weight, path_flag);
|
|
return;
|
|
}
|
|
|
|
/* Model double refraction events as one reflection event with the incident ray mirrored along
|
|
* the surface. */
|
|
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
|
|
sd, sizeof(MicrofacetBsdf), weight);
|
|
if (bsdf) {
|
|
bsdf->N = -N;
|
|
bsdf->T = zero_float3();
|
|
bsdf->alpha_x = bsdf->alpha_y = transmission_roughness;
|
|
bsdf->ior = 1.0f;
|
|
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
|
bsdf->energy_scale = 1.0f;
|
|
bsdf->type = CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID;
|
|
sd->runtime_flag |= (SR_BSDF | SR_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf));
|
|
bsdf_microfacet_setup_fresnel_constant(kg, bsdf, reflect(sd->wi, N), one_spectrum());
|
|
}
|
|
}
|
|
|
|
ccl_device Spectrum bsdf_thin_glass_transmission_eval(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 wi,
|
|
const float3 wo,
|
|
ccl_private float *pdf)
|
|
{
|
|
return bsdf_microfacet_ggx_eval(kg, sc, reflect(wi, sc->N), wo, pdf);
|
|
}
|
|
|
|
ccl_device int bsdf_thin_glass_transmission_sample(KernelGlobals kg,
|
|
const ccl_private ShaderClosure *sc,
|
|
const float3 Ng,
|
|
const float3 wi,
|
|
const float3 rand,
|
|
ccl_private Spectrum *eval,
|
|
ccl_private float3 *wo,
|
|
ccl_private float *pdf,
|
|
ccl_private float2 *sampled_roughness,
|
|
ccl_private float *eta)
|
|
{
|
|
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
|
if (roughness_is_almost_specular(bsdf->alpha_x, bsdf->alpha_y)) {
|
|
/* Effectively transparent. */
|
|
*wo = -wi;
|
|
|
|
/* Some high number for MIS. */
|
|
/* TODO(weizhen): the magic number 1e6f is used in various places, either change it to a define
|
|
* or extract a function. */
|
|
*pdf = 1e6f;
|
|
*eval = one_spectrum() * 1e6f;
|
|
|
|
*sampled_roughness = zero_float2();
|
|
*eta = 1.0f;
|
|
return LABEL_TRANSMIT | LABEL_SINGULAR;
|
|
}
|
|
|
|
const int label = bsdf_microfacet_ggx_sample(
|
|
kg, sc, -Ng, reflect(wi, sc->N), rand, eval, wo, pdf, sampled_roughness, eta);
|
|
if (label == LABEL_NONE) {
|
|
return LABEL_NONE;
|
|
}
|
|
return LABEL_TRANSMIT | LABEL_GLOSSY;
|
|
}
|
|
|
|
ccl_device FresnelCoeff bsdf_thin_glass_setup(KernelGlobals kg,
|
|
ccl_private ShaderData *sd,
|
|
const bool reflective,
|
|
const bool refractive,
|
|
const FresnelCoeff tint,
|
|
const Spectrum weight,
|
|
const float3 N,
|
|
const float roughness,
|
|
const float ior,
|
|
const FresnelThinFilm thinfilm,
|
|
const PathRayVisibility ray_visibility,
|
|
const uint32_t path_flag)
|
|
{
|
|
const float cos_theta_i = dot(N, sd->wi);
|
|
const FresnelCoeff fresnel = bsdf_thin_glass_fresnel(
|
|
kg, reflective, refractive, tint, thinfilm, ior, cos_theta_i);
|
|
|
|
bsdf_thin_glass_reflection_setup(
|
|
kg, sd, tint.reflectance, fresnel.reflectance * weight, N, roughness);
|
|
bsdf_thin_glass_transmission_setup(
|
|
kg, sd, fresnel.transmittance * weight, N, roughness, ior, ray_visibility, path_flag);
|
|
|
|
return fresnel;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* Given the transmittance through a slab at normal incidence, compute the transmittance at a
|
|
* certain incident angle, based on Beer-Lambert law. */
|
|
ccl_device_inline Spectrum slab_color_at_angle(const float3 color,
|
|
const float cos_theta_i,
|
|
const float ior)
|
|
{
|
|
const float optical_depth = ior * inversesqrtf(sqr(ior) - (1.0f - sqr(cos_theta_i)));
|
|
return power(color, optical_depth);
|
|
}
|
|
|
|
/* Set up coat BSDF, and return the coat albedo for layering. */
|
|
ccl_device Spectrum bsdf_coat_setup(KernelGlobals kg,
|
|
ccl_private ShaderData *sd,
|
|
const PathRayVisibility ray_visibility,
|
|
ccl_private Coat &coat)
|
|
|
|
{
|
|
#ifdef __CAUSTICS_TRICKS__
|
|
const bool reflective_caustics = (kernel_data.integrator.caustics_reflective ||
|
|
(ray_visibility & PATH_RAY_VISIBILITY_DIFFUSE) == 0);
|
|
#else
|
|
const bool reflective_caustics = true;
|
|
#endif
|
|
|
|
coat.N = maybe_ensure_valid_specular_reflection(sd, coat.N);
|
|
|
|
if (!isequal(coat.tint, one_spectrum())) {
|
|
/* Compute the color at viewing angle, based on the given tint at normal incidence.
|
|
* OpenPBR Surface Specification v1.1.1, Section 3.4.2.
|
|
* NOTE(OpenPBR): Eq. (77) requires the cosines of both the incoming and the outgoing
|
|
* directions, but we only have access to the incoming direction. We therefore assume that the
|
|
* refracted cosine of both directions are the same. The same approximation is done in Adobe's
|
|
* implementation. */
|
|
const float cosNI = dot(sd->wi, coat.N);
|
|
coat.tint = slab_color_at_angle(coat.tint, cosNI, coat.ior);
|
|
}
|
|
|
|
Spectrum albedo = zero_spectrum();
|
|
if (reflective_caustics) {
|
|
MicrofacetBsdf coat_bsdf;
|
|
ccl_private MicrofacetBsdf *bsdf = bsdf_alloc_maybe_emission(sd, &coat_bsdf, coat.weight);
|
|
if (bsdf) {
|
|
bsdf->N = coat.N;
|
|
bsdf->T = zero_float3();
|
|
bsdf->ior = coat.ior;
|
|
bsdf->alpha_x = bsdf->alpha_y = sqr(coat.roughness);
|
|
|
|
const int runtime_flag = bsdf_microfacet_ggx_setup(bsdf);
|
|
if (bsdf != &coat_bsdf) {
|
|
/* Add flag only if the closure is actually allocated in `sd->closure`. */
|
|
sd->runtime_flag |= runtime_flag;
|
|
}
|
|
bsdf_microfacet_setup_fresnel_dielectric(kg, bsdf, sd->wi);
|
|
|
|
albedo = bsdf_microfacet_estimate_albedo(kg, sd->wi, bsdf, true, false);
|
|
}
|
|
}
|
|
|
|
/* Attenuate lower layers. */
|
|
return (1.0f - coat.tint * (1.0f - albedo)) * coat.weight;
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|