1
1
This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/intern/cycles/kernel/integrator/surface_shader.h

1220 lines
43 KiB
C++

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
/* Functions to evaluate shaders. */
#pragma once
#include "kernel/closure/alloc.h"
#include "kernel/closure/bsdf.h"
#include "kernel/closure/bsdf_util.h"
#include "kernel/closure/emissive.h"
#include "kernel/integrator/guiding.h"
#ifdef __SVM__
# include "kernel/svm/svm.h"
#endif
#ifdef __OSL__
# include "kernel/osl/osl.h"
#endif
CCL_NAMESPACE_BEGIN
/* Guiding */
#ifdef __PATH_GUIDING__
ccl_device float surface_shader_average_sample_weight_squared_roughness(
ccl_private const ShaderData *sd)
{
float avg_squared_roughness = 0.0f;
float sum_sample_weight = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
continue;
}
avg_squared_roughness += sc->sample_weight * bsdf_get_specular_roughness_squared(sc);
sum_sample_weight += sc->sample_weight;
}
avg_squared_roughness = avg_squared_roughness > 0.0f ?
avg_squared_roughness / sum_sample_weight :
0.0f;
return avg_squared_roughness;
}
ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state)
{
/* Have any BSDF to guide? */
if (!(kernel_data.integrator.use_surface_guiding && (sd->flag & SD_BSDF_HAS_EVAL))) {
state->guiding.use_surface_guiding = false;
return;
}
const float surface_guiding_probability = kernel_data.integrator.surface_guiding_probability;
const int guiding_directional_sampling_type =
kernel_data.integrator.guiding_directional_sampling_type;
const float guiding_roughness_threshold = kernel_data.integrator.guiding_roughness_threshold;
float rand_bsdf_guiding = path_state_rng_1D(kg, rng_state, PRNG_SURFACE_BSDF_GUIDING);
/* Compute proportion of diffuse BSDF and BSSRDFs. */
float diffuse_sampling_fraction = 0.0f;
float bssrdf_sampling_fraction = 0.0f;
float bsdf_bssrdf_sampling_sum = 0.0f;
bool fully_opaque = true;
for (int i = 0; i < sd->num_closure; i++) {
ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
const float sweight = sc->sample_weight;
kernel_assert(sweight >= 0.0f);
bsdf_bssrdf_sampling_sum += sweight;
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) && sc->type < CLOSURE_BSDF_TRANSLUCENT_ID) {
diffuse_sampling_fraction += sweight;
}
if (CLOSURE_IS_BSSRDF(sc->type)) {
bssrdf_sampling_fraction += sweight;
}
if (CLOSURE_IS_BSDF_TRANSPARENT(sc->type) || CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) {
fully_opaque = false;
}
}
}
if (bsdf_bssrdf_sampling_sum > 0.0f) {
diffuse_sampling_fraction /= bsdf_bssrdf_sampling_sum;
bssrdf_sampling_fraction /= bsdf_bssrdf_sampling_sum;
}
/* Init guiding */
/* The the roughness because the function returns alpha.x * alpha.y. In addition alpha is squared
* again */
float avg_roughness = surface_shader_average_sample_weight_squared_roughness(sd);
avg_roughness = safe_sqrtf(avg_roughness);
if (!fully_opaque || avg_roughness < guiding_roughness_threshold ||
((guiding_directional_sampling_type == GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS) &&
(diffuse_sampling_fraction <= 0.0f)) ||
!guiding_bsdf_init(kg, state, sd->P, sd->N, rand_bsdf_guiding))
{
state->guiding.use_surface_guiding = false;
state->guiding.surface_guiding_sampling_prob = 0.0f;
return;
}
state->guiding.use_surface_guiding = true;
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS)
{
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability *
diffuse_sampling_fraction;
}
else if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability;
}
else { // GUIDING_DIRECTIONAL_SAMPLING_TYPE_ROUGHNESS
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability * avg_roughness;
}
state->guiding.bssrdf_sampling_prob = bssrdf_sampling_fraction;
state->guiding.sample_surface_guiding_rand = rand_bsdf_guiding;
kernel_assert(state->guiding.surface_guiding_sampling_prob > 0.0f &&
state->guiding.surface_guiding_sampling_prob <= 1.0f);
}
#endif
ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
ConstIntegratorState state,
ccl_private ShaderData *sd,
const uint32_t path_flag)
{
/* Filter out closures. */
if (kernel_data.integrator.filter_closures) {
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) {
sd->closure_emission_background = zero_spectrum();
}
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
sd->flag &= ~SD_BSDF_HAS_EVAL;
}
if (path_flag & PATH_RAY_CAMERA) {
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE)) ||
(CLOSURE_IS_BSDF_GLOSSY(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY)) ||
(CLOSURE_IS_BSDF_TRANSMISSION(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION)))
{
sc->type = CLOSURE_NONE_ID;
sc->sample_weight = 0.0f;
}
else if ((CLOSURE_IS_BSDF_TRANSPARENT(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSPARENT)))
{
sc->type = CLOSURE_HOLDOUT_ID;
sc->sample_weight = 0.0f;
sd->flag |= SD_HOLDOUT;
}
}
}
}
/* Defensive sampling.
*
* We can likely also do defensive sampling at deeper bounces, particularly
* for cases like a perfect mirror but possibly also others. This will need
* a good heuristic. */
if (INTEGRATOR_STATE(state, path, bounce) + INTEGRATOR_STATE(state, path, transparent_bounce) ==
0 &&
sd->num_closure > 1)
{
float sum = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
sum += sc->sample_weight;
}
}
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
sc->sample_weight = max(sc->sample_weight, 0.125f * sum);
}
}
}
/* Filter glossy.
*
* Blurring of bsdf after bounces, for rays that have a small likelihood
* of following this particular path (diffuse, rough glossy) */
if (kernel_data.integrator.filter_glossy != FLT_MAX
#ifdef __MNEE__
&& !(INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_VALID)
#endif
)
{
float blur_pdf = kernel_data.integrator.filter_glossy *
INTEGRATOR_STATE(state, path, min_ray_pdf);
if (blur_pdf < 1.0f) {
float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF(sc->type)) {
bsdf_blur(kg, sc, blur_roughness);
}
}
}
}
}
/* BSDF */
#if 0
ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
const ShaderClosure *sc,
const float3 wo,
const int org_label,
const float2 org_roughness,
const float org_eta)
{
/* Validate the #bsdf_label and #bsdf_roughness_eta functions
* by estimating the values after a BSDF sample. */
const int comp_label = bsdf_label(kg, sc, wo);
kernel_assert(org_label == comp_label);
float2 comp_roughness;
float comp_eta;
bsdf_roughness_eta(kg, sc, &comp_roughness, &comp_eta);
kernel_assert(org_eta == comp_eta);
kernel_assert(org_roughness.x == comp_roughness.x);
kernel_assert(org_roughness.y == comp_roughness.y);
}
#endif
ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light_shader_flags)
{
if (!(light_shader_flags & SHADER_EXCLUDE_ANY)) {
return false;
}
if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) {
if (CLOSURE_IS_BSDF_DIFFUSE(type)) {
return true;
}
}
if (light_shader_flags & SHADER_EXCLUDE_GLOSSY) {
if (CLOSURE_IS_BSDF_GLOSSY(type)) {
return true;
}
}
if (light_shader_flags & SHADER_EXCLUDE_TRANSMIT) {
if (CLOSURE_IS_BSDF_TRANSMISSION(type)) {
return true;
}
}
return false;
}
ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 wo,
ccl_private const ShaderClosure *skip_sc,
ccl_private BsdfEval *result_eval,
float sum_pdf,
float sum_sample_weight,
const uint light_shader_flags)
{
/* This is the veach one-sample model with balance heuristic,
* some PDF factors drop out when using balance heuristic weighting. */
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (sc == skip_sc) {
continue;
}
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
if (CLOSURE_IS_BSDF(sc->type) && !_surface_shader_exclude(sc->type, light_shader_flags)) {
float bsdf_pdf = 0.0f;
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
}
}
sum_sample_weight += sc->sample_weight;
}
}
return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f;
}
ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 wo,
ccl_private BsdfEval *result_eval,
ccl_private float *pdfs,
const uint light_shader_flags)
{
/* This is the veach one-sample model with balance heuristic, some pdf
* factors drop out when using balance heuristic weighting. */
float sum_pdf = 0.0f;
float sum_sample_weight = 0.0f;
bsdf_eval_init(result_eval, CLOSURE_NONE_ID, zero_spectrum());
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
if (CLOSURE_IS_BSDF(sc->type) && !_surface_shader_exclude(sc->type, light_shader_flags)) {
float bsdf_pdf = 0.0f;
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
kernel_assert(bsdf_pdf >= 0.0f);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
kernel_assert(bsdf_pdf * sc->sample_weight >= 0.0f);
pdfs[i] = bsdf_pdf * sc->sample_weight;
}
else {
pdfs[i] = 0.0f;
}
}
else {
pdfs[i] = 0.0f;
}
sum_sample_weight += sc->sample_weight;
}
else {
pdfs[i] = 0.0f;
}
}
if (sum_pdf > 0.0f) {
for (int i = 0; i < sd->num_closure; i++) {
pdfs[i] /= sum_pdf;
}
}
return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f;
}
#ifndef __KERNEL_CUDA__
ccl_device
#else
ccl_device_inline
#endif
float
surface_shader_bsdf_eval(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
const float3 wo,
ccl_private BsdfEval *bsdf_eval,
const uint light_shader_flags)
{
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum());
float pdf = _surface_shader_bsdf_eval_mis(
kg, sd, wo, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (pdf > 0.0f && state->guiding.use_surface_guiding) {
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
const float guide_pdf = guiding_bsdf_pdf(kg, state, wo);
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
pdf = (0.5f * guide_pdf * (1.0f - bssrdf_sampling_prob)) + 0.5f * pdf;
}
else {
pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
(1.0f - guiding_sampling_prob) * pdf;
}
}
#endif
return pdf;
}
/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */
ccl_device_inline ccl_private const ShaderClosure *surface_shader_bsdf_bssrdf_pick(
ccl_private const ShaderData *ccl_restrict sd, ccl_private float3 *rand_bsdf)
{
int sampled = 0;
if (sd->num_closure > 1) {
/* Pick a BSDF or based on sample weights. */
float sum = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
sum += sc->sample_weight;
}
}
float r = (*rand_bsdf).z * sum;
float partial_sum = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
float next_sum = partial_sum + sc->sample_weight;
if (r < next_sum) {
sampled = i;
/* Rescale to reuse for direction sample, to better preserve stratification. */
(*rand_bsdf).z = (r - partial_sum) / sc->sample_weight;
break;
}
partial_sum = next_sum;
}
}
}
return &sd->closure[sampled];
}
/* Return weight for picked BSSRDF. */
ccl_device_inline Spectrum
surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd,
ccl_private const ShaderClosure *ccl_restrict bssrdf_sc)
{
Spectrum weight = bssrdf_sc->weight;
if (sd->num_closure > 1) {
float sum = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
sum += sc->sample_weight;
}
}
weight *= sum / bssrdf_sc->sample_weight;
}
return weight;
}
#ifdef __PATH_GUIDING__
/* Sample direction for picked BSDF, and return evaluation and pdf for all
* BSDFs combined using MIS. */
ccl_device int surface_shader_bsdf_guided_sample_closure_mis(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const float3 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *wo,
ccl_private float *bsdf_pdf,
ccl_private float *unguided_bsdf_pdf,
ccl_private float2 *sampled_rougness,
ccl_private float *eta)
{
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
const bool use_surface_guiding = state->guiding.use_surface_guiding;
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
/* Decide between sampling guiding distribution and BSDF. */
bool sample_guiding = false;
float rand_bsdf_guiding = state->guiding.sample_surface_guiding_rand;
if (use_surface_guiding && rand_bsdf_guiding < guiding_sampling_prob) {
sample_guiding = true;
rand_bsdf_guiding /= guiding_sampling_prob;
}
else {
rand_bsdf_guiding -= guiding_sampling_prob;
rand_bsdf_guiding /= (1.0f - guiding_sampling_prob);
}
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
if (sample_guiding) {
/* Sample guiding distribution. */
guide_pdf = guiding_bsdf_sample(kg, state, float3_to_float2(rand_bsdf), wo);
*bsdf_pdf = 0.0f;
if (guide_pdf != 0.0f) {
float unguided_bsdf_pdfs[MAX_CLOSURE];
*unguided_bsdf_pdf = surface_shader_bsdf_eval_pdfs(
kg, sd, *wo, bsdf_eval, unguided_bsdf_pdfs, 0);
*bsdf_pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
((1.0f - guiding_sampling_prob) * (*unguided_bsdf_pdf));
float sum_pdfs = 0.0f;
if (*unguided_bsdf_pdf > 0.0f) {
int idx = -1;
for (int i = 0; i < sd->num_closure; i++) {
sum_pdfs += unguided_bsdf_pdfs[i];
if (rand_bsdf_guiding <= sum_pdfs) {
idx = i;
break;
}
}
kernel_assert(idx >= 0);
/* Set the default idx to the last in the list.
* in case of numerical problems and rand_bsdf_guiding is just >=1.0f and
* the sum of all unguided_bsdf_pdfs is just < 1.0f. */
idx = (rand_bsdf_guiding > sum_pdfs) ? sd->num_closure - 1 : idx;
label = bsdf_label(kg, &sd->closure[idx], *wo);
}
else {
*bsdf_pdf = 0.0f;
*unguided_bsdf_pdf = 0.0f;
}
}
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
*sampled_rougness = make_float2(1.0f, 1.0f);
*eta = 1.0f;
}
else {
/* Sample BSDF. */
*bsdf_pdf = 0.0f;
label = bsdf_sample(kg,
sd,
sc,
INTEGRATOR_STATE(state, path, flag),
rand_bsdf,
&eval,
wo,
unguided_bsdf_pdf,
sampled_rougness,
eta);
# if 0
// Code path to validate the estimation of the label, sampled roughness and eta
// This should be activated from time to time when the BSDFs change to check if everything
// is still working correctly.
if (*unguided_bsdf_pdf > 0.0f) {
surface_shader_validate_bsdf_sample(kg, sc, *wo, label, sampled_roughness, eta);
}
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
*unguided_bsdf_pdf = _surface_shader_bsdf_eval_mis(
kg, sd, *wo, sc, bsdf_eval, (*unguided_bsdf_pdf) * sweight, sweight, 0);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
*bsdf_pdf = *unguided_bsdf_pdf;
if (use_surface_guiding) {
guide_pdf = guiding_bsdf_pdf(kg, state, *wo);
*bsdf_pdf *= 1.0f - guiding_sampling_prob;
*bsdf_pdf += guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob);
}
}
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
return label;
}
ccl_device int surface_shader_bsdf_guided_sample_closure_ris(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const float3 rand_bsdf,
ccl_private const RNGState *rng_state,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *wo,
ccl_private float *bsdf_pdf,
ccl_private float *mis_pdf,
ccl_private float *unguided_bsdf_pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
const bool use_surface_guiding = state->guiding.use_surface_guiding;
const float guiding_sampling_prob = state->guiding.surface_guiding_sampling_prob;
const float bssrdf_sampling_prob = state->guiding.bssrdf_sampling_prob;
/* Decide between sampling guiding distribution and BSDF. */
float rand_bsdf_guiding = state->guiding.sample_surface_guiding_rand;
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
if (use_surface_guiding && guiding_sampling_prob > 0.0f) {
/* Performing guided sampling using RIS */
// selected RIS candidate
int ris_idx = 0;
// meta data for the two RIS candidates
GuidingRISSample ris_samples[2];
ris_samples[0].rand = rand_bsdf;
ris_samples[1].rand = path_state_rng_3D(kg, rng_state, PRNG_SURFACE_RIS_GUIDING_0);
// ----------------------------------------------------
// generate the first RIS candidate using a BSDF sample
// ----------------------------------------------------
ris_samples[0].label = bsdf_sample(kg,
sd,
sc,
INTEGRATOR_STATE(state, path, flag),
ris_samples[0].rand,
&ris_samples[0].eval,
&ris_samples[0].wo,
&ris_samples[0].bsdf_pdf,
&ris_samples[0].sampled_roughness,
&ris_samples[0].eta);
bsdf_eval_init(&ris_samples[0].bsdf_eval, sc->type, ris_samples[0].eval * sc->weight);
if (ris_samples[0].bsdf_pdf > 0.0f) {
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
ris_samples[0].bsdf_pdf = _surface_shader_bsdf_eval_mis(kg,
sd,
ris_samples[0].wo,
sc,
&ris_samples[0].bsdf_eval,
(ris_samples[0].bsdf_pdf) *
sweight,
sweight,
0);
kernel_assert(reduce_min(bsdf_eval_sum(&ris_samples[0].bsdf_eval)) >= 0.0f);
}
ris_samples[0].avg_bsdf_eval = average(ris_samples[0].bsdf_eval.sum);
ris_samples[0].guide_pdf = guiding_bsdf_pdf(kg, state, ris_samples[0].wo);
ris_samples[0].guide_pdf *= (1.0f - bssrdf_sampling_prob);
ris_samples[0].incoming_radiance_pdf = guiding_surface_incoming_radiance_pdf(
kg, state, ris_samples[0].wo);
ris_samples[0].bsdf_pdf = max(0.0f, ris_samples[0].bsdf_pdf);
}
// ------------------------------------------------------------------------------
// generate the second RIS candidate using a sample from the guiding distribution
// ------------------------------------------------------------------------------
float unguided_bsdf_pdfs[MAX_CLOSURE];
bsdf_eval_init(&ris_samples[1].bsdf_eval, CLOSURE_NONE_ID, eval);
ris_samples[1].guide_pdf = guiding_bsdf_sample(
kg, state, float3_to_float2(ris_samples[1].rand), &ris_samples[1].wo);
ris_samples[1].guide_pdf *= (1.0f - bssrdf_sampling_prob);
ris_samples[1].incoming_radiance_pdf = guiding_surface_incoming_radiance_pdf(
kg, state, ris_samples[1].wo);
ris_samples[1].bsdf_pdf = surface_shader_bsdf_eval_pdfs(
kg, sd, ris_samples[1].wo, &ris_samples[1].bsdf_eval, unguided_bsdf_pdfs, 0);
ris_samples[1].label = ris_samples[0].label;
ris_samples[1].avg_bsdf_eval = average(ris_samples[1].bsdf_eval.sum);
ris_samples[1].bsdf_pdf = max(0.0f, ris_samples[1].bsdf_pdf);
// ------------------------------------------------------------------------------
// calculate the RIS target functions for each RIS candidate
// ------------------------------------------------------------------------------
int num_ris_candidates = 0;
float sum_ris_weights = 0.0f;
if (calculate_ris_target(&ris_samples[0], guiding_sampling_prob)) {
sum_ris_weights += ris_samples[0].ris_weight;
num_ris_candidates++;
}
kernel_assert(ris_samples[0].ris_weight >= 0.0f);
kernel_assert(sum_ris_weights >= 0.0f);
if (calculate_ris_target(&ris_samples[1], guiding_sampling_prob)) {
sum_ris_weights += ris_samples[1].ris_weight;
num_ris_candidates++;
}
kernel_assert(ris_samples[1].ris_weight >= 0.0f);
kernel_assert(sum_ris_weights >= 0.0f);
// ------------------------------------------------------------------------------
// Sample/Select a sample from the RIS candidates proportional to the target
// ------------------------------------------------------------------------------
if (num_ris_candidates == 0 || !(sum_ris_weights > 1e-10f)) {
*bsdf_pdf = 0.0f;
*mis_pdf = 0.0f;
return label;
}
float rand_ris_select = rand_bsdf_guiding * sum_ris_weights;
float sum_ris = 0.0f;
for (int i = 0; i < 2; i++) {
sum_ris += ris_samples[i].ris_weight;
if (rand_ris_select <= sum_ris) {
ris_idx = i;
break;
}
}
kernel_assert(sum_ris >= 0.0f);
kernel_assert(ris_idx < 2);
// ------------------------------------------------------------------------------
// Fill in the sample data for the selected RIS candidate
// ------------------------------------------------------------------------------
guide_pdf = ris_samples[ris_idx].ris_target * (2.0f / sum_ris_weights);
*unguided_bsdf_pdf = ris_samples[ris_idx].bsdf_pdf;
*mis_pdf = 0.5f * (ris_samples[ris_idx].bsdf_pdf + ris_samples[ris_idx].guide_pdf);
*bsdf_pdf = guide_pdf;
*wo = ris_samples[ris_idx].wo;
label = ris_samples[ris_idx].label;
*sampled_roughness = ris_samples[ris_idx].sampled_roughness;
*eta = ris_samples[ris_idx].eta;
*bsdf_eval = ris_samples[ris_idx].bsdf_eval;
kernel_assert(isfinite_safe(guide_pdf));
kernel_assert(isfinite_safe(*bsdf_pdf));
if (!(*bsdf_pdf > 1e-10f)) {
*bsdf_pdf = 0.0f;
*mis_pdf = 0.0f;
return label;
}
kernel_assert(*bsdf_pdf > 0.0f);
kernel_assert(*bsdf_pdf >= 1e-20f);
kernel_assert(guide_pdf >= 0.0f);
/// select label sampled_roughness and eta
if (ris_idx == 1 && ris_samples[1].bsdf_pdf > 0.0f) {
float rnd = path_state_rng_1D(kg, rng_state, PRNG_SURFACE_RIS_GUIDING_1);
float sum_pdfs = 0.0f;
int idx = -1;
for (int i = 0; i < sd->num_closure; i++) {
sum_pdfs += unguided_bsdf_pdfs[i];
if (rnd <= sum_pdfs) {
idx = i;
break;
}
}
// kernel_assert(idx >= 0);
/* Set the default idx to the last in the list.
* in case of numerical problems and rand_bsdf_guiding is just >=1.0f and
* the sum of all unguided_bsdf_pdfs is just < 1.0f. */
idx = (rnd > sum_pdfs) ? sd->num_closure - 1 : idx;
label = bsdf_label(kg, &sd->closure[idx], *wo);
bsdf_roughness_eta(kg, &sd->closure[idx], sampled_roughness, eta);
}
kernel_assert(isfinite_safe(*bsdf_pdf));
kernel_assert(*bsdf_pdf >= 0.0f);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
else {
/* Sample BSDF. */
*bsdf_pdf = 0.0f;
label = bsdf_sample(kg,
sd,
sc,
INTEGRATOR_STATE(state, path, flag),
rand_bsdf,
&eval,
wo,
unguided_bsdf_pdf,
sampled_roughness,
eta);
# if 0
// Code path to validate the estimation of the label, sampled roughness and eta
// This should be activated from time to time when the BSDFs change to check if everything
// is still working correctly.
if (*unguided_bsdf_pdf > 0.0f) {
surface_shader_validate_bsdf_sample(kg, sc, *wo, label, sampled_roughness, eta);
}
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
*unguided_bsdf_pdf = _surface_shader_bsdf_eval_mis(
kg, sd, *wo, sc, bsdf_eval, (*unguided_bsdf_pdf) * sweight, sweight, 0);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
*bsdf_pdf = *unguided_bsdf_pdf;
*mis_pdf = *bsdf_pdf;
}
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
}
return label;
}
ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const float3 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *wo,
ccl_private float *bsdf_pdf,
ccl_private float *mis_pdf,
ccl_private float *unguided_bsdf_pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta,
ccl_private const RNGState *rng_state)
{
int label = LABEL_NONE;
if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS ||
kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_ROUGHNESS)
{
label = surface_shader_bsdf_guided_sample_closure_mis(kg,
state,
sd,
sc,
rand_bsdf,
bsdf_eval,
wo,
bsdf_pdf,
unguided_bsdf_pdf,
sampled_roughness,
eta);
*mis_pdf = (*unguided_bsdf_pdf > 0.0f) ? *bsdf_pdf : 0.0f;
}
else if (kernel_data.integrator.guiding_directional_sampling_type ==
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS)
{
label = surface_shader_bsdf_guided_sample_closure_ris(kg,
state,
sd,
sc,
rand_bsdf,
rng_state,
bsdf_eval,
wo,
bsdf_pdf,
mis_pdf,
unguided_bsdf_pdf,
sampled_roughness,
eta);
}
if (!(*unguided_bsdf_pdf > 0.0f)) {
*bsdf_pdf = 0.0f;
*mis_pdf = 0.0f;
}
return label;
}
#endif
/* Sample direction for picked BSDF, and return evaluation and pdf for all
* BSDFs combined using MIS. */
ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private const ShaderClosure *sc,
const int path_flag,
const float3 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *wo,
ccl_private float *pdf,
ccl_private float2 *sampled_roughness,
ccl_private float *eta)
{
/* BSSRDF should already have been handled elsewhere. */
kernel_assert(CLOSURE_IS_BSDF(sc->type));
int label;
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
label = bsdf_sample(kg, sd, sc, path_flag, rand_bsdf, &eval, wo, pdf, sampled_roughness, eta);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
*pdf = _surface_shader_bsdf_eval_mis(kg, sd, *wo, sc, bsdf_eval, *pdf * sweight, sweight, 0);
}
}
else {
bsdf_eval_init(bsdf_eval, sc->type, zero_spectrum());
}
return label;
}
ccl_device float surface_shader_average_roughness(ccl_private const ShaderData *sd)
{
float roughness = 0.0f;
float sum_weight = 0.0f;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF(sc->type)) {
/* sqrt once to undo the squaring from multiplying roughness on the
* two axes, and once for the squared roughness convention. */
float weight = fabsf(average(sc->weight));
roughness += weight * sqrtf(safe_sqrtf(bsdf_get_roughness_squared(sc)));
sum_weight += weight;
}
}
return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f;
}
ccl_device Spectrum surface_shader_transparency(KernelGlobals kg, ccl_private const ShaderData *sd)
{
if (sd->flag & SD_HAS_ONLY_VOLUME) {
return one_spectrum();
}
else if (sd->flag & SD_TRANSPARENT) {
return sd->closure_transparent_extinction;
}
else {
return zero_spectrum();
}
}
ccl_device void surface_shader_disable_transparency(KernelGlobals kg, ccl_private ShaderData *sd)
{
if (sd->flag & SD_TRANSPARENT) {
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) {
sc->sample_weight = 0.0f;
sc->weight = zero_spectrum();
}
}
sd->flag &= ~SD_TRANSPARENT;
}
}
ccl_device Spectrum surface_shader_alpha(KernelGlobals kg, ccl_private const ShaderData *sd)
{
Spectrum alpha = one_spectrum() - surface_shader_transparency(kg, sd);
alpha = saturate(alpha);
return alpha;
}
ccl_device Spectrum surface_shader_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd)
{
Spectrum eval = zero_spectrum();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type))
eval += bsdf_albedo(sd, sc);
}
return eval;
}
ccl_device Spectrum surface_shader_glossy(KernelGlobals kg, ccl_private const ShaderData *sd)
{
Spectrum eval = zero_spectrum();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_GLOSSY(sc->type))
eval += bsdf_albedo(sd, sc);
}
return eval;
}
ccl_device Spectrum surface_shader_transmission(KernelGlobals kg, ccl_private const ShaderData *sd)
{
Spectrum eval = zero_spectrum();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type))
eval += bsdf_albedo(sd, sc);
}
return eval;
}
ccl_device float3 surface_shader_average_normal(KernelGlobals kg, ccl_private const ShaderData *sd)
{
float3 N = zero_float3();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type))
N += sc->N * fabsf(average(sc->weight));
}
return (is_zero(N)) ? sd->N : normalize(N);
}
ccl_device Spectrum surface_shader_ao(KernelGlobals kg,
ccl_private const ShaderData *sd,
const float ao_factor,
ccl_private float3 *N_)
{
Spectrum eval = zero_spectrum();
float3 N = zero_float3();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc;
eval += sc->weight * ao_factor;
N += bsdf->N * fabsf(average(sc->weight));
}
}
*N_ = (is_zero(N)) ? sd->N : normalize(N);
return eval;
}
#ifdef __SUBSURFACE__
ccl_device float3 surface_shader_bssrdf_normal(ccl_private const ShaderData *sd)
{
float3 N = zero_float3();
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSSRDF(sc->type)) {
ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc;
float avg_weight = fabsf(average(sc->weight));
N += bssrdf->N * avg_weight;
}
}
return (is_zero(N)) ? sd->N : normalize(N);
}
#endif /* __SUBSURFACE__ */
/* Constant emission optimization */
ccl_device bool surface_shader_constant_emission(KernelGlobals kg,
int shader,
ccl_private Spectrum *eval)
{
int shader_index = shader & SHADER_MASK;
int shader_flag = kernel_data_fetch(shaders, shader_index).flags;
if (shader_flag & SD_HAS_CONSTANT_EMISSION) {
const float3 emission_rgb = make_float3(
kernel_data_fetch(shaders, shader_index).constant_emission[0],
kernel_data_fetch(shaders, shader_index).constant_emission[1],
kernel_data_fetch(shaders, shader_index).constant_emission[2]);
*eval = rgb_to_spectrum(emission_rgb);
return true;
}
return false;
}
/* Background */
ccl_device Spectrum surface_shader_background(ccl_private const ShaderData *sd)
{
if (sd->flag & SD_EMISSION) {
return sd->closure_emission_background;
}
else {
return zero_spectrum();
}
}
/* Emission */
ccl_device Spectrum surface_shader_emission(ccl_private const ShaderData *sd)
{
if (sd->flag & SD_EMISSION) {
return emissive_simple_eval(sd->Ng, sd->wi) * sd->closure_emission_background;
}
else {
return zero_spectrum();
}
}
/* Holdout */
ccl_device Spectrum surface_shader_apply_holdout(KernelGlobals kg, ccl_private ShaderData *sd)
{
Spectrum weight = zero_spectrum();
/* For objects marked as holdout, preserve transparency and remove all other
* closures, replacing them with a holdout weight. */
if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) {
if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) {
weight = one_spectrum() - sd->closure_transparent_extinction;
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) {
sc->type = NBUILTIN_CLOSURES;
}
}
sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF));
}
else {
weight = one_spectrum();
}
}
else {
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_HOLDOUT(sc->type)) {
weight += sc->weight;
}
}
}
return weight;
}
/* Surface Evaluation */
template<uint node_feature_mask, typename ConstIntegratorGenericState>
ccl_device void surface_shader_eval(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *ccl_restrict sd,
ccl_global float *ccl_restrict buffer,
uint32_t path_flag,
bool use_caustics_storage = false)
{
/* If path is being terminated, we are tracing a shadow ray or evaluating
* emission, then we don't need to store closures. The emission and shadow
* shader data also do not have a closure array to save GPU memory. */
int max_closures;
if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) {
max_closures = 0;
}
else {
max_closures = use_caustics_storage ? CAUSTICS_MAX_CLOSURE : kernel_data.max_closures;
}
sd->num_closure = 0;
sd->num_closure_left = max_closures;
#ifdef __OSL__
if (kernel_data.kernel_features & KERNEL_FEATURE_OSL) {
osl_eval_nodes<SHADER_TYPE_SURFACE>(kg, state, sd, path_flag);
}
else
#endif
{
#ifdef __SVM__
svm_eval_nodes<node_feature_mask, SHADER_TYPE_SURFACE>(kg, state, sd, buffer, path_flag);
#else
if (sd->object == OBJECT_NONE) {
sd->closure_emission_background = make_spectrum(0.8f);
sd->flag |= SD_EMISSION;
}
else {
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
sd, sizeof(DiffuseBsdf), make_spectrum(0.8f));
if (bsdf != NULL) {
bsdf->N = sd->N;
sd->flag |= bsdf_diffuse_setup(bsdf);
}
}
#endif
}
}
CCL_NAMESPACE_END