Path Guiding: Adding guiding on glossy surfaces via RIS #107782

Merged
Sebastian Herholz merged 1 commits from sherholz/blender:cycles_path_guiding_RIS into main 2023-05-22 16:47:10 +02:00
11 changed files with 550 additions and 33 deletions

View File

@ -209,6 +209,21 @@ enum_guiding_distribution = (
('VMM', "VMM", "Use von Mises-Fisher models as directional distribution", 2),
)
enum_guiding_directional_sampling_types = (
('MIS',
"Diffuse Product MIS",
"Guided diffuse BSDF component based on the incoming light distribution and the cosine product (closed form product)",
0),
('RIS',
"Re-sampled Importance Sampling",
"Perform RIS sampling to guided based on the product of the incoming light distribution and the BSDF",
1),
('ROUGHNESS',
"Roughness-based",
"Adjust the guiding probability based on the roughness of the material components",
2),
)
def enum_openimagedenoise_denoiser(self, context):
import _cycles
@ -568,6 +583,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default='PARALLAX_AWARE_VMM',
)
guiding_directional_sampling_type: EnumProperty(
name="Directional Sampling Type",
description="Type of the directional sampling used for guiding",
items=enum_guiding_directional_sampling_types,
default='RIS',
)
use_surface_guiding: BoolProperty(
name="Surface Guiding",
description="Use guiding when sampling directions on a surface",
@ -617,6 +639,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=True,
)
guiding_roughness_threshold: FloatProperty(
name="Guiding Roughness Threshold",
description="The minimal roughness value of a material to apply guiding",
min=0.0, max=1.0,
default=0.05,
)
max_bounces: IntProperty(
name="Max Bounces",
description="Total maximum number of bounces",

View File

@ -337,6 +337,8 @@ class CYCLES_RENDER_PT_sampling_path_guiding_debug(CyclesDebugButtonsPanel, Pane
layout.active = cscene.use_guiding
layout.prop(cscene, "guiding_distribution_type", text="Distribution Type")
layout.prop(cscene, "guiding_roughness_threshold")
layout.prop(cscene, "guiding_directional_sampling_type", text="Directional Sampling Type")
col = layout.column(align=True)
col.prop(cscene, "surface_guiding_probability")

View File

@ -440,6 +440,13 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
GuidingDistributionType guiding_distribution_type = (GuidingDistributionType)get_enum(
cscene, "guiding_distribution_type", GUIDING_NUM_TYPES, GUIDING_TYPE_PARALLAX_AWARE_VMM);
integrator->set_guiding_distribution_type(guiding_distribution_type);
GuidingDirectionalSamplingType guiding_directional_sampling_type =
(GuidingDirectionalSamplingType)get_enum(cscene,
"guiding_directional_sampling_type",
GUIDING_DIRECTIONAL_SAMPLING_NUM_TYPES,
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS);
integrator->set_guiding_directional_sampling_type(guiding_directional_sampling_type);
integrator->set_guiding_roughness_threshold(get_float(cscene, "guiding_roughness_threshold"));
}
DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background);

View File

@ -15,6 +15,8 @@ struct GuidingParams {
bool use_volume_guiding = false;
GuidingDistributionType type = GUIDING_TYPE_PARALLAX_AWARE_VMM;
GuidingDirectionalSamplingType sampling_type = GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS;
float roughness_threshold = 0.05f;
int training_samples = 128;
bool deterministic = false;
@ -24,7 +26,9 @@ struct GuidingParams {
{
return !((use == other.use) && (use_surface_guiding == other.use_surface_guiding) &&
(use_volume_guiding == other.use_volume_guiding) && (type == other.type) &&
(sampling_type == other.sampling_type) &&
(training_samples == other.training_samples) &&
(roughness_threshold == other.roughness_threshold) &&
(deterministic == other.deterministic));
}
};

View File

@ -207,6 +207,8 @@ KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type)
KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, int, guiding_distribution_type)
KERNEL_STRUCT_MEMBER(integrator, int, guiding_directional_sampling_type)
KERNEL_STRUCT_MEMBER(integrator, float, guiding_roughness_threshold)
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, train_guiding)
KERNEL_STRUCT_MEMBER(integrator, int, use_surface_guiding)
@ -216,6 +218,8 @@ KERNEL_STRUCT_MEMBER(integrator, int, use_guiding_mis_weights)
/* Padding. */
KERNEL_STRUCT_MEMBER(integrator, int, pad1)
KERNEL_STRUCT_MEMBER(integrator, int, pad2)
KERNEL_STRUCT_MEMBER(integrator, int, pad3)
KERNEL_STRUCT_END(KernelIntegrator)
/* SVM. For shader specialization. */

