diff options
Diffstat (limited to 'source/blender/draw/engines/gpencil/gpencil_render.c')
-rw-r--r-- | source/blender/draw/engines/gpencil/gpencil_render.c | 365 |
1 files changed, 120 insertions, 245 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 8c126310ea2..083b04c0600 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -33,66 +33,22 @@ #include "gpencil_engine.h" -/* Get pixel size for render - * This function uses the same calculation used for viewport, because if use - * camera pixelsize, the result is not correct. - */ -static float get_render_pixelsize(float persmat[4][4], int winx, int winy) -{ - float v1[3], v2[3]; - float len_px, len_sc; - - v1[0] = persmat[0][0]; - v1[1] = persmat[1][0]; - v1[2] = persmat[2][0]; - - v2[0] = persmat[0][1]; - v2[1] = persmat[1][1]; - v2[2] = persmat[2][1]; - - len_px = 2.0f / sqrtf(min_ff(len_squared_v3(v1), len_squared_v3(v2))); - len_sc = (float)MAX2(winx, winy); - - return len_px / len_sc; -} - /* init render data */ -void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph) +void GPENCIL_render_init(GPENCIL_Data *vedata, + RenderEngine *engine, + struct RenderLayer *render_layer, + const Depsgraph *depsgraph, + const rcti *rect) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = vedata->stl; GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_TextureList *txl = vedata->txl; Scene *scene = DEG_get_evaluated_scene(depsgraph); const float *viewport_size = DRW_viewport_size_get(); const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - /* In render mode the default framebuffer is not generated - * because there is no viewport. So we need to manually create one - * NOTE : use 32 bit format for precision in render mode. - */ - /* create multisample framebuffer for AA */ - if (U.gpencil_multisamples > 0) { - int rect_w = (int)viewport_size[0]; - int rect_h = (int)viewport_size[1]; - gpencil_multisample_ensure(vedata, rect_w, rect_h); - } - - vedata->render_depth_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH_COMPONENT24, &draw_engine_gpencil_type); - vedata->render_color_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_RGBA32F, &draw_engine_gpencil_type); - GPU_framebuffer_ensure_config(&fbl->main, - {GPU_ATTACHMENT_TEXTURE(vedata->render_depth_tx), - GPU_ATTACHMENT_TEXTURE(vedata->render_color_tx)}); - - /* Alloc transient data. */ - if (!stl->g_data) { - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - /* Set the pers & view matrix. */ - float winmat[4][4], viewmat[4][4], viewinv[4][4], persmat[4][4]; + float winmat[4][4], viewmat[4][4], viewinv[4][4]; struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); float frame = BKE_scene_frame_get(scene); @@ -105,85 +61,101 @@ void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgra DRW_view_default_set(view); DRW_view_set_active(view); - DRW_view_persmat_get(NULL, persmat, false); + /* Create depth texture & color texture from render result. */ + const char *viewname = RE_GetActiveRenderView(engine->re); + RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); + RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - /* calculate pixel size for render */ - stl->storage->render_pixsize = get_render_pixelsize(persmat, viewport_size[0], viewport_size[1]); + float *pix_z = (rpass_z_src) ? rpass_z_src->rect : NULL; + float *pix_col = (rpass_col_src) ? rpass_col_src->rect : NULL; - /* INIT CACHE */ - GPENCIL_cache_init(vedata); + if (!pix_z || !pix_col) { + RE_engine_set_error_message(engine, + "Warning: To render grease pencil, enable Combined and Z passes."); + } + + if (pix_z) { + /* Depth need to be remapped to [0..1] range. */ + pix_z = MEM_dupallocN(pix_z); + + int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + + if (DRW_view_is_persp_get(view)) { + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; + pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(view); + float far = DRW_view_far_distance_get(view); + float range_inv = 1.0f / fabsf(far - near); + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (pix_z[i] + near) * range_inv; + pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); + } + } + } + + const bool do_region = (scene->r.mode & R_BORDER) != 0; + const bool do_clear_z = !pix_z || do_region; + const bool do_clear_col = !pix_col || do_region; + + /* FIXME(fclem): we have a precision loss in the depth buffer because of this reupload. + * Find where it comes from! */ + txl->render_depth_tx = DRW_texture_create_2d( + size[0], size[1], GPU_DEPTH_COMPONENT24, 0, do_region ? NULL : pix_z); + txl->render_color_tx = DRW_texture_create_2d( + size[0], size[1], GPU_RGBA16F, 0, do_region ? NULL : pix_col); + + GPU_framebuffer_ensure_config(&fbl->render_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->render_depth_tx), + GPU_ATTACHMENT_TEXTURE(txl->render_color_tx), + }); + + if (do_clear_z || do_clear_col) { + /* To avoid unpredictable result, clear buffers that have not be initialized. */ + GPU_framebuffer_bind(fbl->render_fb); + if (do_clear_col) { + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(fbl->render_fb, clear_col); + } + if (do_clear_z) { + GPU_framebuffer_clear_depth(fbl->render_fb, 1.0f); + } + } + + if (do_region) { + int x = rect->xmin; + int y = rect->ymin; + int w = BLI_rcti_size_x(rect); + int h = BLI_rcti_size_y(rect); + if (pix_col) { + GPU_texture_update_sub(txl->render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0); + } + if (pix_z) { + GPU_texture_update_sub(txl->render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0); + } + } + + MEM_SAFE_FREE(pix_z); } /* render all objects and select only grease pencil */ static void GPENCIL_render_cache(void *vedata, struct Object *ob, struct RenderEngine *UNUSED(engine), - struct Depsgraph *UNUSED(depsgraph)) + Depsgraph *UNUSED(depsgraph)) { - if (ob && ob->type == OB_GPENCIL) { + if (ob && ELEM(ob->type, OB_GPENCIL, OB_LAMP)) { if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) { GPENCIL_cache_populate(vedata, ob); } } } -/* TODO: Reuse Eevee code in shared module instead to duplicate here */ -static void GPENCIL_render_update_viewvecs(float invproj[4][4], - const float winmat[4][4], - float (*r_viewvecs)[4]) -{ - /* view vectors for the corners of the view frustum. - * Can be used to recreate the world space position easily */ - float view_vecs[4][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f}, - {-1.0f, -1.0f, 1.0f, 1.0f}, - }; - - /* convert the view vectors to view space */ - const bool is_persp = (winmat[3][3] == 0.0f); - for (int i = 0; i < 4; i++) { - mul_project_m4_v3(invproj, view_vecs[i]); - /* normalized trick see: - * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - if (is_persp) { - /* Divide XY by Z. */ - mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]); - } - } - - /** - * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and - * view_vecs[1] is the vector going from the near-bottom-left corner to - * the far-top-right corner. - * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner - * when Z = 1, and top-left corner if Z = 1. - * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) - * distance from the near plane to the far clip plane. - */ - copy_v4_v4(r_viewvecs[0], view_vecs[0]); - - /* we need to store the differences */ - r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; - r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; - r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; -} - -/* Update view_vecs */ -static void GPENCIL_render_update_vecs(GPENCIL_Data *vedata) -{ - GPENCIL_StorageList *stl = vedata->stl; - - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - /* this is separated to keep function equal to Eevee for future reuse of same code */ - GPENCIL_render_update_viewvecs(invproj, winmat, stl->storage->view_vecs); -} - -/* read z-depth render result */ static void GPENCIL_render_result_z(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, @@ -191,45 +163,52 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, { const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; - GPENCIL_StorageList *stl = vedata->stl; if ((view_layer->passflag & SCE_PASS_Z) != 0) { RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname); - GPU_framebuffer_read_depth(vedata->fbl->main, + GPU_framebuffer_read_depth(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect); - bool is_persp = DRW_view_is_persp_get(NULL); - - GPENCIL_render_update_vecs(vedata); - float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); + int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + /* Convert ogl depth [0..1] to view Z [near..far] */ - for (int i = 0; i < BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); i++) { - if (rp->rect[i] == 1.0f) { - rp->rect[i] = 1e10f; /* Background */ - } - else { - if (is_persp) { + if (DRW_view_is_persp_get(NULL)) { + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; rp->rect[i] = winmat[3][2] / (rp->rect[i] + winmat[2][2]); } + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); + float range = fabsf(far - near); + + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } else { - rp->rect[i] = -stl->storage->view_vecs[0][2] + - rp->rect[i] * -stl->storage->view_vecs[1][2]; + rp->rect[i] = -rp->rect[i] * range + near; } } } } } -/* read combined render result */ static void GPENCIL_render_result_combined(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, @@ -238,8 +217,8 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname); GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - GPU_framebuffer_bind(fbl->main); - GPU_framebuffer_read_color(vedata->fbl->main, + GPU_framebuffer_bind(fbl->render_fb); + GPU_framebuffer_read_color(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -249,135 +228,31 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, rp->rect); } -/* helper to blend pixels */ -static void blend_pixel(float top_color[4], float bottom_color[4], float dst_color[4]) -{ - float alpha = top_color[3]; - - /* use blend: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA */ - dst_color[0] = (top_color[0] * alpha) + (bottom_color[0] * (1.0f - alpha)); - dst_color[1] = (top_color[1] * alpha) + (bottom_color[1] * (1.0f - alpha)); - dst_color[2] = (top_color[2] * alpha) + (bottom_color[2] * (1.0f - alpha)); -} - -/* render grease pencil to image */ -void GPENCIL_render_to_image(void *vedata, +void GPENCIL_render_to_image(void *ved, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; const char *viewname = RE_GetActiveRenderView(engine->re); const DRWContextState *draw_ctx = DRW_context_state_get(); - int imgsize = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); - - /* save previous render data */ - RenderPass *rpass_color_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - RenderPass *rpass_depth_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); - float *src_rect_color_data = NULL; - float *src_rect_depth_data = NULL; - if ((rpass_color_src) && (rpass_depth_src) && (rpass_color_src->rect) && - (rpass_depth_src->rect)) { - src_rect_color_data = MEM_dupallocN(rpass_color_src->rect); - src_rect_depth_data = MEM_dupallocN(rpass_depth_src->rect); - } - else { - /* TODO: put this message in a better place */ - printf("Warning: To render grease pencil, enable Combined and Z passes.\n"); - } + Depsgraph *depsgraph = draw_ctx->depsgraph; + GPENCIL_render_init(vedata, engine, render_layer, depsgraph, rect); GPENCIL_engine_init(vedata); - GPENCIL_render_init(vedata, engine, draw_ctx->depsgraph); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - Object *camera = DEG_get_evaluated_object(draw_ctx->depsgraph, RE_GetCamera(engine->re)); - stl->storage->camera = camera; /* save current camera */ - - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - if (fbl->main) { - GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx, 0, 0); - GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx, 0, 0); - /* clean first time the buffer */ - float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_bind(fbl->main); - GPU_framebuffer_clear_color_depth(fbl->main, clearcol, 1.0f); - } - - /* loop all objects and draw */ - DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, GPENCIL_render_cache); + vedata->stl->pd->camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); + /* Loop over all objects and create draw structure. */ + GPENCIL_cache_init(vedata); + DRW_render_object_iter(vedata, engine, depsgraph, GPENCIL_render_cache); GPENCIL_cache_finish(vedata); DRW_render_instance_buffer_finish(); + /* Render the gpencil object and merge the result to the underlying render. */ GPENCIL_draw_scene(vedata); - /* combined data */ GPENCIL_render_result_combined(render_layer, viewname, vedata, rect); - /* z-depth data */ GPENCIL_render_result_z(render_layer, viewname, vedata, rect); - - /* detach textures */ - if (fbl->main) { - GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx); - GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx); - } - - /* merge previous render image with new GP image */ - if (src_rect_color_data) { - RenderPass *rpass_color_gp = RE_pass_find_by_name( - render_layer, RE_PASSNAME_COMBINED, viewname); - RenderPass *rpass_depth_gp = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); - float *gp_rect_color_data = rpass_color_gp->rect; - float *gp_rect_depth_data = rpass_depth_gp->rect; - float *gp_pixel_rgba; - float *gp_pixel_depth; - float *src_pixel_rgba; - float *src_pixel_depth; - - for (int i = 0; i < imgsize; i++) { - gp_pixel_rgba = &gp_rect_color_data[i * 4]; - gp_pixel_depth = &gp_rect_depth_data[i]; - - src_pixel_rgba = &src_rect_color_data[i * 4]; - src_pixel_depth = &src_rect_depth_data[i]; - - /* check grease pencil render transparency */ - if (gp_pixel_rgba[3] > 0.0f) { - if (src_pixel_rgba[3] > 0.0f) { - /* check z-depth */ - if (gp_pixel_depth[0] > src_pixel_depth[0]) { - /* copy source z-depth */ - gp_pixel_depth[0] = src_pixel_depth[0]; - /* blend object on top */ - if (src_pixel_rgba[3] < 1.0f) { - blend_pixel(src_pixel_rgba, gp_pixel_rgba, gp_pixel_rgba); - } - else { - copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); - } - } - else { - /* blend gp render */ - if (gp_pixel_rgba[3] < 1.0f) { - /* premult alpha factor to remove double blend effects */ - mul_v3_fl(gp_pixel_rgba, 1.0f / gp_pixel_rgba[3]); - - blend_pixel(gp_pixel_rgba, src_pixel_rgba, gp_pixel_rgba); - - gp_pixel_rgba[3] = gp_pixel_rgba[3] > src_pixel_rgba[3] ? gp_pixel_rgba[3] : - src_pixel_rgba[3]; - } - } - } - } - else { - copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); - gp_pixel_depth[0] = src_pixel_depth[0]; - } - } - - /* free memory */ - MEM_SAFE_FREE(src_rect_color_data); - MEM_SAFE_FREE(src_rect_depth_data); - } } |