Add Chromium-only Blender WebEngine parity work
This commit is contained in:
161
blender-5.2.0/intern/cycles/kernel/film/adaptive_sampling.h
Normal file
161
blender-5.2.0/intern/cycles/kernel/film/adaptive_sampling.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Check whether the pixel has converged and should not be sampled anymore. */
|
||||
|
||||
ccl_device_forceinline bool film_need_sample_pixel(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_global float *render_buffer)
|
||||
{
|
||||
if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3;
|
||||
return buffer[aux_w_offset] == 0.0f;
|
||||
}
|
||||
|
||||
/* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */
|
||||
|
||||
ccl_device bool film_adaptive_sampling_convergence_check(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int y,
|
||||
const float threshold,
|
||||
const int reset,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED);
|
||||
|
||||
const int render_pixel_index = offset + x + y * stride;
|
||||
ccl_global float *buffer = render_buffer +
|
||||
(uint64_t)render_pixel_index * kernel_data.film.pass_stride;
|
||||
|
||||
/* TODO(Stefan): Is this better in linear, sRGB or something else? */
|
||||
|
||||
const float4 A = kernel_read_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer);
|
||||
if (!reset && A.w != 0.0f) {
|
||||
/* If the pixel was considered converged, its state will not change in this kernel. Early
|
||||
* output before doing any math.
|
||||
*
|
||||
* TODO(sergey): On a GPU it might be better to keep thread alive for better coherency? */
|
||||
return true;
|
||||
}
|
||||
|
||||
const float4 I = kernel_read_pass_float4(buffer + kernel_data.film.pass_combined);
|
||||
|
||||
const float sample = __float_as_uint(buffer[kernel_data.film.pass_sample_count]);
|
||||
const float intensity_scale = kernel_data.film.exposure / sample;
|
||||
|
||||
/* The per pixel error as seen in section 2.1 of
|
||||
* "A hierarchical automatic stopping condition for Monte Carlo global illumination" */
|
||||
const float error_difference = (fabsf(I.x - A.x) + fabsf(I.y - A.y) + fabsf(I.z - A.z)) *
|
||||
intensity_scale;
|
||||
const float intensity = (I.x + I.y + I.z) * intensity_scale;
|
||||
|
||||
/* Anything with R+G+B > 1 is highly exposed - even in sRGB it's a range that
|
||||
* some displays aren't even able to display without significant losses in
|
||||
* detalization. Everything with R+G+B > 3 is overexposed and should receive
|
||||
* even less samples. Filmic-like curves need maximum sampling rate at
|
||||
* intensity near 0.1-0.2, so threshold of 1 for R+G+B leaves an additional
|
||||
* fstop in case it is needed for compositing.
|
||||
*/
|
||||
float error_normalize;
|
||||
if (intensity < 1.0f) {
|
||||
error_normalize = sqrtf(intensity);
|
||||
}
|
||||
else {
|
||||
error_normalize = intensity;
|
||||
}
|
||||
|
||||
/* A small epsilon is added to the divisor to prevent division by zero. */
|
||||
const float error = error_difference / (0.0001f + error_normalize);
|
||||
const bool did_converge = (error < threshold);
|
||||
|
||||
const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3;
|
||||
buffer[aux_w_offset] = did_converge;
|
||||
|
||||
return did_converge;
|
||||
}
|
||||
|
||||
/* This is a simple box filter in two passes.
|
||||
* When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */
|
||||
|
||||
ccl_device void film_adaptive_sampling_filter_x(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int start_x,
|
||||
const int width,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED);
|
||||
|
||||
bool prev = false;
|
||||
for (int x = start_x; x < start_x + width; ++x) {
|
||||
int index = offset + x + y * stride;
|
||||
ccl_global float *buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride;
|
||||
const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3;
|
||||
|
||||
if (buffer[aux_w_offset] == 0.0f) {
|
||||
if (x > start_x && !prev) {
|
||||
index = index - 1;
|
||||
buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride;
|
||||
buffer[aux_w_offset] = 0.0f;
|
||||
}
|
||||
prev = true;
|
||||
}
|
||||
else {
|
||||
if (prev) {
|
||||
buffer[aux_w_offset] = 0.0f;
|
||||
}
|
||||
prev = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device void film_adaptive_sampling_filter_y(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int start_y,
|
||||
const int height,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED);
|
||||
|
||||
bool prev = false;
|
||||
for (int y = start_y; y < start_y + height; ++y) {
|
||||
int index = offset + x + y * stride;
|
||||
ccl_global float *buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride;
|
||||
const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3;
|
||||
|
||||
if (buffer[aux_w_offset] == 0.0f) {
|
||||
if (y > start_y && !prev) {
|
||||
index = index - stride;
|
||||
buffer = render_buffer + (uint64_t)index * kernel_data.film.pass_stride;
|
||||
buffer[aux_w_offset] = 0.0f;
|
||||
}
|
||||
prev = true;
|
||||
}
|
||||
else {
|
||||
if (prev) {
|
||||
buffer[aux_w_offset] = 0.0f;
|
||||
}
|
||||
prev = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
32
blender-5.2.0/intern/cycles/kernel/film/aov_passes.h
Normal file
32
blender-5.2.0/intern/cycles/kernel/film/aov_passes.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device_inline void film_write_aov_pass_value(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int aov_id,
|
||||
const float value)
|
||||
{
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_aov_value + aov_id, value);
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_aov_pass_color(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int aov_id,
|
||||
const float3 color)
|
||||
{
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_aov_color + aov_id,
|
||||
make_float4(color, 1.0f));
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
96
blender-5.2.0/intern/cycles/kernel/film/cryptomatte_passes.h
Normal file
96
blender-5.2.0/intern/cycles/kernel/film/cryptomatte_passes.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Element of ID pass stored in the render buffers.
|
||||
* It is `float2` semantically, but it must be unaligned since the offset of ID passes in the
|
||||
* render buffers might not meet expected by compiler alignment. */
|
||||
struct CryptoPassBufferElement {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
ccl_device_inline void film_write_cryptomatte_slots(ccl_global float *buffer,
|
||||
const int num_slots,
|
||||
const float id,
|
||||
const float weight)
|
||||
{
|
||||
kernel_assert(id != ID_NONE);
|
||||
if (weight == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < num_slots; slot++) {
|
||||
ccl_global CryptoPassBufferElement *id_buffer = (ccl_global CryptoPassBufferElement *)buffer;
|
||||
#ifdef __ATOMIC_PASS_WRITE__
|
||||
/* If the loop reaches an empty slot, the ID isn't in any slot yet - so add it! */
|
||||
if (id_buffer[slot].x == ID_NONE) {
|
||||
/* Use an atomic to claim this slot.
|
||||
* If a different thread got here first, try again from this slot on. */
|
||||
float old_id = atomic_compare_and_swap_float(buffer + slot * 2, ID_NONE, id);
|
||||
if (old_id != ID_NONE && old_id != id) {
|
||||
continue;
|
||||
}
|
||||
atomic_add_and_fetch_float(buffer + slot * 2 + 1, weight);
|
||||
break;
|
||||
}
|
||||
/* If there already is a slot for that ID, add the weight.
|
||||
* If no slot was found, add it to the last. */
|
||||
else if (id_buffer[slot].x == id || slot == num_slots - 1) {
|
||||
atomic_add_and_fetch_float(buffer + slot * 2 + 1, weight);
|
||||
break;
|
||||
}
|
||||
#else /* __ATOMIC_PASS_WRITE__ */
|
||||
/* If the loop reaches an empty slot, the ID isn't in any slot yet - so add it! */
|
||||
if (id_buffer[slot].x == ID_NONE) {
|
||||
id_buffer[slot].x = id;
|
||||
id_buffer[slot].y = weight;
|
||||
break;
|
||||
}
|
||||
/* If there already is a slot for that ID, add the weight.
|
||||
* If no slot was found, add it to the last. */
|
||||
if (id_buffer[slot].x == id || slot == num_slots - 1) {
|
||||
id_buffer[slot].y += weight;
|
||||
break;
|
||||
}
|
||||
#endif /* __ATOMIC_PASS_WRITE__ */
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline void film_sort_cryptomatte_slots(ccl_global float *buffer, const int num_slots)
|
||||
{
|
||||
ccl_global CryptoPassBufferElement *id_buffer = (ccl_global CryptoPassBufferElement *)buffer;
|
||||
for (int slot = 1; slot < num_slots; ++slot) {
|
||||
if (id_buffer[slot].x == ID_NONE) {
|
||||
return;
|
||||
}
|
||||
/* Since we're dealing with a tiny number of elements, insertion sort should be fine. */
|
||||
int i = slot;
|
||||
while (i > 0 && id_buffer[i].y > id_buffer[i - 1].y) {
|
||||
const CryptoPassBufferElement swap = id_buffer[i];
|
||||
id_buffer[i] = id_buffer[i - 1];
|
||||
id_buffer[i - 1] = swap;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* post-sorting for Cryptomatte */
|
||||
ccl_device_inline void film_cryptomatte_post(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int pixel_index)
|
||||
{
|
||||
const int pass_stride = kernel_data.film.pass_stride;
|
||||
const uint64_t render_buffer_offset = (uint64_t)pixel_index * pass_stride;
|
||||
ccl_global float *cryptomatte_buffer = render_buffer + render_buffer_offset +
|
||||
kernel_data.film.pass_cryptomatte;
|
||||
film_sort_cryptomatte_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
224
blender-5.2.0/intern/cycles/kernel/film/data_passes.h
Normal file
224
blender-5.2.0/intern/cycles/kernel/film/data_passes.h
Normal file
@@ -0,0 +1,224 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/integrator/surface_shader.h"
|
||||
|
||||
#include "kernel/camera/camera.h"
|
||||
|
||||
#include "kernel/geom/primitive.h"
|
||||
|
||||
#include "kernel/film/cryptomatte_passes.h"
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device_inline size_t film_write_cryptomatte_pass(ccl_global float *ccl_restrict buffer,
|
||||
const size_t depth,
|
||||
const float id,
|
||||
const float matte_weight)
|
||||
{
|
||||
film_write_cryptomatte_slots(buffer, depth * 2, id, matte_weight);
|
||||
return depth * 4;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_data_passes(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const ccl_private ShaderData *sd,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
#ifdef __PASSES__
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
|
||||
if (!(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't write data passes for paths that were split off for shadow catchers
|
||||
* to avoid double-counting. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int flag = kernel_data.film.pass_flag;
|
||||
|
||||
if (!(flag & PASS_ANY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) {
|
||||
if (INTEGRATOR_STATE(state, path, sample) == 0) {
|
||||
if (flag & PASSMASK(DEPTH)) {
|
||||
const float depth = camera_z_depth(kg, sd->P);
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_depth, depth);
|
||||
}
|
||||
if (flag & PASSMASK(OBJECT_ID)) {
|
||||
const float id = object_pass_id(kg, sd->object);
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_object_id, id);
|
||||
}
|
||||
if (flag & PASSMASK(MATERIAL_ID)) {
|
||||
const float id = shader_pass_id(kg, sd);
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_material_id, id);
|
||||
}
|
||||
if (flag & PASSMASK(POSITION)) {
|
||||
const float3 position = sd->P;
|
||||
film_overwrite_pass_float3(buffer + kernel_data.film.pass_position, position);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(sd->flag & (SD_TRANSPARENT | SD_RAY_PORTAL)) ||
|
||||
kernel_data.film.pass_alpha_threshold == 0.0f ||
|
||||
average(surface_shader_alpha(sd)) >= kernel_data.film.pass_alpha_threshold)
|
||||
{
|
||||
if (flag & PASSMASK(NORMAL)) {
|
||||
const float3 normal = surface_shader_average_normal(sd);
|
||||
film_write_pass_float3(buffer + kernel_data.film.pass_normal, normal);
|
||||
}
|
||||
if (flag & PASSMASK(ROUGHNESS)) {
|
||||
const float roughness = surface_shader_average_roughness(sd);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_roughness, roughness);
|
||||
}
|
||||
if (flag & PASSMASK(UV)) {
|
||||
const float3 uv = primitive_uv(kg, sd);
|
||||
film_write_pass_float3(buffer + kernel_data.film.pass_uv, uv);
|
||||
}
|
||||
if (flag & PASSMASK(MOTION)) {
|
||||
const float4 speed = primitive_motion_vector(kg, sd);
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_motion, speed);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f);
|
||||
}
|
||||
|
||||
INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SINGLE_PASS_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_data.film.cryptomatte_passes) {
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
const float matte_weight = average(throughput) *
|
||||
(1.0f - average(surface_shader_transparency(sd)));
|
||||
if (matte_weight > 0.0f) {
|
||||
ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte;
|
||||
if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) {
|
||||
const float id = object_cryptomatte_id(kg, sd->object);
|
||||
cryptomatte_buffer += film_write_cryptomatte_pass(
|
||||
cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight);
|
||||
}
|
||||
if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) {
|
||||
const float id = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).cryptomatte_id;
|
||||
cryptomatte_buffer += film_write_cryptomatte_pass(
|
||||
cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight);
|
||||
}
|
||||
if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) {
|
||||
const float id = object_cryptomatte_asset_id(kg, sd->object);
|
||||
cryptomatte_buffer += film_write_cryptomatte_pass(
|
||||
cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & PASSMASK(DIFFUSE_COLOR)) {
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_diffuse_color,
|
||||
surface_shader_diffuse(kg, sd) * throughput);
|
||||
}
|
||||
if (flag & PASSMASK(GLOSSY_COLOR)) {
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_glossy_color,
|
||||
surface_shader_glossy(kg, sd) * throughput);
|
||||
}
|
||||
if (flag & PASSMASK(TRANSMISSION_COLOR)) {
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_transmission_color,
|
||||
surface_shader_transmission(kg, sd) * throughput);
|
||||
}
|
||||
if (flag & PASSMASK(MIST)) {
|
||||
/* Bring depth into 0..1 range. */
|
||||
const float mist_start = kernel_data.film.mist_start;
|
||||
const float mist_inv_depth = kernel_data.film.mist_inv_depth;
|
||||
|
||||
const float depth = camera_distance(kg, sd->P);
|
||||
float mist = saturatef((depth - mist_start) * mist_inv_depth);
|
||||
|
||||
/* Falloff */
|
||||
const float mist_falloff = kernel_data.film.mist_falloff;
|
||||
|
||||
if (mist_falloff == 1.0f) {
|
||||
;
|
||||
}
|
||||
else if (mist_falloff == 2.0f) {
|
||||
mist = mist * mist;
|
||||
}
|
||||
else if (mist_falloff == 0.5f) {
|
||||
mist = sqrtf(mist);
|
||||
}
|
||||
else {
|
||||
mist = powf(mist, mist_falloff);
|
||||
}
|
||||
|
||||
/* Modulate by transparency */
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
const Spectrum alpha = surface_shader_alpha(sd);
|
||||
const float mist_output = (1.0f - mist) * average(throughput * alpha);
|
||||
|
||||
/* Note that the final value in the render buffer we want is 1 - mist_output,
|
||||
* to avoid having to tracking this in the Integrator state we do the negation
|
||||
* after rendering. */
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_mist, mist_output);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_data_passes_background(
|
||||
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
#ifdef __PASSES__
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
|
||||
if (!(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't write data passes for paths that were split off for shadow catchers
|
||||
* to avoid double-counting. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int flag = kernel_data.film.pass_flag;
|
||||
|
||||
if (!(flag & PASS_ANY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) {
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
if (INTEGRATOR_STATE(state, path, sample) == 0) {
|
||||
if (flag & PASSMASK(DEPTH)) {
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_depth, 0.0f);
|
||||
}
|
||||
if (flag & PASSMASK(OBJECT_ID)) {
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_object_id, 0.0f);
|
||||
}
|
||||
if (flag & PASSMASK(MATERIAL_ID)) {
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_material_id, 0.0f);
|
||||
}
|
||||
if (flag & PASSMASK(POSITION)) {
|
||||
film_overwrite_pass_float3(buffer + kernel_data.film.pass_position, zero_float3());
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & PASSMASK(MOTION)) {
|
||||
const float4 speed = camera_motion_vector_direction(kg, INTEGRATOR_STATE(state, ray, D));
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_motion, speed);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
277
blender-5.2.0/intern/cycles/kernel/film/denoising_passes.h
Normal file
277
blender-5.2.0/intern/cycles/kernel/film/denoising_passes.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/closure/bsdf.h"
|
||||
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __DENOISING_FEATURES__
|
||||
ccl_device_forceinline float denoising_depth_compute(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const ccl_private ShaderData *sd,
|
||||
const Spectrum denoising_feature_throughput,
|
||||
const bool follow_reflections)
|
||||
{
|
||||
float depth;
|
||||
const float d = sd->ray_length - INTEGRATOR_STATE(state, ray, tmin);
|
||||
if (follow_reflections) {
|
||||
/* Write the ray length minus tmin. */
|
||||
depth = d;
|
||||
}
|
||||
else {
|
||||
/* Write the camera z depth. */
|
||||
const float3 prev_P = sd->P + sd->wi * d;
|
||||
const float prev_depth = camera_z_depth(kg, prev_P);
|
||||
const float new_depth = camera_z_depth(kg, sd->P);
|
||||
depth = new_depth - prev_depth;
|
||||
}
|
||||
|
||||
return ensure_finite(depth * average(denoising_feature_throughput));
|
||||
}
|
||||
|
||||
ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const ccl_private ShaderData *sd,
|
||||
ccl_global float *ccl_restrict
|
||||
render_buffer)
|
||||
{
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
if (!(path_flag & PATH_RAY_DENOISING_FEATURES)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't write denoising passes for paths that were split off for shadow catchers
|
||||
* to avoid double-counting. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_albedo_roughness_weighting = (kernel_data.film.denoising_pass_options_flag &
|
||||
DENOISING_PASS_USE_ALBEDO_ROUGHNESS_WEIGHTING) != 0;
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
float3 normal = zero_float3();
|
||||
Spectrum diffuse_albedo = zero_spectrum();
|
||||
Spectrum specular_albedo = zero_spectrum();
|
||||
Spectrum transparent_albedo = zero_spectrum();
|
||||
float specular_roughness = 0.0f;
|
||||
float sum_weight = 0.0f;
|
||||
float sum_nonspecular_weight = 0.0f;
|
||||
|
||||
for (int i = 0; i < sd->num_closure; i++) {
|
||||
const ccl_private ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Transparency always passes through. */
|
||||
if (CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) {
|
||||
transparent_albedo += sc->weight;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Spectrum closure_albedo = bsdf_albedo(kg, sd, sc, true, true);
|
||||
const float closure_weight = average(closure_albedo);
|
||||
|
||||
/* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */
|
||||
/* If far-field hair, use fiber tangent as feature instead of normal. */
|
||||
normal += (sc->type == CLOSURE_BSDF_HAIR_HUANG_ID ? safe_normalize(sd->dPdu) : sc->N) *
|
||||
closure_weight;
|
||||
|
||||
/* bsdf_get_specular_roughness_squared returns GGX alpha squared (alpha_x*alpha_y). Use sqrtf
|
||||
* to get GGX alpha. */
|
||||
const float roughness = sqrtf(bsdf_get_specular_roughness_squared(sc));
|
||||
|
||||
/* Transition smoothly from specular to diffuse between 0.0 and 0.15 roughness. */
|
||||
const float diffuse_weight = (sc->type == CLOSURE_BSDF_HAIR_HUANG_ID) ?
|
||||
1.0f :
|
||||
smoothstep(0.0f, 0.15f, roughness);
|
||||
|
||||
if (use_albedo_roughness_weighting) {
|
||||
diffuse_albedo += closure_albedo * diffuse_weight;
|
||||
specular_albedo += closure_albedo * (1.0f - diffuse_weight);
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) {
|
||||
diffuse_albedo += closure_albedo;
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type) || CLOSURE_IS_GLASS(sc->type)) {
|
||||
specular_albedo += closure_albedo;
|
||||
}
|
||||
/* Apply sqrtf again to convert GGX alpha to perceptual roughness. */
|
||||
specular_roughness += sqrtf(roughness) * closure_weight;
|
||||
|
||||
sum_weight += closure_weight;
|
||||
sum_nonspecular_weight += closure_weight * diffuse_weight;
|
||||
}
|
||||
|
||||
/* Fraction of non-transparent closures, for smooth blending at transparent surfaces. */
|
||||
const float transparent_weight = average(transparent_albedo);
|
||||
const float total_weight = sum_weight + transparent_weight;
|
||||
|
||||
/* Blend between writing features at this bounce vs. deferring to the next bounce based
|
||||
* on the proportion of diffuse closures. Smoothly transition between 0.0 and 0.5 diffuse
|
||||
* fraction. */
|
||||
float feature_weight = 0.0f;
|
||||
if (sum_weight > 0.0f) {
|
||||
normal /= sum_weight;
|
||||
specular_roughness /= sum_weight;
|
||||
|
||||
feature_weight = smoothstep(0.0f, 0.5f, sum_nonspecular_weight / sum_weight);
|
||||
}
|
||||
|
||||
/* Whether to defer features to the next bounce for individual passes. */
|
||||
const bool follow_reflections = (kernel_data.film.denoising_pass_options_flag &
|
||||
DENOISING_PASS_FOLLOW_REFLECTIONS) != 0;
|
||||
if (!follow_reflections) {
|
||||
feature_weight = 1.0f;
|
||||
}
|
||||
|
||||
const Spectrum denoising_feature_throughput = INTEGRATOR_STATE(
|
||||
state, path, denoising_feature_throughput);
|
||||
const bool is_first_bounce = INTEGRATOR_STATE(state, path, bounce) == 0;
|
||||
|
||||
if (kernel_data.film.pass_denoising_depth != PASS_UNUSED &&
|
||||
(is_first_bounce || follow_reflections))
|
||||
{
|
||||
const float denoising_depth = denoising_depth_compute(
|
||||
kg, state, sd, denoising_feature_throughput, follow_reflections);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth);
|
||||
}
|
||||
|
||||
if (kernel_data.film.pass_denoising_normal != PASS_UNUSED && feature_weight > 0.0f &&
|
||||
(is_first_bounce || follow_reflections))
|
||||
{
|
||||
/* Transform normal into camera space. */
|
||||
const Transform worldtocamera = kernel_data.cam.worldtocamera;
|
||||
float3 denoising_normal = transform_direction(&worldtocamera, normal);
|
||||
const float opaque_fraction = (total_weight > 0.0f) ? (sum_weight / total_weight) : 1.0f;
|
||||
|
||||
denoising_normal = ensure_finite(denoising_normal * opaque_fraction * feature_weight *
|
||||
average(denoising_feature_throughput));
|
||||
film_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal);
|
||||
}
|
||||
|
||||
if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED && feature_weight > 0.0f &&
|
||||
(is_first_bounce || follow_reflections))
|
||||
{
|
||||
const Spectrum denoising_albedo = ensure_finite(diffuse_albedo * feature_weight *
|
||||
denoising_feature_throughput);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo);
|
||||
}
|
||||
|
||||
if (is_first_bounce) {
|
||||
if (kernel_data.film.pass_denoising_roughness != PASS_UNUSED) {
|
||||
const float denoising_roughness = ensure_finite(specular_roughness *
|
||||
average(denoising_feature_throughput));
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_denoising_roughness,
|
||||
denoising_roughness);
|
||||
}
|
||||
|
||||
if (kernel_data.film.pass_denoising_specular_albedo != PASS_UNUSED) {
|
||||
const Spectrum denoising_specular_albedo = ensure_finite(specular_albedo *
|
||||
denoising_feature_throughput);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_specular_albedo,
|
||||
denoising_specular_albedo);
|
||||
}
|
||||
|
||||
if (kernel_data.film.pass_denoising_backward_motion != PASS_UNUSED) {
|
||||
const float3 backward_motion = primitive_motion_vector_backward_depth_delta(kg, sd);
|
||||
film_write_pass_float3(buffer + kernel_data.film.pass_denoising_backward_motion,
|
||||
backward_motion);
|
||||
}
|
||||
}
|
||||
|
||||
/* Portion deferred to the next bounce. Specularity uses the feature weight, transparent
|
||||
* always passes through. */
|
||||
const Spectrum deferred_albedo = specular_albedo * (1.0f - feature_weight) + transparent_albedo;
|
||||
|
||||
if (reduce_max(fabs(deferred_albedo)) > 1e-4f) {
|
||||
INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) *= deferred_albedo;
|
||||
}
|
||||
else {
|
||||
INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_forceinline void film_write_denoising_features_surface_volume(
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const ccl_private ShaderData *sd,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
const bool follow_reflections = (kernel_data.film.denoising_pass_options_flag &
|
||||
DENOISING_PASS_FOLLOW_REFLECTIONS) != 0;
|
||||
const bool is_first_bounce = INTEGRATOR_STATE(state, path, bounce) == 0;
|
||||
|
||||
if (kernel_data.film.pass_denoising_depth != PASS_UNUSED &&
|
||||
(is_first_bounce || follow_reflections))
|
||||
{
|
||||
const Spectrum denoising_feature_throughput = INTEGRATOR_STATE(
|
||||
state, path, denoising_feature_throughput);
|
||||
|
||||
const float denoising_depth = denoising_depth_compute(
|
||||
kg, state, sd, denoising_feature_throughput, follow_reflections);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_forceinline void film_write_denoising_features_volume(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const Spectrum albedo,
|
||||
const bool scatter,
|
||||
ccl_global float *ccl_restrict
|
||||
render_buffer)
|
||||
{
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
const Spectrum denoising_feature_throughput = INTEGRATOR_STATE(
|
||||
state, path, denoising_feature_throughput);
|
||||
|
||||
if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) {
|
||||
/* Assume scatter is sufficiently diffuse to stop writing denoising features. */
|
||||
INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES;
|
||||
|
||||
/* Write view direction as normal. */
|
||||
const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f);
|
||||
film_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal);
|
||||
}
|
||||
|
||||
if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) {
|
||||
/* Write albedo. */
|
||||
const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * albedo);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_forceinline void film_write_denoising_features_background(
|
||||
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
if (!(path_flag & PATH_RAY_DENOISING_FEATURES)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not write default background denoising data for secondary paths. */
|
||||
if (INTEGRATOR_STATE(state, path, bounce) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) {
|
||||
film_overwrite_pass_float(buffer + kernel_data.film.pass_denoising_depth, FLT_MAX);
|
||||
}
|
||||
|
||||
/* 'pass_denoising_albedo' is written by 'film_write_emission_or_background_pass' */
|
||||
}
|
||||
#endif /* __DENOISING_FEATURES__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
700
blender-5.2.0/intern/cycles/kernel/film/light_passes.h
Normal file
700
blender-5.2.0/intern/cycles/kernel/film/light_passes.h
Normal file
@@ -0,0 +1,700 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
#include "kernel/integrator/shadow_catcher.h"
|
||||
|
||||
#include "kernel/sample/pattern.h"
|
||||
#include "util/atomic.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* BSDF Evaluation
|
||||
*
|
||||
* BSDF evaluation result, split between diffuse and glossy. This is used to
|
||||
* accumulate render passes separately. Note that reflection, transmission
|
||||
* and volume scattering are written to different render passes, but we assume
|
||||
* that only one of those can happen at a bounce, and so do not need to accumulate
|
||||
* them separately. */
|
||||
|
||||
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
Spectrum value)
|
||||
{
|
||||
eval->diffuse = zero_spectrum();
|
||||
eval->glossy = zero_spectrum();
|
||||
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
|
||||
eval->diffuse = value;
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
|
||||
eval->glossy = value;
|
||||
}
|
||||
else if (CLOSURE_IS_GLASS(sc->type)) {
|
||||
/* Glass can count as glossy or transmission, depending on which side we end up on. */
|
||||
if (dot(sc->N, wo) > 0.0f) {
|
||||
eval->glossy = value;
|
||||
}
|
||||
}
|
||||
|
||||
eval->sum = value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, Spectrum value)
|
||||
{
|
||||
eval->diffuse = zero_spectrum();
|
||||
eval->glossy = zero_spectrum();
|
||||
eval->sum = value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
Spectrum value)
|
||||
{
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
|
||||
eval->diffuse += value;
|
||||
}
|
||||
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
|
||||
eval->glossy += value;
|
||||
}
|
||||
else if (CLOSURE_IS_GLASS(sc->type)) {
|
||||
if (dot(sc->N, wo) > 0.0f) {
|
||||
eval->glossy += value;
|
||||
}
|
||||
}
|
||||
|
||||
eval->sum += value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, Spectrum value)
|
||||
{
|
||||
eval->sum += value;
|
||||
}
|
||||
|
||||
ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval)
|
||||
{
|
||||
return is_zero(eval->sum);
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, const float value)
|
||||
{
|
||||
eval->diffuse *= value;
|
||||
eval->glossy *= value;
|
||||
eval->sum *= value;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, Spectrum value)
|
||||
{
|
||||
eval->diffuse *= value;
|
||||
eval->glossy *= value;
|
||||
eval->sum *= value;
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum bsdf_eval_sum(const ccl_private BsdfEval *eval)
|
||||
{
|
||||
return eval->sum;
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum bsdf_eval_pass_diffuse_weight(const ccl_private BsdfEval *eval)
|
||||
{
|
||||
/* Ratio of diffuse weight to recover proportions for writing to render pass.
|
||||
* We assume reflection, transmission and volume scatter to be exclusive. */
|
||||
return safe_divide(eval->diffuse, eval->sum);
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum bsdf_eval_pass_glossy_weight(const ccl_private BsdfEval *eval)
|
||||
{
|
||||
/* Ratio of glossy weight to recover proportions for writing to render pass.
|
||||
* We assume reflection, transmission and volume scatter to be exclusive. */
|
||||
return safe_divide(eval->glossy, eval->sum);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Clamping
|
||||
*
|
||||
* Clamping is done on a per-contribution basis so that we can write directly
|
||||
* to render buffers instead of using per-thread memory, and to avoid the
|
||||
* impact of clamping on other contributions. */
|
||||
|
||||
ccl_device_forceinline void film_clamp_light(KernelGlobals kg,
|
||||
ccl_private Spectrum *L,
|
||||
const int bounce)
|
||||
{
|
||||
#ifdef __KERNEL_DEBUG_NAN__
|
||||
if (!isfinite_safe(*L)) {
|
||||
kernel_assert(!"Cycles sample with non-finite value detected");
|
||||
}
|
||||
#endif
|
||||
/* Make sure all components are finite, allowing the contribution to be usable by adaptive
|
||||
* sampling convergence check, but also to make it so render result never causes issues with
|
||||
* post-processing. */
|
||||
*L = ensure_finite(*L);
|
||||
|
||||
#ifdef __CLAMP_SAMPLE__
|
||||
const float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect :
|
||||
kernel_data.integrator.sample_clamp_direct;
|
||||
const float sum = reduce_add(fabs(*L));
|
||||
if (sum > limit) {
|
||||
*L *= limit / sum;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Pass accumulation utilities.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Adaptive sampling.
|
||||
*/
|
||||
|
||||
ccl_device_inline int film_write_sample(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int sample,
|
||||
const int sample_offset)
|
||||
{
|
||||
if (kernel_data.film.pass_sample_count == PASS_UNUSED) {
|
||||
return sample;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
return atomic_fetch_and_add_uint32(
|
||||
(ccl_global uint *)(buffer) + kernel_data.film.pass_sample_count, 1) +
|
||||
sample_offset;
|
||||
}
|
||||
|
||||
ccl_device void film_write_adaptive_buffer(KernelGlobals kg,
|
||||
const int sample,
|
||||
const Spectrum contribution,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
/* Adaptive Sampling. Fill the additional buffer with only one half of the samples and
|
||||
* calculate our stopping criteria. This is the heuristic from "A hierarchical automatic
|
||||
* stopping condition for Monte Carlo global illumination" except that here it is applied
|
||||
* per pixel and not in hierarchical tiles. */
|
||||
|
||||
if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sample_is_class_A(kernel_data.integrator.sampling_pattern, sample)) {
|
||||
const float3 contribution_rgb = spectrum_to_rgb(contribution);
|
||||
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer,
|
||||
make_float4(contribution_rgb.x * 2.0f,
|
||||
contribution_rgb.y * 2.0f,
|
||||
contribution_rgb.z * 2.0f,
|
||||
0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the volume and surface contribution for volume scattering probability guiding. */
|
||||
ccl_device_inline void film_write_volume_scattering_guiding_pass(
|
||||
KernelGlobals kg,
|
||||
ccl_global float *ccl_restrict buffer,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag,
|
||||
const Spectrum contribution)
|
||||
{
|
||||
int pass_offset = PASS_UNUSED;
|
||||
if (path_flag & PATH_RAY_VOLUME_PRIMARY_TRANSMIT) {
|
||||
pass_offset = kernel_data.film.pass_volume_transmit;
|
||||
}
|
||||
else if (path_visibility & PATH_RAY_VISIBILITY_VOLUME_SCATTER) {
|
||||
pass_offset = kernel_data.film.pass_volume_scatter;
|
||||
}
|
||||
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + pass_offset, contribution);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Shadow catcher.
|
||||
*/
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
|
||||
/* Accumulate contribution to the Shadow Catcher pass.
|
||||
*
|
||||
* Returns truth if the contribution is fully handled here and is not to be added to the other
|
||||
* passes (like combined, adaptive sampling). */
|
||||
|
||||
ccl_device bool film_write_shadow_catcher(KernelGlobals kg,
|
||||
const uint32_t path_flag,
|
||||
const Spectrum contribution,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (!kernel_data.integrator.has_shadow_catcher) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
/* Matte pass. */
|
||||
if (kernel_shadow_catcher_is_matte_path(path_flag)) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution);
|
||||
/* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive
|
||||
* sampling is based on how noisy the combined pass is as if there were no catchers in the
|
||||
* scene. */
|
||||
}
|
||||
|
||||
/* Shadow catcher pass. */
|
||||
if (kernel_shadow_catcher_is_object_pass(path_flag)) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device bool film_write_shadow_catcher_transparent(KernelGlobals kg,
|
||||
const uint32_t path_flag,
|
||||
const Spectrum contribution,
|
||||
const float transparent,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (!kernel_data.integrator.has_shadow_catcher) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_BACKGROUND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Matte pass. */
|
||||
if (kernel_shadow_catcher_is_matte_path(path_flag)) {
|
||||
const float3 contribution_rgb = spectrum_to_rgb(contribution);
|
||||
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_shadow_catcher_matte,
|
||||
make_float4(contribution_rgb, transparent));
|
||||
/* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive
|
||||
* sampling is based on how noisy the combined pass is as if there were no catchers in the
|
||||
* scene. */
|
||||
}
|
||||
|
||||
/* Shadow catcher pass. */
|
||||
if (kernel_shadow_catcher_is_object_pass(path_flag)) {
|
||||
/* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the
|
||||
* calculation and the alpha channel of the pass contains numbers of samples contributed to a
|
||||
* pixel of the pass. */
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device void film_write_shadow_catcher_transparent_only(KernelGlobals kg,
|
||||
const uint32_t path_flag,
|
||||
const float transparent,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (!kernel_data.integrator.has_shadow_catcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
/* Matte pass. */
|
||||
if (kernel_shadow_catcher_is_matte_path(path_flag)) {
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write shadow catcher passes on a bounce from the shadow catcher object. */
|
||||
ccl_device_forceinline void film_write_shadow_catcher_bounce_data(
|
||||
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
|
||||
/* Count sample for the shadow catcher object. */
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f);
|
||||
|
||||
/* Since the split is done, the sample does not contribute to the matte, so accumulate it as
|
||||
* transparency to the matte. */
|
||||
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3,
|
||||
average(throughput));
|
||||
}
|
||||
|
||||
#endif /* __SHADOW_CATCHER__ */
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Render passes.
|
||||
*/
|
||||
|
||||
/* Write combined pass. */
|
||||
ccl_device_inline void film_write_combined_pass(KernelGlobals kg,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag,
|
||||
const int sample,
|
||||
const Spectrum contribution,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
if (film_write_shadow_catcher(kg, path_flag, contribution, buffer)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_combined, contribution);
|
||||
}
|
||||
|
||||
film_write_adaptive_buffer(kg, sample, contribution, buffer);
|
||||
film_write_volume_scattering_guiding_pass(kg, buffer, path_visibility, path_flag, contribution);
|
||||
}
|
||||
|
||||
/* Write combined pass with transparency. */
|
||||
ccl_device_inline void film_write_combined_transparent_pass(
|
||||
KernelGlobals kg,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag,
|
||||
const int sample,
|
||||
const Spectrum contribution,
|
||||
const float transparent,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
if (film_write_shadow_catcher_transparent(kg, path_flag, contribution, transparent, buffer)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) {
|
||||
const float3 contribution_rgb = spectrum_to_rgb(contribution);
|
||||
|
||||
film_write_pass_float4(buffer + kernel_data.film.pass_combined,
|
||||
make_float4(contribution_rgb, transparent));
|
||||
}
|
||||
|
||||
film_write_adaptive_buffer(kg, sample, contribution, buffer);
|
||||
film_write_volume_scattering_guiding_pass(kg, buffer, path_visibility, path_flag, contribution);
|
||||
}
|
||||
|
||||
/* Write background or emission to appropriate pass. */
|
||||
ccl_device_inline void film_write_emission_or_background_pass(
|
||||
KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
Spectrum contribution,
|
||||
ccl_global float *ccl_restrict buffer,
|
||||
const int pass,
|
||||
const int lightgroup = LIGHTGROUP_NONE)
|
||||
{
|
||||
if (!(kernel_data.film.light_pass_flag & PASS_ANY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __PASSES__
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
int pass_offset = PASS_UNUSED;
|
||||
|
||||
/* Denoising albedo. */
|
||||
# ifdef __DENOISING_FEATURES__
|
||||
if (path_flag & PATH_RAY_DENOISING_FEATURES) {
|
||||
if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) {
|
||||
const Spectrum denoising_feature_throughput = INTEGRATOR_STATE(
|
||||
state, path, denoising_feature_throughput);
|
||||
const Spectrum denoising_albedo = denoising_feature_throughput * contribution;
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo);
|
||||
}
|
||||
}
|
||||
# endif /* __DENOISING_FEATURES__ */
|
||||
|
||||
const bool is_shadowcatcher = (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) != 0;
|
||||
if (!is_shadowcatcher && lightgroup != LIGHTGROUP_NONE &&
|
||||
kernel_data.film.pass_lightgroup != PASS_UNUSED)
|
||||
{
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
|
||||
if (!(path_flag & PATH_RAY_ANY_PASS)) {
|
||||
/* Directly visible, write to emission or background pass. */
|
||||
pass_offset = pass;
|
||||
}
|
||||
else if (is_shadowcatcher) {
|
||||
/* Don't write any light passes for shadow catcher, for easier
|
||||
* compositing back together of the combined pass. */
|
||||
return;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const Spectrum diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
const Spectrum glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
||||
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight;
|
||||
film_write_pass_spectrum(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
}
|
||||
|
||||
/* Single write call for GPU coherence. */
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + pass_offset, contribution);
|
||||
}
|
||||
#endif /* __PASSES__ */
|
||||
}
|
||||
|
||||
/* Write light contribution to render buffer. */
|
||||
ccl_device_inline void film_write_direct_light(KernelGlobals kg,
|
||||
ConstIntegratorShadowState state,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
/* The throughput for shadow paths already contains the light shader evaluation. */
|
||||
Spectrum contribution = INTEGRATOR_STATE(state, shadow_path, throughput);
|
||||
film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce));
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer_shadow(kg, state, render_buffer);
|
||||
|
||||
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, shadow_path, visibility);
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag);
|
||||
const int sample = INTEGRATOR_STATE(state, shadow_path, sample);
|
||||
|
||||
/* Ambient occlusion. */
|
||||
if (path_flag & PATH_RAY_SHADOW_FOR_AO) {
|
||||
if ((kernel_data.kernel_features & KERNEL_FEATURE_AO_PASS) &&
|
||||
(path_visibility & PATH_RAY_VISIBILITY_CAMERA))
|
||||
{
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_ao, contribution);
|
||||
}
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) {
|
||||
const Spectrum ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput);
|
||||
film_write_combined_pass(
|
||||
kg, path_visibility, path_flag, sample, contribution * ao_weight, buffer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Direct light shadow. */
|
||||
film_write_combined_pass(kg, path_visibility, path_flag, sample, contribution, buffer);
|
||||
|
||||
#ifdef __PASSES__
|
||||
if (kernel_data.film.light_pass_flag & PASS_ANY) {
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag);
|
||||
|
||||
/* Don't write any light passes for shadow catcher, for easier
|
||||
* compositing back together of the combined pass. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write lightgroup pass. LIGHTGROUP_NONE is ~0 so decode from unsigned to signed */
|
||||
const int lightgroup = (int)(INTEGRATOR_STATE(state, shadow_path, lightgroup)) - 1;
|
||||
if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
int pass_offset = PASS_UNUSED;
|
||||
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const Spectrum diffuse_weight = INTEGRATOR_STATE(state, shadow_path, pass_diffuse_weight);
|
||||
const Spectrum glossy_weight = INTEGRATOR_STATE(state, shadow_path, pass_glossy_weight);
|
||||
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight;
|
||||
film_write_pass_spectrum(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
|
||||
/* Single write call for GPU coherence. */
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + pass_offset, contribution);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Write transparency to render buffer.
|
||||
*
|
||||
* Note that we accumulate transparency = 1 - alpha in the render buffer.
|
||||
* Otherwise we'd have to write alpha on path termination, which happens
|
||||
* in many places. */
|
||||
ccl_device_inline void film_write_transparent(KernelGlobals kg,
|
||||
const uint32_t path_flag,
|
||||
const float transparent,
|
||||
ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) {
|
||||
film_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent);
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_CATCHER__
|
||||
film_write_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer);
|
||||
#endif
|
||||
|
||||
if (path_flag & PATH_RAY_VOLUME_PRIMARY_TRANSMIT) {
|
||||
kernel_assert(kernel_data.film.pass_volume_transmit != PASS_UNUSED);
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_volume_transmit,
|
||||
make_spectrum(transparent));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write holdout to render buffer. */
|
||||
ccl_device_inline void film_write_holdout(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
const uint32_t path_flag,
|
||||
const float transparent,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
film_write_transparent(kg, path_flag, transparent, buffer);
|
||||
}
|
||||
|
||||
/* Write background contribution to render buffer.
|
||||
*
|
||||
* Includes transparency, matching film_write_transparent. */
|
||||
ccl_device_inline void film_write_background(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
const Spectrum L,
|
||||
const float transparent,
|
||||
const bool is_transparent_background_ray,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
Spectrum contribution = INTEGRATOR_STATE(state, path, throughput) * L;
|
||||
film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1);
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, path, visibility);
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
|
||||
if (is_transparent_background_ray) {
|
||||
film_write_transparent(kg, path_flag, transparent, buffer);
|
||||
}
|
||||
else {
|
||||
const int sample = INTEGRATOR_STATE(state, path, sample);
|
||||
film_write_combined_transparent_pass(
|
||||
kg, path_visibility, path_flag, sample, contribution, transparent, buffer);
|
||||
}
|
||||
film_write_emission_or_background_pass(kg,
|
||||
state,
|
||||
contribution,
|
||||
buffer,
|
||||
kernel_data.film.pass_background,
|
||||
kernel_data.background.lightgroup);
|
||||
}
|
||||
|
||||
/* Write emission to render buffer. */
|
||||
ccl_device_inline void film_write_volume_emission(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
const Spectrum L,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int lightgroup = LIGHTGROUP_NONE)
|
||||
{
|
||||
Spectrum contribution = L;
|
||||
film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1);
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, path, visibility);
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
const int sample = INTEGRATOR_STATE(state, path, sample);
|
||||
|
||||
film_write_combined_pass(kg, path_visibility, path_flag, sample, contribution, buffer);
|
||||
film_write_emission_or_background_pass(
|
||||
kg, state, contribution, buffer, kernel_data.film.pass_emission, lightgroup);
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_surface_emission(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
const Spectrum L,
|
||||
const float mis_weight,
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int lightgroup = LIGHTGROUP_NONE)
|
||||
{
|
||||
Spectrum contribution = INTEGRATOR_STATE(state, path, throughput) * L * mis_weight;
|
||||
film_clamp_light(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1);
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
|
||||
const PathRayVisibility path_visibility = INTEGRATOR_STATE(state, path, visibility);
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
const int sample = INTEGRATOR_STATE(state, path, sample);
|
||||
|
||||
film_write_combined_pass(kg, path_visibility, path_flag, sample, contribution, buffer);
|
||||
film_write_emission_or_background_pass(
|
||||
kg, state, contribution, buffer, kernel_data.film.pass_emission, lightgroup);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
599
blender-5.2.0/intern/cycles/kernel/film/read.h
Normal file
599
blender-5.2.0/intern/cycles/kernel/film/read.h
Normal file
@@ -0,0 +1,599 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Functions to retrieving render passes for display or output. Reading from
|
||||
* the raw render buffer and normalizing based on the number of samples,
|
||||
* computing alpha, compositing shadow catchers, etc. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "util/color.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Common utilities.
|
||||
*/
|
||||
|
||||
/* The input buffer contains transparency = 1 - alpha, this converts it to
|
||||
* alpha. Also clamp since alpha might end up outside of 0..1 due to Russian
|
||||
* roulette. */
|
||||
ccl_device_forceinline float film_transparency_to_alpha(const float transparency)
|
||||
{
|
||||
return saturatef(1.0f - transparency);
|
||||
}
|
||||
|
||||
ccl_device_inline float film_get_scale(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (kfilm_convert->pass_sample_count == PASS_UNUSED) {
|
||||
return kfilm_convert->scale;
|
||||
}
|
||||
|
||||
if (kfilm_convert->pass_use_filter) {
|
||||
const uint sample_count = *(
|
||||
(const ccl_global uint *)(buffer + kfilm_convert->pass_sample_count));
|
||||
return kfilm_convert->scale / sample_count;
|
||||
}
|
||||
|
||||
return kfilm_convert->scale;
|
||||
}
|
||||
|
||||
ccl_device_inline float film_get_scale_exposure(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
if (kfilm_convert->pass_sample_count == PASS_UNUSED) {
|
||||
return kfilm_convert->scale_exposure;
|
||||
}
|
||||
|
||||
const float scale = film_get_scale(kfilm_convert, buffer);
|
||||
|
||||
if (kfilm_convert->pass_use_exposure) {
|
||||
return scale * kfilm_convert->exposure;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
ccl_device_inline bool film_get_scale_and_scale_exposure(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict scale,
|
||||
ccl_private float *ccl_restrict scale_exposure)
|
||||
{
|
||||
if (kfilm_convert->pass_sample_count == PASS_UNUSED) {
|
||||
*scale = kfilm_convert->scale;
|
||||
*scale_exposure = kfilm_convert->scale_exposure;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (kfilm_convert->pass_use_filter) {
|
||||
const uint sample_count = *(
|
||||
(const ccl_global uint *)(buffer + kfilm_convert->pass_sample_count));
|
||||
if (!sample_count) {
|
||||
*scale = 0.0f;
|
||||
*scale_exposure = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
*scale = kfilm_convert->scale / sample_count;
|
||||
}
|
||||
else {
|
||||
*scale = kfilm_convert->scale;
|
||||
}
|
||||
|
||||
if (kfilm_convert->pass_use_exposure) {
|
||||
*scale_exposure = *scale * kfilm_convert->exposure;
|
||||
}
|
||||
else {
|
||||
*scale_exposure = *scale;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Float (scalar) passes.
|
||||
*/
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_depth(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const float f = *in;
|
||||
|
||||
pixel[0] = (f == 0.0f) ? 1e10f : f * scale_exposure;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_mist(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const float f = *in;
|
||||
|
||||
/* Note that we accumulate 1 - mist in the kernel to avoid having to
|
||||
* track the mist values in the integrator state. */
|
||||
pixel[0] = saturatef(1.0f - f * scale_exposure);
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_sample_count(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
/* TODO(sergey): Consider normalizing into the [0..1] range, so that it is possible to see
|
||||
* meaningful value when adaptive sampler stopped rendering image way before the maximum
|
||||
* number of samples was reached (for examples when number of samples is set to 0 in
|
||||
* viewport). */
|
||||
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const float f = *in;
|
||||
|
||||
pixel[0] = __float_as_uint(f) * kfilm_convert->scale;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_volume_majorant(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const ccl_global float *count = buffer + kfilm_convert->pass_divide;
|
||||
const float f = *in;
|
||||
|
||||
pixel[0] = (*count != 0.0f) ? expf(-(f * scale_exposure) / *count) : 0.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_rgbe(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const float3 f = rgbe_to_rgb(RGBE(*in));
|
||||
|
||||
pixel[0] = f.x;
|
||||
pixel[1] = f.y;
|
||||
pixel[2] = f.z;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_float(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 1);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const float f = *in;
|
||||
|
||||
pixel[0] = f * scale_exposure;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Float 3 passes.
|
||||
*/
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_light_path(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 3);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
/* Read light pass. */
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
float3 f = make_float3(in[0], in[1], in[2]);
|
||||
|
||||
/* Optionally add indirect light pass. */
|
||||
if (kfilm_convert->pass_indirect != PASS_UNUSED) {
|
||||
const ccl_global float *in_indirect = buffer + kfilm_convert->pass_indirect;
|
||||
const float3 f_indirect = make_float3(in_indirect[0], in_indirect[1], in_indirect[2]);
|
||||
f += f_indirect;
|
||||
}
|
||||
|
||||
/* Optionally divide out color. */
|
||||
if (kfilm_convert->pass_divide != PASS_UNUSED) {
|
||||
const ccl_global float *in_divide = buffer + kfilm_convert->pass_divide;
|
||||
const float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]);
|
||||
f = safe_divide_even_color(f, f_divide);
|
||||
|
||||
/* Exposure only, sample scale cancels out. */
|
||||
f *= kfilm_convert->exposure;
|
||||
}
|
||||
else {
|
||||
/* Sample scale and exposure. */
|
||||
f *= film_get_scale_exposure(kfilm_convert, buffer);
|
||||
}
|
||||
|
||||
pixel[0] = f.x;
|
||||
pixel[1] = f.y;
|
||||
pixel[2] = f.z;
|
||||
|
||||
/* Optional alpha channel. */
|
||||
if (kfilm_convert->num_components >= 4) {
|
||||
if (kfilm_convert->pass_combined != PASS_UNUSED) {
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
|
||||
|
||||
const ccl_global float *in_combined = buffer + kfilm_convert->pass_combined;
|
||||
const float alpha = in_combined[3] * scale;
|
||||
pixel[3] = film_transparency_to_alpha(alpha);
|
||||
}
|
||||
else {
|
||||
pixel[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_float3(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 3);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
|
||||
const float3 f = make_float3(in[0], in[1], in[2]) * scale_exposure;
|
||||
|
||||
pixel[0] = f.x;
|
||||
pixel[1] = f.y;
|
||||
pixel[2] = f.z;
|
||||
|
||||
/* Optional alpha channel. */
|
||||
if (kfilm_convert->num_components >= 4) {
|
||||
if (kfilm_convert->pass_combined != PASS_UNUSED) {
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
|
||||
|
||||
const ccl_global float *in_combined = buffer + kfilm_convert->pass_combined;
|
||||
const float alpha = in_combined[3] * scale;
|
||||
pixel[3] = film_transparency_to_alpha(alpha);
|
||||
}
|
||||
else {
|
||||
pixel[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Float4 passes.
|
||||
*/
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_motion(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components == 4);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
kernel_assert(kfilm_convert->pass_motion_weight != PASS_UNUSED);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
const ccl_global float *in_weight = buffer + kfilm_convert->pass_motion_weight;
|
||||
|
||||
const float weight = in_weight[0];
|
||||
const float weight_inv = (weight > 0.0f) ? 1.0f / weight : 0.0f;
|
||||
|
||||
const float4 motion = make_float4(in[0], in[1], in[2], in[3]) * weight_inv;
|
||||
|
||||
pixel[0] = motion.x;
|
||||
pixel[1] = motion.y;
|
||||
pixel[2] = motion.z;
|
||||
pixel[3] = motion.w;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_cryptomatte(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components == 4);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
const float scale = film_get_scale(kfilm_convert, buffer);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
|
||||
const float4 f = make_float4(in[0], in[1], in[2], in[3]);
|
||||
|
||||
/* x and z contain integer IDs, don't rescale them.
|
||||
* y and w contain matte weights, they get scaled. */
|
||||
pixel[0] = f.x;
|
||||
pixel[1] = f.y * scale;
|
||||
pixel[2] = f.z;
|
||||
pixel[3] = f.w * scale;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_float4(const ccl_global KernelFilmConvert *ccl_restrict
|
||||
kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components == 4);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
|
||||
const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure;
|
||||
const float alpha = in[3] * scale;
|
||||
|
||||
pixel[0] = color.x;
|
||||
pixel[1] = color.y;
|
||||
pixel[2] = color.z;
|
||||
pixel[3] = alpha;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_combined(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components == 4);
|
||||
|
||||
/* 3rd channel contains transparency = 1 - alpha for the combined pass. */
|
||||
|
||||
kernel_assert(kfilm_convert->num_components == 4);
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) {
|
||||
pixel[0] = 0.0f;
|
||||
pixel[1] = 0.0f;
|
||||
pixel[2] = 0.0f;
|
||||
pixel[3] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
const ccl_global float *in = buffer + kfilm_convert->pass_offset;
|
||||
|
||||
const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure;
|
||||
const float alpha = in[3] * scale;
|
||||
|
||||
pixel[0] = color.x;
|
||||
pixel[1] = color.y;
|
||||
pixel[2] = color.z;
|
||||
pixel[3] = film_transparency_to_alpha(alpha);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Shadow catcher.
|
||||
*/
|
||||
|
||||
ccl_device_inline float3 film_calculate_shadow_catcher_denoised(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED);
|
||||
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure);
|
||||
|
||||
const ccl_global float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher;
|
||||
|
||||
const float3 pixel = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]) * scale_exposure;
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
ccl_device_inline float3 safe_divide_shadow_catcher(const float3 a, const float3 b)
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
x = (b.x != 0.0f) ? a.x / b.x : 1.0f;
|
||||
y = (b.y != 0.0f) ? a.y / b.y : 1.0f;
|
||||
z = (b.z != 0.0f) ? a.z / b.z : 1.0f;
|
||||
|
||||
return make_float3(x, y, z);
|
||||
}
|
||||
|
||||
ccl_device_inline float3
|
||||
film_calculate_shadow_catcher(const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
/* For the shadow catcher pass we divide combined pass by the shadow catcher.
|
||||
* Note that denoised shadow catcher pass contains value which only needs ot be scaled (but not
|
||||
* to be calculated as division). */
|
||||
|
||||
if (kfilm_convert->is_denoised) {
|
||||
return film_calculate_shadow_catcher_denoised(kfilm_convert, buffer);
|
||||
}
|
||||
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher_sample_count != PASS_UNUSED);
|
||||
|
||||
/* If there is no shadow catcher object in this pixel, there is no modification of the light
|
||||
* needed, so return one. */
|
||||
const ccl_global float *in_catcher_sample_count =
|
||||
buffer + kfilm_convert->pass_shadow_catcher_sample_count;
|
||||
const float num_samples = in_catcher_sample_count[0];
|
||||
if (num_samples == 0.0f) {
|
||||
return one_float3();
|
||||
}
|
||||
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED);
|
||||
const ccl_global float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher;
|
||||
|
||||
/* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual
|
||||
* shadow catcher objects in the scene. In this case there will be no auxiliary passes required
|
||||
* for the decision (to save up memory). So delay the asserts to this point so that the number of
|
||||
* samples check handles such configuration. */
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
kernel_assert(kfilm_convert->pass_combined != PASS_UNUSED);
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
const ccl_global float *in_combined = buffer + kfilm_convert->pass_combined;
|
||||
const ccl_global float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte;
|
||||
|
||||
/* No scaling needed. The integration works in way that number of samples in the combined and
|
||||
* shadow catcher passes are the same, and exposure is canceled during the division. */
|
||||
const float3 color_catcher = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]);
|
||||
const float3 color_combined = make_float3(in_combined[0], in_combined[1], in_combined[2]);
|
||||
const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]);
|
||||
|
||||
/* Need to ignore contribution of the matte object when doing division (otherwise there will be
|
||||
* artifacts caused by anti-aliasing). Since combined pass is used for adaptive sampling and need
|
||||
* to contain matte objects, we subtract matte objects contribution here. This is the same as if
|
||||
* the matte objects were not accumulated to the combined pass. */
|
||||
const float3 combined_no_matte = color_combined - color_matte;
|
||||
|
||||
const float3 shadow_catcher = safe_divide_shadow_catcher(combined_no_matte, color_catcher);
|
||||
|
||||
const float scale = film_get_scale(kfilm_convert, buffer);
|
||||
const float transparency = in_combined[3] * scale;
|
||||
const float alpha = film_transparency_to_alpha(transparency);
|
||||
|
||||
/* Alpha-over on white using transparency of the combined pass. This allows to eliminate
|
||||
* artifacts which are happening on an edge of a shadow catcher when using transparent film.
|
||||
* Note that we treat shadow catcher as straight alpha here because alpha got canceled out
|
||||
* during the division. */
|
||||
const float3 pixel = (1.0f - alpha) * one_float3() + alpha * shadow_catcher;
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
/* The approximation of the shadow is 1 - average(shadow_catcher_pass). A better approximation
|
||||
* is possible.
|
||||
*
|
||||
* The matte is alpha-overed onto the shadow (which is kind of alpha-overing shadow onto footage,
|
||||
* and then alpha-overing synthetic objects on top). */
|
||||
|
||||
kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED);
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED);
|
||||
kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED);
|
||||
|
||||
float scale;
|
||||
float scale_exposure;
|
||||
if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) {
|
||||
return zero_float4();
|
||||
}
|
||||
|
||||
const ccl_global float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte;
|
||||
|
||||
const float3 shadow_catcher = film_calculate_shadow_catcher(kfilm_convert, buffer);
|
||||
const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]) * scale_exposure;
|
||||
|
||||
const float transparency = in_matte[3] * scale;
|
||||
const float alpha = saturatef(1.0f - transparency);
|
||||
|
||||
const float alpha_matte = (1.0f - alpha) * (1.0f - saturatef(average(shadow_catcher))) + alpha;
|
||||
|
||||
if (kfilm_convert->use_approximate_shadow_catcher_background) {
|
||||
kernel_assert(kfilm_convert->pass_background != PASS_UNUSED);
|
||||
|
||||
const ccl_global float *in_background = buffer + kfilm_convert->pass_background;
|
||||
const float3 color_background = make_float3(
|
||||
in_background[0], in_background[1], in_background[2]) *
|
||||
scale_exposure;
|
||||
const float3 alpha_over = color_matte + color_background * (1.0f - alpha_matte);
|
||||
return make_float4(alpha_over, 1.0f);
|
||||
}
|
||||
|
||||
return make_float4(color_matte, alpha_matte);
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_shadow_catcher(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components >= 3);
|
||||
|
||||
const float3 pixel_value = film_calculate_shadow_catcher(kfilm_convert, buffer);
|
||||
|
||||
pixel[0] = pixel_value.x;
|
||||
pixel[1] = pixel_value.y;
|
||||
pixel[2] = pixel_value.z;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_get_pass_pixel_shadow_catcher_matte_with_shadow(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
kernel_assert(kfilm_convert->num_components == 3 || kfilm_convert->num_components == 4);
|
||||
|
||||
const float4 pixel_value = film_calculate_shadow_catcher_matte_with_shadow(kfilm_convert,
|
||||
buffer);
|
||||
|
||||
pixel[0] = pixel_value.x;
|
||||
pixel[1] = pixel_value.y;
|
||||
pixel[2] = pixel_value.z;
|
||||
if (kfilm_convert->num_components == 4) {
|
||||
pixel[3] = pixel_value.w;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Compositing and overlays.
|
||||
*/
|
||||
|
||||
ccl_device_inline void film_apply_pass_pixel_overlays_rgba(
|
||||
const ccl_global KernelFilmConvert *ccl_restrict kfilm_convert,
|
||||
const ccl_global float *ccl_restrict buffer,
|
||||
ccl_private float *ccl_restrict pixel)
|
||||
{
|
||||
if (kfilm_convert->show_active_pixels && kfilm_convert->pass_adaptive_aux_buffer != PASS_UNUSED)
|
||||
{
|
||||
if (buffer[kfilm_convert->pass_adaptive_aux_buffer + 3] == 0.0f) {
|
||||
const float3 active_rgb = make_float3(1.0f, 0.0f, 0.0f);
|
||||
const float3 mix_rgb = interp(make_float3(pixel[0], pixel[1], pixel[2]), active_rgb, 0.5f);
|
||||
pixel[0] = mix_rgb.x;
|
||||
pixel[1] = mix_rgb.y;
|
||||
pixel[2] = mix_rgb.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
155
blender-5.2.0/intern/cycles/kernel/film/volume_guiding_denoise.h
Normal file
155
blender-5.2.0/intern/cycles/kernel/film/volume_guiding_denoise.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/film/write.h"
|
||||
|
||||
/* Denoise volume scattering probability guiding buffers. */
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Two-pass Gaussian filter. */
|
||||
ccl_device void volume_guiding_filter_x(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int center_x,
|
||||
const int min_x,
|
||||
const int max_x,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_volume_scatter != PASS_UNUSED);
|
||||
kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED);
|
||||
|
||||
const int radius = 5;
|
||||
const int filter_width = radius * 2 + 1;
|
||||
|
||||
/* sigma = 1.5 with integral according to
|
||||
* https://lisyarus.github.io/blog/posts/blur-coefficients-generator.html
|
||||
* https://bartwronski.com/2021/10/31/practical-gaussian-filter-binomial-filter-and-small-sigma-gaussians/
|
||||
*/
|
||||
const float gaussian_params[filter_width] = {0.0012273699895602f,
|
||||
0.0084674212370284f,
|
||||
0.0379843612914121f,
|
||||
0.1108921888487800f,
|
||||
0.2108379677336155f,
|
||||
0.2611813817992076f,
|
||||
0.2108379677336155f,
|
||||
0.1108921888487800f,
|
||||
0.0379843612914121f,
|
||||
0.0084674212370284f,
|
||||
0.0012273699895602f};
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(
|
||||
kg, center_x, y, offset, stride, render_buffer);
|
||||
|
||||
/* Apply Gaussian filter in x direction. */
|
||||
float3 scatter = zero_float3(), transmit = zero_float3();
|
||||
for (int dx = 0; dx < filter_width; dx++) {
|
||||
const int x = center_x + dx - radius;
|
||||
if (x < min_x || x >= max_x) {
|
||||
/* Ignore boundary pixels. */
|
||||
continue;
|
||||
}
|
||||
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(
|
||||
kg, x, y, offset, stride, render_buffer);
|
||||
|
||||
const float weight = gaussian_params[dx] /
|
||||
__float_as_uint(buffer[kernel_data.film.pass_sample_count]);
|
||||
|
||||
scatter += kernel_read_pass_float3(buffer + kernel_data.film.pass_volume_scatter) * weight;
|
||||
transmit += kernel_read_pass_float3(buffer + kernel_data.film.pass_volume_transmit) * weight;
|
||||
}
|
||||
|
||||
/* Write to the buffer. */
|
||||
film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_scatter_denoised, scatter);
|
||||
film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_transmit_denoised, transmit);
|
||||
}
|
||||
|
||||
ccl_device void volume_guiding_filter_y(KernelGlobals kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int min_y,
|
||||
const int max_y,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
kernel_assert(kernel_data.film.pass_volume_scatter != PASS_UNUSED);
|
||||
|
||||
const int radius = 5;
|
||||
const int filter_width = radius * 2 + 1;
|
||||
|
||||
const float gaussian_params[filter_width] = {0.0012273699895602f,
|
||||
0.0084674212370284f,
|
||||
0.0379843612914121f,
|
||||
0.1108921888487800f,
|
||||
0.2108379677336155f,
|
||||
0.2611813817992076f,
|
||||
0.2108379677336155f,
|
||||
0.1108921888487800f,
|
||||
0.0379843612914121f,
|
||||
0.0084674212370284f,
|
||||
0.0012273699895602f};
|
||||
|
||||
/* Store neighboring values to avoid overwriting. */
|
||||
float3 scatter_neighbors[filter_width], transmit_neighbors[filter_width];
|
||||
|
||||
/* Initialize neighbors. */
|
||||
for (int i = 0; i < filter_width; i++) {
|
||||
const int y = min_y + i;
|
||||
if (i >= radius || y < min_y || y >= max_y) {
|
||||
/* Out-of-boundary neighbors are initialized with zero. */
|
||||
scatter_neighbors[i] = transmit_neighbors[i] = zero_float3();
|
||||
}
|
||||
else {
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(
|
||||
kg, x, y, offset, stride, render_buffer);
|
||||
scatter_neighbors[i] = kernel_read_pass_rgbe(buffer +
|
||||
kernel_data.film.pass_volume_scatter_denoised);
|
||||
transmit_neighbors[i] = kernel_read_pass_rgbe(
|
||||
buffer + kernel_data.film.pass_volume_transmit_denoised);
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply Gaussian filter in y direction. */
|
||||
int index = radius;
|
||||
for (int y = min_y; y < max_y; y++) {
|
||||
/* Fetch the furthest neighbor to the right. */
|
||||
const int next_y = y + radius;
|
||||
if (next_y < min_y || next_y >= max_y) {
|
||||
scatter_neighbors[index] = zero_float3();
|
||||
transmit_neighbors[index] = zero_float3();
|
||||
}
|
||||
else {
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(
|
||||
kg, x, next_y, offset, stride, render_buffer);
|
||||
scatter_neighbors[index] = kernel_read_pass_rgbe(
|
||||
buffer + kernel_data.film.pass_volume_scatter_denoised);
|
||||
transmit_neighbors[index] = kernel_read_pass_rgbe(
|
||||
buffer + kernel_data.film.pass_volume_transmit_denoised);
|
||||
}
|
||||
|
||||
/* Slide the kernel to the right. */
|
||||
index = (index + 1) % filter_width;
|
||||
|
||||
/* Apply convolution. */
|
||||
float3 scatter = zero_float3(), transmit = zero_float3();
|
||||
for (int i = 0; i < filter_width; i++) {
|
||||
scatter += gaussian_params[i] * scatter_neighbors[(index + i) % filter_width];
|
||||
transmit += gaussian_params[i] * transmit_neighbors[(index + i) % filter_width];
|
||||
}
|
||||
|
||||
/* Write to the buffers. */
|
||||
ccl_global float *buffer = film_pass_pixel_render_buffer(
|
||||
kg, x, y, offset, stride, render_buffer);
|
||||
film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_scatter_denoised,
|
||||
fabs(scatter));
|
||||
film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_transmit_denoised,
|
||||
fabs(transmit));
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
158
blender-5.2.0/intern/cycles/kernel/film/write.h
Normal file
158
blender-5.2.0/intern/cycles/kernel/film/write.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
|
||||
#include "kernel/util/colorspace.h"
|
||||
|
||||
#include "util/types_rgbe.h"
|
||||
|
||||
#ifdef __KERNEL_GPU__
|
||||
# include "util/atomic.h"
|
||||
# define __ATOMIC_PASS_WRITE__
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Get pointer to pixel in render buffer. */
|
||||
|
||||
ccl_device_forceinline ccl_global float *film_pass_pixel_render_buffer(
|
||||
KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
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;
|
||||
return render_buffer + render_buffer_offset;
|
||||
}
|
||||
|
||||
ccl_device_forceinline ccl_global float *film_pass_pixel_render_buffer_shadow(
|
||||
KernelGlobals kg,
|
||||
ConstIntegratorShadowState state,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
const uint32_t render_pixel_index = INTEGRATOR_STATE(state, shadow_path, render_pixel_index);
|
||||
const uint64_t render_buffer_offset = (uint64_t)render_pixel_index *
|
||||
kernel_data.film.pass_stride;
|
||||
return render_buffer + render_buffer_offset;
|
||||
}
|
||||
|
||||
ccl_device_forceinline ccl_global float *film_pass_pixel_render_buffer(
|
||||
KernelGlobals kg,
|
||||
const int x,
|
||||
const int y,
|
||||
const int offset,
|
||||
const int stride,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
{
|
||||
const int render_pixel_index = offset + x + y * stride;
|
||||
return render_buffer + (uint64_t)render_pixel_index * kernel_data.film.pass_stride;
|
||||
}
|
||||
|
||||
/* Accumulate in passes. */
|
||||
|
||||
ccl_device_inline void film_write_pass_float(ccl_global float *ccl_restrict buffer,
|
||||
const float value)
|
||||
{
|
||||
#ifdef __ATOMIC_PASS_WRITE__
|
||||
atomic_add_and_fetch_float(buffer, value);
|
||||
#else
|
||||
*buffer += value;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_pass_float3(ccl_global float *ccl_restrict buffer,
|
||||
const float3 value)
|
||||
{
|
||||
#ifdef __ATOMIC_PASS_WRITE__
|
||||
ccl_global float *buf_x = buffer + 0;
|
||||
ccl_global float *buf_y = buffer + 1;
|
||||
ccl_global float *buf_z = buffer + 2;
|
||||
|
||||
atomic_add_and_fetch_float(buf_x, value.x);
|
||||
atomic_add_and_fetch_float(buf_y, value.y);
|
||||
atomic_add_and_fetch_float(buf_z, value.z);
|
||||
#else
|
||||
buffer[0] += value.x;
|
||||
buffer[1] += value.y;
|
||||
buffer[2] += value.z;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_pass_spectrum(ccl_global float *ccl_restrict buffer,
|
||||
Spectrum value)
|
||||
{
|
||||
film_write_pass_float3(buffer, spectrum_to_rgb(value));
|
||||
}
|
||||
|
||||
ccl_device_inline void film_write_pass_float4(ccl_global float *ccl_restrict buffer,
|
||||
const float4 value)
|
||||
{
|
||||
#ifdef __ATOMIC_PASS_WRITE__
|
||||
ccl_global float *buf_x = buffer + 0;
|
||||
ccl_global float *buf_y = buffer + 1;
|
||||
ccl_global float *buf_z = buffer + 2;
|
||||
ccl_global float *buf_w = buffer + 3;
|
||||
|
||||
atomic_add_and_fetch_float(buf_x, value.x);
|
||||
atomic_add_and_fetch_float(buf_y, value.y);
|
||||
atomic_add_and_fetch_float(buf_z, value.z);
|
||||
atomic_add_and_fetch_float(buf_w, value.w);
|
||||
#else
|
||||
buffer[0] += value.x;
|
||||
buffer[1] += value.y;
|
||||
buffer[2] += value.z;
|
||||
buffer[3] += value.w;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline void film_overwrite_pass_rgbe(ccl_global float *ccl_restrict buffer,
|
||||
const float3 value)
|
||||
{
|
||||
*buffer = rgb_to_rgbe(value).f;
|
||||
}
|
||||
|
||||
/* Overwrite for passes that only write on sample 0. This assumes only a single thread will write
|
||||
* to this pixel and no atomics are needed. */
|
||||
|
||||
ccl_device_inline void film_overwrite_pass_float(ccl_global float *ccl_restrict buffer,
|
||||
const float value)
|
||||
{
|
||||
*buffer = value;
|
||||
}
|
||||
|
||||
ccl_device_inline void film_overwrite_pass_float3(ccl_global float *ccl_restrict buffer,
|
||||
const float3 value)
|
||||
{
|
||||
buffer[0] = value.x;
|
||||
buffer[1] = value.y;
|
||||
buffer[2] = value.z;
|
||||
}
|
||||
|
||||
/* Read back from passes. */
|
||||
|
||||
ccl_device_inline float kernel_read_pass_float(const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
return *buffer;
|
||||
}
|
||||
|
||||
ccl_device_inline float3 kernel_read_pass_float3(const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
return make_float3(buffer[0], buffer[1], buffer[2]);
|
||||
}
|
||||
|
||||
ccl_device_inline float4 kernel_read_pass_float4(ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
return make_float4(buffer[0], buffer[1], buffer[2], buffer[3]);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 kernel_read_pass_rgbe(const ccl_global float *ccl_restrict buffer)
|
||||
{
|
||||
return rgbe_to_rgb(RGBE(*buffer));
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user