View File

@ -7,10 +7,66 @@
#include "kernel/closure/bsdf.h"
#include "kernel/film/write.h"
#if OPENPGL_VERSION_MINOR >= 5
# define RIS_INCOMING_RADIANCE
#endif
CCL_NAMESPACE_BEGIN
/* Utilities. */
struct GuidingRISSample {
float3 rand;
float2 sampled_roughness;
float eta{1.0f};
int label;
float3 wo;
float bsdf_pdf{0.0f};
float guide_pdf{0.0f};
float ris_target{0.0f};
float ris_pdf{0.0f};
float ris_weight{0.0f};
#ifdef RIS_INCOMING_RADIANCE
float incoming_radiance_pdf{0.0f};
#else
float cosine{0.0f};
#endif
BsdfEval bsdf_eval;
float avg_bsdf_eval{0.0f};
Spectrum eval{zero_spectrum()};
};
ccl_device_forceinline bool calculate_ris_target(ccl_private GuidingRISSample *ris_sample,
ccl_private const float guiding_sampling_prob)
{
#if defined(__PATH_GUIDING__)
const float pi_factor = 2.0f;
if (ris_sample->avg_bsdf_eval > 0.0f && ris_sample->bsdf_pdf > 1e-10f &&
ris_sample->guide_pdf > 0.0f)
{
# ifdef RIS_INCOMING_RADIANCE
ris_sample->ris_target = (ris_sample->avg_bsdf_eval *
((((1.0f - guiding_sampling_prob) * (1.0f / (pi_factor * M_PI_F))) +
(guiding_sampling_prob * ris_sample->incoming_radiance_pdf))));
# else
ris_sample->ris_target = (ris_sample->avg_bsdf_eval / ris_sample->cosine *
((((1.0f - guiding_sampling_prob) * (1.0f / (pi_factor * M_PI_F))) +
(guiding_sampling_prob * ris_sample->guide_pdf))));
# endif
ris_sample->ris_pdf = (0.5f * (ris_sample->bsdf_pdf + ris_sample->guide_pdf));
ris_sample->ris_weight = ris_sample->ris_target / ris_sample->ris_pdf;
return true;
}
ris_sample->ris_target = 0.0f;
ris_sample->ris_pdf = 0.0f;
return false;
#else
return false;
#endif
}
#if defined(__PATH_GUIDING__)
static pgl_vec3f guiding_vec3f(const float3 v)
{
@ -241,7 +297,7 @@ ccl_device_forceinline void guiding_record_volume_bounce(KernelGlobals kg,
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, pdf);
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
openpgl::cpp::SetIsDelta(state->guiding.path_segment, false);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.f);
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0f);
openpgl::cpp::SetRoughness(state->guiding.path_segment, roughness);
#endif
}
@ -259,11 +315,11 @@ ccl_device_forceinline void guiding_record_volume_transmission(KernelGlobals kg,
if (state->guiding.path_segment) {
// TODO (sherholz): need to find a better way to avoid this check
if ((transmittance_weight[0] < 0.f || !std::isfinite(transmittance_weight[0]) ||
if ((transmittance_weight[0] < 0.0f || !std::isfinite(transmittance_weight[0]) ||
std::isnan(transmittance_weight[0])) ||
(transmittance_weight[1] < 0.f || !std::isfinite(transmittance_weight[1]) ||
(transmittance_weight[1] < 0.0f || !std::isfinite(transmittance_weight[1]) ||
std::isnan(transmittance_weight[1])) ||
(transmittance_weight[2] < 0.f || !std::isfinite(transmittance_weight[2]) ||
(transmittance_weight[2] < 0.0f || !std::isfinite(transmittance_weight[2]) ||
std::isnan(transmittance_weight[2])))
{
}
@ -438,7 +494,7 @@ ccl_device_forceinline void guiding_write_debug_passes(KernelGlobals kg,
sum_sample_weight += sc->sample_weight;
}
avg_roughness = avg_roughness > 0.f ? avg_roughness / sum_sample_weight : 0.f;
avg_roughness = avg_roughness > 0.0f ? avg_roughness / sum_sample_weight : 0.0f;
film_write_pass_float(buffer + kernel_data.film.pass_guiding_avg_roughness, avg_roughness);
}
@ -498,6 +554,17 @@ ccl_device_forceinline float guiding_bsdf_pdf(KernelGlobals kg,
#endif
}
ccl_device_forceinline float guiding_surface_incoming_radiance_pdf(KernelGlobals kg,
IntegratorState state,
const float3 wo)
{
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4 && OPENPGL_VERSION_MINOR >= 5
return kg->opgl_surface_sampling_distribution->IncomingRadiancePDF(guiding_vec3f(wo));
#else
return 0.0f;
#endif
}
/* Guided Volume Phases */
ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg,

View File

@ -372,6 +372,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
float2 bsdf_sampled_roughness = make_float2(1.0f, 1.0f);
float bsdf_eta = 1.0f;
float mis_pdf = 1.0f;
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (kernel_data.integrator.use_surface_guiding) {
@ -383,9 +384,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
&bsdf_eval,
&bsdf_wo,
&bsdf_pdf,
&mis_pdf,
&unguided_bsdf_pdf,
&bsdf_sampled_roughness,
&bsdf_eta);
&bsdf_eta,
rng_state);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
@ -410,7 +413,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
}
mis_pdf = bsdf_pdf;
unguided_bsdf_pdf = bsdf_pdf;
}
@ -445,7 +448,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
/* Update path state */
if (!(label & LABEL_TRANSPARENT)) {
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf;
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = mis_pdf;
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sd->N;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));

View File

@ -24,6 +24,28 @@ 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,
@ -36,6 +58,9 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
}
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. */
@ -43,6 +68,8 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
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)) {
@ -56,6 +83,10 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
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;
}
}
}
@ -64,17 +95,36 @@ ccl_device_inline void surface_shader_prepare_guiding(KernelGlobals kg,
bssrdf_sampling_fraction /= bsdf_bssrdf_sampling_sum;
}
/* Init guiding (diffuse BSDFs only for now). */
if (!(diffuse_sampling_fraction > 0.0f &&
guiding_bsdf_init(kg, state, sd->P, sd->N, rand_bsdf_guiding)))
/* 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;
state->guiding.surface_guiding_sampling_prob = surface_guiding_probability *
diffuse_sampling_fraction;
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;
@ -325,12 +375,20 @@ ccl_device_inline
kg, sd, wo, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
if (state->guiding.use_surface_guiding) {
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);
pdf = (guiding_sampling_prob * guide_pdf * (1.0f - bssrdf_sampling_prob)) +
(1.0f - guiding_sampling_prob) * pdf;
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
@ -406,17 +464,17 @@ surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict s
/* 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(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)
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));
@ -478,6 +536,10 @@ ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
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);
@ -499,6 +561,9 @@ ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
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);
}
@ -529,6 +594,309 @@ ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
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);
# ifdef RIS_INCOMING_RADIANCE
ris_samples[0].incoming_radiance_pdf = guiding_surface_incoming_radiance_pdf(
kg, state, ris_samples[0].wo);
# else
ris_samples[0].cosine = max(0.01f, fabsf(dot(sd->N, ris_samples[0].wo)));
# endif
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);
# ifdef RIS_INCOMING_RADIANCE
ris_samples[1].incoming_radiance_pdf = guiding_surface_incoming_radiance_pdf(
kg, state, ris_samples[1].wo);
# else
ris_samples[1].cosine = max(0.01f, fabsf(dot(sd->N, ris_samples[1].wo)));
# endif
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;
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

View File

@ -163,6 +163,11 @@ enum PathTraceDimension {
PRNG_SURFACE_AO = 4,
PRNG_SURFACE_BEVEL = 5,
PRNG_SURFACE_BSDF_GUIDING = 6,
/* Guiding RIS */
PRNG_SURFACE_RIS_GUIDING_0 = 10,
PRNG_SURFACE_RIS_GUIDING_1 = 11,
/* Volume */
PRNG_VOLUME_PHASE = 3,
PRNG_VOLUME_PHASE_CHANNEL = 4,
@ -506,6 +511,16 @@ typedef enum GuidingDistributionType {
GUIDING_NUM_TYPES,
} GuidingDistributionType;
/* Guiding Directional Sampling Type */
typedef enum GuidingDirectionalSamplingType {
GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS = 0,
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS = 1,
GUIDING_DIRECTIONAL_SAMPLING_TYPE_ROUGHNESS = 2,
GUIDING_DIRECTIONAL_SAMPLING_NUM_TYPES,
} GuidingDirectionalSamplingType;
/* Camera Type */
enum CameraType { CAMERA_PERSPECTIVE, CAMERA_ORTHOGRAPHIC, CAMERA_PANORAMA };

