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
19
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();
165 down_ps_.state_set(DRW_STATE_WRITE_COLOR);
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();
175 down2_ps_.state_set(DRW_STATE_WRITE_COLOR);
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();
182 blur_ps_.state_set(DRW_STATE_WRITE_COLOR);
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();
193 blur2_ps_.state_set(DRW_STATE_WRITE_COLOR);
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();
201 resolve_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
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
244 blur1_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blur_tx_));
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
252 resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_tx));
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between camera
void submit(PassSimple &pass, View &view)
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
#define T
detail::Pass< command::DrawCommandBuf > PassSimple
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, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
float x
float y