Blender V5.0
workbench_effect_antialiasing.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_jitter_2d.h"
8#include "BLI_math_geom.h"
9#include "BLI_smaa_textures.h"
10
12
14 void init_samples(MutableSpan<float2> samples)
15 {
16 BLI_jitter_init(reinterpret_cast<float (*)[2]>(samples.data()), samples.size());
17
18 /* Find closest element to center */
19 int closest_index = 0;
20 float closest_squared_distance = 1.0f;
21 for (int i : samples.index_range()) {
22 const float2 sample = samples[i];
23 const float squared_dist = math::length_squared(sample);
24 if (squared_dist < closest_squared_distance) {
25 closest_squared_distance = squared_dist;
26 closest_index = i;
27 }
28 }
29
30 const float2 closest_sample = samples[closest_index];
31
32 for (float2 &sample : samples) {
33 /* Move jitter samples so that closest sample is in center */
34 sample -= closest_sample;
35 /* Avoid samples outside range (wrap around). */
36 sample = {fmodf(sample.x + 0.5f, 1.0f), fmodf(sample.y + 0.5f, 1.0f)};
37 /* Recenter the distribution[-1..1]. */
38 sample = (sample * 2.0f) - 1.0f;
39 }
40
41 /* Swap center sample to the start of the array */
42 if (closest_index != 0) {
43 std::swap(samples[0], samples[closest_index]);
44 }
45
46 /* Sort list based on farthest distance with previous. */
47 for (int i = 0; i < samples.size() - 2; i++) {
48 float squared_dist = 0.0;
49 int index = i;
50 for (int j = i + 1; j < samples.size(); j++) {
51 const float _squared_dist = math::length_squared(samples[i] - samples[j]);
52 if (_squared_dist > squared_dist) {
53 squared_dist = _squared_dist;
54 index = j;
55 }
56 }
57 std::swap(samples[i + 1], samples[index]);
58 }
59 }
60
61 public:
62 std::array<float2, 5> x5;
63 std::array<float2, 8> x8;
64 std::array<float2, 11> x11;
65 std::array<float2, 16> x16;
66 std::array<float2, 32> x32;
67
69 {
70 init_samples(x5);
71 init_samples(x8);
72 init_samples(x11);
73 init_samples(x16);
74 init_samples(x32);
75 }
76};
77
79{
80 static const TaaSamples taa_samples;
81 return taa_samples;
82}
83
84static float filter_blackman_harris(float x, const float width)
85{
86 if (x > width * 0.5f) {
87 return 0.0f;
88 }
89 x = 2.0f * M_PI * clamp_f((x / width + 0.5f), 0.0f, 1.0f);
90 return 0.35875f - 0.48829f * math::cos(x) + 0.14128f * math::cos(2.0f * x) -
91 0.01168f * math::cos(3.0f * x);
92}
93
94/* Compute weights for the 3x3 neighborhood using a 1.5px filter. */
95static void setup_taa_weights(const float2 offset, float r_weights[9], float &r_weight_sum)
96{
97 /* NOTE: If filter width is bigger than 2.0f, then we need to sample more neighborhood. */
98 const float filter_width = 2.0f;
99 r_weight_sum = 0.0f;
100 int i = 0;
101 for (int x = -1; x <= 1; x++) {
102 for (int y = -1; y <= 1; y++, i++) {
103 float2 sample_co = float2(x, y) - offset;
104 float r = len_v2(sample_co);
105 /* fclem: Is radial distance ok here? */
106 float weight = filter_blackman_harris(r, filter_width);
107 r_weight_sum += weight;
108 r_weights[i] = weight;
109 }
110 }
111}
112
114{
115 smaa_search_tx_.ensure_2d(gpu::TextureFormat::UNORM_8,
119 GPU_texture_filter_mode(smaa_search_tx_, true);
120
121 smaa_area_tx_.ensure_2d(gpu::TextureFormat::UNORM_8_8,
125 GPU_texture_filter_mode(smaa_area_tx_, true);
126}
127
128void AntiAliasingPass::init(const SceneState &scene_state)
129{
130 enabled_ = scene_state.draw_aa;
131}
132
133void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resources)
134{
135 overlay_depth_ps_.init();
136 overlay_depth_ps_.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
138 overlay_depth_ps_.state_stencil(0x00, 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
139 overlay_depth_ps_.shader_set(ShaderCache::get().overlay_depth.get());
140 overlay_depth_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
141
142 if (!enabled_) {
143 taa_accumulation_tx_.free();
144 sample0_depth_tx_.free();
145 return;
146 }
147
148 smaa_viewport_metrics_ = float4(float2(1.0f / float2(scene_state.resolution)),
149 scene_state.resolution);
150 smaa_mix_factor_ = 1.0f - clamp_f(scene_state.sample / 4.0f, 0.0f, 1.0f);
151
152 taa_accumulation_tx_.ensure_2d(gpu::TextureFormat::SFLOAT_16_16_16_16,
153 scene_state.resolution,
155 sample0_depth_tx_.ensure_2d(gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8,
156 scene_state.resolution,
158
159 taa_accumulation_ps_.init();
160 taa_accumulation_ps_.state_set(scene_state.sample == 0 ?
163 taa_accumulation_ps_.shader_set(ShaderCache::get().taa_accumulation.get());
164 taa_accumulation_ps_.bind_texture("color_buffer", &resources.color_tx);
165 taa_accumulation_ps_.push_constant("samplesWeights", weights_, 9);
166 taa_accumulation_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
167
168 smaa_edge_detect_ps_.init();
169 smaa_edge_detect_ps_.state_set(DRW_STATE_WRITE_COLOR);
170 smaa_edge_detect_ps_.shader_set(ShaderCache::get().smaa_edge_detect.get());
171 smaa_edge_detect_ps_.bind_texture("color_tx", &taa_accumulation_tx_);
172 smaa_edge_detect_ps_.push_constant("viewport_metrics", &smaa_viewport_metrics_, 1);
173 smaa_edge_detect_ps_.clear_color(float4(0.0f));
174 smaa_edge_detect_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
175
176 smaa_aa_weight_ps_.init();
177 smaa_aa_weight_ps_.state_set(DRW_STATE_WRITE_COLOR);
178 smaa_aa_weight_ps_.shader_set(ShaderCache::get().smaa_aa_weight.get());
179 smaa_aa_weight_ps_.bind_texture("edges_tx", &smaa_edge_tx_);
180 smaa_aa_weight_ps_.bind_texture("area_tx", smaa_area_tx_);
181 smaa_aa_weight_ps_.bind_texture("search_tx", smaa_search_tx_);
182 smaa_aa_weight_ps_.push_constant("viewport_metrics", &smaa_viewport_metrics_, 1);
183 smaa_aa_weight_ps_.clear_color(float4(0.0f));
184 smaa_aa_weight_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
185
186 smaa_resolve_ps_.init();
187 smaa_resolve_ps_.state_set(DRW_STATE_WRITE_COLOR);
188 smaa_resolve_ps_.shader_set(ShaderCache::get().smaa_resolve.get());
189 smaa_resolve_ps_.bind_texture("blend_tx", &smaa_weight_tx_);
190 smaa_resolve_ps_.bind_texture("color_tx", &taa_accumulation_tx_);
191 smaa_resolve_ps_.push_constant("viewport_metrics", &smaa_viewport_metrics_, 1);
192 smaa_resolve_ps_.push_constant("mix_factor", &smaa_mix_factor_, 1);
193 smaa_resolve_ps_.push_constant("taa_accumulated_weight", &weight_accum_, 1);
194 smaa_resolve_ps_.clear_color(float4(0.0f));
195 smaa_resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
196}
197
199{
200 const View &default_view = View::default_get();
201 const float4x4 &viewmat = default_view.viewmat();
202 float4x4 winmat = default_view.winmat();
203 float4x4 persmat = default_view.persmat();
204
205 if (!enabled_) {
206 view.sync(viewmat, winmat);
207 return;
208 }
209
210 const TaaSamples &taa_samples = get_taa_samples();
211
212 float2 sample_offset;
213 switch (scene_state.samples_len) {
214 default:
215 case 5:
216 sample_offset = taa_samples.x5[scene_state.sample];
217 break;
218 case 8:
219 sample_offset = taa_samples.x8[scene_state.sample];
220 break;
221 case 11:
222 sample_offset = taa_samples.x11[scene_state.sample];
223 break;
224 case 16:
225 sample_offset = taa_samples.x16[scene_state.sample];
226 break;
227 case 32:
228 sample_offset = taa_samples.x32[scene_state.sample];
229 break;
230 }
231
232 setup_taa_weights(sample_offset, weights_, weights_sum_);
233
234 window_translate_m4(winmat.ptr(),
235 persmat.ptr(),
236 sample_offset.x / scene_state.resolution.x,
237 sample_offset.y / scene_state.resolution.y);
238
239 view.sync(viewmat, winmat);
240}
241
243 Manager &manager,
244 View &view,
245 const SceneState &scene_state,
246 SceneResources &resources,
247 gpu::Texture *depth_in_front_tx)
248{
249 if (resources.depth_in_front_tx.is_valid() && scene_state.sample == 0) {
250 overlay_depth_fb_.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
251 overlay_depth_fb_.bind();
252 manager.submit(overlay_depth_ps_);
253 }
254
255 if (!enabled_) {
256 return;
257 }
258
259 const bool last_sample = scene_state.sample + 1 == scene_state.samples_len;
260
261 if (scene_state.samples_len > 1) {
262 if (scene_state.sample == 0) {
263 GPU_texture_copy(sample0_depth_tx_, resources.depth_tx);
264 if (resources.depth_in_front_tx.is_valid()) {
265 sample0_depth_in_front_tx_.ensure_2d(gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8,
266 scene_state.resolution,
268 GPU_texture_copy(sample0_depth_in_front_tx_, resources.depth_in_front_tx);
269 }
270 else {
271 sample0_depth_in_front_tx_.free();
272 }
273 }
274 else if (!draw_ctx->is_scene_render() || last_sample) {
275 /* Copy back the saved depth buffer for correct overlays. */
276 GPU_texture_copy(resources.depth_tx, sample0_depth_tx_);
277 if (sample0_depth_in_front_tx_.is_valid()) {
278 GPU_texture_copy(depth_in_front_tx, sample0_depth_in_front_tx_);
279 }
280 }
281 }
282
288 const bool taa_finished = scene_state.sample >= scene_state.samples_len;
289 if (!taa_finished) {
290 if (scene_state.sample == 0) {
291 weight_accum_ = 0;
292 }
293 /* Accumulate result to the TAA buffer. */
294 taa_accumulation_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(taa_accumulation_tx_));
295 taa_accumulation_fb_.bind();
296 manager.submit(taa_accumulation_ps_, view);
297 weight_accum_ += weights_sum_;
298 }
299
301 smaa_weight_tx_.acquire(scene_state.resolution,
302 gpu::TextureFormat::UNORM_8_8_8_8,
304 smaa_edge_tx_.acquire(scene_state.resolution,
305 gpu::TextureFormat::UNORM_8_8,
307
308 if (!draw_ctx->is_image_render() || last_sample || taa_finished) {
309 /* After a certain point SMAA is no longer necessary. */
310 if (smaa_mix_factor_ > 0.0f) {
311 smaa_edge_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(smaa_edge_tx_));
312 smaa_edge_fb_.bind();
313 manager.submit(smaa_edge_detect_ps_, view);
314
315 smaa_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(smaa_weight_tx_));
316 smaa_weight_fb_.bind();
317 manager.submit(smaa_aa_weight_ps_, view);
318 }
319 smaa_resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx));
320 smaa_resolve_fb_.bind();
321 manager.submit(smaa_resolve_ps_, view);
322 }
323
324 smaa_edge_tx_.release();
325 smaa_weight_tx_.release();
326}
327
328} // namespace blender::workbench
void BLI_jitter_init(float(*jitarr)[2], int num)
Definition jitter_2d.cc:127
MINLINE float clamp_f(float value, float min, float max)
#define M_PI
void window_translate_m4(float winmat[4][4], float perspmat[4][4], float x, float y)
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
#define SEARCHTEX_HEIGHT
const unsigned char searchTexBytes[]
#define SEARCHTEX_WIDTH
#define AREATEX_HEIGHT
const unsigned char areaTexBytes[]
#define AREATEX_WIDTH
static AppView * view
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
@ GPU_PRIM_TRIS
void GPU_texture_copy(blender::gpu::Texture *dst, blender::gpu::Texture *src)
@ GPU_DATA_UBYTE
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
void GPU_texture_filter_mode(blender::gpu::Texture *texture, bool use_filter)
void GPU_texture_update(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr T * data() const
Definition BLI_span.hh:539
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
void submit(PassSimple &pass, View &view)
const float4x4 & winmat(int view_id=0) const
Definition draw_view.hh:148
const float4x4 persmat(int view_id=0) const
Definition draw_view.hh:161
const float4x4 & viewmat(int view_id=0) const
Definition draw_view.hh:136
static View & default_get()
Definition draw_view.cc:317
void setup_view(View &view, const SceneState &scene_state)
void draw(const DRWContext *draw_ctx, Manager &manager, View &view, const SceneState &scene_state, SceneResources &resources, gpu::Texture *depth_in_front_tx)
void sync(const SceneState &scene_state, SceneResources &resources)
#define fmodf(x, y)
@ DRW_STATE_STENCIL_EQUAL
Definition draw_state.hh:47
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_BLEND_ADD_FULL
Definition draw_state.hh:53
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_ALWAYS
Definition draw_state.hh:36
T length_squared(const VecBase< T, Size > &a)
T cos(const AngleRadianBase< T > &a)
static float filter_blackman_harris(float x, const float width)
static void setup_taa_weights(const float2 offset, float r_weights[9], float &r_weight_sum)
static const TaaSamples & get_taa_samples()
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
bool is_image_render() const
bool is_scene_render() const
const c_style_mat & ptr() const
i
Definition text_draw.cc:230