Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/kernel/integrator')
-rw-r--r--intern/cycles/kernel/integrator/displacement_shader.h4
-rw-r--r--intern/cycles/kernel/integrator/guiding.h547
-rw-r--r--intern/cycles/kernel/integrator/init_from_bake.h54
-rw-r--r--intern/cycles/kernel/integrator/intersect_closest.h11
-rw-r--r--intern/cycles/kernel/integrator/mnee.h12
-rw-r--r--intern/cycles/kernel/integrator/path_state.h18
-rw-r--r--intern/cycles/kernel/integrator/shade_background.h3
-rw-r--r--intern/cycles/kernel/integrator/shade_light.h3
-rw-r--r--intern/cycles/kernel/integrator/shade_shadow.h2
-rw-r--r--intern/cycles/kernel/integrator/shade_surface.h108
-rw-r--r--intern/cycles/kernel/integrator/shade_volume.h125
-rw-r--r--intern/cycles/kernel/integrator/shadow_state_template.h10
-rw-r--r--intern/cycles/kernel/integrator/state.h4
-rw-r--r--intern/cycles/kernel/integrator/state_flow.h6
-rw-r--r--intern/cycles/kernel/integrator/state_template.h33
-rw-r--r--intern/cycles/kernel/integrator/subsurface.h3
-rw-r--r--intern/cycles/kernel/integrator/subsurface_disk.h9
-rw-r--r--intern/cycles/kernel/integrator/subsurface_random_walk.h16
-rw-r--r--intern/cycles/kernel/integrator/surface_shader.h307
-rw-r--r--intern/cycles/kernel/integrator/volume_shader.h271
20 files changed, 1419 insertions, 127 deletions
diff --git a/intern/cycles/kernel/integrator/displacement_shader.h b/intern/cycles/kernel/integrator/displacement_shader.h
index 839dfe244ac..a6e9d674396 100644
--- a/intern/cycles/kernel/integrator/displacement_shader.h
+++ b/intern/cycles/kernel/integrator/displacement_shader.h
@@ -24,8 +24,8 @@ ccl_device void displacement_shader_eval(KernelGlobals kg,
/* this will modify sd->P */
#ifdef __OSL__
- if (kg->osl) {
- OSLShader::eval_displacement(kg, state, sd);
+ if (kernel_data.kernel_features & KERNEL_FEATURE_OSL) {
+ osl_eval_nodes<SHADER_TYPE_DISPLACEMENT>(kg, state, sd, 0);
}
else
#endif
diff --git a/intern/cycles/kernel/integrator/guiding.h b/intern/cycles/kernel/integrator/guiding.h
new file mode 100644
index 00000000000..634bba2a9b4
--- /dev/null
+++ b/intern/cycles/kernel/integrator/guiding.h
@@ -0,0 +1,547 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+#pragma once
+
+#include "kernel/closure/alloc.h"
+#include "kernel/closure/bsdf.h"
+#include "kernel/film/write.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Utilities. */
+
+#if defined(__PATH_GUIDING__)
+static pgl_vec3f guiding_vec3f(const float3 v)
+{
+ return openpgl::cpp::Vector3(v.x, v.y, v.z);
+}
+
+static pgl_point3f guiding_point3f(const float3 v)
+{
+ return openpgl::cpp::Point3(v.x, v.y, v.z);
+}
+#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(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private const ShaderData *sd)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ 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();
+ openpgl::cpp::SetPosition(state->guiding.path_segment, guiding_point3f(sd->P));
+ openpgl::cpp::SetDirectionOut(state->guiding.path_segment, guiding_vec3f(sd->I));
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private const ShaderData *sd,
+ const Spectrum weight,
+ const float pdf,
+ const float3 N,
+ const float3 omega_in,
+ const float2 roughness,
+ const float eta)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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);
+
+ 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(omega_in));
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ const Spectrum Le,
+ const float mis_weight)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ const float3 P,
+ const float3 I)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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();
+ 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 transmission of the path at the point of entry while passing
+ * the surface boundary.*/
+ccl_device_forceinline void guiding_record_bssrdf_weight(KernelGlobals kg,
+ IntegratorState state,
+ const Spectrum weight,
+ const Spectrum albedo)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ /* 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);
+
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ const float pdf,
+ const float3 N,
+ const float3 omega_in,
+ const Spectrum weight,
+ const Spectrum albedo)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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);
+
+ 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(omega_in));
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ const float3 P,
+ const float3 I)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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();
+
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private const ShaderData *sd,
+ const Spectrum weight,
+ const float pdf,
+ const float3 omega_in,
+ const float roughness)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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);
+
+ 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(omega_in));
+ 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::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(KernelGlobals kg,
+ IntegratorState state,
+ const float3 transmittance_weight)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ 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]) ||
+ std::isnan(transmittance_weight[0])) ||
+ (transmittance_weight[1] < 0.f || !std::isfinite(transmittance_weight[1]) ||
+ std::isnan(transmittance_weight[1])) ||
+ (transmittance_weight[2] < 0.f || !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(KernelGlobals kg,
+ IntegratorState state,
+ const Spectrum Le)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ 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(
+ KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ 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();
+ 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,
+ * distant 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(KernelGlobals kg,
+ IntegratorState state,
+ const Spectrum L,
+ const float mis_weight)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ 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);
+ const float3 P = ray_P + (1e6f) * ray_D;
+ 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 the scattered contribution of a next event estimation
+ * (i.e., a direct light estimate scattered at the current path vertex
+ * towards the previous vertex).*/
+ccl_device_forceinline void guiding_record_direct_light(KernelGlobals kg,
+ IntegratorShadowState state)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+ if (state->shadow_path.path_segment) {
+ 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);
+ openpgl::cpp::AddScatteredContribution(state->shadow_path.path_segment, guiding_vec3f(Lo_rgb));
+ }
+#endif
+}
+
+/* Record Russian Roulette */
+/* Records the probability of continuing the path at the current path segment. */
+ccl_device_forceinline void guiding_record_continuation_probability(
+ KernelGlobals kg, IntegratorState state, const float continuation_probability)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!kernel_data.integrator.train_guiding) {
+ return;
+ }
+
+ 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(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private const ShaderData *sd,
+ 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;
+ }
+
+ const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index);
+ const uint64_t render_buffer_offset = (uint64_t)render_pixel_index *
+ kernel_data.film.pass_stride;
+ ccl_global float *buffer = render_buffer + render_buffer_offset;
+
+ 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++) {
+ ccl_private const 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.f ? avg_roughness / sum_sample_weight : 0.f;
+
+ film_write_pass_float(buffer + kernel_data.film.pass_guiding_avg_roughness, avg_roughness);
+ }
+# endif
+#endif
+}
+
+/* Guided BSDFs */
+
+ccl_device_forceinline bool guiding_bsdf_init(KernelGlobals kg,
+ IntegratorState state,
+ const float3 P,
+ const float3 N,
+ ccl_private float &rand)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (kg->opgl_surface_sampling_distribution->Init(
+ kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
+ kg->opgl_surface_sampling_distribution->ApplyCosineProduct(guiding_point3f(N));
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+ccl_device_forceinline float guiding_bsdf_sample(KernelGlobals kg,
+ IntegratorState state,
+ const float2 rand_bsdf,
+ ccl_private float3 *omega_in)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ pgl_vec3f wo;
+ const pgl_point2f rand = openpgl::cpp::Point2(rand_bsdf.x, rand_bsdf.y);
+ const float pdf = kg->opgl_surface_sampling_distribution->SamplePDF(rand, wo);
+ *omega_in = make_float3(wo.x, wo.y, wo.z);
+ return pdf;
+#else
+ return 0.0f;
+#endif
+}
+
+ccl_device_forceinline float guiding_bsdf_pdf(KernelGlobals kg,
+ IntegratorState state,
+ const float3 omega_in)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ return kg->opgl_surface_sampling_distribution->PDF(guiding_vec3f(omega_in));
+#else
+ return 0.0f;
+#endif
+}
+
+/* Guided Volume Phases */
+
+ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg,
+ IntegratorState state,
+ const float3 P,
+ const float3 D,
+ const float g,
+ 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 (kg->opgl_volume_sampling_distribution->Init(
+ kg->opgl_guiding_field, guiding_point3f(P), rand, true)) {
+ kg->opgl_volume_sampling_distribution->ApplySingleLobeHenyeyGreensteinProduct(guiding_vec3f(D),
+ g);
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+ccl_device_forceinline float guiding_phase_sample(KernelGlobals kg,
+ IntegratorState state,
+ const float2 rand_phase,
+ ccl_private float3 *omega_in)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ pgl_vec3f wo;
+ const pgl_point2f rand = openpgl::cpp::Point2(rand_phase.x, rand_phase.y);
+ const float pdf = kg->opgl_volume_sampling_distribution->SamplePDF(rand, wo);
+ *omega_in = make_float3(wo.x, wo.y, wo.z);
+ return pdf;
+#else
+ return 0.0f;
+#endif
+}
+
+ccl_device_forceinline float guiding_phase_pdf(KernelGlobals kg,
+ IntegratorState state,
+ const float3 omega_in)
+{
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ return kg->opgl_volume_sampling_distribution->PDF(guiding_vec3f(omega_in));
+#else
+ return 0.0f;
+#endif
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h
index eca2c0b9ffb..cc3fbe3fe39 100644
--- a/intern/cycles/kernel/integrator/init_from_bake.h
+++ b/intern/cycles/kernel/integrator/init_from_bake.h
@@ -156,6 +156,13 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
u = v;
v = 1.0f - tmp - v;
+ const float tmpdx = dudx;
+ const float tmpdy = dudy;
+ dudx = dvdx;
+ dudy = dvdy;
+ dvdx = -tmpdx - dvdx;
+ dvdy = -tmpdy - dvdy;
+
/* Position and normal on triangle. */
const int object = kernel_data.bake.object_index;
float3 P, Ng;
@@ -210,8 +217,51 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
/* Setup ray. */
Ray ray ccl_optional_struct_init;
- ray.P = P + N;
- ray.D = -N;
+
+ if (kernel_data.bake.use_camera) {
+ float3 D = camera_direction_from_point(kg, P);
+
+ const float DN = dot(D, N);
+
+ /* Nudge camera direction, so that the faces facing away from the camera still have
+ * somewhat usable shading. (Otherwise, glossy faces would be simply black.)
+ *
+ * The surface normal offset affects smooth surfaces. Lower values will make
+ * smooth surfaces more faceted, but higher values may show up from the camera
+ * at grazing angles.
+ *
+ * This value can actually be pretty high before it's noticeably wrong. */
+ const float surface_normal_offset = 0.2f;
+
+ /* Keep the ray direction at least `surface_normal_offset` "above" the smooth normal. */
+ if (DN <= surface_normal_offset) {
+ D -= N * (DN - surface_normal_offset);
+ D = normalize(D);
+ }
+
+ /* On the backside, just lerp towards the surface normal for the ray direction,
+ * as DN goes from 0.0 to -1.0. */
+ if (DN <= 0.0f) {
+ D = normalize(mix(D, N, -DN));
+ }
+
+ /* We don't want to bake the back face, so make sure the ray direction never
+ * goes behind the geometry (flat) normal. This is a fail-safe, and should rarely happen. */
+ const float true_normal_epsilon = 0.00001f;
+
+ if (dot(D, Ng) <= true_normal_epsilon) {
+ D -= Ng * (dot(D, Ng) - true_normal_epsilon);
+ D = normalize(D);
+ }
+
+ ray.P = P + D;
+ ray.D = -D;
+ }
+ else {
+ ray.P = P + N;
+ ray.D = -N;
+ }
+
ray.tmin = 0.0f;
ray.tmax = FLT_MAX;
ray.time = 0.5f;
diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h
index c7c3d74fa21..b9a81e25bcc 100644
--- a/intern/cycles/kernel/integrator/intersect_closest.h
+++ b/intern/cycles/kernel/integrator/intersect_closest.h
@@ -7,6 +7,7 @@
#include "kernel/film/light_passes.h"
+#include "kernel/integrator/guiding.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/shadow_catcher.h"
@@ -48,13 +49,15 @@ ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg,
* surfaces in front of emission do we need to evaluate the shader, since we
* perform MIS as part of indirect rays. */
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
- const float probability = path_state_continuation_probability(kg, state, path_flag);
- INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = probability;
+ const float continuation_probability = path_state_continuation_probability(kg, state, path_flag);
+ INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = continuation_probability;
- if (probability != 1.0f) {
+ guiding_record_continuation_probability(kg, state, continuation_probability);
+
+ if (continuation_probability != 1.0f) {
const float terminate = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE);
- if (probability == 0.0f || terminate >= probability) {
+ if (continuation_probability == 0.0f || terminate >= continuation_probability) {
if (shader_flags & SD_HAS_EMISSION) {
/* Mark path to be terminated right after shader evaluation on the surface. */
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE;
diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h
index a0ad7afe591..23885306885 100644
--- a/intern/cycles/kernel/integrator/mnee.h
+++ b/intern/cycles/kernel/integrator/mnee.h
@@ -279,7 +279,15 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg,
}
/* Compute constraint derivatives. */
-ccl_device_forceinline bool mnee_compute_constraint_derivatives(
+
+# if defined(__KERNEL_METAL__)
+/* Temporary workaround for front-end compilation bug (incorrect MNEE rendering when this is
+ * inlined). */
+__attribute__((noinline))
+# else
+ccl_device_forceinline
+# endif
+bool mnee_compute_constraint_derivatives(
int vertex_count,
ccl_private ManifoldVertex *vertices,
ccl_private const float3 &surface_sample_pos,
@@ -807,7 +815,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
float3 wo = normalize_len(vertices[0].p - sd->P, &wo_len);
/* Initialize throughput and evaluate receiver bsdf * |n.wo|. */
- surface_shader_bsdf_eval(kg, sd, wo, false, throughput, ls->shader);
+ surface_shader_bsdf_eval(kg, state, sd, wo, throughput, ls->shader);
/* Update light sample with new position / direct.ion
* and keep pdf in vertex area measure */
diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h
index 54560905397..7197f0f2f3a 100644
--- a/intern/cycles/kernel/integrator/path_state.h
+++ b/intern/cycles/kernel/integrator/path_state.h
@@ -56,6 +56,18 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f;
INTEGRATOR_STATE_WRITE(state, path, throughput) = one_spectrum();
+#ifdef __PATH_GUIDING__
+ INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) = 1.0f;
+ INTEGRATOR_STATE_WRITE(state, guiding, path_segment) = nullptr;
+ INTEGRATOR_STATE_WRITE(state, guiding, use_surface_guiding) = false;
+ INTEGRATOR_STATE_WRITE(state, guiding, sample_surface_guiding_rand) = 0.5f;
+ INTEGRATOR_STATE_WRITE(state, guiding, surface_guiding_sampling_prob) = 0.0f;
+ INTEGRATOR_STATE_WRITE(state, guiding, bssrdf_sampling_prob) = 0.0f;
+ INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
+ INTEGRATOR_STATE_WRITE(state, guiding, sample_volume_guiding_rand) = 0.5f;
+ INTEGRATOR_STATE_WRITE(state, guiding, volume_guiding_sampling_prob) = 0.0f;
+#endif
+
#ifdef __MNEE__
INTEGRATOR_STATE_WRITE(state, path, mnee) = 0;
#endif
@@ -249,7 +261,11 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals kg,
/* Probabilistic termination: use sqrt() to roughly match typical view
* transform and do path termination a bit later on average. */
- return min(sqrtf(reduce_max(fabs(INTEGRATOR_STATE(state, path, throughput)))), 1.0f);
+ Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ throughput *= INTEGRATOR_STATE(state, path, unguided_throughput);
+#endif
+ return min(sqrtf(reduce_max(fabs(throughput))), 1.0f);
}
ccl_device_inline bool path_state_ao_bounce(KernelGlobals kg, ConstIntegratorState state)
diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h
index 30ce0999258..8fc5689683a 100644
--- a/intern/cycles/kernel/integrator/shade_background.h
+++ b/intern/cycles/kernel/integrator/shade_background.h
@@ -5,6 +5,7 @@
#include "kernel/film/light_passes.h"
+#include "kernel/integrator/guiding.h"
#include "kernel/integrator/surface_shader.h"
#include "kernel/light/light.h"
@@ -124,6 +125,7 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf);
}
+ guiding_record_background(kg, state, L, mis_weight);
L *= mis_weight;
}
@@ -185,6 +187,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
}
/* Write to render buffer. */
+ guiding_record_background(kg, state, light_eval, mis_weight);
film_write_surface_emission(
kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup);
}
diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h
index f2d65eddfbb..e0b0500dc78 100644
--- a/intern/cycles/kernel/integrator/shade_light.h
+++ b/intern/cycles/kernel/integrator/shade_light.h
@@ -18,6 +18,8 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
Intersection isect ccl_optional_struct_init;
integrator_state_read_isect(kg, state, &isect);
+ guiding_record_light_surface_segment(kg, state, &isect);
+
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float ray_time = INTEGRATOR_STATE(state, ray, time);
@@ -66,6 +68,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
}
/* Write to render buffer. */
+ guiding_record_surface_emission(kg, state, light_eval, mis_weight);
film_write_surface_emission(kg, state, light_eval, mis_weight, render_buffer, ls.group);
}
diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h
index ba18aed6ff0..bedb15ddf89 100644
--- a/intern/cycles/kernel/integrator/shade_shadow.h
+++ b/intern/cycles/kernel/integrator/shade_shadow.h
@@ -3,6 +3,7 @@
#pragma once
+#include "kernel/integrator/guiding.h"
#include "kernel/integrator/shade_volume.h"
#include "kernel/integrator/surface_shader.h"
#include "kernel/integrator/volume_stack.h"
@@ -165,6 +166,7 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg,
return;
}
else {
+ guiding_record_direct_light(kg, state);
film_write_direct_light(kg, state, render_buffer);
integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW);
return;
diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h
index c19f56a9b70..067d35ef9e3 100644
--- a/intern/cycles/kernel/integrator/shade_surface.h
+++ b/intern/cycles/kernel/integrator/shade_surface.h
@@ -9,6 +9,7 @@
#include "kernel/integrator/mnee.h"
+#include "kernel/integrator/guiding.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/subsurface.h"
#include "kernel/integrator/surface_shader.h"
@@ -101,7 +102,7 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg,
}
ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
- ConstIntegratorState state,
+ IntegratorState state,
ccl_private const ShaderData *sd,
ccl_global float *ccl_restrict
render_buffer)
@@ -128,6 +129,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf);
}
+ guiding_record_surface_emission(kg, state, L, mis_weight);
film_write_surface_emission(
kg, state, L, mis_weight, render_buffer, object_lightgroup(kg, sd->object));
}
@@ -171,7 +173,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
Ray ray ccl_optional_struct_init;
BsdfEval bsdf_eval ccl_optional_struct_init;
- const bool is_transmission = surface_shader_is_transmission(sd, ls.D);
+
+ const bool is_transmission = dot(ls.D, sd->N) < 0.0f;
#ifdef __MNEE__
int mnee_vertex_count = 0;
@@ -182,13 +185,15 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
const bool use_caustics = kernel_data_fetch(lights, ls.lamp).use_caustics;
if (use_caustics) {
/* Are we on a caustic caster? */
- if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER))
+ if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER)) {
return;
+ }
/* Are we on a caustic receiver? */
- if (!is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER))
+ if (!is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER)) {
mnee_vertex_count = kernel_path_mnee_sample(
kg, state, sd, emission_sd, rng_state, &ls, &bsdf_eval);
+ }
}
}
}
@@ -207,8 +212,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
}
/* Evaluate BSDF. */
- const float bsdf_pdf = surface_shader_bsdf_eval(
- kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader);
+ const float bsdf_pdf = surface_shader_bsdf_eval(kg, state, sd, ls.D, &bsdf_eval, ls.shader);
bsdf_eval_mul(&bsdf_eval, light_eval / ls.pdf);
if (ls.shader & SHADER_USE_MIS) {
@@ -256,8 +260,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
/* Copy state from main path to shadow path. */
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
- const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) *
- bsdf_eval_sum(&bsdf_eval);
+ const Spectrum unlit_throughput = INTEGRATOR_STATE(state, path, throughput);
+ const Spectrum throughput = unlit_throughput * bsdf_eval_sum(&bsdf_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
PackedSpectrum pass_diffuse_weight;
@@ -327,6 +331,11 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
shadow_state, shadow_path, lightgroup) = (ls.type != LIGHT_BACKGROUND) ?
ls.group + 1 :
kernel_data.background.lightgroup + 1;
+#ifdef __PATH_GUIDING__
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
+ state, guiding, path_segment);
+#endif
}
/* Path tracing: bounce off or through surface with new direction. */
@@ -352,16 +361,52 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
#endif
/* BSDF closure, sample direction. */
- float bsdf_pdf;
+ float bsdf_pdf = 0.0f, unguided_bsdf_pdf = 0.0f;
BsdfEval bsdf_eval ccl_optional_struct_init;
float3 bsdf_omega_in ccl_optional_struct_init;
int label;
- label = surface_shader_bsdf_sample_closure(
- kg, sd, sc, rand_bsdf, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf);
+ float2 bsdf_sampled_roughness = make_float2(1.0f, 1.0f);
+ float bsdf_eta = 1.0f;
+
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (kernel_data.integrator.use_surface_guiding) {
+ label = surface_shader_bsdf_guided_sample_closure(kg,
+ state,
+ sd,
+ sc,
+ rand_bsdf,
+ &bsdf_eval,
+ &bsdf_omega_in,
+ &bsdf_pdf,
+ &unguided_bsdf_pdf,
+ &bsdf_sampled_roughness,
+ &bsdf_eta);
+
+ if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
+ return LABEL_NONE;
+ }
- if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
- return LABEL_NONE;
+ INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= bsdf_pdf / unguided_bsdf_pdf;
+ }
+ else
+#endif
+ {
+ label = surface_shader_bsdf_sample_closure(kg,
+ sd,
+ sc,
+ rand_bsdf,
+ &bsdf_eval,
+ &bsdf_omega_in,
+ &bsdf_pdf,
+ &bsdf_sampled_roughness,
+ &bsdf_eta);
+
+ if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
+ return LABEL_NONE;
+ }
+
+ unguided_bsdf_pdf = bsdf_pdf;
}
if (label & LABEL_TRANSPARENT) {
@@ -381,9 +426,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
}
/* Update throughput. */
- Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
- throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf;
- INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput;
+ const Spectrum bsdf_weight = bsdf_eval_sum(&bsdf_eval) / bsdf_pdf;
+ INTEGRATOR_STATE_WRITE(state, path, throughput) *= bsdf_weight;
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
@@ -398,10 +442,21 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
if (!(label & LABEL_TRANSPARENT)) {
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
- bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
+ unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
}
path_state_next(kg, state, label);
+
+ guiding_record_surface_bounce(kg,
+ state,
+ sd,
+ bsdf_weight,
+ bsdf_pdf,
+ sd->N,
+ normalize(bsdf_omega_in),
+ bsdf_sampled_roughness,
+ bsdf_eta);
+
return label;
}
@@ -423,14 +478,15 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState
ccl_device_forceinline bool integrate_surface_terminate(IntegratorState state,
const uint32_t path_flag)
{
- const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
- 0.0f :
- INTEGRATOR_STATE(state, path, continuation_probability);
- if (probability == 0.0f) {
+ const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
+ 0.0f :
+ INTEGRATOR_STATE(
+ state, path, continuation_probability);
+ if (continuation_probability == 0.0f) {
return true;
}
- else if (probability != 1.0f) {
- INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability;
+ else if (continuation_probability != 1.0f) {
+ INTEGRATOR_STATE_WRITE(state, path, throughput) /= continuation_probability;
}
return false;
@@ -538,6 +594,8 @@ ccl_device bool integrate_surface(KernelGlobals kg,
#ifdef __VOLUME__
if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
+ guiding_record_surface_segment(kg, state, &sd);
+
#ifdef __SUBSURFACE__
/* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
@@ -603,6 +661,10 @@ ccl_device bool integrate_surface(KernelGlobals kg,
RNGState rng_state;
path_state_rng_load(state, &rng_state);
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ surface_shader_prepare_guiding(kg, state, &sd, &rng_state);
+ guiding_write_debug_passes(kg, state, &sd, render_buffer);
+#endif
/* Direct light. */
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
integrate_surface_direct_light<node_feature_mask>(kg, state, &sd, &rng_state);
diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h
index aaef92729d6..a8324cda2dc 100644
--- a/intern/cycles/kernel/integrator/shade_volume.h
+++ b/intern/cycles/kernel/integrator/shade_volume.h
@@ -7,6 +7,7 @@
#include "kernel/film/denoising_passes.h"
#include "kernel/film/light_passes.h"
+#include "kernel/integrator/guiding.h"
#include "kernel/integrator/intersect_closest.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/volume_shader.h"
@@ -612,6 +613,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
const Spectrum emission = volume_emission_integrate(
&coeff, closure_flag, transmittance, dt);
accum_emission += result.indirect_throughput * emission;
+ guiding_record_volume_emission(kg, state, emission);
}
}
@@ -761,7 +763,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
/* Evaluate BSDF. */
BsdfEval phase_eval ccl_optional_struct_init;
- const float phase_pdf = volume_shader_phase_eval(kg, sd, phases, ls->D, &phase_eval);
+ float phase_pdf = volume_shader_phase_eval(kg, state, sd, phases, ls->D, &phase_eval);
if (ls->shader & SHADER_USE_MIS) {
float mis_weight = light_sample_mis_weight_nee(kg, ls->pdf, phase_pdf);
@@ -848,6 +850,12 @@ ccl_device_forceinline void integrate_volume_direct_light(
ls->group + 1 :
kernel_data.background.lightgroup + 1;
+# ifdef __PATH_GUIDING__
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = throughput;
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
+ state, guiding, path_segment);
+# endif
+
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
}
@@ -861,18 +869,54 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
{
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT);
- const float2 rand_phase = path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE);
+ float2 rand_phase = path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE);
+
+ ccl_private const ShaderVolumeClosure *svc = volume_shader_phase_pick(phases, &rand_phase);
/* Phase closure, sample direction. */
- float phase_pdf;
+ float phase_pdf = 0.0f, unguided_phase_pdf = 0.0f;
BsdfEval phase_eval ccl_optional_struct_init;
float3 phase_omega_in ccl_optional_struct_init;
+ float sampled_roughness = 1.0f;
+ int label;
+
+# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (kernel_data.integrator.use_guiding) {
+ label = volume_shader_phase_guided_sample(kg,
+ state,
+ sd,
+ svc,
+ rand_phase,
+ &phase_eval,
+ &phase_omega_in,
+ &phase_pdf,
+ &unguided_phase_pdf,
+ &sampled_roughness);
+
+ if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
+ return false;
+ }
- const int label = volume_shader_phase_sample(
- kg, sd, phases, rand_phase, &phase_eval, &phase_omega_in, &phase_pdf);
+ INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= phase_pdf / unguided_phase_pdf;
+ }
+ else
+# endif
+ {
+ label = volume_shader_phase_sample(kg,
+ sd,
+ phases,
+ svc,
+ rand_phase,
+ &phase_eval,
+ &phase_omega_in,
+ &phase_pdf,
+ &sampled_roughness);
+
+ if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
+ return false;
+ }
- if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
- return false;
+ unguided_phase_pdf = phase_pdf;
}
/* Setup ray. */
@@ -887,9 +931,15 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;
INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
+ const Spectrum phase_weight = bsdf_eval_sum(&phase_eval) / phase_pdf;
+
+ /* Add phase function sampling data to the path segment. */
+ guiding_record_volume_bounce(
+ kg, state, sd, phase_weight, phase_pdf, normalize(phase_omega_in), sampled_roughness);
+
/* Update throughput. */
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
- const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf;
+ const Spectrum throughput_phase = throughput * phase_weight;
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase;
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
@@ -900,7 +950,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
/* Update path state */
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
- phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
+ unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
path_state_next(kg, state, label);
return true;
@@ -939,6 +989,10 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i))
const float step_size = volume_stack_step_size(kg, volume_read_lambda_pass);
+# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ const float3 initial_throughput = INTEGRATOR_STATE(state, path, throughput);
+# endif
+
/* TODO: expensive to zero closures? */
VolumeIntegrateResult result = {};
volume_integrate_heterogeneous(kg,
@@ -956,17 +1010,50 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
* to be terminated. That will shading evaluating to leave out any scattering closures,
* but emission and absorption are still handled for multiple importance sampling. */
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
- const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ?
- 0.0f :
- INTEGRATOR_STATE(state, path, continuation_probability);
- if (probability == 0.0f) {
+ const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ?
+ 0.0f :
+ INTEGRATOR_STATE(
+ state, path, continuation_probability);
+ if (continuation_probability == 0.0f) {
return VOLUME_PATH_MISSED;
}
+# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ bool guiding_generated_new_segment = false;
+ if (kernel_data.integrator.use_guiding) {
+ /* Record transmittance using change in throughput. */
+ float3 transmittance_weight = spectrum_to_rgb(
+ safe_divide_color(result.indirect_throughput, initial_throughput));
+ guiding_record_volume_transmission(kg, state, transmittance_weight);
+
+ if (result.indirect_scatter) {
+ const float3 P = ray->P + result.indirect_t * ray->D;
+
+ /* Record volume segment up to direct scatter position.
+ * TODO: volume segment is wrong when direct_t and indirect_t. */
+ if (result.direct_scatter && (result.direct_t == result.indirect_t)) {
+ guiding_record_volume_segment(kg, state, P, sd.I);
+ guiding_generated_new_segment = true;
+ }
+
+# if PATH_GUIDING_LEVEL >= 4
+ /* TODO: this position will be wrong for direct light pdf computation,
+ * since the direct light position may be different? */
+ volume_shader_prepare_guiding(
+ kg, state, &sd, &rng_state, P, ray->D, &result.direct_phases, direct_sample_method);
+# endif
+ }
+ else {
+ /* No guiding if we don't scatter. */
+ state->guiding.use_volume_guiding = false;
+ }
+ }
+# endif
+
/* Direct light. */
if (result.direct_scatter) {
const float3 direct_P = ray->P + result.direct_t * ray->D;
- result.direct_throughput /= probability;
+ result.direct_throughput /= continuation_probability;
integrate_volume_direct_light(kg,
state,
&sd,
@@ -979,16 +1066,22 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
/* Indirect light.
*
- * Only divide throughput by probability if we scatter. For the attenuation
+ * Only divide throughput by continuation_probability if we scatter. For the attenuation
* case the next surface will already do this division. */
if (result.indirect_scatter) {
- result.indirect_throughput /= probability;
+ result.indirect_throughput /= continuation_probability;
}
INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput;
if (result.indirect_scatter) {
sd.P = ray->P + result.indirect_t * ray->D;
+# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
+ if (!guiding_generated_new_segment) {
+ guiding_record_volume_segment(kg, state, sd.P, sd.I);
+ }
+# endif
+
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
return VOLUME_PATH_SCATTERED;
}
diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h
index 3b490ecffdd..d731d1df339 100644
--- a/intern/cycles/kernel/integrator/shadow_state_template.h
+++ b/intern/cycles/kernel/integrator/shadow_state_template.h
@@ -40,6 +40,16 @@ KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, pass_glossy_weight, KERNEL_FEA
KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING)
/* Light group. */
KERNEL_STRUCT_MEMBER(shadow_path, uint8_t, lightgroup, KERNEL_FEATURE_PATH_TRACING)
+/* Path guiding. */
+KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, unlit_throughput, KERNEL_FEATURE_PATH_GUIDING)
+#ifdef __PATH_GUIDING__
+KERNEL_STRUCT_MEMBER(shadow_path,
+ openpgl::cpp::PathSegment *,
+ path_segment,
+ KERNEL_FEATURE_PATH_GUIDING)
+#else
+KERNEL_STRUCT_MEMBER(shadow_path, uint64_t, path_segment, KERNEL_FEATURE_PATH_GUIDING)
+#endif
KERNEL_STRUCT_END(shadow_path)
/********************************** Shadow Ray *******************************/
diff --git a/intern/cycles/kernel/integrator/state.h b/intern/cycles/kernel/integrator/state.h
index d1907bd6e16..f0fdc6f0d54 100644
--- a/intern/cycles/kernel/integrator/state.h
+++ b/intern/cycles/kernel/integrator/state.h
@@ -31,6 +31,10 @@
#include "util/types.h"
+#ifdef __PATH_GUIDING__
+# include "util/guiding.h"
+#endif
+
#pragma once
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h
index 4b03c665e17..40961b1c5fb 100644
--- a/intern/cycles/kernel/integrator/state_flow.h
+++ b/intern/cycles/kernel/integrator/state_flow.h
@@ -76,6 +76,9 @@ ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init(
&kernel_integrator_state.next_shadow_path_index[0], 1);
atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1);
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel;
+# ifdef __PATH_GUIDING__
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
+# endif
return shadow_state;
}
@@ -181,6 +184,9 @@ ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init(
{
IntegratorShadowState shadow_state = (is_ao) ? &state->ao : &state->shadow;
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel;
+# ifdef __PATH_GUIDING__
+ INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = nullptr;
+# endif
return shadow_state;
}
diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h
index f4e280e4cb2..610621f0abe 100644
--- a/intern/cycles/kernel/integrator/state_template.h
+++ b/intern/cycles/kernel/integrator/state_template.h
@@ -47,6 +47,9 @@ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(path, float, continuation_probability, KERNEL_FEATURE_PATH_TRACING)
/* Throughput. */
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, throughput, KERNEL_FEATURE_PATH_TRACING)
+/* Factor to multiple with throughput to get remove any guiding PDFS.
+ * Such throughput without guiding PDFS is used for Russian roulette termination. */
+KERNEL_STRUCT_MEMBER(path, float, unguided_throughput, KERNEL_FEATURE_PATH_GUIDING)
/* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES)
KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES)
@@ -98,3 +101,33 @@ KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_END_ARRAY(volume_stack,
KERNEL_STRUCT_VOLUME_STACK_SIZE,
KERNEL_STRUCT_VOLUME_STACK_SIZE)
+
+/************************************ Path Guiding *****************************/
+KERNEL_STRUCT_BEGIN(guiding)
+#ifdef __PATH_GUIDING__
+/* Current path segment of the random walk/path. */
+KERNEL_STRUCT_MEMBER(guiding,
+ openpgl::cpp::PathSegment *,
+ path_segment,
+ KERNEL_FEATURE_PATH_GUIDING)
+#else
+/* Current path segment of the random walk/path. */
+KERNEL_STRUCT_MEMBER(guiding, uint64_t, path_segment, KERNEL_FEATURE_PATH_GUIDING)
+#endif
+/* If surface guiding is enabled */
+KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUIDING)
+/* Random number used for additional guiding decisions (e.g., cache query, selection to use guiding
+ * or BSDF sampling) */
+KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
+/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/
+KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
+/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/
+KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
+/* If volume guiding is enabled */
+KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING)
+/* Random number used for additional guiding decisions (e.g., cache query, selection to use guiding
+ * or BSDF sampling) */
+KERNEL_STRUCT_MEMBER(guiding, float, sample_volume_guiding_rand, KERNEL_FEATURE_PATH_GUIDING)
+/* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob). */
+KERNEL_STRUCT_MEMBER(guiding, float, volume_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING)
+KERNEL_STRUCT_END(guiding)
diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h
index 15c2cb1c708..efd293e4141 100644
--- a/intern/cycles/kernel/integrator/subsurface.h
+++ b/intern/cycles/kernel/integrator/subsurface.h
@@ -78,6 +78,9 @@ ccl_device int subsurface_bounce(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, subsurface, radius) = bssrdf->radius;
INTEGRATOR_STATE_WRITE(state, subsurface, anisotropy) = bssrdf->anisotropy;
+ /* Path guiding. */
+ guiding_record_bssrdf_weight(kg, state, weight, bssrdf->albedo);
+
return LABEL_SUBSURFACE_SCATTER;
}
diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h
index a44b6a74d7b..16fb45392f4 100644
--- a/intern/cycles/kernel/integrator/subsurface_disk.h
+++ b/intern/cycles/kernel/integrator/subsurface_disk.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
+#include "kernel/integrator/guiding.h"
+
CCL_NAMESPACE_BEGIN
/* BSSRDF using disk based importance sampling.
@@ -173,8 +175,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
if (r < next_sum) {
/* Return exit point. */
- INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight * sum_weights / sample_weight;
-
+ const Spectrum resampled_weight = weight * sum_weights / sample_weight;
+ INTEGRATOR_STATE_WRITE(state, path, throughput) *= resampled_weight;
ss_isect.hits[0] = ss_isect.hits[hit];
ss_isect.Ng[0] = ss_isect.Ng[hit];
@@ -182,6 +184,9 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
ray.D = ss_isect.Ng[hit];
ray.tmin = 0.0f;
ray.tmax = 1.0f;
+
+ guiding_record_bssrdf_bounce(
+ kg, state, 1.0f, Ng, -Ng, resampled_weight, INTEGRATOR_STATE(state, subsurface, albedo));
return true;
}
diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h
index a6a59e286c9..fdcb66c32f5 100644
--- a/intern/cycles/kernel/integrator/subsurface_random_walk.h
+++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h
@@ -5,6 +5,8 @@
#include "kernel/bvh/bvh.h"
+#include "kernel/integrator/guiding.h"
+
CCL_NAMESPACE_BEGIN
/* Random walk subsurface scattering.
@@ -203,7 +205,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
const float anisotropy = INTEGRATOR_STATE(state, subsurface, anisotropy);
Spectrum sigma_t, alpha;
- Spectrum throughput = INTEGRATOR_STATE_WRITE(state, path, throughput);
+ Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput);
Spectrum sigma_s = sigma_t * alpha;
@@ -350,7 +352,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
}
}
- /* Sample direction along ray. */
+ /* Sample distance along ray. */
float t = -logf(1.0f - randt) / sample_sigma_t;
/* On the first bounce, we use the ray-cast to check if the opposite side is nearby.
@@ -432,6 +434,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
if (hit) {
kernel_assert(isfinite_safe(throughput));
+
+ guiding_record_bssrdf_bounce(
+ kg,
+ state,
+ pdf,
+ N,
+ D,
+ safe_divide_color(throughput, INTEGRATOR_STATE(state, path, throughput)),
+ albedo);
+
INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput;
}
diff --git a/intern/cycles/kernel/integrator/surface_shader.h b/intern/cycles/kernel/integrator/surface_shader.h
index 64b5556f7e9..5e47a34f77e 100644
--- a/intern/cycles/kernel/integrator/surface_shader.h
+++ b/intern/cycles/kernel/integrator/surface_shader.h
@@ -10,6 +10,8 @@
#include "kernel/closure/bsdf_util.h"
#include "kernel/closure/emissive.h"
+#include "kernel/integrator/guiding.h"
+
#ifdef __SVM__
# include "kernel/svm/svm.h"
#endif
@@ -19,6 +21,67 @@
CCL_NAMESPACE_BEGIN
+/* Guiding */
+
+#ifdef __PATH_GUIDING__
+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;
+ 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;
+
+ 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 (bsdf_bssrdf_sampling_sum > 0.0f) {
+ diffuse_sampling_fraction /= bsdf_bssrdf_sampling_sum;
+ 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))) {
+ state->guiding.use_surface_guiding = false;
+ return;
+ }
+
+ state->guiding.use_surface_guiding = true;
+ state->guiding.surface_guiding_sampling_prob = surface_guiding_probability *
+ diffuse_sampling_fraction;
+ 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,
@@ -108,12 +171,27 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
}
/* BSDF */
-
-ccl_device_inline bool surface_shader_is_transmission(ccl_private const ShaderData *sd,
- const float3 omega_in)
+#if 0
+ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
+ const ShaderClosure *sc,
+ const float3 omega_in,
+ const int org_label,
+ const float2 org_roughness,
+ const float org_eta)
{
- return dot(sd->N, omega_in) < 0.0f;
+ /* Validate the the bsdf_label and bsdf_roughness_eta functions
+ * by estimating the values after a bsdf sample. */
+ const int comp_label = bsdf_label(kg, sc, omega_in);
+ 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)
{
@@ -141,7 +219,6 @@ ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light
ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 omega_in,
- const bool is_transmission,
ccl_private const ShaderClosure *skip_sc,
ccl_private BsdfEval *result_eval,
float sum_pdf,
@@ -160,7 +237,7 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
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, omega_in, is_transmission, &bsdf_pdf);
+ Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
@@ -175,6 +252,55 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
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 omega_in,
+ 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, omega_in, &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
@@ -182,16 +308,28 @@ ccl_device_inline
#endif
float
surface_shader_bsdf_eval(KernelGlobals kg,
+ IntegratorState state,
ccl_private ShaderData *sd,
const float3 omega_in,
- const bool is_transmission,
ccl_private BsdfEval *bsdf_eval,
const uint light_shader_flags)
{
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum());
- return _surface_shader_bsdf_eval_mis(
- kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
+ float pdf = _surface_shader_bsdf_eval_mis(
+ kg, sd, omega_in, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
+
+#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (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, omega_in);
+ 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. */
@@ -259,6 +397,135 @@ surface_shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict s
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(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private ShaderData *sd,
+ ccl_private const ShaderClosure *sc,
+ const float2 rand_bsdf,
+ ccl_private BsdfEval *bsdf_eval,
+ ccl_private float3 *omega_in,
+ 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, rand_bsdf, omega_in);
+ *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, *omega_in, 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], *omega_in);
+ }
+ }
+
+ 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,
+ rand_bsdf.x,
+ rand_bsdf.y,
+ &eval,
+ omega_in,
+ unguided_bsdf_pdf,
+ sampled_rougness,
+ eta);
+# if 0
+ if (*unguided_bsdf_pdf > 0.0f) {
+ surface_shader_validate_bsdf_sample(kg, sc, *omega_in, 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, *omega_in, 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, *omega_in);
+ *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;
+}
+#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,
@@ -267,7 +534,9 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
const float2 rand_bsdf,
ccl_private BsdfEval *bsdf_eval,
ccl_private float3 *omega_in,
- ccl_private float *pdf)
+ 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));
@@ -276,18 +545,21 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
- label = bsdf_sample(kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf);
+ label = bsdf_sample(
+ kg, sd, sc, rand_bsdf.x, rand_bsdf.y, &eval, omega_in, pdf, sampled_roughness, eta);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
if (sd->num_closure > 1) {
- const bool is_transmission = surface_shader_is_transmission(sd, *omega_in);
float sweight = sc->sample_weight;
*pdf = _surface_shader_bsdf_eval_mis(
- kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0);
+ kg, sd, *omega_in, sc, bsdf_eval, *pdf * sweight, sweight, 0);
}
}
+ else {
+ bsdf_eval_init(bsdf_eval, sc->type, zero_spectrum());
+ }
return label;
}
@@ -555,13 +827,8 @@ ccl_device void surface_shader_eval(KernelGlobals kg,
sd->num_closure_left = max_closures;
#ifdef __OSL__
- if (kg->osl) {
- if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) {
- OSLShader::eval_background(kg, state, sd, path_flag);
- }
- else {
- OSLShader::eval_surface(kg, state, sd, path_flag);
- }
+ if (kernel_data.kernel_features & KERNEL_FEATURE_OSL) {
+ osl_eval_nodes<SHADER_TYPE_SURFACE>(kg, state, sd, path_flag);
}
else
#endif
diff --git a/intern/cycles/kernel/integrator/volume_shader.h b/intern/cycles/kernel/integrator/volume_shader.h
index 31039bfdcf5..f9050647c6d 100644
--- a/intern/cycles/kernel/integrator/volume_shader.h
+++ b/intern/cycles/kernel/integrator/volume_shader.h
@@ -22,6 +22,7 @@ CCL_NAMESPACE_BEGIN
#ifdef __VOLUME__
/* Merging */
+
ccl_device_inline void volume_shader_merge_closures(ccl_private ShaderData *sd)
{
/* Merge identical closures to save closure space with stacked volumes. */
@@ -88,6 +89,119 @@ ccl_device_inline void volume_shader_copy_phases(ccl_private ShaderVolumePhases
}
}
+/* Guiding */
+
+# ifdef __PATH_GUIDING__
+ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private ShaderData *sd,
+ ccl_private const RNGState *rng_state,
+ const float3 P,
+ const float3 D,
+ ccl_private ShaderVolumePhases *phases,
+ const VolumeSampleMethod direct_sample_method)
+{
+ /* Have any phase functions to guide? */
+ const int num_phases = phases->num_closure;
+ if (!kernel_data.integrator.use_volume_guiding || num_phases == 0) {
+ state->guiding.use_volume_guiding = false;
+ return;
+ }
+
+ const float volume_guiding_probability = kernel_data.integrator.volume_guiding_probability;
+ float rand_phase_guiding = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_GUIDING);
+
+ /* If we have more than one phase function we select one random based on its
+ * sample weight to calculate the product distribution for guiding. */
+ int phase_id = 0;
+ float phase_weight = 1.0f;
+
+ if (num_phases > 1) {
+ /* Pick a phase closure based on sample weights. */
+ float sum = 0.0f;
+
+ for (phase_id = 0; phase_id < num_phases; phase_id++) {
+ ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
+ sum += svc->sample_weight;
+ }
+
+ float r = rand_phase_guiding * sum;
+ float partial_sum = 0.0f;
+
+ for (phase_id = 0; phase_id < num_phases; phase_id++) {
+ ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
+ float next_sum = partial_sum + svc->sample_weight;
+
+ if (r <= next_sum) {
+ /* Rescale to reuse. */
+ rand_phase_guiding = (r - partial_sum) / svc->sample_weight;
+ phase_weight = svc->sample_weight / sum;
+ break;
+ }
+
+ partial_sum = next_sum;
+ }
+
+ /* Adjust the sample weight of the component used for guiding. */
+ phases->closure[phase_id].sample_weight *= volume_guiding_probability;
+ }
+
+ /* Init guiding for selected phase function. */
+ ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
+ if (!guiding_phase_init(kg, state, P, D, svc->g, rand_phase_guiding)) {
+ state->guiding.use_volume_guiding = false;
+ return;
+ }
+
+ state->guiding.use_volume_guiding = true;
+ state->guiding.sample_volume_guiding_rand = rand_phase_guiding;
+ state->guiding.volume_guiding_sampling_prob = volume_guiding_probability * phase_weight;
+
+ kernel_assert(state->guiding.volume_guiding_sampling_prob > 0.0f &&
+ state->guiding.volume_guiding_sampling_prob <= 1.0f);
+}
+# endif
+
+/* Phase Evaluation & Sampling */
+
+/* Randomly sample a volume phase function proportional to ShaderClosure.sample_weight. */
+ccl_device_inline ccl_private const ShaderVolumeClosure *volume_shader_phase_pick(
+ ccl_private const ShaderVolumePhases *phases, ccl_private float2 *rand_phase)
+{
+ int sampled = 0;
+
+ if (phases->num_closure > 1) {
+ /* pick a phase closure based on sample weights */
+ float sum = 0.0f;
+
+ for (int i = 0; i < phases->num_closure; i++) {
+ ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
+ sum += svc->sample_weight;
+ }
+
+ float r = (*rand_phase).x * sum;
+ float partial_sum = 0.0f;
+
+ for (int i = 0; i < phases->num_closure; i++) {
+ ccl_private const ShaderVolumeClosure *svc = &phases->closure[i];
+ float next_sum = partial_sum + svc->sample_weight;
+
+ if (r <= next_sum) {
+ /* Rescale to reuse for volume phase direction sample. */
+ sampled = i;
+ (*rand_phase).x = (r - partial_sum) / svc->sample_weight;
+ break;
+ }
+
+ partial_sum = next_sum;
+ }
+ }
+
+ /* todo: this isn't quite correct, we don't weight anisotropy properly
+ * depending on color channels, even if this is perhaps not a common case */
+ return &phases->closure[sampled];
+}
+
ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
const float3 omega_in,
@@ -117,88 +231,139 @@ ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderDa
ccl_device float volume_shader_phase_eval(KernelGlobals kg,
ccl_private const ShaderData *sd,
- ccl_private const ShaderVolumePhases *phases,
+ ccl_private const ShaderVolumeClosure *svc,
const float3 omega_in,
ccl_private BsdfEval *phase_eval)
{
- bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
+ float phase_pdf = 0.0f;
+ Spectrum eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf);
+
+ if (phase_pdf != 0.0f) {
+ bsdf_eval_accum(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
+ }
- return _volume_shader_phase_eval_mis(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f);
+ return phase_pdf;
}
-ccl_device int volume_shader_phase_sample(KernelGlobals kg,
+ccl_device float volume_shader_phase_eval(KernelGlobals kg,
+ IntegratorState state,
ccl_private const ShaderData *sd,
ccl_private const ShaderVolumePhases *phases,
- float2 rand_phase,
- ccl_private BsdfEval *phase_eval,
- ccl_private float3 *omega_in,
- ccl_private float *pdf)
+ const float3 omega_in,
+ ccl_private BsdfEval *phase_eval)
{
- int sampled = 0;
+ bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
- if (phases->num_closure > 1) {
- /* pick a phase closure based on sample weights */
- float sum = 0.0f;
+ float pdf = _volume_shader_phase_eval_mis(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f);
- for (sampled = 0; sampled < phases->num_closure; sampled++) {
- ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
- sum += svc->sample_weight;
- }
+# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
+ if (state->guiding.use_volume_guiding) {
+ const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
+ const float guide_pdf = guiding_phase_pdf(kg, state, omega_in);
+ pdf = (guiding_sampling_prob * guide_pdf) + (1.0f - guiding_sampling_prob) * pdf;
+ }
+# endif
- float r = rand_phase.x * sum;
- float partial_sum = 0.0f;
+ return pdf;
+}
- for (sampled = 0; sampled < phases->num_closure; sampled++) {
- ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
- float next_sum = partial_sum + svc->sample_weight;
+# ifdef __PATH_GUIDING__
+ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
+ IntegratorState state,
+ ccl_private const ShaderData *sd,
+ ccl_private const ShaderVolumeClosure *svc,
+ const float2 rand_phase,
+ ccl_private BsdfEval *phase_eval,
+ ccl_private float3 *omega_in,
+ ccl_private float *phase_pdf,
+ ccl_private float *unguided_phase_pdf,
+ ccl_private float *sampled_roughness)
+{
+ const bool use_volume_guiding = state->guiding.use_volume_guiding;
+ const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
+
+ /* Decide between sampling guiding distribution and phase. */
+ float rand_phase_guiding = state->guiding.sample_volume_guiding_rand;
+ bool sample_guiding = false;
+ if (use_volume_guiding && rand_phase_guiding < guiding_sampling_prob) {
+ sample_guiding = true;
+ rand_phase_guiding /= guiding_sampling_prob;
+ }
+ else {
+ rand_phase_guiding -= guiding_sampling_prob;
+ rand_phase_guiding /= (1.0f - guiding_sampling_prob);
+ }
- if (r <= next_sum) {
- /* Rescale to reuse for BSDF direction sample. */
- rand_phase.x = (r - partial_sum) / svc->sample_weight;
- break;
- }
+ /* Initialize to zero. */
+ int label = LABEL_NONE;
+ Spectrum eval = zero_spectrum();
- partial_sum = next_sum;
- }
+ *unguided_phase_pdf = 0.0f;
+ float guide_pdf = 0.0f;
+ *sampled_roughness = 1.0f - fabsf(svc->g);
+
+ bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
- if (sampled == phases->num_closure) {
- *pdf = 0.0f;
- return LABEL_NONE;
+ if (sample_guiding) {
+ /* Sample guiding distribution. */
+ guide_pdf = guiding_phase_sample(kg, state, rand_phase, omega_in);
+ *phase_pdf = 0.0f;
+
+ if (guide_pdf != 0.0f) {
+ *unguided_phase_pdf = volume_shader_phase_eval(kg, sd, svc, *omega_in, phase_eval);
+ *phase_pdf = (guiding_sampling_prob * guide_pdf) +
+ ((1.0f - guiding_sampling_prob) * (*unguided_phase_pdf));
+ label = LABEL_VOLUME_SCATTER;
}
}
+ else {
+ /* Sample phase. */
+ *phase_pdf = 0.0f;
+ label = volume_phase_sample(
+ sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, unguided_phase_pdf);
+
+ if (*unguided_phase_pdf != 0.0f) {
+ bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
+
+ *phase_pdf = *unguided_phase_pdf;
+ if (use_volume_guiding) {
+ guide_pdf = guiding_phase_pdf(kg, state, *omega_in);
+ *phase_pdf *= 1.0f - guiding_sampling_prob;
+ *phase_pdf += guiding_sampling_prob * guide_pdf;
+ }
- /* todo: this isn't quite correct, we don't weight anisotropy properly
- * depending on color channels, even if this is perhaps not a common case */
- ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled];
- int label;
- Spectrum eval = zero_spectrum();
-
- *pdf = 0.0f;
- label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
+ kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
+ }
+ else {
+ bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
+ }
- if (*pdf != 0.0f) {
- bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
+ kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
}
return label;
}
+# endif
-ccl_device int volume_shader_phase_sample_closure(KernelGlobals kg,
- ccl_private const ShaderData *sd,
- ccl_private const ShaderVolumeClosure *sc,
- const float2 rand_phase,
- ccl_private BsdfEval *phase_eval,
- ccl_private float3 *omega_in,
- ccl_private float *pdf)
+ccl_device int volume_shader_phase_sample(KernelGlobals kg,
+ ccl_private const ShaderData *sd,
+ ccl_private const ShaderVolumePhases *phases,
+ ccl_private const ShaderVolumeClosure *svc,
+ float2 rand_phase,
+ ccl_private BsdfEval *phase_eval,
+ ccl_private float3 *omega_in,
+ ccl_private float *pdf,
+ ccl_private float *sampled_roughness)
{
- int label;
+ *sampled_roughness = 1.0f - fabsf(svc->g);
Spectrum eval = zero_spectrum();
*pdf = 0.0f;
- label = volume_phase_sample(sd, sc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
+ int label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, omega_in, pdf);
- if (*pdf != 0.0f)
+ if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
+ }
return label;
}
@@ -328,8 +493,8 @@ ccl_device_inline void volume_shader_eval(KernelGlobals kg,
/* evaluate shader */
# ifdef __OSL__
- if (kg->osl) {
- OSLShader::eval_volume(kg, state, sd, path_flag);
+ if (kernel_data.kernel_features & KERNEL_FEATURE_OSL) {
+ osl_eval_nodes<SHADER_TYPE_VOLUME>(kg, state, sd, path_flag);
}
else
# endif