blender/intern/cycles/kernel/film/data_passes.h
Patrick Mours 50d5140924 Fix #165293: Cycles: Denoising motion pass broken with transparent objects
There was different behavior between PASS_MOTION and
PASS_DENOISING_BACKWARD_MOTION when it comes to transparent objects:
The former was selecting a single hit to take motion from, the latter
was accumulating motion across all transparent hits of the first bounce.
In contrast to albedo, where mixing multiple transparent layers can
help as input for the denoiser, doing that for motion vectors does not
really make sense, since the vectors need to accurately point at a
specific spot in pixel space. This patch therefore attempts to
integrate the same logic for PASS_DENOISING_BACKWARD_MOTION, re-using
the existing `PATH_RAY_SINGLE_PASS_DONE` flag. Since
`film_write_data_passes` (which updates that flag) is called before
`film_write_denoising_features_surface`, the previous state of the flag
is passed into both functions, instead of reading it at the beginning
of each function again.

Pull Request: https://projects.blender.org/blender/blender/pulls/165379
2026-10-21 19:19:32 +02:00

218 lines
8.2 KiB
C

/* 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 uint32_t path_flag,
const ccl_private ShaderData *sd,
ccl_global float *ccl_restrict render_buffer)
{
#ifdef __PASSES__
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) && kernel_data.film.pass_denoising_backward_motion == PASS_UNUSED) {
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->runtime_flag & (SR_TRANSPARENT | SR_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,
const uint32_t path_flag,
ccl_global float *ccl_restrict
render_buffer)
{
#ifdef __PASSES__
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());
}
}
}
#endif
}
CCL_NAMESPACE_END