Note: this is a partial fix, that makes NEE and forward path consistent only when `max_transparent_bounce > 0`. It is much more involved to make forward path tracing support a max transparent bounce of 0, but since we don't expect people to set up a very low number of transparent bounces, it is less important to support that specific case. Pull Request: https://projects.blender.org/blender/blender/pulls/138098
185 lines
6.6 KiB
C++
185 lines
6.6 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/globals.h"
|
|
#include "kernel/types.h"
|
|
|
|
#include "kernel/bvh/bvh.h"
|
|
|
|
#include "kernel/integrator/state.h"
|
|
#include "kernel/integrator/state_flow.h"
|
|
#include "kernel/integrator/state_util.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* Visibility for the shadow ray. */
|
|
ccl_device_forceinline uint integrate_intersect_shadow_visibility(KernelGlobals kg,
|
|
ConstIntegratorShadowState state)
|
|
{
|
|
uint visibility = PATH_RAY_SHADOW;
|
|
|
|
#ifdef __SHADOW_CATCHER__
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag);
|
|
visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility);
|
|
#endif
|
|
|
|
return visibility;
|
|
}
|
|
|
|
ccl_device bool integrate_intersect_shadow_opaque(KernelGlobals kg,
|
|
IntegratorShadowState state,
|
|
const ccl_private Ray *ray,
|
|
const uint visibility)
|
|
{
|
|
/* Mask which will pick only opaque visibility bits from the `visibility`.
|
|
* Calculate the mask at compile time: the visibility will either be a high bits for the shadow
|
|
* catcher objects, or lower bits for the regular objects (there is no need to check the path
|
|
* state here again). */
|
|
constexpr const uint opaque_mask = SHADOW_CATCHER_VISIBILITY_SHIFT(PATH_RAY_SHADOW_OPAQUE) |
|
|
PATH_RAY_SHADOW_OPAQUE;
|
|
|
|
const bool opaque_hit = scene_intersect_shadow(kg, ray, visibility & opaque_mask);
|
|
|
|
/* Only record the number of hits if nothing was hit, so that the shadow shading kernel does not
|
|
* consider any intersections. There is no need to write anything to the state if the hit is
|
|
* opaque because in this case the path is terminated. */
|
|
if (!opaque_hit) {
|
|
INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0;
|
|
}
|
|
|
|
return opaque_hit;
|
|
}
|
|
|
|
ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals kg,
|
|
ConstIntegratorShadowState state)
|
|
{
|
|
const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce;
|
|
const int transparent_bounce = INTEGRATOR_STATE(state, shadow_path, transparent_bounce);
|
|
|
|
return max(transparent_max_bounce - transparent_bounce, 0);
|
|
}
|
|
|
|
#ifdef __TRANSPARENT_SHADOWS__
|
|
# ifndef __KERNEL_GPU__
|
|
ccl_device int shadow_intersections_compare(const void *a, const void *b)
|
|
{
|
|
const Intersection *isect_a = (const Intersection *)a;
|
|
const Intersection *isect_b = (const Intersection *)b;
|
|
|
|
if (isect_a->t < isect_b->t) {
|
|
return -1;
|
|
}
|
|
if (isect_a->t > isect_b->t) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
# endif
|
|
|
|
ccl_device_inline void sort_shadow_intersections(IntegratorShadowState state, uint num_hits)
|
|
{
|
|
kernel_assert(num_hits > 0);
|
|
|
|
# ifdef __KERNEL_GPU__
|
|
/* Use bubble sort which has more friendly memory pattern on GPU. */
|
|
bool swapped;
|
|
do {
|
|
swapped = false;
|
|
for (int j = 0; j < num_hits - 1; ++j) {
|
|
if (INTEGRATOR_STATE_ARRAY(state, shadow_isect, j, t) >
|
|
INTEGRATOR_STATE_ARRAY(state, shadow_isect, j + 1, t))
|
|
{
|
|
struct Intersection tmp_j ccl_optional_struct_init;
|
|
struct Intersection tmp_j_1 ccl_optional_struct_init;
|
|
integrator_state_read_shadow_isect(state, &tmp_j, j);
|
|
integrator_state_read_shadow_isect(state, &tmp_j_1, j + 1);
|
|
integrator_state_write_shadow_isect(state, &tmp_j_1, j);
|
|
integrator_state_write_shadow_isect(state, &tmp_j, j + 1);
|
|
swapped = true;
|
|
}
|
|
}
|
|
--num_hits;
|
|
} while (swapped);
|
|
# else
|
|
Intersection *isect_array = (Intersection *)state->shadow_isect;
|
|
qsort(isect_array, num_hits, sizeof(Intersection), shadow_intersections_compare);
|
|
# endif
|
|
}
|
|
|
|
ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg,
|
|
IntegratorShadowState state,
|
|
const ccl_private Ray *ray,
|
|
const uint visibility)
|
|
{
|
|
/* Limit the number hits to the max transparent bounces allowed and the size that we
|
|
* have available in the integrator state. */
|
|
const uint max_transparent_hits = integrate_shadow_max_transparent_hits(kg, state);
|
|
uint num_hits = 0;
|
|
float throughput = 1.0f;
|
|
bool opaque_hit = scene_intersect_shadow_all(
|
|
kg, state, ray, visibility, max_transparent_hits, &num_hits, &throughput);
|
|
|
|
/* Computed throughput from baked shadow transparency, where we can bypass recording
|
|
* intersections and shader evaluation. */
|
|
if (throughput != 1.0f) {
|
|
INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) *= throughput;
|
|
}
|
|
|
|
if (!opaque_hit) {
|
|
const uint num_recorded_hits = min(num_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE);
|
|
|
|
if (num_recorded_hits > 0) {
|
|
sort_shadow_intersections(state, num_recorded_hits);
|
|
}
|
|
|
|
INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = num_hits;
|
|
}
|
|
else {
|
|
INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0;
|
|
}
|
|
|
|
return opaque_hit;
|
|
}
|
|
#endif
|
|
|
|
ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowState state)
|
|
{
|
|
PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW);
|
|
|
|
/* Read ray from integrator state into local memory. */
|
|
Ray ray ccl_optional_struct_init;
|
|
integrator_state_read_shadow_ray(state, &ray);
|
|
integrator_state_read_shadow_ray_self(kg, state, &ray);
|
|
/* Compute visibility. */
|
|
const uint visibility = integrate_intersect_shadow_visibility(kg, state);
|
|
|
|
#ifdef __TRANSPARENT_SHADOWS__
|
|
/* TODO: compile different kernels depending on this? Especially for OptiX
|
|
* conditional trace calls are bad. */
|
|
const bool opaque_hit = (kernel_data.integrator.transparent_shadows) ?
|
|
integrate_intersect_shadow_transparent(kg, state, &ray, visibility) :
|
|
integrate_intersect_shadow_opaque(kg, state, &ray, visibility);
|
|
#else
|
|
const bool opaque_hit = integrate_intersect_shadow_opaque(kg, state, &ray, visibility);
|
|
#endif
|
|
|
|
if (opaque_hit) {
|
|
/* Hit an opaque surface, shadow path ends here. */
|
|
integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW);
|
|
return;
|
|
}
|
|
|
|
/* Hit nothing or transparent surfaces, continue to shadow kernel
|
|
* for shading and render buffer output.
|
|
*
|
|
* TODO: could also write to render buffer directly if no transparent shadows?
|
|
* Could save a kernel execution for the common case. */
|
|
integrator_shadow_path_next(
|
|
kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW);
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|