Files
blender/intern/cycles/kernel/integrator/intersect_volume_stack.h

231 lines
8.2 KiB
C

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/bvh/bvh.h"
#include "kernel/geom/geom.h"
#include "kernel/integrator/shader_eval.h"
#include "kernel/integrator/volume_stack.h"
CCL_NAMESPACE_BEGIN
ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
IntegratorState state,
const float3 from_P,
const float3 to_P)
{
PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK);
ShaderDataTinyStorage stack_sd_storage;
ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage);
kernel_assert(kernel_data.integrator.use_volumes);
Ray volume_ray ccl_optional_struct_init;
volume_ray.P = from_P;
volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t);
volume_ray.self.object = INTEGRATOR_STATE(state, isect, object);
volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
volume_ray.self.light_object = OBJECT_NONE;
volume_ray.self.light_prim = PRIM_NONE;
/* Store to avoid global fetches on every intersection step. */
const uint volume_stack_size = kernel_data.volume_stack_size;
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint32_t visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, PATH_RAY_ALL_VISIBILITY);
#ifdef __VOLUME_RECORD_ALL__
Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume_all(
kg, &volume_ray, hits, 2 * volume_stack_size, visibility);
if (num_hits > 0) {
Intersection *isect = hits;
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
for (uint hit = 0; hit < num_hits; ++hit, ++isect) {
shader_setup_from_ray(kg, stack_sd, &volume_ray, isect);
volume_stack_enter_exit(kg, state, stack_sd);
}
}
#else
Intersection isect;
int step = 0;
while (step < 2 * volume_stack_size &&
scene_intersect_volume(kg, &volume_ray, &isect, visibility)) {
shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect);
volume_stack_enter_exit(kg, state, stack_sd);
/* Move ray forward. */
volume_ray.P = stack_sd->P;
volume_ray.self.object = isect.object;
volume_ray.self.prim = isect.prim;
if (volume_ray.t != FLT_MAX) {
volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t);
}
++step;
}
#endif
}
ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState state)
{
PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK);
ShaderDataTinyStorage stack_sd_storage;
ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage);
Ray volume_ray ccl_optional_struct_init;
integrator_state_read_ray(kg, state, &volume_ray);
/* Trace ray in random direction. Any direction works, Z up is a guess to get the
* fewest hits. */
volume_ray.D = make_float3(0.0f, 0.0f, 1.0f);
volume_ray.t = FLT_MAX;
volume_ray.self.object = OBJECT_NONE;
volume_ray.self.prim = PRIM_NONE;
volume_ray.self.light_object = OBJECT_NONE;
volume_ray.self.light_prim = PRIM_NONE;
int stack_index = 0, enclosed_index = 0;
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint32_t visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, PATH_RAY_CAMERA);
/* Initialize volume stack with background volume For shadow catcher the
* background volume is always assumed to be CG. */
if (kernel_data.background.volume_shader != SHADER_NONE) {
if (!(path_flag & PATH_RAY_SHADOW_CATCHER_PASS)) {
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, stack_index, object) = OBJECT_NONE;
INTEGRATOR_STATE_ARRAY_WRITE(
state, volume_stack, stack_index, shader) = kernel_data.background.volume_shader;
stack_index++;
}
}
/* Store to avoid global fetches on every intersection step. */
const uint volume_stack_size = kernel_data.volume_stack_size;
#ifdef __VOLUME_RECORD_ALL__
Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume_all(
kg, &volume_ray, hits, 2 * volume_stack_size, visibility);
if (num_hits > 0) {
int enclosed_volumes[MAX_VOLUME_STACK_SIZE];
Intersection *isect = hits;
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
for (uint hit = 0; hit < num_hits; ++hit, ++isect) {
shader_setup_from_ray(kg, stack_sd, &volume_ray, isect);
if (stack_sd->flag & SD_BACKFACING) {
bool need_add = true;
for (int i = 0; i < enclosed_index && need_add; ++i) {
/* If ray exited the volume and never entered to that volume
* it means that camera is inside such a volume.
*/
if (enclosed_volumes[i] == stack_sd->object) {
need_add = false;
}
}
for (int i = 0; i < stack_index && need_add; ++i) {
/* Don't add intersections twice. */
VolumeStack entry = integrator_state_read_volume_stack(state, i);
if (entry.object == stack_sd->object) {
need_add = false;
break;
}
}
if (need_add && stack_index < volume_stack_size - 1) {
const VolumeStack new_entry = {stack_sd->object, stack_sd->shader};
integrator_state_write_volume_stack(state, stack_index, new_entry);
++stack_index;
}
}
else {
/* If ray from camera enters the volume, this volume shouldn't
* be added to the stack on exit.
*/
enclosed_volumes[enclosed_index++] = stack_sd->object;
}
}
}
#else
/* CUDA does not support definition of a variable size arrays, so use the maximum possible. */
int enclosed_volumes[MAX_VOLUME_STACK_SIZE];
int step = 0;
while (stack_index < volume_stack_size - 1 && enclosed_index < MAX_VOLUME_STACK_SIZE - 1 &&
step < 2 * volume_stack_size) {
Intersection isect;
if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) {
break;
}
shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect);
if (stack_sd->flag & SD_BACKFACING) {
/* If ray exited the volume and never entered to that volume
* it means that camera is inside such a volume.
*/
bool need_add = true;
for (int i = 0; i < enclosed_index && need_add; ++i) {
/* If ray exited the volume and never entered to that volume
* it means that camera is inside such a volume.
*/
if (enclosed_volumes[i] == stack_sd->object) {
need_add = false;
}
}
for (int i = 0; i < stack_index && need_add; ++i) {
/* Don't add intersections twice. */
VolumeStack entry = integrator_state_read_volume_stack(state, i);
if (entry.object == stack_sd->object) {
need_add = false;
break;
}
}
if (need_add) {
const VolumeStack new_entry = {stack_sd->object, stack_sd->shader};
integrator_state_write_volume_stack(state, stack_index, new_entry);
++stack_index;
}
}
else {
/* If ray from camera enters the volume, this volume shouldn't
* be added to the stack on exit.
*/
enclosed_volumes[enclosed_index++] = stack_sd->object;
}
/* Move ray forward. */
volume_ray.P = stack_sd->P;
volume_ray.self.object = isect.object;
volume_ray.self.prim = isect.prim;
++step;
}
#endif
/* Write terminator. */
const VolumeStack new_entry = {OBJECT_NONE, SHADER_NONE};
integrator_state_write_volume_stack(state, stack_index, new_entry);
}
ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorState state)
{
integrator_volume_stack_init(kg, state);
if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_PASS) {
/* Volume stack re-init for shadow catcher, continue with shading of hit. */
integrator_intersect_next_kernel_after_shadow_catcher_volume<
DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK>(kg, state);
}
else {
/* Volume stack init for camera rays, continue with intersection of camera ray. */
INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK,
DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST);
}
}
CCL_NAMESPACE_END