Blender V5.0
eevee_view.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
17
18#include "DRW_render.hh"
19
20#include "GPU_debug.hh"
21
22#include "eevee_instance.hh"
23
24#include "eevee_view.hh"
25
26namespace blender::eevee {
27
28/* -------------------------------------------------------------------- */
31
33
35{
36 int2 render_extent = inst_.film.render_extent_get();
37
38 if (false /* inst_.camera.is_panoramic() */) {
39 int64_t render_pixel_count = render_extent.x * int64_t(render_extent.y);
40 /* Divide pixel count between the 6 views. Rendering to a square target. */
41 extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6)));
42 /* TODO(@fclem): Clip unused views here. */
43 is_enabled_ = true;
44 }
45 else {
46 extent_ = render_extent;
47 /* Only enable -Z view. */
48 is_enabled_ = (StringRefNull(name_) == "negZ_view");
49 }
50
51 if (!is_enabled_) {
52 return;
53 }
54
55 /* Create views. */
56 const CameraData &cam = inst_.camera.data_get();
57
58 float4x4 viewmat, winmat;
59 if (false /* inst_.camera.is_panoramic() */) {
60 /* TODO(@fclem) Over-scans. */
61 /* For now a mandatory 5% over-scan for DoF. */
62 float side = cam.clip_near * 1.05f;
63 float near = cam.clip_near;
64 float far = cam.clip_far;
65 winmat = math::projection::perspective(-side, side, -side, side, near, far);
66 viewmat = face_matrix_ * cam.viewmat;
67 }
68 else {
69 viewmat = cam.viewmat;
70 winmat = cam.winmat;
71 }
72
73 main_view_.sync(viewmat, winmat);
74}
75
77{
78 if (!is_enabled_) {
79 return;
80 }
81
82 update_view();
83
85
86 /* Needs to be before planar_probes because it needs correct crypto-matte & render-pass buffers
87 * to reuse the same deferred shaders. */
88 RenderBuffers &rbufs = inst_.render_buffers;
89 rbufs.acquire(extent_);
90
91 /* Needs to be before anything else because it query its own gbuffer. */
92 inst_.planar_probes.set_view(render_view_, extent_);
93
94 combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
96 prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
98
99 GBuffer &gbuf = inst_.gbuffer;
100 gbuf.acquire(extent_,
101 inst_.pipelines.deferred.header_layer_count(),
102 inst_.pipelines.deferred.closure_layer_count(),
103 inst_.pipelines.deferred.normal_layer_count());
104
105 gbuffer_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
111
112 /* If camera has any motion, compute motion vector in the film pass. Otherwise, we avoid float
113 * precision issue by setting the motion of all static geometry to 0. */
114 float4 clear_velocity = float4(inst_.velocity.camera_has_motion() ? VELOCITY_INVALID : 0.0f);
115
116 GPU_framebuffer_bind(prepass_fb_);
117 GPU_framebuffer_clear_color(prepass_fb_, clear_velocity);
118 /* Alpha stores transmittance. So start at 1. */
119 float4 clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
120 GPU_framebuffer_bind(combined_fb_);
121 GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, inst_.film.depth.clear_value);
122 inst_.pipelines.background.clear(render_view_);
123
124 /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
125 inst_.lights.set_view(render_view_, extent_);
126
127 inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
128
129 inst_.volume.draw_prepass(main_view_);
130
131 /* TODO(Miguel Pozo): Deferred and forward prepass should happen before the GBuffer pass. */
132 inst_.pipelines.deferred.render(main_view_,
133 render_view_,
134 prepass_fb_,
135 combined_fb_,
136 gbuffer_fb_,
137 extent_,
138 rt_buffer_opaque_,
139 rt_buffer_refract_);
140
141 inst_.pipelines.background.render(render_view_, combined_fb_);
142
143 inst_.gbuffer.release();
144
145 inst_.volume.draw_compute(main_view_, extent_);
146
147 inst_.ambient_occlusion.render_pass(render_view_);
148
149 inst_.pipelines.forward.render(render_view_, prepass_fb_, combined_fb_, extent_);
150
151 render_transparent_pass(rbufs);
152
153 inst_.lights.debug_draw(render_view_, combined_fb_);
154 inst_.hiz_buffer.debug_draw(render_view_, combined_fb_);
155 inst_.shadows.debug_draw(render_view_, combined_fb_);
156 inst_.volume_probes.viewport_draw(render_view_, combined_fb_);
157 inst_.sphere_probes.viewport_draw(render_view_, combined_fb_);
158 inst_.planar_probes.viewport_draw(render_view_, combined_fb_);
159
160 gpu::Texture *combined_final_tx = render_postfx(rbufs.combined_tx);
161 inst_.film.accumulate(jitter_view_, combined_final_tx);
162
163 rbufs.release();
164 postfx_tx_.release();
165
167}
168
169void ShadingView::render_transparent_pass(RenderBuffers &rbufs)
170{
171 if (rbufs.data.transparent_id != -1) {
172 transparent_fb_.ensure(
175 /* Alpha stores transmittance. So start at 1. */
176 float4 clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
177 GPU_framebuffer_bind(transparent_fb_);
178 GPU_framebuffer_clear_color(transparent_fb_, clear_color);
179 inst_.pipelines.forward.render(render_view_, prepass_fb_, transparent_fb_, rbufs.extent_get());
180 }
181}
182
183gpu::Texture *ShadingView::render_postfx(gpu::Texture *input_tx)
184{
185 if (!inst_.depth_of_field.postfx_enabled() && !inst_.motion_blur.postfx_enabled()) {
186 return input_tx;
187 }
188 postfx_tx_.acquire(extent_, gpu::TextureFormat::SFLOAT_16_16_16_16);
189
190 /* Fix a sync bug on AMD + Mesa when volume + motion blur create artifacts
191 * except if there is a clear event between them. */
192 if (inst_.volume.enabled() && inst_.motion_blur.postfx_enabled() &&
193 !inst_.depth_of_field.postfx_enabled() &&
195 {
196 postfx_tx_.clear(float4(0.0f));
197 }
198
199 gpu::Texture *output_tx = postfx_tx_;
200
201 /* Swapping is done internally. Actual output is set to the next input. */
202 inst_.motion_blur.render(render_view_, &input_tx, &output_tx);
203 inst_.depth_of_field.render(render_view_, &input_tx, &output_tx, dof_buffer_);
204
205 return input_tx;
206}
207
208void ShadingView::update_view()
209{
210 const Film &film = inst_.film;
211
212 float4x4 viewmat = main_view_.viewmat();
213 float4x4 winmat = main_view_.winmat();
214
215 if (film.scaling_factor_get() > 1) {
216 /* This whole section ensures that the render target pixel grid will match the film pixel grid.
217 * Otherwise the weight computation inside the film accumulation will be wrong. */
218
219 float left, right, bottom, top, near, far;
220 projmat_dimensions(winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
221 const float2 bottom_left_with_overscan = float2(left, bottom);
222 const float2 top_right_with_overscan = float2(right, top);
223 const float2 render_size_with_overscan = top_right_with_overscan - bottom_left_with_overscan;
224
225 float2 bottom_left = bottom_left_with_overscan;
226 float2 top_right = top_right_with_overscan;
227 float2 render_size = render_size_with_overscan;
228
229 float overscan = inst_.camera.overscan();
230 if (overscan > 0.0f) {
231 /* Size of overscan on the screen. */
232 const float max_size_with_overscan = math::reduce_max(render_size);
233 const float max_size_original = max_size_with_overscan / (1.0f + 2.0f * overscan);
234 const float overscan_size = (max_size_with_overscan - max_size_original) / 2.0f;
235 /* Undo overscan to get the initial dimension of the screen. */
236 bottom_left = bottom_left_with_overscan + overscan_size;
237 top_right = top_right_with_overscan - overscan_size;
238 /* Render target size on the screen (without overscan). */
239 render_size = top_right - bottom_left;
240 }
241
242 /* Final pixel size on the screen. */
243 const float2 pixel_size = render_size / float2(film.film_extent_get());
244
245 /* Render extent in final film pixel unit. */
246 const int2 render_extent = film.render_extent_get() * film.scaling_factor_get();
247 const int overscan_pixels = film.render_overscan_get() * film.scaling_factor_get();
248
249 const float2 render_bottom_left = bottom_left - pixel_size * float(overscan_pixels);
250 const float2 render_top_right = render_bottom_left + pixel_size * float2(render_extent);
251
252 if (main_view_.is_persp()) {
253 winmat = math::projection::perspective(render_bottom_left.x,
254 render_top_right.x,
255 render_bottom_left.y,
256 render_top_right.y,
257 near,
258 far);
259 }
260 else {
261 winmat = math::projection::orthographic(render_bottom_left.x,
262 render_top_right.x,
263 render_bottom_left.y,
264 render_top_right.y,
265 near,
266 far);
267 }
268 }
269
270 /* Anti-Aliasing / Super-Sampling jitter. */
271 float2 jitter = inst_.film.pixel_jitter_get() / float2(extent_);
272 /* Transform to NDC space. */
273 jitter *= 2.0f;
274
275 window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter));
276 jitter_view_.sync(viewmat, winmat);
277
278 /* FIXME(fclem): The offset may be noticeably large and the culling might make object pop
279 * out of the blurring radius. To fix this, use custom enlarged culling matrix. */
280 inst_.depth_of_field.jitter_apply(winmat, viewmat);
281 render_view_.sync(viewmat, winmat);
282}
283
285
286/* -------------------------------------------------------------------- */
289
291{
292 const auto update_info = inst_.sphere_probes.world_update_info_pop();
293 if (!update_info.has_value()) {
294 return;
295 }
296
297 View view = {"Capture.View"};
298 GPU_debug_group_begin("World.Capture");
299
300 if (update_info->do_render) {
301 for (int face : IndexRange(6)) {
302 float4x4 view_m4 = cubeface_mat(face);
303 float4x4 win_m4 = math::projection::perspective(-update_info->clipping_distances.x,
304 update_info->clipping_distances.x,
305 -update_info->clipping_distances.x,
306 update_info->clipping_distances.x,
307 update_info->clipping_distances.x,
308 update_info->clipping_distances.y);
309 view.sync(view_m4, win_m4);
310
311 combined_fb_.ensure(GPU_ATTACHMENT_NONE,
312 GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.sphere_probes.cubemap_tx_, face));
313 GPU_framebuffer_bind(combined_fb_);
314 inst_.pipelines.world.render(view);
315 }
316
317 inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, true);
318 }
319
321}
322
324{
325 Framebuffer prepass_fb;
326 View view = {"Capture.View"};
327 while (const auto update_info = inst_.sphere_probes.probe_update_info_pop()) {
328 GPU_debug_group_begin("Probe.Capture");
329
330 if (!inst_.pipelines.data.is_sphere_probe) {
331 inst_.pipelines.data.is_sphere_probe = true;
332 inst_.uniform_data.push_update();
333 }
334
335 int2 extent = int2(update_info->cube_target_extent);
336 inst_.render_buffers.acquire(extent);
337
338 inst_.render_buffers.vector_tx.clear(float4(0.0f));
339 prepass_fb.ensure(GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx),
340 GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.vector_tx));
341
342 inst_.gbuffer.acquire(extent,
343 inst_.pipelines.probe.header_layer_count(),
344 inst_.pipelines.probe.closure_layer_count(),
345 inst_.pipelines.probe.normal_layer_count());
346
347 for (int face : IndexRange(6)) {
348 float4x4 view_m4 = cubeface_mat(face);
349 view_m4 = math::translate(view_m4, -update_info->probe_pos);
350 float4x4 win_m4 = math::projection::perspective(-update_info->clipping_distances.x,
351 update_info->clipping_distances.x,
352 -update_info->clipping_distances.x,
353 update_info->clipping_distances.x,
354 update_info->clipping_distances.x,
355 update_info->clipping_distances.y);
356 view.sync(view_m4, win_m4);
357
358 combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx),
359 GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.sphere_probes.cubemap_tx_, face));
360
361 gbuffer_fb_.ensure(GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx),
362 GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.sphere_probes.cubemap_tx_, face),
363 GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.header_tx.layer_view(0), 0),
364 GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.normal_tx.layer_view(0), 0),
365 GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.closure_tx.layer_view(0), 0),
366 GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.closure_tx.layer_view(1), 0));
367
368 GPU_framebuffer_bind(combined_fb_);
370 combined_fb_, float4(0.0f, 0.0f, 0.0f, 1.0f), inst_.film.depth.clear_value);
371 inst_.pipelines.probe.render(view, prepass_fb, combined_fb_, gbuffer_fb_, extent);
372 }
373
374 inst_.render_buffers.release();
375 inst_.gbuffer.release();
377 inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, false);
378 }
379
380 if (inst_.pipelines.data.is_sphere_probe) {
381 inst_.pipelines.data.is_sphere_probe = false;
382 inst_.uniform_data.push_update();
383 }
384}
385
387
388/* -------------------------------------------------------------------- */
391
393{
394 if (!inst_.lookdev.enabled_) {
395 return;
396 }
397 GPU_debug_group_begin("Lookdev");
398
399 const float radius = inst_.lookdev.sphere_radius_;
400 const float clip = inst_.camera.data_get().clip_near;
402 -radius, radius, -radius, radius, clip);
403 const float4x4 &view_m4 = inst_.camera.data_get().viewmat;
404 view_.sync(view_m4, win_m4);
405
406 inst_.lookdev.draw(view_);
407 inst_.lookdev.display();
408
410}
411
413
414} // namespace blender::eevee
void window_translate_m4(float winmat[4][4], float perspmat[4][4], float x, float y)
void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, float *r_top, float *r_near, float *r_far)
#define UNPACK2(a)
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_TEXTURE_CUBEFACE(_texture, _face)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_clear_color(blender::gpu::FrameBuffer *fb, const float clear_col[4])
#define GPU_ATTACHMENT_TEXTURE_LAYER(_texture, _layer)
void GPU_framebuffer_clear_color_depth(blender::gpu::FrameBuffer *fb, const float clear_col[4], float clear_depth)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
@ GPU_DEVICE_ATI
@ GPU_DRIVER_OFFICIAL
@ GPU_OS_UNIX
bool GPU_type_matches_ex(GPUDeviceType device, GPUOSType os, GPUDriverType driver, GPUBackendType backend)
long long int int64_t
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)
gpu::Texture * layer_view(int layer)
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent)
nullptr float
#define VELOCITY_INVALID
uint top
static int left
float4x4 cubeface_mat(int face)
MatBase< T, 4, 4 > orthographic(T left, T right, T bottom, T top, T near_clip, T far_clip)
Create an orthographic projection matrix using OpenGL coordinate convention: Maps each axis range to ...
MatBase< T, 4, 4 > orthographic_infinite(T left, T right, T bottom, T top)
Create an orthographic projection matrix using OpenGL coordinate convention: Maps each axis range to ...
MatBase< T, 4, 4 > perspective(T left, T right, T bottom, T top, T near_clip, T far_clip)
Create a perspective projection matrix using OpenGL coordinate convention: Maps each axis range to [-...
T reduce_max(const VecBase< T, Size > &a)
MatBase< T, NumCol, NumRow > translate(const MatBase< T, NumCol, NumRow > &mat, const VectorT &translation)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define sqrtf
#define ceilf
const c_style_mat & ptr() const
void acquire(int2 extent, int header_count, int data_count, int normal_count)
float x
float y