Blender V5.0
gpencil_render.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_geom.h"
10#include "BLI_math_matrix.h"
11#include "BLI_rect.h"
12
13#include "BKE_colortools.hh"
14
15#include "DRW_render.hh"
16
17#include "BKE_object.hh"
18
20
21#include "RE_engine.h"
22#include "RE_pipeline.h"
23#include "render_types.h"
24
25#include "IMB_imbuf_types.hh"
26
28
29namespace blender::draw::gpencil {
30
31/* Remap depth from views-pace to [0..1] to be able to use it with as GPU depth buffer. */
32static void remap_depth(const View &view, MutableSpan<float> pix_z)
33{
34 if (view.is_persp()) {
35 const float4x4 &winmat = view.winmat();
36 for (auto &pix : pix_z) {
37 pix = (-winmat[3][2] / -pix) - winmat[2][2];
38 pix = clamp_f(pix * 0.5f + 0.5f, 0.0f, 1.0f);
39 }
40 }
41 else {
42 /* Keep in mind, near and far distance are negatives. */
43 const float near = view.near_clip();
44 const float far = view.far_clip();
45 const float range_inv = 1.0f / fabsf(far - near);
46 for (auto &pix : pix_z) {
47 pix = (pix + near) * range_inv;
48 pix = clamp_f(pix, 0.0f, 1.0f);
49 }
50 }
51}
52
53static void render_set_view(RenderEngine *engine,
54 const Depsgraph *depsgraph,
55 const float2 aa_offset = float2{0.0f})
56{
58
59 float4x4 winmat, viewinv;
60 RE_GetCameraWindow(engine->re, camera, winmat.ptr());
61 RE_GetCameraModelMatrix(engine->re, camera, viewinv.ptr());
62
63 window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(aa_offset));
64
65 View::default_set(math::invert(viewinv), winmat);
66}
67
68static void render_init_buffers(const DRWContext *draw_ctx,
69 Instance &inst,
70 RenderEngine *engine,
71 RenderLayer *render_layer,
72 const rcti *rect,
73 const bool use_separated_pass)
74{
75 const int2 size = int2(draw_ctx->viewport_size_get());
77
78 /* Create depth texture & color texture from render result. */
79 const char *viewname = RE_GetActiveRenderView(engine->re);
80 RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_DEPTH, viewname);
81 RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
82
83 float *pix_z = (rpass_z_src) ? rpass_z_src->ibuf->float_buffer.data : nullptr;
84 float *pix_col = (rpass_col_src) ? rpass_col_src->ibuf->float_buffer.data : nullptr;
85
86 if (!pix_z || !pix_col) {
88 engine, "Warning: To render Grease Pencil, enable Combined and Depth passes.");
89 }
90
91 if (pix_z) {
92 /* Depth need to be remapped to [0..1] range. */
93 pix_z = static_cast<float *>(MEM_dupallocN(pix_z));
94 remap_depth(view, {pix_z, rpass_z_src->rectx * rpass_z_src->recty});
95 }
96
97 const bool do_region = (!use_separated_pass) && !(rect->xmin == 0 && rect->ymin == 0 &&
98 rect->xmax == size.x && rect->ymax == size.y);
99 const bool do_clear_z = !pix_z || do_region;
100 const bool do_clear_col = use_separated_pass || (!pix_col) || do_region;
101
102 /* FIXME(fclem): we have a precision loss in the depth buffer because of this re-upload.
103 * Find where it comes from! */
104 /* In multi view render the textures can be reused. */
105 if (inst.render_depth_tx.is_valid() && !do_clear_z) {
107 }
108 else {
112 gpu::TextureFormat::SFLOAT_32_DEPTH, int2(size), usage, do_region ? nullptr : pix_z);
113 }
114 if (inst.render_color_tx.is_valid() && !do_clear_col) {
116 }
117 else {
121 gpu::TextureFormat::SFLOAT_16_16_16_16, int2(size), usage, do_region ? nullptr : pix_col);
122 }
123
126
127 if (do_clear_z || do_clear_col) {
128 /* To avoid unpredictable result, clear buffers that have not be initialized. */
130 if (do_clear_col) {
131 const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
132 GPU_framebuffer_clear_color(inst.render_fb, clear_col);
133 }
134 if (do_clear_z) {
136 }
137 }
138
139 if (do_region) {
140 int x = rect->xmin;
141 int y = rect->ymin;
142 int w = BLI_rcti_size_x(rect);
143 int h = BLI_rcti_size_y(rect);
144 if (pix_col) {
145 GPU_texture_update_sub(inst.render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0);
146 }
147 if (pix_z) {
148 GPU_texture_update_sub(inst.render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0);
149 }
150 }
151
152 MEM_SAFE_FREE(pix_z);
153}
154
155static void render_result_z(const DRWContext *draw_ctx,
156 RenderLayer *rl,
157 const char *viewname,
158 Instance &instance,
159 const rcti *rect)
160{
161 ViewLayer *view_layer = draw_ctx->view_layer;
162 if ((view_layer->passflag & SCE_PASS_DEPTH) == 0) {
163 return;
164 }
166 if (rp == nullptr) {
167 return;
168 }
169
170 float *ro_buffer_data = rp->ibuf->float_buffer.data;
171
173 rect->xmin,
174 rect->ymin,
175 BLI_rcti_size_x(rect),
176 BLI_rcti_size_y(rect),
178 ro_buffer_data);
179
180 float4x4 winmat = View::default_get().winmat();
181
182 int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
183
184 /* Convert GPU depth [0..1] to view Z [near..far] */
185 if (View::default_get().is_persp()) {
186 for (int i = 0; i < pix_num; i++) {
187 if (ro_buffer_data[i] == 1.0f) {
188 ro_buffer_data[i] = 1e10f; /* Background */
189 }
190 else {
191 ro_buffer_data[i] = ro_buffer_data[i] * 2.0f - 1.0f;
192 ro_buffer_data[i] = winmat[3][2] / (ro_buffer_data[i] + winmat[2][2]);
193 }
194 }
195 }
196 else {
197 /* Keep in mind, near and far distance are negatives. */
198 float near = View::default_get().near_clip();
199 float far = View::default_get().far_clip();
200 float range = fabsf(far - near);
201
202 for (int i = 0; i < pix_num; i++) {
203 if (ro_buffer_data[i] == 1.0f) {
204 ro_buffer_data[i] = 1e10f; /* Background */
205 }
206 else {
207 ro_buffer_data[i] = ro_buffer_data[i] * range - near;
208 }
209 }
210 }
211}
212
214 const char *viewname,
215 Instance &instance,
216 const rcti *rect)
217{
219
220 Framebuffer read_fb;
222 GPU_framebuffer_bind(read_fb);
224 rect->xmin,
225 rect->ymin,
226 BLI_rcti_size_x(rect),
227 BLI_rcti_size_y(rect),
228 4,
229 0,
231 rp->ibuf->float_buffer.data);
232}
233
234static void render_result_separated_pass(float *data, Instance &instance, const rcti *rect)
235{
236 Framebuffer read_fb;
238 GPU_framebuffer_bind(read_fb);
240 rect->xmin,
241 rect->ymin,
242 BLI_rcti_size_x(rect),
243 BLI_rcti_size_y(rect),
244 4,
245 0,
247 data);
248}
249
250/* This is taken from blender::eevee::Sampling::cdf_from_curvemapping. */
251static void cdf_from_curvemapping(const CurveMapping &curve, Array<float> &cdf)
252{
253 BLI_assert(cdf.size() > 1);
254 cdf[0] = 0.0f;
255 /* Actual CDF evaluation. */
256 for (const int u : IndexRange(cdf.size() - 1)) {
257 const float x = float(u + 1) / float(cdf.size() - 1);
258 cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
259 }
260 /* Normalize the CDF. */
261 for (const int u : cdf.index_range()) {
262 cdf[u] /= cdf.last();
263 }
264 /* Just to make sure. */
265 cdf.last() = 1.0f;
266}
267
268/* This is taken from blender::eevee::Sampling::cdf_invert. */
269static void cdf_invert(Array<float> &cdf, Array<float> &inverted_cdf)
270{
271 BLI_assert(cdf.first() == 0.0f && cdf.last() == 1.0f);
272 for (const int u : inverted_cdf.index_range()) {
273 const float x = clamp_f(u / float(inverted_cdf.size() - 1), 1e-5f, 1.0f - 1e-5f);
274 for (const int i : cdf.index_range().drop_front(1)) {
275 if (cdf[i] >= x) {
276 const float t = (x - cdf[i]) / (cdf[i] - cdf[i - 1]);
277 inverted_cdf[u] = (float(i) + t) / float(cdf.size() - 1);
278 break;
279 }
280 }
281 }
282}
283
284/* This is taken from blender::eevee::MotionBlurModule::shutter_time_to_scene_time. */
285static float shutter_time_to_scene_time(const int shutter_position,
286 const float shutter_time,
287 const float frame_time,
288 float time)
289{
290 switch (shutter_position) {
291 case SCE_MB_START:
292 /* No offset. */
293 break;
294 case SCE_MB_CENTER:
295 time -= 0.5f;
296 break;
297 case SCE_MB_END:
298 time -= 1.0;
299 break;
300 default:
301 BLI_assert_msg(false, "Invalid motion blur position enum!");
302 break;
303 }
304 time *= shutter_time;
305 time += frame_time;
306 return time;
307}
308
309static void render_frame(RenderEngine *engine,
310 Depsgraph *depsgraph,
311 const DRWContext *draw_ctx,
312 RenderLayer *render_layer,
313 const rcti rect,
314 gpencil::Instance &inst,
315 Manager &manager,
316 const bool separated_pass)
317{
318 Scene *scene = draw_ctx->scene;
319
320 const float aa_radius = clamp_f(scene->r.gauss, 0.0f, 100.0f);
321
322 const bool motion_blur_enabled = (scene->r.mode & R_MBLUR) != 0 &&
323 (draw_ctx->view_layer->layflag & SCE_LAY_MOTION_BLUR) != 0 &&
325
326 const int motion_steps_count =
327 motion_blur_enabled ? max_ii(1, scene->grease_pencil_settings.motion_blur_steps) * 2 + 1 : 1;
328 const int total_step_count = ceil_to_multiple_u(scene->grease_pencil_settings.aa_samples,
329 motion_steps_count);
330 const int aa_per_step = total_step_count / motion_steps_count;
331
332 const int shutter_position = scene->r.motion_blur_position;
333 const float shutter_time = scene->r.motion_blur_shutter;
334
335 const int initial_frame = scene->r.cfra;
336 const float initial_subframe = scene->r.subframe;
337 const float frame_time = initial_frame + initial_subframe;
338
339 Array<float> time_steps(motion_steps_count);
340 if (motion_blur_enabled) {
342
345 cdf_invert(cdf, time_steps);
346
347 for (float &scene_time : time_steps) {
348 scene_time = shutter_time_to_scene_time(
349 shutter_position, shutter_time, frame_time, scene_time);
350 }
351 }
352 else {
353 BLI_assert(time_steps.size() == 1);
354 time_steps.first() = frame_time;
355 }
356
357 int sample_i = 0;
358 for (const float time : time_steps) {
359 inst.init();
360
361 if (motion_blur_enabled) {
362 DRW_render_set_time(engine, depsgraph, floorf(time), fractf(time));
363 }
364
366
367 manager.begin_sync();
368
369 /* Loop over all objects and create draw structure. */
370 inst.begin_sync();
371 DRW_render_object_iter(engine, depsgraph, [&](ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
372 if (!ELEM(ob_ref.object->type, OB_GREASE_PENCIL, OB_LAMP)) {
373 return;
374 }
376 return;
377 }
378 inst.object_sync(ob_ref, manager);
379 });
380 inst.end_sync();
381
382 manager.end_sync();
383
384 for ([[maybe_unused]] const int i : IndexRange(aa_per_step)) {
385 const float2 aa_sample = Instance::antialiasing_sample_get(sample_i, total_step_count) *
386 aa_radius;
387 const float2 aa_offset = 2.0f * aa_sample / float2(inst.render_color_tx.size());
388 render_set_view(engine, depsgraph, aa_offset);
389 render_init_buffers(draw_ctx, inst, engine, render_layer, &rect, separated_pass);
390
391 /* Render the gpencil object and merge the result to the underlying render. */
392 inst.draw(manager);
393
394 /* Weight of this render SSAA sample. The sum of previous samples is weighted by
395 * `1 - weight`. This diminishes after each new sample as we want all samples to be equally
396 * weighted inside the final result (inside the combined buffer). This weighting scheme
397 * allows to always store the resolved result making it ready for in-progress display or
398 * read-back. */
399 const float weight = 1.0f / (1.0f + sample_i);
400 inst.antialiasing_accumulate(manager, weight);
401
402 sample_i++;
403 }
404 }
405
406 if (motion_blur_enabled) {
407 /* Restore original frame number. This is because the render pipeline expects it. */
408 RE_engine_frame_set(engine, initial_frame, initial_subframe);
409 }
410}
411
412void Engine::render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect)
413{
414 const char *viewname = RE_GetActiveRenderView(engine->re);
415
416 const DRWContext *draw_ctx = DRW_context_get();
417 Depsgraph *depsgraph = draw_ctx->depsgraph;
418
420 Render *re = engine->re;
422 re->result, RE_PASSNAME_GREASE_PENCIL, 4, "RGBA", render_layer->name, viewname, true);
423 }
424
426
427 Manager &manager = *DRW_manager_get();
428
429 render_set_view(engine, depsgraph);
430 render_init_buffers(draw_ctx, inst, engine, render_layer, &rect, false);
431
432 render_frame(engine, depsgraph, draw_ctx, render_layer, rect, inst, manager, false);
433 render_result_combined(render_layer, viewname, inst, &rect);
434
435 float *pass_data = RE_RenderLayerGetPass(render_layer, RE_PASSNAME_GREASE_PENCIL, viewname);
436 if (pass_data) {
437 render_frame(engine, depsgraph, draw_ctx, render_layer, rect, inst, manager, true);
438 render_result_separated_pass(pass_data, inst, &rect);
439 }
440
441 /* Transfer depth in the last step, because if we need to render separate pass, we need original
442 * untouched depth buffer. */
443 render_result_z(draw_ctx, render_layer, viewname, inst, &rect);
444}
445
446} // namespace blender::draw::gpencil
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_changed(CurveMapping *cumap, bool rem_doubles)
General operations, lookup, etc. for blender objects.
@ OB_VISIBLE_SELF
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE int max_ii(int a, int b)
void window_translate_m4(float winmat[4][4], float perspmat[4][4], float x, float y)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
#define UNPACK2(a)
#define ELEM(...)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
#define CM_TABLE
@ GREASE_PENCIL_AS_SEPARATE_PASS
@ OB_GREASE_PENCIL
@ OB_LAMP
#define RE_PASSNAME_COMBINED
@ SCE_LAY_MOTION_BLUR
@ SCE_MB_START
@ SCE_MB_END
@ SCE_MB_CENTER
@ R_MBLUR
#define RE_PASSNAME_DEPTH
#define RE_PASSNAME_GREASE_PENCIL
@ SCE_PASS_DEPTH
static AppView * view
#define GPU_ATTACHMENT_TEXTURE(_texture)
void GPU_framebuffer_read_color(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height, int channels, int slot, eGPUDataFormat data_format, void *r_data)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_clear_depth(blender::gpu::FrameBuffer *fb, float clear_depth)
void GPU_framebuffer_clear_color(blender::gpu::FrameBuffer *fb, const float clear_col[4])
void GPU_framebuffer_read_depth(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height, eGPUDataFormat data_format, void *r_data)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
void GPU_texture_update_sub(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *pixels, int offset_x, int offset_y, int offset_z, int width, int height, int depth)
@ GPU_DATA_FLOAT
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
void GPU_texture_update(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
#define MEM_SAFE_FREE(v)
BMesh const char void * data
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
Definition BLI_array.hh:256
const T & last(const int64_t n=0) const
Definition BLI_array.hh:296
IndexRange index_range() const
Definition BLI_array.hh:360
const T & first() const
Definition BLI_array.hh:281
constexpr IndexRange drop_front(int64_t n) const
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 begin_sync(Object *object_active=nullptr)
bool ensure_2d(blender::gpu::TextureFormat format, int2 extent, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
int3 size(int miplvl=0) const
static void default_set(const float4x4 &view_mat, const float4x4 &win_mat)
Definition draw_view.cc:322
const float4x4 & winmat(int view_id=0) const
Definition draw_view.hh:148
float near_clip(int view_id=0) const
Definition draw_view.hh:115
static View & default_get()
Definition draw_view.cc:317
float far_clip(int view_id=0) const
Definition draw_view.hh:106
nullptr float
const DRWContext * DRW_context_get()
void DRW_render_object_iter(RenderEngine *engine, Depsgraph *depsgraph, std::function< void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)> callback)
int DRW_object_visibility_in_active_context(const Object *ob)
void DRW_render_set_time(RenderEngine *engine, Depsgraph *depsgraph, int frame, float subframe)
blender::draw::Manager * DRW_manager_get()
void RE_GetCameraModelMatrix(const Render *re, const Object *camera, float r_modelmat[4][4])
void RE_GetCameraWindow(Render *re, const Object *camera, float r_winmat[4][4])
Object * RE_GetCamera(Render *re)
void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
void RE_engine_set_error_message(RenderEngine *engine, const char *msg)
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
static void render_set_view(RenderEngine *engine, const Depsgraph *depsgraph, const float2 aa_offset=float2{0.0f})
static void render_result_separated_pass(float *data, Instance &instance, const rcti *rect)
static void render_result_combined(RenderLayer *rl, const char *viewname, Instance &instance, const rcti *rect)
static void cdf_invert(Array< float > &cdf, Array< float > &inverted_cdf)
static void render_frame(RenderEngine *engine, Depsgraph *depsgraph, const DRWContext *draw_ctx, RenderLayer *render_layer, const rcti rect, gpencil::Instance &inst, Manager &manager, const bool separated_pass)
static void remap_depth(const View &view, MutableSpan< float > pix_z)
static void cdf_from_curvemapping(const CurveMapping &curve, Array< float > &cdf)
static float shutter_time_to_scene_time(const int shutter_position, const float shutter_time, const float frame_time, float time)
static void render_init_buffers(const DRWContext *draw_ctx, Instance &inst, RenderEngine *engine, RenderLayer *render_layer, const rcti *rect, const bool use_separated_pass)
static void render_result_z(const DRWContext *draw_ctx, RenderLayer *rl, const char *viewname, Instance &instance, const rcti *rect)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define floorf
#define fabsf
#define fractf
void RE_create_render_pass(RenderResult *rr, const char *name, int channels, const char *chan_id, const char *layername, const char *viewname, const bool allocate)
RenderPass * RE_pass_find_by_name(RenderLayer *rl, const char *name, const char *viewname)
const char * RE_GetActiveRenderView(Render *re)
float * RE_RenderLayerGetPass(RenderLayer *rl, const char *name, const char *viewname)
RenderResult * result
Depsgraph * depsgraph
blender::float2 viewport_size_get() const
Scene * scene
ViewLayer * view_layer
ImBufFloatBuffer float_buffer
float motion_blur_shutter
struct CurveMapping mblur_shutter_curve
struct Render * re
Definition RE_engine.h:137
char name[RE_MAXNAME]
Definition RE_pipeline.h:89
struct ImBuf * ibuf
Definition RE_pipeline.h:67
struct SceneGpencil grease_pencil_settings
struct RenderData r
int grease_pencil_flags
const c_style_mat & ptr() const
static void render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect)
void draw(Manager &manager) final
static float2 antialiasing_sample_get(int sample_index, int sample_count)
void object_sync(ObjectRef &ob_ref, Manager &manager) final
void antialiasing_accumulate(Manager &manager, float alpha)
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230