Blender V4.3
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_smaa_textures.h"
9
11
13 void init_samples(MutableSpan<float2> samples)
14 {
15 BLI_jitter_init(reinterpret_cast<float(*)[2]>(samples.data()), samples.size());
16
17 /* Find closest element to center */
18 int closest_index = 0;
19 float closest_squared_distance = 1.0f;
20 for (int i : samples.index_range()) {
21 const float2 sample = samples[i];
22 const float squared_dist = math::length_squared(sample);
23 if (squared_dist < closest_squared_distance) {
24 closest_squared_distance = squared_dist;
25 closest_index = i;
26 }
27 }
28
29 const float2 closest_sample = samples[closest_index];
30
31 for (float2 &sample : samples) {
32 /* Move jitter samples so that closest sample is in center */
33 sample -= closest_sample;
34 /* Avoid samples outside range (wrap around). */
35 sample = {fmodf(sample.x + 0.5f, 1.0f), fmodf(sample.y + 0.5f, 1.0f)};
36 /* Recenter the distribution[-1..1]. */
37 sample = (sample * 2.0f) - 1.0f;
38 }
39
40 /* Swap center sample to the start of the array */
41 if (closest_index != 0) {
42 std::swap(samples[0], samples[closest_index]);
43 }
44
45 /* Sort list based on farthest distance with previous. */
46 for (int i = 0; i < samples.size() - 2; i++) {
47 float squared_dist = 0.0;
48 int index = i;
49 for (int j = i + 1; j < samples.size(); j++) {
50 const float _squared_dist = math::length_squared(samples[i] - samples[j]);
51 if (_squared_dist > squared_dist) {
52 squared_dist = _squared_dist;
53 index = j;
54 }
55 }
56 std::swap(samples[i + 1], samples[index]);
57 }
58 }
59
60 public:
61 std::array<float2, 5> x5;
62 std::array<float2, 8> x8;
63 std::array<float2, 11> x11;
64 std::array<float2, 16> x16;
65 std::array<float2, 32> x32;
66
68 {
69 init_samples(x5);
70 init_samples(x8);
71 init_samples(x11);
72 init_samples(x16);
73 init_samples(x32);
74 }
75};
76
78{
79 static const TaaSamples taa_samples;
80 return taa_samples;
81}
82
83static float filter_blackman_harris(float x, const float width)
84{
85 if (x > width * 0.5f) {
86 return 0.0f;
87 }
88 x = 2.0f * M_PI * clamp_f((x / width + 0.5f), 0.0f, 1.0f);
89 return 0.35875f - 0.48829f * math::cos(x) + 0.14128f * math::cos(2.0f * x) -
90 0.01168f * math::cos(3.0f * x);
91}
92
93/* Compute weights for the 3x3 neighborhood using a 1.5px filter. */
94static void setup_taa_weights(const float2 offset, float r_weights[9], float &r_weight_sum)
95{
96 /* NOTE: If filter width is bigger than 2.0f, then we need to sample more neighborhood. */
97 const float filter_width = 2.0f;
98 r_weight_sum = 0.0f;
99 int i = 0;
100 for (int x = -1; x <= 1; x++) {
101 for (int y = -1; y <= 1; y++, i++) {
102 float2 sample_co = float2(x, y) - offset;
103 float r = len_v2(sample_co);
104 /* fclem: Is radial distance ok here? */
105 float weight = filter_blackman_harris(r, filter_width);
106 r_weight_sum += weight;
107 r_weights[i] = weight;
108 }
109 }
110}
111
123
124void AntiAliasingPass::init(const SceneState &scene_state)
125{
126 enabled_ = scene_state.draw_aa;
127}
128
129void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resources)
130{
131 overlay_depth_ps_.init();
134 overlay_depth_ps_.state_stencil(0x00, 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
135 overlay_depth_ps_.shader_set(ShaderCache::get().overlay_depth.get());
136 overlay_depth_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
137
138 if (!enabled_) {
139 taa_accumulation_tx_.free();
140 sample0_depth_tx_.free();
141 return;
142 }
143
144 smaa_viewport_metrics_ = float4(float2(1.0f / float2(scene_state.resolution)),
145 scene_state.resolution);
146 smaa_mix_factor_ = 1.0f - clamp_f(scene_state.sample / 4.0f, 0.0f, 1.0f);
147
148 taa_accumulation_tx_.ensure_2d(GPU_RGBA16F,
149 scene_state.resolution,
151 sample0_depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8,
152 scene_state.resolution,
154
155 taa_accumulation_ps_.init();
156 taa_accumulation_ps_.state_set(scene_state.sample == 0 ?
159 taa_accumulation_ps_.shader_set(ShaderCache::get().taa_accumulation.get());
160 taa_accumulation_ps_.bind_texture("colorBuffer", &resources.color_tx);
161 taa_accumulation_ps_.push_constant("samplesWeights", weights_, 9);
162 taa_accumulation_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
163
164 smaa_edge_detect_ps_.init();
165 smaa_edge_detect_ps_.state_set(DRW_STATE_WRITE_COLOR);
166 smaa_edge_detect_ps_.shader_set(ShaderCache::get().smaa_edge_detect.get());
167 smaa_edge_detect_ps_.bind_texture("colorTex", &taa_accumulation_tx_);
168 smaa_edge_detect_ps_.push_constant("viewportMetrics", &smaa_viewport_metrics_, 1);
169 smaa_edge_detect_ps_.clear_color(float4(0.0f));
170 smaa_edge_detect_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
171
172 smaa_aa_weight_ps_.init();
173 smaa_aa_weight_ps_.state_set(DRW_STATE_WRITE_COLOR);
174 smaa_aa_weight_ps_.shader_set(ShaderCache::get().smaa_aa_weight.get());
175 smaa_aa_weight_ps_.bind_texture("edgesTex", &smaa_edge_tx_);
176 smaa_aa_weight_ps_.bind_texture("areaTex", smaa_area_tx_);
177 smaa_aa_weight_ps_.bind_texture("searchTex", smaa_search_tx_);
178 smaa_aa_weight_ps_.push_constant("viewportMetrics", &smaa_viewport_metrics_, 1);
179 smaa_aa_weight_ps_.clear_color(float4(0.0f));
180 smaa_aa_weight_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
181
182 smaa_resolve_ps_.init();
183 smaa_resolve_ps_.state_set(DRW_STATE_WRITE_COLOR);
184 smaa_resolve_ps_.shader_set(ShaderCache::get().smaa_resolve.get());
185 smaa_resolve_ps_.bind_texture("blendTex", &smaa_weight_tx_);
186 smaa_resolve_ps_.bind_texture("colorTex", &taa_accumulation_tx_);
187 smaa_resolve_ps_.push_constant("viewportMetrics", &smaa_viewport_metrics_, 1);
188 smaa_resolve_ps_.push_constant("mixFactor", &smaa_mix_factor_, 1);
189 smaa_resolve_ps_.push_constant("taaAccumulatedWeight", &weight_accum_, 1);
190 smaa_resolve_ps_.clear_color(float4(0.0f));
191 smaa_resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
192}
193
194void AntiAliasingPass::setup_view(View &view, const SceneState &scene_state)
195{
196 if (!enabled_) {
197 return;
198 }
199
200 const TaaSamples &taa_samples = get_taa_samples();
201
202 float2 sample_offset;
203 switch (scene_state.samples_len) {
204 default:
205 case 5:
206 sample_offset = taa_samples.x5[scene_state.sample];
207 break;
208 case 8:
209 sample_offset = taa_samples.x8[scene_state.sample];
210 break;
211 case 11:
212 sample_offset = taa_samples.x11[scene_state.sample];
213 break;
214 case 16:
215 sample_offset = taa_samples.x16[scene_state.sample];
216 break;
217 case 32:
218 sample_offset = taa_samples.x32[scene_state.sample];
219 break;
220 }
221
222 setup_taa_weights(sample_offset, weights_, weights_sum_);
223
224 /* TODO(@pragma37): New API equivalent? */
225 const DRWView *default_view = DRW_view_default_get();
226 float4x4 winmat, viewmat, persmat;
227 /* Construct new matrices from transform delta */
228 DRW_view_winmat_get(default_view, winmat.ptr(), false);
229 DRW_view_viewmat_get(default_view, viewmat.ptr(), false);
230 DRW_view_persmat_get(default_view, persmat.ptr(), false);
231
232 window_translate_m4(winmat.ptr(),
233 persmat.ptr(),
234 sample_offset.x / scene_state.resolution.x,
235 sample_offset.y / scene_state.resolution.y);
236
237 view.sync(viewmat, winmat);
238}
239
241 View &view,
242 const SceneState &scene_state,
243 SceneResources &resources,
244 GPUTexture *depth_in_front_tx)
245{
246 if (resources.depth_in_front_tx.is_valid() && scene_state.sample == 0) {
247 overlay_depth_fb_.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
248 overlay_depth_fb_.bind();
249 manager.submit(overlay_depth_ps_);
250 }
251
252 if (!enabled_) {
253 return;
254 }
255
256 const bool last_sample = scene_state.sample + 1 == scene_state.samples_len;
257
258 if (scene_state.samples_len > 1) {
259 if (scene_state.sample == 0) {
260 GPU_texture_copy(sample0_depth_tx_, resources.depth_tx);
261 if (resources.depth_in_front_tx.is_valid()) {
262 sample0_depth_in_front_tx_.ensure_2d(
264 GPU_texture_copy(sample0_depth_in_front_tx_, resources.depth_in_front_tx);
265 }
266 else {
267 sample0_depth_in_front_tx_.free();
268 }
269 }
270 else if (!DRW_state_is_scene_render() || last_sample) {
271 /* Copy back the saved depth buffer for correct overlays. */
272 GPU_texture_copy(resources.depth_tx, sample0_depth_tx_);
273 if (sample0_depth_in_front_tx_.is_valid()) {
274 GPU_texture_copy(depth_in_front_tx, sample0_depth_in_front_tx_);
275 }
276 }
277 }
278
284 const bool taa_finished = scene_state.sample >= scene_state.samples_len;
285 if (!taa_finished) {
286 if (scene_state.sample == 0) {
287 weight_accum_ = 0;
288 }
289 /* Accumulate result to the TAA buffer. */
290 taa_accumulation_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(taa_accumulation_tx_));
291 taa_accumulation_fb_.bind();
292 manager.submit(taa_accumulation_ps_, view);
293 weight_accum_ += weights_sum_;
294 }
295
297 smaa_weight_tx_.acquire(scene_state.resolution,
298 GPU_RGBA8,
300 smaa_edge_tx_.acquire(scene_state.resolution,
301 GPU_RG8,
303
304 if (!DRW_state_is_image_render() || last_sample) {
305 /* After a certain point SMAA is no longer necessary. */
306 if (smaa_mix_factor_ > 0.0f) {
307 smaa_edge_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(smaa_edge_tx_));
308 smaa_edge_fb_.bind();
309 manager.submit(smaa_edge_detect_ps_, view);
310
311 smaa_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(smaa_weight_tx_));
312 smaa_weight_fb_.bind();
313 manager.submit(smaa_aa_weight_ps_, view);
314 }
315 smaa_resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx));
316 smaa_resolve_fb_.bind();
317 manager.submit(smaa_resolve_ps_, view);
318 }
319
320 smaa_edge_tx_.release();
321 smaa_weight_tx_.release();
322}
323
324} // namespace blender::workbench
void BLI_jitter_init(float(*jitarr)[2], int num)
Definition jitter_2d.c: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
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
@ GPU_PRIM_TRIS
void GPU_texture_copy(GPUTexture *dst, GPUTexture *src)
@ GPU_DATA_UBYTE
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
@ GPU_DEPTH24_STENCIL8
@ GPU_RG8
@ GPU_R8
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr T * data() const
Definition BLI_span.hh:540
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
void ensure(GPUAttachment depth=GPU_ATTACHMENT_NONE, GPUAttachment color1=GPU_ATTACHMENT_NONE, GPUAttachment color2=GPU_ATTACHMENT_NONE, GPUAttachment color3=GPU_ATTACHMENT_NONE, GPUAttachment color4=GPU_ATTACHMENT_NONE, GPUAttachment color5=GPU_ATTACHMENT_NONE, GPUAttachment color6=GPU_ATTACHMENT_NONE, GPUAttachment color7=GPU_ATTACHMENT_NONE, GPUAttachment color8=GPU_ATTACHMENT_NONE)
void submit(PassSimple &pass, View &view)
void acquire(int2 extent, eGPUTextureFormat format, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL)
bool ensure_2d(eGPUTextureFormat format, int2 extent, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
void clear_color(float4 color)
Definition draw_pass.hh:911
void draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first=-1, ResourceHandle handle={0}, uint custom_id=0)
Definition draw_pass.hh:833
void state_set(DRWState state, int clip_plane_count=0)
Definition draw_pass.hh:954
void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask)
Definition draw_pass.hh:966
void push_constant(const char *name, const float &data)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void setup_view(View &view, const SceneState &scene_state)
void draw(Manager &manager, View &view, const SceneState &scene_state, SceneResources &resources, GPUTexture *depth_in_front_tx)
void sync(const SceneState &scene_state, SceneResources &resources)
#define fmodf(x, y)
bool DRW_state_is_image_render()
bool DRW_state_is_scene_render()
const DRWView * DRW_view_default_get()
void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse)
void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse)
void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse)
@ 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
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
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()
VecBase< float, 4 > float4
VecBase< float, 2 > float2
unsigned char uint8_t
Definition stdint.h:78
const c_style_mat & ptr() const