Blender V4.3
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
20#include "workbench_private.hh"
21
22#include "BKE_camera.h"
24
25namespace blender::workbench {
29static void square_to_circle(float x, float y, float &r, float &T)
30{
31 if (x > -y) {
32 if (x > y) {
33 r = x;
34 T = M_PI_4 * (y / x);
35 }
36 else {
37 r = y;
38 T = M_PI_4 * (2 - (x / y));
39 }
40 }
41 else {
42 if (x < y) {
43 r = -x;
44 T = M_PI_4 * (4 + (y / x));
45 }
46 else {
47 r = -y;
48 if (y != 0) {
49 T = M_PI_4 * (6 - (x / y));
50 }
51 else {
52 T = 0.0f;
53 }
54 }
55 }
56}
57
58void DofPass::setup_samples()
59{
60 float4 *sample = samples_buf_.begin();
61 for (int i = 0; i <= kernel_radius_; i++) {
62 for (int j = -kernel_radius_; j <= kernel_radius_; j++) {
63 for (int k = -kernel_radius_; k <= kernel_radius_; k++) {
64 if (abs(j) > i || abs(k) > i) {
65 continue;
66 }
67 if (abs(j) < i && abs(k) < i) {
68 continue;
69 }
70
71 float2 coord = float2(j, k) / float2(kernel_radius_);
72 float r = 0;
73 float T = 0;
74 square_to_circle(coord.x, coord.y, r, T);
75 sample->z = r;
76
77 /* Bokeh shape parameterization. */
78 if (blades_ > 1.0f) {
79 float denom = T - (2.0 * M_PI / blades_) * floorf((blades_ * T + M_PI) / (2.0 * M_PI));
80 r *= math::cos(M_PI / blades_) / math::cos(denom);
81 }
82
83 T += rotation_;
84
85 sample->x = r * math::cos(T) * ratio_;
86 sample->y = r * math::sin(T);
87 sample->w = 0;
88 sample++;
89 }
90 }
91 }
92 samples_buf_.push_update();
93}
94
95void DofPass::init(const SceneState &scene_state)
96{
97 enabled_ = scene_state.draw_dof;
98
99 if (!enabled_) {
100 source_tx_.free();
101 coc_halfres_tx_.free();
102 return;
103 }
104
105 offset_ = scene_state.sample / float(scene_state.samples_len);
106
107 int2 half_res = scene_state.resolution / 2;
108 half_res = {max_ii(half_res.x, 1), max_ii(half_res.y, 1)};
109
111 source_tx_.ensure_2d(GPU_RGBA16F, half_res, usage, nullptr, 3);
112 source_tx_.ensure_mip_views();
113 source_tx_.filter_mode(true);
114 coc_halfres_tx_.ensure_2d(GPU_RG8, half_res, usage, nullptr, 3);
115 coc_halfres_tx_.ensure_mip_views();
116 coc_halfres_tx_.filter_mode(true);
117
118 const Camera *camera = scene_state.camera;
119
120 /* Parameters */
121 float fstop = camera->dof.aperture_fstop;
122 float sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y);
123 float focus_dist = BKE_camera_object_dof_distance(scene_state.camera_object);
124 float focal_len = camera->lens;
125
126 /* TODO(fclem): De-duplicate with EEVEE. */
127 const float scale_camera = 0.001f;
128 /* We want radius here for the aperture number. */
129 float aperture = 0.5f * scale_camera * focal_len / fstop;
130 float focal_len_scaled = scale_camera * focal_len;
131 float sensor_scaled = scale_camera * sensor;
132
133 if (RegionView3D *rv3d = DRW_context_state_get()->rv3d) {
134 sensor_scaled *= rv3d->viewcamtexcofac[0];
135 }
136
137 aperture_size_ = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
138 distance_ = -focus_dist;
139 invsensor_size_ = scene_state.resolution.x / sensor_scaled;
140
141 near_ = -camera->clip_start;
142 far_ = -camera->clip_end;
143
144 float blades = camera->dof.aperture_blades;
145 float rotation = camera->dof.aperture_rotation;
146 float ratio = 1.0f / camera->dof.aperture_ratio;
147
148 if (blades_ != blades || rotation_ != rotation || ratio_ != ratio) {
149 blades_ = blades;
150 rotation_ = rotation;
151 ratio_ = ratio;
152 setup_samples();
153 }
154}
155
157{
158 if (!enabled_) {
159 return;
160 }
161
163
164 down_ps_.init();
166 down_ps_.shader_set(ShaderCache::get().dof_prepare.get());
167 down_ps_.bind_texture("sceneColorTex", &resources.color_tx);
168 down_ps_.bind_texture("sceneDepthTex", &resources.depth_tx);
169 down_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
170 down_ps_.push_constant("dofParams", float3(aperture_size_, distance_, invsensor_size_));
171 down_ps_.push_constant("nearFar", float2(near_, far_));
172 down_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
173
174 down2_ps_.init();
176 down2_ps_.shader_set(ShaderCache::get().dof_downsample.get());
177 down2_ps_.bind_texture("sceneColorTex", &source_tx_, sampler_state);
178 down2_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
179 down2_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
180
181 blur_ps_.init();
183 blur_ps_.shader_set(ShaderCache::get().dof_blur1.get());
184 blur_ps_.bind_ubo("samples", samples_buf_);
185 blur_ps_.bind_texture("noiseTex", resources.jitter_tx);
186 blur_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
187 blur_ps_.bind_texture("halfResColorTex", &source_tx_, sampler_state);
188 blur_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
189 blur_ps_.push_constant("noiseOffset", offset_);
190 blur_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
191
192 blur2_ps_.init();
194 blur2_ps_.shader_set(ShaderCache::get().dof_blur2.get());
195 blur2_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
196 blur2_ps_.bind_texture("blurTex", &blur_tx_);
197 blur2_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
198 blur2_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
199
200 resolve_ps_.init();
202 resolve_ps_.shader_set(ShaderCache::get().dof_resolve.get());
203 resolve_ps_.bind_texture("halfResColorTex", &source_tx_, sampler_state);
204 resolve_ps_.bind_texture("sceneDepthTex", &resources.depth_tx);
205 resolve_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
206 resolve_ps_.push_constant("dofParams", float3(aperture_size_, distance_, invsensor_size_));
207 resolve_ps_.push_constant("nearFar", float2(near_, far_));
208 resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
209}
210
211void DofPass::draw(Manager &manager, View &view, SceneResources &resources, int2 resolution)
212{
213 if (!enabled_) {
214 return;
215 }
216
217 DRW_stats_group_start("Depth Of Field");
218
219 int2 half_res = {max_ii(resolution.x / 2, 1), max_ii(resolution.y / 2, 1)};
220 blur_tx_.acquire(
222
223 downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
224 GPU_ATTACHMENT_TEXTURE(source_tx_),
225 GPU_ATTACHMENT_TEXTURE(coc_halfres_tx_));
226 downsample_fb_.bind();
227 manager.submit(down_ps_, view);
228
229 struct CallbackData {
230 Manager &manager;
231 View &view;
232 PassSimple &pass;
233 };
234 CallbackData callback_data = {manager, view, down2_ps_};
235
236 auto downsample_level = [](void *callback_data, int /*level*/) {
237 CallbackData *cd = static_cast<CallbackData *>(callback_data);
238 cd->manager.submit(cd->pass, cd->view);
239 };
240
242 downsample_fb_, 2, downsample_level, static_cast<void *>(&callback_data));
243
245 blur1_fb_.bind();
246 manager.submit(blur_ps_, view);
247
248 blur2_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(source_tx_));
249 blur2_fb_.bind();
250 manager.submit(blur2_ps_, view);
251
253 resolve_fb_.bind();
254 manager.submit(resolve_ps_, view);
255
256 blur_tx_.release();
257
259}
260
262{
263 return enabled_;
264}
265
266} // 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
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *framebuffer, int max_level, void(*per_level_callback)(void *user_data, int level), void *user_data)
@ GPU_PRIM_TRIS
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_RG8
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
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_mip_views(bool cube_as_array=false)
void filter_mode(bool do_filter)
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 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 bind_ubo(const char *name, GPUUniformBuf *buffer)
void push_constant(const char *name, const float &data)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void sync(SceneResources &resources)
void draw(Manager &manager, View &view, SceneResources &resources, int2 resolution)
void init(const SceneState &scene_state)
#define floorf(x)
#define fabsf(x)
const float * DRW_viewport_invert_size_get()
const DRWContextState * DRW_context_state_get()
void DRW_stats_group_start(const char *name)
void DRW_stats_group_end()
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_BLEND_CUSTOM
Definition draw_state.hh:63
draw_view in_light_buf[] float
T cos(const AngleRadianBase< T > &a)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
static void square_to_circle(float x, float y, float &r, float &T)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
float x
float y