Blender V5.0
workbench_effect_dof.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
19
20#include "workbench_private.hh"
21
22#include "BKE_camera.h"
23
24#include "GPU_debug.hh"
25
26namespace blender::workbench {
30static void square_to_circle(float x, float y, float &r, float &T)
31{
32 if (x > -y) {
33 if (x > y) {
34 r = x;
35 T = M_PI_4 * (y / x);
36 }
37 else {
38 r = y;
39 T = M_PI_4 * (2 - (x / y));
40 }
41 }
42 else {
43 if (x < y) {
44 r = -x;
45 T = M_PI_4 * (4 + (y / x));
46 }
47 else {
48 r = -y;
49 if (y != 0) {
50 T = M_PI_4 * (6 - (x / y));
51 }
52 else {
53 T = 0.0f;
54 }
55 }
56 }
57}
58
59void DofPass::setup_samples()
60{
61 float4 *sample = samples_buf_.begin();
62 for (int i = 0; i <= kernel_radius_; i++) {
63 for (int j = -kernel_radius_; j <= kernel_radius_; j++) {
64 for (int k = -kernel_radius_; k <= kernel_radius_; k++) {
65 if (abs(j) > i || abs(k) > i) {
66 continue;
67 }
68 if (abs(j) < i && abs(k) < i) {
69 continue;
70 }
71
72 float2 coord = float2(j, k) / float2(kernel_radius_);
73 float r = 0;
74 float T = 0;
75 square_to_circle(coord.x, coord.y, r, T);
76 sample->z = r;
77
78 /* Bokeh shape parameterization. */
79 if (blades_ > 1.0f) {
80 float denom = T - (2.0 * M_PI / blades_) * floorf((blades_ * T + M_PI) / (2.0 * M_PI));
81 r *= math::cos(M_PI / blades_) / math::cos(denom);
82 }
83
84 T += rotation_;
85
86 sample->x = r * math::cos(T) * ratio_;
87 sample->y = r * math::sin(T);
88 sample->w = 0;
89 sample++;
90 }
91 }
92 }
93 samples_buf_.push_update();
94}
95
96void DofPass::init(const SceneState &scene_state, const DRWContext *draw_ctx)
97{
98 enabled_ = scene_state.draw_dof;
99
100 if (!enabled_) {
101 source_tx_.free();
102 coc_halfres_tx_.free();
103 return;
104 }
105
106 offset_ = scene_state.sample / float(scene_state.samples_len);
107
108 int2 half_res = scene_state.resolution / 2;
109 half_res = {max_ii(half_res.x, 1), max_ii(half_res.y, 1)};
110
112 source_tx_.ensure_2d(gpu::TextureFormat::SFLOAT_16_16_16_16, half_res, usage, nullptr, 3);
113 source_tx_.ensure_mip_views();
114 source_tx_.filter_mode(true);
115 coc_halfres_tx_.ensure_2d(gpu::TextureFormat::UNORM_8_8, half_res, usage, nullptr, 3);
116 coc_halfres_tx_.ensure_mip_views();
117 coc_halfres_tx_.filter_mode(true);
118
119 const Camera *camera = scene_state.camera;
120
121 /* Parameters */
122 float fstop = camera->dof.aperture_fstop;
123 float sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y);
124 float focus_dist = BKE_camera_object_dof_distance(scene_state.camera_object);
125 float focal_len = camera->lens;
126
127 /* TODO(fclem): De-duplicate with EEVEE. */
128 const float scale_camera = 0.001f;
129 /* We want radius here for the aperture number. */
130 float aperture = 0.5f * scale_camera * focal_len / fstop;
131 float focal_len_scaled = scale_camera * focal_len;
132 float sensor_scaled = scale_camera * sensor;
133
134 if (RegionView3D *rv3d = draw_ctx->rv3d) {
135 sensor_scaled *= rv3d->viewcamtexcofac[0];
136 }
137
138 aperture_size_ = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
139 distance_ = -focus_dist;
140 invsensor_size_ = scene_state.resolution.x / sensor_scaled;
141
142 near_ = -camera->clip_start;
143 far_ = -camera->clip_end;
144
145 float blades = camera->dof.aperture_blades;
146 float rotation = camera->dof.aperture_rotation;
147 float ratio = 1.0f / camera->dof.aperture_ratio;
148
149 if (blades_ != blades || rotation_ != rotation || ratio_ != ratio) {
150 blades_ = blades;
151 rotation_ = rotation;
152 ratio_ = ratio;
153 setup_samples();
154 }
155}
156
157void DofPass::sync(SceneResources &resources, const DRWContext *draw_ctx)
158{
159 if (!enabled_) {
160 return;
161 }
162
164
165 const float2 viewport_size_inv = 1.0f / draw_ctx->viewport_size_get();
166
167 down_ps_.init();
168 down_ps_.state_set(DRW_STATE_WRITE_COLOR);
169 down_ps_.shader_set(ShaderCache::get().dof_prepare.get());
170 down_ps_.bind_texture("scene_color_tx", &resources.color_tx);
171 down_ps_.bind_texture("scene_depth_tx", &resources.depth_tx);
172 down_ps_.push_constant("inverted_viewport_size", viewport_size_inv);
173 down_ps_.push_constant("dof_params", float3(aperture_size_, distance_, invsensor_size_));
174 down_ps_.push_constant("near_far", float2(near_, far_));
175 down_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
176
177 down2_ps_.init();
178 down2_ps_.state_set(DRW_STATE_WRITE_COLOR);
179 down2_ps_.shader_set(ShaderCache::get().dof_downsample.get());
180 down2_ps_.bind_texture("scene_color_tx", source_tx_.mip_view(0), sampler_state);
181 down2_ps_.bind_texture("input_coc_tx", coc_halfres_tx_.mip_view(0), sampler_state);
182 down2_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
183
184 down3_ps_.init();
185 down3_ps_.state_set(DRW_STATE_WRITE_COLOR);
186 down3_ps_.shader_set(ShaderCache::get().dof_downsample.get());
187 down3_ps_.bind_texture("scene_color_tx", source_tx_.mip_view(1), sampler_state);
188 down3_ps_.bind_texture("input_coc_tx", coc_halfres_tx_.mip_view(1), sampler_state);
189 down3_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
190
191 blur_ps_.init();
192 blur_ps_.state_set(DRW_STATE_WRITE_COLOR);
193 blur_ps_.shader_set(ShaderCache::get().dof_blur1.get());
194 blur_ps_.bind_ubo("samples", samples_buf_);
195 blur_ps_.bind_texture("noise_tx", resources.jitter_tx);
196 blur_ps_.bind_texture("input_coc_tx", &coc_halfres_tx_, sampler_state);
197 blur_ps_.bind_texture("half_res_color_tx", &source_tx_, sampler_state);
198 blur_ps_.push_constant("inverted_viewport_size", viewport_size_inv);
199 blur_ps_.push_constant("noise_offset", offset_);
200 blur_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
201
202 blur2_ps_.init();
203 blur2_ps_.state_set(DRW_STATE_WRITE_COLOR);
204 blur2_ps_.shader_set(ShaderCache::get().dof_blur2.get());
205 blur2_ps_.bind_texture("input_coc_tx", &coc_halfres_tx_, sampler_state);
206 blur2_ps_.bind_texture("blur_tx", &blur_tx_);
207 blur2_ps_.push_constant("inverted_viewport_size", viewport_size_inv);
208 blur2_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
209
210 resolve_ps_.init();
211 resolve_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
212 resolve_ps_.shader_set(ShaderCache::get().dof_resolve.get());
213 resolve_ps_.bind_texture("half_res_color_tx", &source_tx_, sampler_state);
214 resolve_ps_.bind_texture("scene_depth_tx", &resources.depth_tx);
215 resolve_ps_.push_constant("inverted_viewport_size", viewport_size_inv);
216 resolve_ps_.push_constant("dof_params", float3(aperture_size_, distance_, invsensor_size_));
217 resolve_ps_.push_constant("near_far", float2(near_, far_));
218 resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
219}
220
221void DofPass::draw(Manager &manager, View &view, SceneResources &resources, int2 resolution)
222{
223 if (!enabled_) {
224 return;
225 }
226
227 GPU_debug_group_begin("Depth Of Field");
228
229 int2 half_res = {max_ii(resolution.x / 2, 1), max_ii(resolution.y / 2, 1)};
230 blur_tx_.acquire(half_res,
231 gpu::TextureFormat::SFLOAT_16_16_16_16,
233
234 downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
235 GPU_ATTACHMENT_TEXTURE_MIP(source_tx_, 0),
236 GPU_ATTACHMENT_TEXTURE_MIP(coc_halfres_tx_, 0));
237 downsample_fb_.bind();
238 manager.submit(down_ps_, view);
239
240 downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
241 GPU_ATTACHMENT_TEXTURE_MIP(source_tx_, 1),
242 GPU_ATTACHMENT_TEXTURE_MIP(coc_halfres_tx_, 1));
243 downsample_fb_.bind();
244 manager.submit(down2_ps_, view);
245
246 downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
247 GPU_ATTACHMENT_TEXTURE_MIP(source_tx_, 2),
248 GPU_ATTACHMENT_TEXTURE_MIP(coc_halfres_tx_, 2));
249 downsample_fb_.bind();
250 manager.submit(down3_ps_, view);
251
252 blur1_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blur_tx_));
253 blur1_fb_.bind();
254 manager.submit(blur_ps_, view);
255
256 blur2_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(source_tx_));
257 blur2_fb_.bind();
258 manager.submit(blur2_ps_, view);
259
260 resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx));
261 resolve_fb_.bind();
262 manager.submit(resolve_ps_, view);
263
264 blur_tx_.release();
265
267}
268
270{
271 return enabled_;
272}
273
274} // namespace blender::workbench
Camera data-block and utility functions.
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y)
float BKE_camera_object_dof_distance(const struct Object *ob)
MINLINE int max_ii(int a, int b)
#define M_PI
#define M_PI_4
static AppView * view
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
#define GPU_ATTACHMENT_TEXTURE_MIP(_texture, _mip)
@ GPU_PRIM_TRIS
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
void submit(PassSimple &pass, View &view)
void sync(SceneResources &resources, const DRWContext *draw_ctx)
void draw(Manager &manager, View &view, SceneResources &resources, int2 resolution)
void init(const SceneState &scene_state, const DRWContext *draw_ctx)
nullptr float
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_BLEND_CUSTOM
Definition draw_state.hh:63
#define abs
#define T
T cos(const AngleRadianBase< T > &a)
T sin(const AngleRadianBase< T > &a)
static void square_to_circle(float x, float y, float &r, float &T)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define floorf
#define fabsf
RegionView3D * rv3d
blender::float2 viewport_size_get() const
float x
float y
i
Definition text_draw.cc:230