View File

@ -60,10 +60,17 @@ NODE_DEFINE(Integrator)
SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024);
SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f);
static NodeEnum guiding_ditribution_enum;
guiding_ditribution_enum.insert("PARALLAX_AWARE_VMM", GUIDING_TYPE_PARALLAX_AWARE_VMM);
guiding_ditribution_enum.insert("DIRECTIONAL_QUAD_TREE", GUIDING_TYPE_DIRECTIONAL_QUAD_TREE);
guiding_ditribution_enum.insert("VMM", GUIDING_TYPE_VMM);
static NodeEnum guiding_distribution_enum;
guiding_distribution_enum.insert("PARALLAX_AWARE_VMM", GUIDING_TYPE_PARALLAX_AWARE_VMM);
guiding_distribution_enum.insert("DIRECTIONAL_QUAD_TREE", GUIDING_TYPE_DIRECTIONAL_QUAD_TREE);
guiding_distribution_enum.insert("VMM", GUIDING_TYPE_VMM);
static NodeEnum guiding_directional_sampling_type_enum;
guiding_directional_sampling_type_enum.insert("MIS",
GUIDING_DIRECTIONAL_SAMPLING_TYPE_PRODUCT_MIS);
guiding_directional_sampling_type_enum.insert("RIS", GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS);
guiding_directional_sampling_type_enum.insert("ROUGHNESS",
GUIDING_DIRECTIONAL_SAMPLING_TYPE_ROUGHNESS);
SOCKET_BOOLEAN(use_guiding, "Guiding", false);
SOCKET_BOOLEAN(deterministic_guiding, "Deterministic Guiding", true);
@ -76,8 +83,13 @@ NODE_DEFINE(Integrator)
SOCKET_BOOLEAN(use_guiding_mis_weights, "Use MIS Weights", true);
SOCKET_ENUM(guiding_distribution_type,
"Guiding Distribution Type",
guiding_ditribution_enum,
guiding_distribution_enum,
GUIDING_TYPE_PARALLAX_AWARE_VMM);
SOCKET_ENUM(guiding_directional_sampling_type,
"Guiding Directional Sampling Type",
guiding_directional_sampling_type_enum,
GUIDING_DIRECTIONAL_SAMPLING_TYPE_RIS);
SOCKET_FLOAT(guiding_roughness_threshold, "Guiding Roughness Threshold", 0.05f);
SOCKET_BOOLEAN(caustics_reflective, "Reflective Caustics", true);
SOCKET_BOOLEAN(caustics_refractive, "Refractive Caustics", true);
@ -239,6 +251,8 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->use_guiding_direct_light = use_guiding_direct_light;
kintegrator->use_guiding_mis_weights = use_guiding_mis_weights;
kintegrator->guiding_distribution_type = guiding_params.type;
kintegrator->guiding_directional_sampling_type = guiding_params.sampling_type;
kintegrator->guiding_roughness_threshold = guiding_params.roughness_threshold;
kintegrator->seed = seed;
@ -409,7 +423,9 @@ GuidingParams Integrator::get_guiding_params(const Device *device) const
guiding_params.type = guiding_distribution_type;
guiding_params.training_samples = guiding_training_samples;
guiding_params.deterministic = deterministic_guiding;
guiding_params.sampling_type = guiding_directional_sampling_type;
// In Blender/Cycles the user set roughness is squared to behave more linear.
guiding_params.roughness_threshold = guiding_roughness_threshold * guiding_roughness_threshold;
return guiding_params;
}
CCL_NAMESPACE_END

View File

@ -54,6 +54,8 @@ class Integrator : public Node {
NODE_SOCKET_API(bool, use_guiding_direct_light);
NODE_SOCKET_API(bool, use_guiding_mis_weights);
NODE_SOCKET_API(GuidingDistributionType, guiding_distribution_type);
NODE_SOCKET_API(GuidingDirectionalSamplingType, guiding_directional_sampling_type);
NODE_SOCKET_API(float, guiding_roughness_threshold);
NODE_SOCKET_API(bool, caustics_reflective)
NODE_SOCKET_API(bool, caustics_refractive)