diff options
Diffstat (limited to 'intern/cycles/kernel/integrator/guiding.h')
-rw-r--r-- | intern/cycles/kernel/integrator/guiding.h | 547 |
1 files changed, 547 insertions, 0 deletions
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 |