Blender V5.0
volume_guiding_denoise.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "kernel/film/write.h"
8
9/* Denoise volume scattering probability guiding buffers. */
10
12
13/* Two-pass Gaussian filter. */
16 const int y,
17 const int center_x,
18 const int min_x,
19 const int max_x,
20 const int offset,
21 const int stride)
22{
23 kernel_assert(kernel_data.film.pass_volume_scatter != PASS_UNUSED);
24 kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED);
25
26 const int radius = 5;
27 const int filter_width = radius * 2 + 1;
28
29 /* sigma = 1.5 with integral according to
30 * https://lisyarus.github.io/blog/posts/blur-coefficients-generator.html
31 * https://bartwronski.com/2021/10/31/practical-gaussian-filter-binomial-filter-and-small-sigma-gaussians/
32 */
33 const float gaussian_params[filter_width] = {0.0012273699895602f,
34 0.0084674212370284f,
35 0.0379843612914121f,
36 0.1108921888487800f,
37 0.2108379677336155f,
38 0.2611813817992076f,
39 0.2108379677336155f,
40 0.1108921888487800f,
41 0.0379843612914121f,
42 0.0084674212370284f,
43 0.0012273699895602f};
44
46 kg, center_x, y, offset, stride, render_buffer);
47
48 /* Apply Gaussian filter in x direction. */
49 float3 scatter = zero_float3(), transmit = zero_float3();
50 for (int dx = 0; dx < filter_width; dx++) {
51 const int x = center_x + dx - radius;
52 if (x < min_x || x >= max_x) {
53 /* Ignore boundary pixels. */
54 continue;
55 }
56
58 kg, x, y, offset, stride, render_buffer);
59
60 const float weight = gaussian_params[dx] /
61 __float_as_uint(buffer[kernel_data.film.pass_sample_count]);
62
63 scatter += kernel_read_pass_float3(buffer + kernel_data.film.pass_volume_scatter) * weight;
64 transmit += kernel_read_pass_float3(buffer + kernel_data.film.pass_volume_transmit) * weight;
65 }
66
67 /* Write to the buffer. */
68 film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_scatter_denoised, scatter);
69 film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_transmit_denoised, transmit);
70}
71
74 const int x,
75 const int min_y,
76 const int max_y,
77 const int offset,
78 const int stride)
79{
80 kernel_assert(kernel_data.film.pass_volume_scatter != PASS_UNUSED);
81
82 const int radius = 5;
83 const int filter_width = radius * 2 + 1;
84
85 const float gaussian_params[filter_width] = {0.0012273699895602f,
86 0.0084674212370284f,
87 0.0379843612914121f,
88 0.1108921888487800f,
89 0.2108379677336155f,
90 0.2611813817992076f,
91 0.2108379677336155f,
92 0.1108921888487800f,
93 0.0379843612914121f,
94 0.0084674212370284f,
95 0.0012273699895602f};
96
97 /* Store neighboring values to avoid overwriting. */
98 float3 scatter_neighbors[filter_width], transmit_neighbors[filter_width];
99
100 /* Initialze neighbors. */
101 for (int i = 0; i < filter_width; i++) {
102 const int y = min_y + i;
103 if (i >= radius || y < min_y || y >= max_y) {
104 /* Out-of-boundary neighbors are initialized with zero. */
105 scatter_neighbors[i] = transmit_neighbors[i] = zero_float3();
106 }
107 else {
109 kg, x, y, offset, stride, render_buffer);
110 scatter_neighbors[i] = kernel_read_pass_rgbe(buffer +
111 kernel_data.film.pass_volume_scatter_denoised);
112 transmit_neighbors[i] = kernel_read_pass_rgbe(
113 buffer + kernel_data.film.pass_volume_transmit_denoised);
114 }
115 }
116
117 /* Apply Gaussian filter in y direction. */
118 int index = radius;
119 for (int y = min_y; y < max_y; y++) {
120 /* Fetch the furthest neighbor to the right. */
121 const int next_y = y + radius;
122 if (next_y < min_y || next_y >= max_y) {
123 scatter_neighbors[index] = zero_float3();
124 transmit_neighbors[index] = zero_float3();
125 }
126 else {
128 kg, x, next_y, offset, stride, render_buffer);
129 scatter_neighbors[index] = kernel_read_pass_rgbe(
130 buffer + kernel_data.film.pass_volume_scatter_denoised);
131 transmit_neighbors[index] = kernel_read_pass_rgbe(
132 buffer + kernel_data.film.pass_volume_transmit_denoised);
133 }
134
135 /* Slide the kernel to the right. */
136 index = (index + 1) % filter_width;
137
138 /* Apply convolution. */
139 float3 scatter = zero_float3(), transmit = zero_float3();
140 for (int i = 0; i < filter_width; i++) {
141 scatter += gaussian_params[i] * scatter_neighbors[(index + i) % filter_width];
142 transmit += gaussian_params[i] * transmit_neighbors[(index + i) % filter_width];
143 }
144
145 /* Write to the buffers. */
147 kg, x, y, offset, stride, render_buffer);
148 film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_scatter_denoised,
149 fabs(scatter));
150 film_overwrite_pass_rgbe(buffer + kernel_data.film.pass_volume_transmit_denoised,
151 fabs(transmit));
152 }
153}
154
#define kernel_assert(cond)
#define kernel_data
#define PASS_UNUSED
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_global
#define CCL_NAMESPACE_END
#define __float_as_uint(x)
ccl_gpu_kernel_postfix ccl_global KernelWorkTile const int ccl_global float * render_buffer
ccl_device_inline float2 fabs(const float2 a)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
closure color scatter(string phase, float Anisotropy, float IOR, float Backscatter, float Alpha, float Diameter)
#define ccl_device
i
Definition text_draw.cc:230
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)
CCL_NAMESPACE_BEGIN 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)
ccl_device_inline float3 kernel_read_pass_float3(const ccl_global float *ccl_restrict buffer)
Definition write.h:143
ccl_device_inline void film_overwrite_pass_rgbe(ccl_global float *ccl_restrict buffer, const float3 value)
Definition write.h:113
CCL_NAMESPACE_BEGIN ccl_device_forceinline ccl_global float * film_pass_pixel_render_buffer(KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer)
Definition write.h:24
ccl_device_inline float3 kernel_read_pass_rgbe(const ccl_global float *ccl_restrict buffer)
Definition write.h:153