mirror of
https://github.com/blender/blender
synced 2026-10-25 03:07:01 +03:00
This appears to have been caused by invalid use of assert() instead of kernel_assert() in the kernel. I don't think it's actually hitting that assert, but maybe it generated an unsupported instruction or something along those lines? The one that caused the actual problem is in svm/convert.h. I changed more instances that are currently CPU only but risk becoming enabled on the CPU with future code changes. Thanks to Sahar A. Kashi for finding this. Pull Request: https://projects.blender.org/blender/blender/pulls/163970
752 lines
30 KiB
C++
752 lines
30 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/globals.h"
|
|
#include "kernel/types.h"
|
|
|
|
#include "kernel/integrator/state.h"
|
|
#include "kernel/util/colorspace.h"
|
|
|
|
/* FIXME: The below include could be guarded behind `ifdef WITH_CYCLES_DEBUG`, but Metal
|
|
* pre-processing is not expanding guarded include files properly. */
|
|
#include "kernel/closure/bsdf.h"
|
|
|
|
#include "util/color.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
#define GUIDING_FLT_LARGE 1.844e18f
|
|
#define GUIDING_MAX_LIGHT_DISTANCE 1e6f
|
|
|
|
/* Utilities. */
|
|
|
|
struct GuidingRISSample {
|
|
float3 rand;
|
|
float2 sampled_roughness;
|
|
/* The relative IOR of the outgoing media and the incoming media. */
|
|
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};
|
|
|
|
float incoming_radiance_pdf{0.0f};
|
|
BsdfEval bsdf_eval;
|
|
float avg_bsdf_eval{0.0f};
|
|
Spectrum eval{zero_spectrum()};
|
|
};
|
|
|
|
ccl_device_forceinline bool calculate_ris_target(
|
|
ccl_attr_maybe_unused ccl_private GuidingRISSample *ris_sample,
|
|
ccl_attr_maybe_unused const ccl_private 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)
|
|
{
|
|
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))));
|
|
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)
|
|
{
|
|
return {v.x, v.y, v.z};
|
|
}
|
|
|
|
ccl_device_forceinline pgl_point3f guiding_point3f(const float3 v)
|
|
{
|
|
return {v.x, v.y, v.z};
|
|
}
|
|
|
|
ccl_device_forceinline float3 make_float3(const pgl_vec3f v)
|
|
{
|
|
return make_float3(v.x, v.y, v.z);
|
|
}
|
|
|
|
ccl_device_forceinline bool is_guiding_valid(const float3 v)
|
|
{
|
|
const Interval<float> interval = {-GUIDING_FLT_LARGE, GUIDING_FLT_LARGE};
|
|
bool valid = true;
|
|
valid &= isfinite_safe(v);
|
|
valid &= interval.contains(v.x);
|
|
valid &= interval.contains(v.y);
|
|
valid &= interval.contains(v.z);
|
|
|
|
return valid;
|
|
}
|
|
ccl_device_forceinline float3 clamp_guiding_position(const float3 p)
|
|
{
|
|
/* Clamping to the range of +/- GUIDING_FLT_LARGE / 5.0 to avoid potential numerical problems on
|
|
* the OpenPGL side. NOTE: The clamping is mainly a robustness fallback and might not be needed
|
|
* at all. */
|
|
return clamp(p, make_float3(-GUIDING_FLT_LARGE / 5.0f), make_float3(GUIDING_FLT_LARGE / 5.0f));
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Path recording for guiding. */
|
|
|
|
/* Record Surface Interactions */
|
|
|
|
/* Records/Adds a new path segment with the current path vertex on a surface.
|
|
* If the path is not terminated this call is usually followed by a call of
|
|
* guiding_record_surface_bounce. */
|
|
ccl_device_forceinline void guiding_record_surface_segment(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const ccl_private ShaderData *sd)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const pgl_vec3f zero = guiding_vec3f(zero_float3());
|
|
const pgl_vec3f one = guiding_vec3f(one_float3());
|
|
|
|
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
|
|
/* FIXME: investigate and fix why state->guiding.path_segment could be nullptr. */
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
float3 p = sd->P;
|
|
kernel_assert(is_guiding_valid(p));
|
|
p = clamp_guiding_position(p);
|
|
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(p));
|
|
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(sd->wi));
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
|
|
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
|
|
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the surface scattering event at the current vertex position of the segment. */
|
|
ccl_device_forceinline void guiding_record_surface_bounce(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const Spectrum weight,
|
|
ccl_attr_maybe_unused const float pdf,
|
|
ccl_attr_maybe_unused const float3 N,
|
|
ccl_attr_maybe_unused const float3 wo,
|
|
ccl_attr_maybe_unused const float2 roughness,
|
|
ccl_attr_maybe_unused const float eta)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const float min_roughness = safe_sqrtf(fminf(roughness.x, roughness.y));
|
|
const bool is_delta = (min_roughness == 0.0f);
|
|
const float3 weight_rgb = spectrum_to_rgb(weight);
|
|
const float3 normal = clamp(N, -one_float3(), one_float3());
|
|
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(one_float3()));
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
|
|
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
|
|
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(wo));
|
|
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, is_delta);
|
|
openpgl::cpp::SetEta(state->guiding.path_segment, eta);
|
|
openpgl::cpp::SetRoughness(state->guiding.path_segment, min_roughness);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the emission at the current surface intersection (physical or virtual) */
|
|
ccl_device_forceinline void guiding_record_surface_emission(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const Spectrum Le,
|
|
ccl_attr_maybe_unused const float mis_weight)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
const float3 Le_rgb = spectrum_to_rgb(Le);
|
|
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, guiding_vec3f(Le_rgb));
|
|
openpgl::cpp::SetMiWeight(state->guiding.path_segment, mis_weight);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Record BSSRDF Interactions */
|
|
|
|
/* Records/Adds a new path segment where the vertex position is the point of entry
|
|
* of the sub surface scattering boundary.
|
|
* If the path is not terminated this call is usually followed by a call of
|
|
* guiding_record_bssrdf_weight and guiding_record_bssrdf_bounce. */
|
|
ccl_device_forceinline void guiding_record_bssrdf_segment(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState
|
|
state,
|
|
ccl_attr_maybe_unused const float3 P,
|
|
ccl_attr_maybe_unused const float3 wi)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const pgl_vec3f zero = guiding_vec3f(zero_float3());
|
|
const pgl_vec3f one = guiding_vec3f(one_float3());
|
|
|
|
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
|
|
/* FIXME: investigate and fix why state->guiding.path_segment could be nullptr. */
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
kernel_assert(is_guiding_valid(P));
|
|
float3 p = clamp_guiding_position(P);
|
|
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(p));
|
|
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(wi));
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
|
|
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
|
|
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the transmission of the path at the point of entry while passing
|
|
* the surface boundary. */
|
|
ccl_device_forceinline void guiding_record_bssrdf_weight(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const Spectrum weight,
|
|
ccl_attr_maybe_unused const Spectrum albedo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
/* NOTE: Albedo left out here, will be included in guiding_record_bssrdf_bounce. */
|
|
const float3 weight_rgb = spectrum_to_rgb(safe_divide_color(weight, albedo));
|
|
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment,
|
|
guiding_vec3f(zero_float3()));
|
|
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.0f);
|
|
openpgl::cpp::SetRoughness(state->guiding.path_segment, 1.0f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the direction at the point of entry the path takes when sampling the SSS contribution.
|
|
* If not terminated this function is usually followed by a call of
|
|
* guiding_record_volume_transmission to record the transmittance between the point of entry and
|
|
* the point of exit. */
|
|
ccl_device_forceinline void guiding_record_bssrdf_bounce(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const float pdf,
|
|
ccl_attr_maybe_unused const float3 N,
|
|
ccl_attr_maybe_unused const float3 wo,
|
|
ccl_attr_maybe_unused const Spectrum weight,
|
|
ccl_attr_maybe_unused const Spectrum albedo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const float3 normal = clamp(N, -one_float3(), one_float3());
|
|
const float3 weight_rgb = spectrum_to_rgb(weight * albedo);
|
|
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
|
|
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
|
|
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(wo));
|
|
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, pdf);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(weight_rgb));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Record Volume Interactions */
|
|
|
|
/* Records/Adds a new path segment with the current path vertex being inside a volume.
|
|
* If the path is not terminated this call is usually followed by a call of
|
|
* guiding_record_volume_bounce. */
|
|
ccl_device_forceinline void guiding_record_volume_segment(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState
|
|
state,
|
|
ccl_attr_maybe_unused const float3 P,
|
|
ccl_attr_maybe_unused const float3 I)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const pgl_vec3f zero = guiding_vec3f(zero_float3());
|
|
const pgl_vec3f one = guiding_vec3f(one_float3());
|
|
|
|
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
|
|
/* FIXME: investigate and fix why state->guiding.path_segment could be nullptr. */
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
kernel_assert(is_guiding_valid(P));
|
|
float3 p = clamp_guiding_position(P);
|
|
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(p));
|
|
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(I));
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
|
|
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
|
|
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the volume scattering event at the current vertex position of the segment. */
|
|
ccl_device_forceinline void guiding_record_volume_bounce(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const Spectrum weight,
|
|
ccl_attr_maybe_unused const float pdf,
|
|
ccl_attr_maybe_unused const float3 wo,
|
|
ccl_attr_maybe_unused const float roughness)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const float3 weight_rgb = spectrum_to_rgb(weight);
|
|
const float3 normal = make_float3(0.0f, 0.0f, 1.0f);
|
|
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, true);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, guiding_vec3f(one_float3()));
|
|
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(normal));
|
|
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(wo));
|
|
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.0f);
|
|
openpgl::cpp::SetRoughness(state->guiding.path_segment, roughness);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the transmission (a.k.a. transmittance weight) between the current path segment
|
|
* and the next one, when the path is inside or passes a volume. */
|
|
ccl_device_forceinline void guiding_record_volume_transmission(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const float3 transmittance_weight)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
if (state->guiding.path_segment) {
|
|
// TODO (sherholz): need to find a better way to avoid this check
|
|
if ((transmittance_weight[0] < 0.0f || !std::isfinite(transmittance_weight[0]) ||
|
|
std::isnan(transmittance_weight[0])) ||
|
|
(transmittance_weight[1] < 0.0f || !std::isfinite(transmittance_weight[1]) ||
|
|
std::isnan(transmittance_weight[1])) ||
|
|
(transmittance_weight[2] < 0.0f || !std::isfinite(transmittance_weight[2]) ||
|
|
std::isnan(transmittance_weight[2])))
|
|
{
|
|
}
|
|
else {
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment,
|
|
guiding_vec3f(transmittance_weight));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records the emission of a volume at the vertex of the current path segment. */
|
|
ccl_device_forceinline void guiding_record_volume_emission(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState
|
|
state,
|
|
ccl_attr_maybe_unused const Spectrum Le)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
if (state->guiding.path_segment) {
|
|
const float3 Le_rgb = spectrum_to_rgb(Le);
|
|
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, guiding_vec3f(Le_rgb));
|
|
openpgl::cpp::SetMiWeight(state->guiding.path_segment, 1.0f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Record Light Interactions */
|
|
|
|
/* Adds a pseudo path vertex/segment when intersecting a virtual light source.
|
|
* (e.g., area, sphere, or disk light). This call is often followed
|
|
* a call of guiding_record_surface_emission, if the intersected light source
|
|
* emits light in the direction of the path. */
|
|
ccl_device_forceinline void guiding_record_light_surface_segment(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const ccl_private Intersection *ccl_restrict isect)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const pgl_vec3f zero = guiding_vec3f(zero_float3());
|
|
const pgl_vec3f one = guiding_vec3f(one_float3());
|
|
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
|
|
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
|
|
const float3 P = ray_P + isect->t * ray_D;
|
|
|
|
state->guiding.path_segment = kg->opgl_path_segment_storage->NextSegment();
|
|
/* FIXME: investigate and fix why state->guiding.path_segment could be nullptr. */
|
|
kernel_assert(state->guiding.path_segment != nullptr);
|
|
if (state->guiding.path_segment != nullptr) {
|
|
kernel_assert(is_guiding_valid(P));
|
|
float3 p = clamp_guiding_position(P);
|
|
openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(p));
|
|
openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(-ray_D));
|
|
openpgl::cpp::SetNormal(state->guiding.path_segment, guiding_vec3f(-ray_D));
|
|
openpgl::cpp::SetDirectionIn(state->guiding.path_segment, guiding_vec3f(ray_D));
|
|
openpgl::cpp::SetPDFDirectionIn(state->guiding.path_segment, 1.0f);
|
|
openpgl::cpp::SetVolumeScatter(state->guiding.path_segment, false);
|
|
openpgl::cpp::SetScatteredContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetDirectContribution(state->guiding.path_segment, zero);
|
|
openpgl::cpp::SetTransmittanceWeight(state->guiding.path_segment, one);
|
|
openpgl::cpp::SetScatteringWeight(state->guiding.path_segment, one);
|
|
openpgl::cpp::SetEta(state->guiding.path_segment, 1.0f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Records/Adds a final path segment when the path leaves the scene and
|
|
* intersects with a background light (e.g., background color,
|
|
* sun light, or env map). The vertex for this segment is placed along
|
|
* the current ray far out the scene. */
|
|
ccl_device_forceinline void guiding_record_background(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const Spectrum L,
|
|
ccl_attr_maybe_unused const float mis_weight)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
const float3 L_rgb = spectrum_to_rgb(L);
|
|
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
|
|
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
|
|
float3 P = ray_P + GUIDING_MAX_LIGHT_DISTANCE * ray_D;
|
|
kernel_assert(is_guiding_valid(P));
|
|
P = clamp_guiding_position(P);
|
|
const float3 normal = make_float3(0.0f, 0.0f, 1.0f);
|
|
|
|
openpgl::cpp::PathSegment background_segment;
|
|
openpgl::cpp::SetPosition(&background_segment, guiding_vec3f(P));
|
|
openpgl::cpp::SetNormal(&background_segment, guiding_vec3f(normal));
|
|
openpgl::cpp::SetDirectionOut(&background_segment, guiding_vec3f(-ray_D));
|
|
openpgl::cpp::SetDirectContribution(&background_segment, guiding_vec3f(L_rgb));
|
|
openpgl::cpp::SetMiWeight(&background_segment, mis_weight);
|
|
kg->opgl_path_segment_storage->AddSegment(background_segment);
|
|
#endif
|
|
}
|
|
|
|
/* Records direct lighting from either next event estimation or a dedicated BSDF
|
|
* sampled shadow ray. */
|
|
ccl_device_forceinline void guiding_record_direct_light(
|
|
ccl_attr_maybe_unused KernelGlobals kg, ccl_attr_maybe_unused IntegratorShadowState state)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag);
|
|
if (path_flag & PATH_RAY_SHADOW_FOR_AO) {
|
|
return;
|
|
}
|
|
if (state->shadow_path.path_segment) {
|
|
/* Estimating the out-scattered radiance at the current path segment.
|
|
* NOTE: in the light linking case this estimate is the incoming radiance.*/
|
|
const Spectrum Lo = safe_divide_color(INTEGRATOR_STATE(state, shadow_path, throughput),
|
|
INTEGRATOR_STATE(state, shadow_path, unlit_throughput));
|
|
const float3 Lo_rgb = spectrum_to_rgb(Lo);
|
|
|
|
if (!(path_flag & PATH_RAY_SHADOW_FOR_LIGHT_LINKING)) {
|
|
/* Scattered contribution of a next event estimation (i.e., a direct light estimate
|
|
* scattered at the current path vertex towards the previous vertex). */
|
|
openpgl::cpp::AddScatteredContribution(state->shadow_path.path_segment,
|
|
guiding_vec3f(Lo_rgb));
|
|
}
|
|
else {
|
|
/* The contribution comes from a light linking forward ray. We need to record this
|
|
* contribution as scattered contribution at the current path segment. To be able to guide
|
|
* towards this light source we add a directional sample directly to the guiding
|
|
* training data storage. */
|
|
const float3 scattering_weight = make_float3(
|
|
state->shadow_path.path_segment->scatteringWeight);
|
|
openpgl::cpp::AddScatteredContribution(state->shadow_path.path_segment,
|
|
guiding_vec3f(scattering_weight * Lo_rgb));
|
|
|
|
/* Adding an additional training sample for the guiding cache in the direction of the linked
|
|
* light source. */
|
|
float dist = INTEGRATOR_STATE(state, shadow_ray, tmax);
|
|
openpgl::cpp::SampleData pgl_sample;
|
|
pgl_sample.direction = state->shadow_path.path_segment->directionIn;
|
|
pgl_sample.pdf = state->shadow_path.path_segment->pdfDirectionIn;
|
|
pgl_sample.position = state->shadow_path.path_segment->position;
|
|
pgl_sample.flags = state->shadow_path.path_segment->volumeScatter ?
|
|
openpgl::cpp::SampleData::EInsideVolume :
|
|
0;
|
|
pgl_sample.weight = safe_divide(reduce_max(Lo_rgb), pgl_sample.pdf);
|
|
if (!kernel_data.integrator.use_guiding_mis_weights) {
|
|
const float mis_weight = INTEGRATOR_STATE(
|
|
state, shadow_path, guiding_light_linking_mis_weight);
|
|
pgl_sample.weight = safe_divide(pgl_sample.weight, mis_weight);
|
|
}
|
|
|
|
/* Checking if the light source is an infinite one (e.g., background, sun). If so the
|
|
* distance is set to GUIDING_MAX_LIGHT_DISTANCE.
|
|
* NOTE: checking for FLT_MAX is not working.*/
|
|
pgl_sample.distance = dist > GUIDING_FLT_LARGE ? GUIDING_MAX_LIGHT_DISTANCE : dist;
|
|
kg->opgl_sample_data_storage->AddSample(pgl_sample);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Record Russian Roulette */
|
|
/* Records the probability of continuing the path at the current path segment. */
|
|
ccl_device_forceinline void guiding_record_continuation_probability(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const float continuation_probability)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
kernel_assert((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) == 0);
|
|
|
|
if (state->guiding.path_segment) {
|
|
openpgl::cpp::SetRussianRouletteProbability(state->guiding.path_segment,
|
|
continuation_probability);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Path guiding debug render passes. */
|
|
|
|
/* Write a set of path guiding related debug information (e.g., guiding probability at first
|
|
* bounce) into separate rendering passes. */
|
|
ccl_device_forceinline void guiding_write_debug_passes(
|
|
ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused IntegratorState state,
|
|
ccl_attr_maybe_unused const ccl_private ShaderData *sd,
|
|
ccl_attr_maybe_unused ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
# ifdef WITH_CYCLES_DEBUG
|
|
if (!kernel_data.integrator.train_guiding) {
|
|
return;
|
|
}
|
|
|
|
if (INTEGRATOR_STATE(state, path, bounce) != 0) {
|
|
return;
|
|
}
|
|
|
|
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
|
|
|
if (kernel_data.film.pass_guiding_probability != PASS_UNUSED) {
|
|
float guiding_prob = state->guiding.surface_guiding_sampling_prob;
|
|
film_write_pass_float(buffer + kernel_data.film.pass_guiding_probability, guiding_prob);
|
|
}
|
|
|
|
if (kernel_data.film.pass_guiding_avg_roughness != PASS_UNUSED) {
|
|
float avg_roughness = 0.0f;
|
|
float sum_sample_weight = 0.0f;
|
|
for (int i = 0; i < sd->num_closure; i++) {
|
|
const ccl_private ShaderClosure *sc = &sd->closure[i];
|
|
|
|
if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
|
|
continue;
|
|
}
|
|
avg_roughness += sc->sample_weight * bsdf_get_specular_roughness_squared(sc);
|
|
sum_sample_weight += sc->sample_weight;
|
|
}
|
|
|
|
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);
|
|
}
|
|
# else
|
|
(void)kg;
|
|
(void)state;
|
|
(void)sd;
|
|
(void)render_buffer;
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
/* Guided BSDFs */
|
|
|
|
ccl_device_forceinline bool guiding_bsdf_init(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float3 P,
|
|
ccl_attr_maybe_unused const float3 N,
|
|
ccl_attr_maybe_unused ccl_private float &rand)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
if (guiding_ssd->Init(guiding_guiding_field, guiding_point3f(P), rand)) {
|
|
guiding_ssd->ApplyCosineProduct(guiding_point3f(N));
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
ccl_device_forceinline float guiding_bsdf_sample(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float2 rand_bsdf,
|
|
ccl_attr_maybe_unused ccl_private float3 *wo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
pgl_vec3f pgl_wo;
|
|
const pgl_point2f rand = {rand_bsdf.x, rand_bsdf.y};
|
|
const float pdf = guiding_ssd->SamplePDF(rand, pgl_wo);
|
|
*wo = make_float3(pgl_wo.x, pgl_wo.y, pgl_wo.z);
|
|
return pdf;
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
ccl_device_forceinline float guiding_bsdf_pdf(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float3 wo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
return guiding_ssd->PDF(guiding_vec3f(wo));
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
ccl_device_forceinline float guiding_surface_incoming_radiance_pdf(
|
|
ccl_attr_maybe_unused KernelGlobals kg, ccl_attr_maybe_unused const float3 wo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
return guiding_ssd->IncomingRadiancePDF(guiding_vec3f(wo));
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
/* Guided Volume Phases */
|
|
|
|
ccl_device_forceinline bool guiding_phase_init(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float3 P,
|
|
ccl_attr_maybe_unused const float3 D,
|
|
ccl_attr_maybe_unused const float g,
|
|
ccl_attr_maybe_unused ccl_private float &rand)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
/* we do not need to guide almost delta phase functions */
|
|
if (fabsf(g) >= 0.99f) {
|
|
return false;
|
|
}
|
|
|
|
if (guiding_vsd->Init(guiding_guiding_field, guiding_point3f(P), rand)) {
|
|
guiding_vsd->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D), g);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
ccl_device_forceinline float guiding_phase_sample(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float2 rand_phase,
|
|
ccl_attr_maybe_unused ccl_private float3 *wo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
pgl_vec3f pgl_wo;
|
|
const pgl_point2f rand = {rand_phase.x, rand_phase.y};
|
|
const float pdf = guiding_vsd->SamplePDF(rand, pgl_wo);
|
|
*wo = make_float3(pgl_wo.x, pgl_wo.y, pgl_wo.z);
|
|
return pdf;
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
ccl_device_forceinline float guiding_phase_pdf(ccl_attr_maybe_unused KernelGlobals kg,
|
|
ccl_attr_maybe_unused const float3 wo)
|
|
{
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
return guiding_vsd->PDF(guiding_vec3f(wo));
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|