diff options
36 files changed, 2102 insertions, 72 deletions
diff --git a/SConstruct b/SConstruct index 7de9f2c243c..225b532c8a0 100644 --- a/SConstruct +++ b/SConstruct @@ -761,6 +761,11 @@ if B.targets != ['cudakernels']: data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vertex_world.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl") data_to_c_simple("source/blender/gpu/shaders/gpu_shader_vsm_store_vert.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_lib.glsl") + data_to_c_simple("source/blender/gpu/shaders/gpu_shader_fx_vert.glsl") data_to_c_simple("intern/opencolorio/gpu_shader_display_transform.glsl") # --- blender --- diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 0b0b9b6690d..7fc2c2ec084 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -177,6 +177,7 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): layout = self.layout cam = context.camera + dof_options = cam.gpu_dof layout.label(text="Focus:") @@ -187,6 +188,7 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col.active = cam.dof_object is None col.prop(cam, "dof_distance", text="Distance") + col.prop(dof_options, "fstop", text="GPU fstop") class DATA_PT_camera_display(CameraButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index d2c95715a7e..b5bd13781f7 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2929,8 +2929,26 @@ class VIEW3D_PT_view3d_shading(Panel): col.prop(view, "show_textured_shadeless") col.prop(view, "show_backface_culling") - if obj and obj.mode == 'EDIT' and view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'}: - col.prop(view, "show_occlude_wire") + + if view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'}: + if obj and obj.mode == 'EDIT': + col.prop(view, "show_occlude_wire") + + fx_settings = view.fx_settings + + sub = col.column() + sub.active = view.region_3d.view_perspective == 'CAMERA' + sub.prop(fx_settings, "use_dof") + + col.prop(fx_settings, "use_ssao", text="Ambient Occlusion") + if fx_settings.use_ssao: + ssao_settings = fx_settings.ssao + subcol = col.column(align=True) + subcol.prop(ssao_settings, "factor") + subcol.prop(ssao_settings, "distance_max") + subcol.prop(ssao_settings, "attenuation") + subcol.prop(ssao_settings, "samples") + subcol.prop(ssao_settings, "color") class VIEW3D_PT_view3d_motion_tracking(Panel): diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 1cc232a8ed6..d87d5dfc2cc 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -46,6 +46,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_freestyle_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_genfile.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 26d4986d93e..77296920ee2 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -46,6 +46,7 @@ struct RenderData; struct Scene; struct rctf; struct View3D; +struct GPUFXSettings; /* Camera Datablock */ @@ -122,6 +123,8 @@ bool BKE_camera_view_frame_fit_to_scene(struct Scene *scene, struct View3D *v3d, bool BKE_camera_view_frame_fit_to_coords(struct Scene *scene, float (*cos)[3], int num_cos, struct Object *camera_ob, float r_co[3], float *r_scale); +void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 3eb6ba7b6fa..29590c52b23 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -52,6 +52,7 @@ struct wmKeyConfig; struct wmNotifier; struct wmWindow; struct wmWindowManager; +struct GPUFXSettings; #include "BLI_compiler_attrs.h" @@ -294,6 +295,7 @@ void BKE_screen_view3d_scene_sync(struct bScreen *sc); void BKE_screen_view3d_main_sync(ListBase *screen_lb, struct Scene *scene); void BKE_screen_view3d_twmode_remove(struct View3D *v3d, const int i); void BKE_screen_view3d_main_twmode_remove(ListBase *screen_lb, struct Scene *scene, const int i); +void BKE_screen_gpu_fx_validate(struct GPUFXSettings *fx_settings); /* zoom factor conversion */ float BKE_screen_view3d_zoom_to_fac(float camzoom); diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index e424823d8dc..e1d771dad65 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -49,6 +49,8 @@ #include "BKE_main.h" #include "BKE_screen.h" +#include "GPU_compositing.h" + /****************************** Camera Datablock *****************************/ void *BKE_camera_add(Main *bmain, const char *name) @@ -66,7 +68,9 @@ void *BKE_camera_add(Main *bmain, const char *name) cam->ortho_scale = 6.0; cam->flag |= CAM_SHOWPASSEPARTOUT; cam->passepartalpha = 0.5f; - + + GPU_fx_compositor_init_dof_settings(&cam->gpu_dof); + return cam; } @@ -686,3 +690,18 @@ bool BKE_camera_view_frame_fit_to_coords( return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); } +void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings) +{ + if (camera->type == OB_CAMERA) { + Camera *cam = camera->data; + r_fx_settings->dof = &cam->gpu_dof; + r_fx_settings->dof->focal_length = cam->lens; + r_fx_settings->dof->sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); + if (cam->dof_ob) { + r_fx_settings->dof->focus_distance = len_v3v3(cam->dof_ob->obmat[3], camera->obmat[3]); + } + else { + r_fx_settings->dof->focus_distance = cam->YF_dofdist; + } + } +} diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c9dba38b713..a6ac4551f4a 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -39,6 +39,8 @@ #include "MEM_guardedalloc.h" +#include "GPU_compositing.h" + #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -598,3 +600,26 @@ float BKE_screen_view3d_zoom_from_fac(float zoomfac) { return ((sqrtf(4.0f * zoomfac) - (float)M_SQRT2) * 50.0f); } + +void BKE_screen_gpu_fx_validate(GPUFXSettings *fx_settings) +{ + /* currently we use DOF from the camera _only_, + * so we never allocate this, only copy from the Camera */ +#if 0 + if ((fx_settings->dof == NULL) && + (fx_settings->fx_flag & GPU_FX_FLAG_DOF)) + { + GPUDOFSettings *fx_dof; + fx_dof = fx_settings->dof = MEM_callocN(sizeof(GPUDOFSettings), __func__); + } +#endif + + if ((fx_settings->ssao == NULL) && + (fx_settings->fx_flag & GPU_FX_FLAG_SSAO)) + { + GPUSSAOSettings *fx_ssao; + fx_ssao = fx_settings->ssao = MEM_callocN(sizeof(GPUSSAOSettings), __func__); + + GPU_fx_compositor_init_ssao_settings(fx_ssao); + } +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 66fbca395a8..204a7af69ef 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6421,6 +6421,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) rv3d->render_engine = NULL; rv3d->sms = NULL; rv3d->smooth_timer = NULL; + rv3d->compositor = NULL; } } } @@ -6528,7 +6529,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc) /* add local view3d too */ else if (sa->spacetype == SPACE_VIEW3D) blo_do_versions_view3d_split_250(sa->spacedata.first, &sa->regionbase); - + /* incase we set above */ sa->butspacetype = sa->spacetype; @@ -6575,6 +6576,11 @@ static bool direct_link_screen(FileData *fd, bScreen *sc) /* render can be quite heavy, set to solid on load */ if (v3d->drawtype == OB_RENDER) v3d->drawtype = OB_SOLID; + + if (v3d->fx_settings.dof) + v3d->fx_settings.dof = newdataadr(fd, v3d->fx_settings.dof); + if (v3d->fx_settings.ssao) + v3d->fx_settings.ssao = newdataadr(fd, v3d->fx_settings.ssao); blo_do_versions_view3d_split_250(v3d, &sl->regionbase); } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 572566df6a2..23bf291b981 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -35,6 +35,7 @@ #define DNA_DEPRECATED_ALLOW #include "DNA_brush_types.h" +#include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" #include "DNA_sdna_types.h" @@ -562,6 +563,16 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } FOREACH_NODETREE_END } + + if (!DNA_struct_elem_find(fd->filesdna, "Camera", "GPUDOFSettings", "gpu_dof")) { + Camera *ca; + for (ca = main->camera.first; ca; ca = ca->id.next) { + ca->gpu_dof.fstop = 128.0f; + ca->gpu_dof.focal_length = 1.0f; + ca->gpu_dof.focus_distance = 1.0f; + ca->gpu_dof.sensor = 1.0f; + } + } } if (!MAIN_VERSION_ATLEAST(main, 273, 7)) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 6031400684c..308ab448fcd 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2682,6 +2682,11 @@ static void write_screens(WriteData *wd, ListBase *scrbase) for (bgpic= v3d->bgpicbase.first; bgpic; bgpic= bgpic->next) writestruct(wd, DATA, "BGpic", 1, bgpic); if (v3d->localvd) writestruct(wd, DATA, "View3D", 1, v3d->localvd); + + if (v3d->fx_settings.ssao) + writestruct(wd, DATA, "GPUSSAOSettings", 1, v3d->fx_settings.ssao); + if (v3d->fx_settings.dof) + writestruct(wd, DATA, "GPUDOFSettings", 1, v3d->fx_settings.dof); } else if (sl->spacetype==SPACE_IPO) { SpaceIpo *sipo= (SpaceIpo *)sl; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index b62d9960117..e6434a19952 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -61,6 +61,10 @@ struct rcti; struct wmOperator; struct wmOperatorType; struct wmWindow; +struct GPUFX; +struct GPUOffScreen; +struct GPUFXSettings; +enum eGPUFXFlags; /* for derivedmesh drawing callbacks, for view3d_select, .... */ typedef struct ViewContext { @@ -303,8 +307,11 @@ int ED_view3d_scene_layer_set(int lay, const int *values, int *active); bool ED_view3d_context_activate(struct bContext *C); void ED_view3d_draw_offscreen_init(struct Scene *scene, struct View3D *v3d); -void ED_view3d_draw_offscreen(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, - int winx, int winy, float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky); +void ED_view3d_draw_offscreen( + struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4], + float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, + struct GPUOffScreen *ofs, + struct GPUFX *fx, struct GPUFXSettings *fx_settings); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, bool draw_background, int alpha_mode, char err_out[256]); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index fc6ff4eb0c0..402e72db217 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_camera_types.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_blenlib.h" @@ -42,6 +43,7 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "BKE_camera.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -68,6 +70,7 @@ #include "GPU_extensions.h" #include "GPU_glew.h" +#include "GPU_compositing.h" #include "render_intern.h" @@ -92,6 +95,7 @@ typedef struct OGLRender { ImageUser iuser; GPUOffScreen *ofs; + GPUFX *fx; int sizex, sizey; int write_still; @@ -180,7 +184,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) int i; unsigned char *gp_rect; - GPU_offscreen_bind(oglrender->ofs); + GPU_offscreen_bind(oglrender->ofs, true); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -200,22 +204,27 @@ static void screen_opengl_render_apply(OGLRender *oglrender) rgba_uchar_to_float(col_src, &gp_rect[i]); blend_color_mix_float(&rr->rectf[i], &rr->rectf[i], col_src); } - GPU_offscreen_unbind(oglrender->ofs); + GPU_offscreen_unbind(oglrender->ofs, true); MEM_freeN(gp_rect); } } else if (view_context) { + bool is_persp; + /* full copy */ + GPUFXSettings fx_settings = v3d->fx_settings; + ED_view3d_draw_offscreen_init(scene, v3d); - GPU_offscreen_bind(oglrender->ofs); /* bind */ + GPU_offscreen_bind(oglrender->ofs, true); /* bind */ /* render 3d view */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { /*int is_ortho = scene->r.mode & R_ORTHO;*/ camera = v3d->camera; RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat); - + is_persp = true; + BKE_camera_to_gpu_dof(camera, &fx_settings); } else { rctf viewplane; @@ -224,12 +233,17 @@ static void screen_opengl_render_apply(OGLRender *oglrender) bool is_ortho = ED_view3d_viewplane_get(v3d, rv3d, sizex, sizey, &viewplane, &clipsta, &clipend, NULL); if (is_ortho) orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clipend, clipend); else perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend); + + is_persp = !is_ortho; } rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect"); if ((scene->r.mode & R_OSA) == 0) { - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky); + ED_view3d_draw_offscreen( + scene, v3d, ar, sizex, sizey, NULL, winmat, + draw_bgpic, draw_sky, is_persp, + oglrender->ofs, oglrender->fx, &fx_settings); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); } else { @@ -242,7 +256,10 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BLI_jitter_init(jit_ofs, scene->r.osa); /* first sample buffer, also initializes 'rv3d->persmat' */ - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky); + ED_view3d_draw_offscreen( + scene, v3d, ar, sizex, sizey, NULL, winmat, + draw_bgpic, draw_sky, is_persp, + oglrender->ofs, oglrender->fx, &fx_settings); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -255,7 +272,10 @@ static void screen_opengl_render_apply(OGLRender *oglrender) (jit_ofs[j][0] * 2.0f) / sizex, (jit_ofs[j][1] * 2.0f) / sizey); - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, draw_bgpic, draw_sky); + ED_view3d_draw_offscreen( + scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, + draw_bgpic, draw_sky, is_persp, + oglrender->ofs, oglrender->fx, &fx_settings); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -268,7 +288,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) MEM_freeN(accum_buffer); } - GPU_offscreen_unbind(oglrender->ofs); /* unbind */ + GPU_offscreen_unbind(oglrender->ofs, true); /* unbind */ } else { /* shouldnt suddenly give errors mid-render but possible */ @@ -448,6 +468,9 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) * running notifiers again will overwrite */ oglrender->scene->customdata_mask |= oglrender->scene->customdata_mask_modal; + if (oglrender->v3d->fx_settings.fx_flag & (GPU_FX_FLAG_DOF | GPU_FX_FLAG_SSAO)) { + oglrender->fx = GPU_fx_compositor_create(); + } } /* create render */ @@ -497,6 +520,9 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene); + if (oglrender->fx) + GPU_fx_compositor_destroy(oglrender->fx); + GPU_offscreen_free(oglrender->ofs); oglrender->scene->customdata_mask_modal = 0; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 6ac2f426ed7..01a70d36eda 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -7392,7 +7392,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) { if (ob->type == OB_MESH) { if (dt < OB_SOLID) { - zbufoff = 1; + zbufoff = true; dt = OB_SOLID; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 25085368dac..ff212a1d74e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -56,6 +56,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" +#include "GPU_compositing.h" #include "BIF_gl.h" @@ -418,6 +419,11 @@ static void view3d_free(SpaceLink *sl) BKE_previewimg_free(&vd->defmaterial->preview); MEM_freeN(vd->defmaterial); } + + if (vd->fx_settings.ssao) + MEM_freeN(vd->fx_settings.ssao); + if (vd->fx_settings.dof) + MEM_freeN(vd->fx_settings.dof); } @@ -459,7 +465,11 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) } v3dn->properties_storage = NULL; - + if (v3dn->fx_settings.dof) + v3dn->fx_settings.dof = MEM_dupallocN(v3do->fx_settings.dof); + if (v3dn->fx_settings.ssao) + v3dn->fx_settings.ssao = MEM_dupallocN(v3do->fx_settings.ssao); + return (SpaceLink *)v3dn; } @@ -559,6 +569,11 @@ static void view3d_main_area_exit(wmWindowManager *wm, ARegion *ar) GPU_offscreen_free(rv3d->gpuoffscreen); rv3d->gpuoffscreen = NULL; } + + if (rv3d->compositor) { + GPU_fx_compositor_destroy(rv3d->compositor); + rv3d->compositor = NULL; + } } static int view3d_ob_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) @@ -713,6 +728,9 @@ static void view3d_main_area_free(ARegion *ar) if (rv3d->gpuoffscreen) { GPU_offscreen_free(rv3d->gpuoffscreen); } + if (rv3d->compositor) { + GPU_fx_compositor_destroy(rv3d->compositor); + } MEM_freeN(rv3d); ar->regiondata = NULL; @@ -736,6 +754,7 @@ static void *view3d_main_area_duplicate(void *poin) new->render_engine = NULL; new->sms = NULL; new->smooth_timer = NULL; + new->compositor = NULL; return new; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 929929c7dd0..94d0ffcc9ee 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -94,6 +94,7 @@ #include "GPU_draw.h" #include "GPU_material.h" #include "GPU_extensions.h" +#include "GPU_compositing.h" #include "view3d_intern.h" /* own include */ @@ -1374,7 +1375,7 @@ static void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d) } if (rv3d->gpuoffscreen) - GPU_offscreen_bind(rv3d->gpuoffscreen); + GPU_offscreen_bind(rv3d->gpuoffscreen, true); else glScissor(ar->winrct.xmin, ar->winrct.ymin, BLI_rcti_size_x(&ar->winrct), BLI_rcti_size_y(&ar->winrct)); @@ -1397,7 +1398,7 @@ static void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d) draw_object_backbufsel(scene, v3d, rv3d, base->object); if (rv3d->gpuoffscreen) - GPU_offscreen_unbind(rv3d->gpuoffscreen); + GPU_offscreen_unbind(rv3d->gpuoffscreen, true); else ar->swap = 0; /* mark invalid backbuf for wm draw */ @@ -1423,10 +1424,10 @@ void view3d_opengl_read_pixels(ARegion *ar, int x, int y, int w, int h, int form RegionView3D *rv3d = ar->regiondata; if (rv3d->gpuoffscreen) { - GPU_offscreen_bind(rv3d->gpuoffscreen); + GPU_offscreen_bind(rv3d->gpuoffscreen, true); glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); glReadPixels(x, y, w, h, format, type, data); - GPU_offscreen_unbind(rv3d->gpuoffscreen); + GPU_offscreen_unbind(rv3d->gpuoffscreen, true); } else { glReadPixels(ar->winrct.xmin + x, ar->winrct.ymin + y, w, h, format, type, data); @@ -2532,7 +2533,10 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) invert_m4_m4(rv3d.persinv, rv3d.viewinv); /* no need to call ED_view3d_draw_offscreen_init since shadow buffers were already updated */ - ED_view3d_draw_offscreen(scene, v3d, &ar, winsize, winsize, viewmat, winmat, false, false); + ED_view3d_draw_offscreen( + scene, v3d, &ar, winsize, winsize, viewmat, winmat, + false, false, true, + NULL, NULL, NULL); GPU_lamp_shadow_buffer_unbind(shadow->lamp); v3d->drawtype = drawtype; @@ -3049,13 +3053,18 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar, bool /* ED_view3d_draw_offscreen_init should be called before this to initialize * stuff like shadow buffers */ -void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, int winy, - float viewmat[4][4], float winmat[4][4], - bool do_bgpic, bool do_sky) +void ED_view3d_draw_offscreen( + Scene *scene, View3D *v3d, ARegion *ar, int winx, int winy, + float viewmat[4][4], float winmat[4][4], + bool do_bgpic, bool do_sky, bool is_persp, + GPUOffScreen *ofs, + GPUFX *fx, GPUFXSettings *fx_settings) { struct bThemeState theme_state; int bwinx, bwiny; rcti brect; + bool do_compositing = false; + RegionView3D *rv3d = ar->regiondata; glPushMatrix(); @@ -3082,9 +3091,15 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, * warning! can be slow so only free animated images - campbell */ GPU_free_images_anim(); } - /* setup view matrices */ + + /* setup view matrices before fx or unbinding the offscreen buffers will cause issues */ view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); - + + /* framebuffer fx needed, we need to draw offscreen first */ + if (v3d->fx_settings.fx_flag && fx) { + do_compositing = GPU_fx_compositor_initialize_passes(fx, &ar->winrct, NULL, fx_settings); + } + /* clear opengl buffers */ if (do_sky) { view3d_main_area_clear(scene, v3d, ar, true); @@ -3097,6 +3112,13 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, /* main drawing call */ view3d_draw_objects(NULL, scene, v3d, ar, NULL, do_bgpic, true); + /* post process */ + if (do_compositing) { + if (!winmat) + is_persp = rv3d->is_persp; + GPU_fx_do_composite_pass(fx, winmat, is_persp, scene, ofs); + } + if ((v3d->flag2 & V3D_RENDER_SHADOW) == 0) { /* draw grease-pencil stuff */ ED_region_pixelspace(ar); @@ -3131,7 +3153,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in ImBuf *ibuf; GPUOffScreen *ofs; bool draw_sky = (alpha_mode == R_ADDSKY); - + /* state changes make normal drawing go weird otherwise */ glPushAttrib(GL_LIGHTING_BIT); @@ -3144,11 +3166,13 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in ED_view3d_draw_offscreen_init(scene, v3d); - GPU_offscreen_bind(ofs); + GPU_offscreen_bind(ofs, true); /* render 3d view */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { CameraParams params; + GPUFXSettings fx_settings = {0}; + Object *camera = v3d->camera; BKE_camera_params_init(¶ms); /* fallback for non camera objects */ @@ -3158,10 +3182,18 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in BKE_camera_params_compute_viewplane(¶ms, sizex, sizey, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background, draw_sky); + BKE_camera_to_gpu_dof(camera, &fx_settings); + + ED_view3d_draw_offscreen( + scene, v3d, ar, sizex, sizey, NULL, params.winmat, + draw_background, draw_sky, !params.is_ortho, + ofs, NULL, &fx_settings); } else { - ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background, draw_sky); + ED_view3d_draw_offscreen( + scene, v3d, ar, sizex, sizey, NULL, NULL, + draw_background, draw_sky, true, + ofs, NULL, NULL); } /* read in pixels & stamp */ @@ -3173,7 +3205,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in GPU_offscreen_read_pixels(ofs, GL_UNSIGNED_BYTE, ibuf->rect); /* unbind */ - GPU_offscreen_unbind(ofs); + GPU_offscreen_unbind(ofs, true); GPU_offscreen_free(ofs); glPopAttrib(); @@ -3451,13 +3483,15 @@ static void update_lods(Scene *scene, float camera_pos[3]) } #endif - static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3D *v3d, ARegion *ar, const char **grid_unit) { RegionView3D *rv3d = ar->regiondata; unsigned int lay_used = v3d->lay_used; - + + /* post processing */ + bool do_compositing = false; + /* shadow buffers, before we setup matrices */ if (draw_glsl_material(scene, NULL, v3d, v3d->drawtype)) gpu_update_lamps_shadows(scene, v3d); @@ -3481,6 +3515,22 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 } #endif + /* framebuffer fx needed, we need to draw offscreen first */ + if (v3d->fx_settings.fx_flag) { + GPUFXSettings fx_settings; + BKE_screen_gpu_fx_validate(&v3d->fx_settings); + fx_settings = v3d->fx_settings; + if (!rv3d->compositor) + rv3d->compositor = GPU_fx_compositor_create(); + + if (rv3d->persp == RV3D_CAMOB && v3d->camera) + BKE_camera_to_gpu_dof(v3d->camera, &fx_settings); + else { + fx_settings.dof = NULL; + } + do_compositing = GPU_fx_compositor_initialize_passes(rv3d->compositor, &ar->winrct, &ar->drawrct, &fx_settings); + } + /* clear the background */ view3d_main_area_clear(scene, v3d, ar, false); @@ -3492,6 +3542,11 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 /* main drawing call */ view3d_draw_objects(C, scene, v3d, ar, grid_unit, true, false); + /* post process */ + if (do_compositing) { + GPU_fx_do_composite_pass(rv3d->compositor, rv3d->winmat, rv3d->is_persp, scene, NULL); + } + /* Disable back anti-aliasing */ if (U.ogl_multisamples != USER_MULTISAMPLE_NONE) { glDisable(GL_MULTISAMPLE_ARB); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 361c247767f..7e32105a916 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -54,6 +54,21 @@ set(SRC intern/gpu_material.c intern/gpu_simple_shader.c intern/gpu_select.c + intern/gpu_compositing.c + + shaders/gpu_shader_fx_lib.glsl + shaders/gpu_shader_fx_ssao_frag.glsl + shaders/gpu_shader_fx_dof_frag.glsl + shaders/gpu_shader_fx_dof_vert.glsl + shaders/gpu_shader_fx_vert.glsl + shaders/gpu_shader_material.glsl + shaders/gpu_shader_sep_gaussian_blur_frag.glsl + shaders/gpu_shader_sep_gaussian_blur_vert.glsl + shaders/gpu_shader_simple_frag.glsl + shaders/gpu_shader_simple_vert.glsl + shaders/gpu_shader_vertex.glsl + shaders/gpu_shader_vsm_store_frag.glsl + shaders/gpu_shader_vsm_store_vert.glsl GPU_buffers.h GPU_draw.h @@ -63,6 +78,7 @@ set(SRC GPU_material.h GPU_simple_shader.h GPU_select.h + GPU_compositing.h intern/gpu_codegen.h intern/gpu_extensions_private.h ) @@ -76,6 +92,11 @@ data_to_c_simple(shaders/gpu_shader_vertex.glsl SRC) data_to_c_simple(shaders/gpu_shader_vertex_world.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_ssao_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_dof_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_dof_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fx_lib.glsl SRC) if(WITH_GAMEENGINE) add_definitions(-DWITH_GAMEENGINE) diff --git a/source/blender/gpu/GPU_compositing.h b/source/blender/gpu/GPU_compositing.h new file mode 100644 index 00000000000..796341d4830 --- /dev/null +++ b/source/blender/gpu/GPU_compositing.h @@ -0,0 +1,88 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Antony Riakiotakis. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file GPU_compositing.h + * \ingroup gpu + */ + +#ifndef __GPU_COMPOSITING_H__ +#define __GPU_COMPOSITING_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* opaque handle for framebuffer compositing effects (defined in gpu_compositing.c )*/ +typedef struct GPUFX GPUFX; +struct GPUDOFSettings; +struct GPUSSAOSettings; +struct GPUOffScreen; +struct GPUFXSettings; +struct RegionView3D; +struct rcti; +struct Scene; +struct View3D; +enum eGPUFXFlags; + +/**** Public API *****/ + +typedef enum GPUFXShaderEffect { + /* Screen space ambient occlusion shader */ + GPU_SHADER_FX_SSAO = 1, + + /* depth of field passes. Yep, quite a complex effect */ + GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE = 2, + GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO = 3, + GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE = 4, + GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR = 5, + GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE = 6, +} GPUFXShaderEffect; + +/* keep in synch with enum above! */ +#define MAX_FX_SHADERS 11 + +/* generate a new FX compositor */ +GPUFX *GPU_fx_compositor_create(void); + +/* destroy a text compositor */ +void GPU_fx_compositor_destroy(GPUFX *fx); + +/* initialize a framebuffer with size taken from the viewport */ +bool GPU_fx_compositor_initialize_passes( + GPUFX *fx, const struct rcti *rect, const struct rcti *scissor_rect, + const struct GPUFXSettings *fx_settings); + +/* do compositing on the fx passes that have been initialized */ +bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, struct Scene *scene, struct GPUOffScreen *ofs); + +void GPU_fx_compositor_init_dof_settings(struct GPUDOFSettings *dof); +void GPU_fx_compositor_init_ssao_settings(struct GPUSSAOSettings *ssao); +#ifdef __cplusplus +} +#endif + +#endif // __GPU_COMPOSITING_H__ diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index bb0cf2dd2b9..739d45663f8 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -107,11 +107,19 @@ int GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver); * - if created with from_blender, will not free the texture */ +typedef enum GPUHDRType { + GPU_HDR_NONE = 0, + GPU_HDR_HALF_FLOAT = 1, + GPU_HDR_FULL_FLOAT = (1 << 1), +} GPUHDRType; + GPUTexture *GPU_texture_create_1D(int w, float *pixels, char err_out[256]); -GPUTexture *GPU_texture_create_2D(int w, int h, float *pixels, char err_out[256]); +GPUTexture *GPU_texture_create_2D(int w, int h, float *pixels, GPUHDRType hdr, char err_out[256]); GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, float *fpixels); GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]); GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]); +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, float *pixels, char err_out[256]); +GPUTexture *GPU_texture_create_1D_procedural(int w, float *pixels, char err_out[256]); GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, bool is_data, double time, int mipmap); GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); @@ -126,6 +134,8 @@ void GPU_texture_ref(GPUTexture *tex); void GPU_texture_bind(GPUTexture *tex, int number); void GPU_texture_unbind(GPUTexture *tex); +void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter); + GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex); int GPU_texture_target(GPUTexture *tex); @@ -145,11 +155,13 @@ void GPU_texture_bind_as_framebuffer(GPUTexture *tex); GPUFrameBuffer *GPU_framebuffer_create(void); int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]); void GPU_framebuffer_texture_detach(GPUTexture *tex); -void GPU_framebuffer_slot_bind(GPUFrameBuffer *fb, int slot); +void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot); void GPU_framebuffer_texture_unbind(GPUFrameBuffer *fb, GPUTexture *tex); void GPU_framebuffer_free(GPUFrameBuffer *fb); bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); +void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot); + void GPU_framebuffer_restore(void); void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex); @@ -159,8 +171,8 @@ void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *b GPUOffScreen *GPU_offscreen_create(int width, int height, char err_out[256]); void GPU_offscreen_free(GPUOffScreen *ofs); -void GPU_offscreen_bind(GPUOffScreen *ofs); -void GPU_offscreen_unbind(GPUOffScreen *ofs); +void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); +void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); int GPU_offscreen_width(GPUOffScreen *ofs); int GPU_offscreen_height(GPUOffScreen *ofs); @@ -177,7 +189,7 @@ void GPU_shader_unbind(void); int GPU_shader_get_uniform(GPUShader *shader, const char *name); void GPU_shader_uniform_vector(GPUShader *shader, int location, int length, - int arraysize, float *value); + int arraysize, const float *value); void GPU_shader_uniform_texture(GPUShader *shader, int location, GPUTexture *tex); void GPU_shader_uniform_int(GPUShader *shader, int location, int value); @@ -190,6 +202,8 @@ typedef enum GPUBuiltinShader { } GPUBuiltinShader; GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader); +GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp); + void GPU_shader_free_builtin_shaders(void); /* Vertex attributes for shaders */ diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript index e44a99286a8..158cfb67dda 100644 --- a/source/blender/gpu/SConscript +++ b/source/blender/gpu/SConscript @@ -65,6 +65,11 @@ import os sources.extend(( os.path.join(env['DATA_SOURCES'], "gpu_shader_simple_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_simple_vert.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_ssao_frag.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_frag.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_dof_vert.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_lib.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fx_vert.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_material.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_sep_gaussian_blur_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_sep_gaussian_blur_vert.glsl.c"), diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 97064f43f5d..9e4d0d5f823 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -972,7 +972,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType input->textype = type; //input->tex = GPU_texture_create_2D(link->texturesize, link->texturesize, link->ptr2, NULL); - input->tex = GPU_texture_create_2D(link->texturesize, 1, link->ptr1, NULL); + input->tex = GPU_texture_create_2D(link->texturesize, 1, link->ptr1, GPU_HDR_NONE, NULL); input->textarget = GL_TEXTURE_2D; MEM_freeN(link->ptr1); diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c new file mode 100644 index 00000000000..b423c14bd4b --- /dev/null +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -0,0 +1,863 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Antony Riakiotakis. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/gpu/intern/gpu_compositing.c + * \ingroup gpu + * + * System that manages framebuffer compositing. + */ + +#include "BLI_sys_types.h" +#include "BLI_rect.h" +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_linklist.h" + +#include "BLI_rand.h" +#include "BLI_listbase.h" + +#include "DNA_vec_types.h" +#include "DNA_view3d_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_camera_types.h" +#include "DNA_gpu_types.h" + +#include "GPU_extensions.h" +#include "GPU_compositing.h" + +#include "GL/glew.h" + +#include "MEM_guardedalloc.h" + +static const float fullscreencos[4][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}}; +static const float fullscreenuvs[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}}; + +struct GPUFX { + /* we borrow the term gbuffer from deferred rendering however this is just a regular + * depth/color framebuffer. Could be extended later though */ + GPUFrameBuffer *gbuffer; + + /* texture bound to the first color attachment of the gbuffer */ + GPUTexture *color_buffer; + + /* second texture used for ping-pong compositing */ + GPUTexture *color_buffer_sec; + + /* all those buffers below have to coexist. Fortunately they are all quarter sized (1/16th of memory) of original framebuffer */ + int dof_downsampled_w; + int dof_downsampled_h; + + /* texture used for near coc and color blurring calculation */ + GPUTexture *dof_near_coc_buffer; + /* blurred near coc buffer. */ + GPUTexture *dof_near_coc_blurred_buffer; + /* final near coc buffer. */ + GPUTexture *dof_near_coc_final_buffer; + + /* half size blur buffer */ + GPUTexture *dof_half_downsampled; + /* high quality dof texture downsamplers. 6 levels means 64 pixels wide */ + GPUTexture *dof_nearfar_coc[6]; + GPUTexture *dof_near_blur; + GPUTexture *dof_far_blur; + GPUTexture *dof_concentric_samples_tex; + + /* texture bound to the depth attachment of the gbuffer */ + GPUTexture *depth_buffer; + + /* texture used for jittering for various effects */ + GPUTexture *jitter_buffer; + + /* texture used for ssao */ + int ssao_sample_count; + GPUTexture *ssao_concentric_samples_tex; + + /* dimensions of the gbuffer */ + int gbuffer_dim[2]; + + GPUFXSettings settings; + + /* or-ed flags of enabled effects */ + int effects; + + /* number of passes, needed to detect if ping pong buffer allocation is needed */ + int num_passes; + + /* we have a stencil, restore the previous state */ + bool restore_stencil; +}; + +/* concentric mapping, see "A Low Distortion Map Between Disk and Square" and + * http://psgraphics.blogspot.nl/2011/01/improved-code-for-concentric-map.html */ +static GPUTexture * create_concentric_sample_texture(int side) +{ + GPUTexture *tex; + float midpoint = 0.5f * (side - 1); + float *texels = (float *)MEM_mallocN(sizeof(float) * 2 * side * side, "concentric_tex"); + int i, j; + + for (i = 0; i < side; i++) { + for (j = 0; j < side; j++) { + int index = (i * side + j) * 2; + float a = 1.0f - i / midpoint; + float b = 1.0f - j / midpoint; + float phi, r; + if (a * a > b * b) { + r = a; + phi = (M_PI_4) * (b / a); + } + else { + r = b; + phi = M_PI_2 - (M_PI_4) * (a / b); + } + texels[index] = r * cos(phi); + texels[index + 1] = r * sin(phi); + } + } + + tex = GPU_texture_create_1D_procedural(side * side, texels, NULL); + MEM_freeN(texels); + return tex; +} + +/* generate a new FX compositor */ +GPUFX *GPU_fx_compositor_create(void) +{ + GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor"); + + return fx; +} + +static void cleanup_fx_dof_buffers(GPUFX *fx) +{ + if (fx->dof_near_coc_blurred_buffer) { + GPU_texture_free(fx->dof_near_coc_blurred_buffer); + fx->dof_near_coc_blurred_buffer = NULL; + } + if (fx->dof_near_coc_buffer) { + GPU_texture_free(fx->dof_near_coc_buffer); + fx->dof_near_coc_buffer = NULL; + } + if (fx->dof_near_coc_final_buffer) { + GPU_texture_free(fx->dof_near_coc_final_buffer); + fx->dof_near_coc_final_buffer = NULL; + } + + if (fx->dof_half_downsampled) { + GPU_texture_free(fx->dof_half_downsampled); + fx->dof_half_downsampled = NULL; + } + if (fx->dof_nearfar_coc[0]) { + int i; + for (i = 0; i < 6; i++) { + GPU_texture_free(fx->dof_nearfar_coc[i]); + fx->dof_nearfar_coc[i] = NULL; + } + } + if (fx->dof_near_blur) { + GPU_texture_free(fx->dof_near_blur); + fx->dof_near_blur = NULL; + } + if (fx->dof_far_blur) { + GPU_texture_free(fx->dof_far_blur); + fx->dof_far_blur = NULL; + } + if (fx->dof_concentric_samples_tex) { + GPU_texture_free(fx->dof_concentric_samples_tex); + fx->dof_concentric_samples_tex = NULL; + } +} + +static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo) +{ + if (fx->color_buffer) { + GPU_framebuffer_texture_detach(fx->color_buffer); + GPU_texture_free(fx->color_buffer); + fx->color_buffer = NULL; + } + + if (fx->color_buffer_sec) { + GPU_framebuffer_texture_detach(fx->color_buffer_sec); + GPU_texture_free(fx->color_buffer_sec); + fx->color_buffer_sec = NULL; + } + + if (fx->depth_buffer) { + GPU_framebuffer_texture_detach(fx->depth_buffer); + GPU_texture_free(fx->depth_buffer); + fx->depth_buffer = NULL; + } + + cleanup_fx_dof_buffers(fx); + + if (fx->ssao_concentric_samples_tex) { + GPU_texture_free(fx->ssao_concentric_samples_tex); + fx->ssao_concentric_samples_tex = NULL; + } + + if (fx->jitter_buffer && do_fbo) { + GPU_texture_free(fx->jitter_buffer); + fx->jitter_buffer = NULL; + } + + if (fx->gbuffer && do_fbo) { + GPU_framebuffer_free(fx->gbuffer); + fx->gbuffer = NULL; + } +} + +/* destroy a text compositor */ +void GPU_fx_compositor_destroy(GPUFX *fx) +{ + cleanup_fx_gl_data(fx, true); + MEM_freeN(fx); +} + +static GPUTexture * create_jitter_texture(void) +{ + float jitter [64 * 64][2]; + int i; + + for (i = 0; i < 64 * 64; i++) { + jitter[i][0] = 2.0f * BLI_frand() - 1.0; + jitter[i][1] = 2.0f * BLI_frand() - 1.0; + normalize_v2(jitter[i]); + } + + return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], NULL); +} + + +bool GPU_fx_compositor_initialize_passes( + GPUFX *fx, const rcti *rect, const rcti *scissor_rect, + const GPUFXSettings *fx_settings) +{ + int w = BLI_rcti_size_x(rect) + 1, h = BLI_rcti_size_y(rect) + 1; + char err_out[256]; + int num_passes = 0; + char fx_flag = fx_settings->fx_flag; + + fx->effects = 0; + + if (!fx_settings) { + cleanup_fx_gl_data(fx, true); + return false; + } + + /* disable effects if no options passed for them */ + if (!fx_settings->dof) { + fx_flag &= ~GPU_FX_FLAG_DOF; + } + if (!fx_settings->ssao || fx_settings->ssao->samples < 1) { + fx_flag &= ~GPU_FX_FLAG_SSAO; + } + + if (!fx_flag) { + cleanup_fx_gl_data(fx, true); + return false; + } + + fx->num_passes = 0; + /* dof really needs a ping-pong buffer to work */ + if (fx_flag & GPU_FX_FLAG_DOF) + num_passes++; + + if (fx_flag & GPU_FX_FLAG_SSAO) + num_passes++; + + if (!fx->gbuffer) + fx->gbuffer = GPU_framebuffer_create(); + + /* try creating the jitter texture */ + if (!fx->jitter_buffer) + fx->jitter_buffer = create_jitter_texture(); + + if (!fx->gbuffer) + return false; + + /* check if color buffers need recreation */ + if (!fx->color_buffer || !fx->depth_buffer || w != fx->gbuffer_dim[0] || h != fx->gbuffer_dim[1]) { + cleanup_fx_gl_data(fx, false); + + if (!(fx->color_buffer = GPU_texture_create_2D(w, h, NULL, GPU_HDR_NONE, err_out))) { + printf(".256%s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + + if (!(fx->depth_buffer = GPU_texture_create_depth(w, h, err_out))) { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + } + + if (fx_flag & GPU_FX_FLAG_SSAO) { + if (fx_settings->ssao->samples != fx->ssao_sample_count || !fx->ssao_concentric_samples_tex) { + if (fx_settings->ssao->samples < 1) + fx_settings->ssao->samples = 1; + + fx->ssao_sample_count = fx_settings->ssao->samples; + + if (fx->ssao_concentric_samples_tex) { + GPU_texture_free(fx->ssao_concentric_samples_tex); + } + + fx->ssao_concentric_samples_tex = create_concentric_sample_texture(fx_settings->ssao->samples); + } + } + else { + if (fx->ssao_concentric_samples_tex) { + GPU_texture_free(fx->ssao_concentric_samples_tex); + fx->ssao_concentric_samples_tex = NULL; + } + } + + /* create textures for dof effect */ + if (fx_flag & GPU_FX_FLAG_DOF) { + if (!fx->dof_near_coc_buffer || !fx->dof_near_coc_blurred_buffer || !fx->dof_near_coc_final_buffer) { + fx->dof_downsampled_w = w / 4; + fx->dof_downsampled_h = h / 4; + + if (!(fx->dof_near_coc_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_near_coc_blurred_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + if (!(fx->dof_near_coc_final_buffer = GPU_texture_create_2D( + fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) + { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + } + } + else { + /* cleanup unnecessary buffers */ + cleanup_fx_dof_buffers(fx); + } + + /* we need to pass data between shader stages, allocate an extra color buffer */ + if (num_passes > 1) { + if(!fx->color_buffer_sec) { + if (!(fx->color_buffer_sec = GPU_texture_create_2D(w, h, NULL, GPU_HDR_NONE, err_out))) { + printf(".256%s\n", err_out); + cleanup_fx_gl_data(fx, true); + return false; + } + } + } + else { + if (fx->color_buffer_sec) { + GPU_framebuffer_texture_detach(fx->color_buffer_sec); + GPU_texture_free(fx->color_buffer_sec); + fx->color_buffer_sec = NULL; + } + } + + /* bind the buffers */ + + /* first depth buffer, because system assumes read/write buffers */ + if(!GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, err_out)) + printf("%.256s\n", err_out); + + if(!GPU_framebuffer_texture_attach(fx->gbuffer, fx->color_buffer, 0, err_out)) + printf("%.256s\n", err_out); + + if(!GPU_framebuffer_check_valid(fx->gbuffer, err_out)) + printf("%.256s\n", err_out); + + GPU_texture_bind_as_framebuffer(fx->color_buffer); + + /* enable scissor test. It's needed to ensure sculpting works correctly */ + if (scissor_rect) { + int w_sc = BLI_rcti_size_x(scissor_rect) + 1; + int h_sc = BLI_rcti_size_y(scissor_rect) + 1; + glPushAttrib(GL_SCISSOR_BIT); + glEnable(GL_SCISSOR_TEST); + glScissor(scissor_rect->xmin - rect->xmin, scissor_rect->ymin - rect->ymin, + w_sc, h_sc); + fx->restore_stencil = true; + } + else { + fx->restore_stencil = false; + } + + fx->effects = fx_flag; + + if (fx_settings) + fx->settings = *fx_settings; + fx->gbuffer_dim[0] = w; + fx->gbuffer_dim[1] = h; + + fx->num_passes = num_passes; + + return true; +} + +static void gpu_fx_bind_render_target(int *passes_left, GPUFX *fx, struct GPUOffScreen *ofs, GPUTexture *target) +{ + if ((*passes_left)-- == 1) { + GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); + if (ofs) { + GPU_offscreen_bind(ofs, false); + } + else + GPU_framebuffer_restore(); + } + else { + /* bind the ping buffer to the color buffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, target, 0, NULL); + } +} + +bool GPU_fx_do_composite_pass(GPUFX *fx, float projmat[4][4], bool is_persp, struct Scene *scene, struct GPUOffScreen *ofs) +{ + GPUTexture *src, *target; + int numslots = 0; + float invproj[4][4]; + int i; + /* number of passes left. when there are no more passes, the result is passed to the frambuffer */ + int passes_left = fx->num_passes; + /* view vectors for the corners of the view frustum. Can be used to recreate the world space position easily */ + float viewvecs[3][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} + }; + + if (fx->effects == 0) + return false; + + /* first, unbind the render-to-texture framebuffer */ + GPU_framebuffer_texture_detach(fx->color_buffer); + GPU_framebuffer_texture_detach(fx->depth_buffer); + + if (fx->restore_stencil) + glPopAttrib(); + + src = fx->color_buffer; + target = fx->color_buffer_sec; + + /* set up quad buffer */ + glVertexPointer(2, GL_FLOAT, 0, fullscreencos); + glTexCoordPointer(2, GL_FLOAT, 0, fullscreenuvs); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + /* full screen FX pass */ + + /* invert the view matrix */ + invert_m4_m4(invproj, projmat); + + /* convert the view vectors to view space */ + for (i = 0; i < 3; i++) { + mul_m4_v4(invproj, viewvecs[i]); + /* normalized trick see http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ + mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]); + if (is_persp) + mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]); + viewvecs[i][3] = 1.0; + } + + /* we need to store the differences */ + viewvecs[1][0] -= viewvecs[0][0]; + viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1]; + + /* calculate a depth offset as well */ + if (!is_persp) { + float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; + mul_m4_v4(invproj, vec_far); + mul_v3_fl(vec_far, 1.0f / vec_far[3]); + viewvecs[1][2] = vec_far[2] - viewvecs[0][2]; + } + + /* set invalid color in case shader fails */ + glColor3f(1.0, 0.0, 1.0); + glDisable(GL_DEPTH_TEST); + + /* ssao pass */ + if (fx->effects & GPU_FX_FLAG_SSAO) { + GPUShader *ssao_shader; + ssao_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_SSAO, is_persp); + if (ssao_shader) { + const GPUSSAOSettings *fx_ssao = fx->settings.ssao; + int color_uniform, depth_uniform; + int ssao_uniform, ssao_color_uniform, viewvecs_uniform, ssao_sample_params_uniform; + int ssao_jitter_uniform, ssao_concentric_tex; + float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, fx_ssao->attenuation, 0.0f}; + float sample_params[4]; + + sample_params[0] = fx->ssao_sample_count * fx->ssao_sample_count; + /* multiplier so we tile the random texture on screen */ + sample_params[2] = fx->gbuffer_dim[0] / 64.0; + sample_params[3] = fx->gbuffer_dim[1] / 64.0; + + ssao_uniform = GPU_shader_get_uniform(ssao_shader, "ssao_params"); + ssao_color_uniform = GPU_shader_get_uniform(ssao_shader, "ssao_color"); + color_uniform = GPU_shader_get_uniform(ssao_shader, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(ssao_shader, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(ssao_shader, "viewvecs"); + ssao_sample_params_uniform = GPU_shader_get_uniform(ssao_shader, "ssao_sample_params"); + ssao_concentric_tex = GPU_shader_get_uniform(ssao_shader, "ssao_concentric_tex"); + ssao_jitter_uniform = GPU_shader_get_uniform(ssao_shader, "jitter_tex"); + + GPU_shader_bind(ssao_shader); + + GPU_shader_uniform_vector(ssao_shader, ssao_uniform, 4, 1, ssao_params); + GPU_shader_uniform_vector(ssao_shader, ssao_color_uniform, 4, 1, fx_ssao->color); + GPU_shader_uniform_vector(ssao_shader, viewvecs_uniform, 4, 3, viewvecs[0]); + GPU_shader_uniform_vector(ssao_shader, ssao_sample_params_uniform, 4, 1, sample_params); + + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(ssao_shader, color_uniform, src); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_depth_texture_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(ssao_shader, depth_uniform, fx->depth_buffer); + + GPU_texture_bind(fx->jitter_buffer, numslots++); + GPU_shader_uniform_texture(ssao_shader, ssao_jitter_uniform, fx->jitter_buffer); + + GPU_texture_bind(fx->ssao_concentric_samples_tex, numslots++); + GPU_shader_uniform_texture(ssao_shader, ssao_concentric_tex, fx->ssao_concentric_samples_tex); + + /* draw */ + gpu_fx_bind_render_target(&passes_left, fx, ofs, target); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* disable bindings */ + GPU_texture_unbind(src); + GPU_depth_texture_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + GPU_texture_unbind(fx->jitter_buffer); + GPU_texture_unbind(fx->ssao_concentric_samples_tex); + + /* may not be attached, in that case this just returns */ + if (target) { + GPU_framebuffer_texture_detach(target); + if (ofs) { + GPU_offscreen_bind(ofs, false); + } + else { + GPU_framebuffer_restore(); + } + } + + /* swap here, after src/target have been unbound */ + SWAP(GPUTexture *, target, src); + numslots = 0; + } + } + + /* second pass, dof */ + if (fx->effects & GPU_FX_FLAG_DOF) { + const GPUDOFSettings *fx_dof = fx->settings.dof; + GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3, *dof_shader_pass4, *dof_shader_pass5; + float dof_params[4]; + float scale = scene->unit.system ? scene->unit.scale_length : 1.0f; + float scale_camera = 0.001f / scale; + float aperture = 2.0f * scale_camera * fx_dof->focal_length / fx_dof->fstop; + + dof_params[0] = aperture * fabs(scale_camera * fx_dof->focal_length / (fx_dof->focus_distance - scale_camera * fx_dof->focal_length)); + dof_params[1] = fx_dof->focus_distance; + dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); + dof_params[3] = 0.0f; + + /* DOF effect has many passes but most of them are performed on a texture whose dimensions are 4 times less than the original + * (16 times lower than original screen resolution). Technique used is not very exact but should be fast enough and is based + * on "Practical Post-Process Depth of Field" see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html */ + dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE, is_persp); + dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO, is_persp); + dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE, is_persp); + dof_shader_pass4 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR, is_persp); + dof_shader_pass5 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE, is_persp); + + /* error occured, restore framebuffers and return */ + if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3 && dof_shader_pass4 && dof_shader_pass5)) { + GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); + GPU_framebuffer_restore(); + return false; + } + + /* pass first, first level of blur in low res buffer */ + { + int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; + int viewvecs_uniform; + + float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; + + dof_uniform = GPU_shader_get_uniform(dof_shader_pass1, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass1, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass1, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass1, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass1, "viewvecs"); + + GPU_shader_bind(dof_shader_pass1); + + GPU_shader_uniform_vector(dof_shader_pass1, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass1, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass1, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(dof_shader_pass1, color_uniform, src); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_depth_texture_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass1, depth_uniform, fx->depth_buffer); + + /* target is the downsampled coc buffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); + /* binding takes care of setting the viewport to the downsampled size */ + GPU_texture_bind_as_framebuffer(fx->dof_near_coc_buffer); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(src); + GPU_depth_texture_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); + numslots = 0; + } + + /* second pass, gaussian blur the downsampled image */ + { + int invrendertargetdim_uniform, color_uniform, depth_uniform, dof_uniform; + int viewvecs_uniform; + float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), + 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; + float tmp = invrendertargetdim[0]; + invrendertargetdim[0] = 0.0f; + + dof_params[2] = GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer) / (scale_camera * fx_dof->sensor); + + dof_uniform = GPU_shader_get_uniform(dof_shader_pass2, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass2, "invrendertargetdim"); + color_uniform = GPU_shader_get_uniform(dof_shader_pass2, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass2, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass2, "viewvecs"); + + /* Blurring vertically */ + GPU_shader_bind(dof_shader_pass2); + + GPU_shader_uniform_vector(dof_shader_pass2, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass2, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_depth_texture_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass2, depth_uniform, fx->depth_buffer); + + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_buffer); + + /* use final buffer as a temp here */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); + + /* Drawing quad */ + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* *unbind/detach */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); + + /* Blurring horizontally */ + invrendertargetdim[0] = tmp; + invrendertargetdim[1] = 0.0f; + GPU_shader_uniform_vector(dof_shader_pass2, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + + GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass2, color_uniform, fx->dof_near_coc_final_buffer); + + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_blurred_buffer, 0, NULL); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* *unbind/detach */ + GPU_depth_texture_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + GPU_texture_unbind(fx->dof_near_coc_final_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_blurred_buffer); + + dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); + + numslots = 0; + } + + /* third pass, calculate near coc */ + { + int near_coc_downsampled, near_coc_blurred; + + near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass3, "colorbuffer"); + near_coc_blurred = GPU_shader_get_uniform(dof_shader_pass3, "blurredcolorbuffer"); + + GPU_shader_bind(dof_shader_pass3); + + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, near_coc_downsampled, fx->dof_near_coc_buffer); + + GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass3, near_coc_blurred, fx->dof_near_coc_blurred_buffer); + + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); + + /* unbinding here restores the size to the original */ + GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); + + numslots = 0; + } + + /* fourth pass blur final coc once to eliminate discontinuities */ + { + int near_coc_downsampled; + int invrendertargetdim_uniform; + float invrendertargetdim[2] = {1.0f / GPU_texture_opengl_width(fx->dof_near_coc_blurred_buffer), + 1.0f / GPU_texture_opengl_height(fx->dof_near_coc_blurred_buffer)}; + + near_coc_downsampled = GPU_shader_get_uniform(dof_shader_pass4, "colorbuffer"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass4, "invrendertargetdim"); + + GPU_shader_bind(dof_shader_pass4); + + GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass4, near_coc_downsampled, fx->dof_near_coc_final_buffer); + GPU_shader_uniform_vector(dof_shader_pass4, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + + GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_final_buffer); + + /* unbinding here restores the size to the original */ + GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_near_coc_buffer); + GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); + + numslots = 0; + } + + /* final pass, merge blurred layers according to final calculated coc */ + { + int medium_blurred_uniform, high_blurred_uniform, original_uniform, depth_uniform, dof_uniform; + int invrendertargetdim_uniform, viewvecs_uniform; + float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; + + medium_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "mblurredcolorbuffer"); + high_blurred_uniform = GPU_shader_get_uniform(dof_shader_pass5, "blurredcolorbuffer"); + dof_uniform = GPU_shader_get_uniform(dof_shader_pass5, "dof_params"); + invrendertargetdim_uniform = GPU_shader_get_uniform(dof_shader_pass5, "invrendertargetdim"); + original_uniform = GPU_shader_get_uniform(dof_shader_pass5, "colorbuffer"); + depth_uniform = GPU_shader_get_uniform(dof_shader_pass5, "depthbuffer"); + viewvecs_uniform = GPU_shader_get_uniform(dof_shader_pass5, "viewvecs"); + + GPU_shader_bind(dof_shader_pass5); + + GPU_shader_uniform_vector(dof_shader_pass5, dof_uniform, 4, 1, dof_params); + GPU_shader_uniform_vector(dof_shader_pass5, invrendertargetdim_uniform, 2, 1, invrendertargetdim); + GPU_shader_uniform_vector(dof_shader_pass5, viewvecs_uniform, 4, 3, viewvecs[0]); + + GPU_texture_bind(src, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, original_uniform, src); + + GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, high_blurred_uniform, fx->dof_near_coc_blurred_buffer); + + GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); + GPU_shader_uniform_texture(dof_shader_pass5, medium_blurred_uniform, fx->dof_near_coc_buffer); + + GPU_texture_bind(fx->depth_buffer, numslots++); + GPU_depth_texture_mode(fx->depth_buffer, false, true); + GPU_shader_uniform_texture(dof_shader_pass5, depth_uniform, fx->depth_buffer); + + /* if this is the last pass, prepare for rendering on the frambuffer */ + gpu_fx_bind_render_target(&passes_left, fx, ofs, target); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* disable bindings */ + GPU_texture_unbind(fx->dof_near_coc_buffer); + GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); + GPU_texture_unbind(src); + GPU_depth_texture_mode(fx->depth_buffer, true, false); + GPU_texture_unbind(fx->depth_buffer); + + /* may not be attached, in that case this just returns */ + if (target) { + GPU_framebuffer_texture_detach(target); + if (ofs) { + GPU_offscreen_bind(ofs, false); + } + else { + GPU_framebuffer_restore(); + } + } + + SWAP(GPUTexture *, target, src); + numslots = 0; + } + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + GPU_shader_unbind(); + + return true; +} + +void GPU_fx_compositor_init_dof_settings(GPUDOFSettings *fx_dof) +{ + fx_dof->fstop = 128.0f; + fx_dof->focal_length = 1.0f; + fx_dof->focus_distance = 1.0f; + fx_dof->sensor = 1.0f; +} + +void GPU_fx_compositor_init_ssao_settings(GPUSSAOSettings *fx_ssao) +{ + fx_ssao->factor = 1.0f; + fx_ssao->distance_max = 0.2f; + fx_ssao->attenuation = 1.0f; + fx_ssao->samples = 4; +} diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index a6b711fd556..4d1a9770c78 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -47,6 +47,7 @@ #include "GPU_draw.h" #include "GPU_extensions.h" +#include "GPU_compositing.h" #include "GPU_simple_shader.h" #include "intern/gpu_extensions_private.h" @@ -78,10 +79,17 @@ extern char datatoc_gpu_shader_vsm_store_vert_glsl[]; extern char datatoc_gpu_shader_vsm_store_frag_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_vert_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_frag_glsl[]; +extern char datatoc_gpu_shader_fx_vert_glsl[]; +extern char datatoc_gpu_shader_fx_ssao_frag_glsl[]; +extern char datatoc_gpu_shader_fx_dof_frag_glsl[]; +extern char datatoc_gpu_shader_fx_dof_vert_glsl[]; +extern char datatoc_gpu_shader_fx_lib_glsl[]; typedef struct GPUShaders { GPUShader *vsm_store; GPUShader *sep_gaussian_blur; + /* cache for shader fx. Those can exist in combinations so store them here */ + GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; } GPUShaders; static struct GPUGlobal { @@ -364,7 +372,9 @@ static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, i MEM_freeN(pixels); } -static GPUTexture *GPU_texture_create_nD(int w, int h, int n, float *fpixels, int depth, char err_out[256]) +static GPUTexture *GPU_texture_create_nD( + int w, int h, int n, float *fpixels, int depth, GPUHDRType hdr_type, int components, + char err_out[256]) { GPUTexture *tex; GLenum type, format, internalformat; @@ -411,12 +421,45 @@ static GPUTexture *GPU_texture_create_nD(int w, int h, int n, float *fpixels, in internalformat = GL_DEPTH_COMPONENT; } else { - type = GL_UNSIGNED_BYTE; - format = GL_RGBA; - internalformat = GL_RGBA8; + type = GL_FLOAT; + + if (components == 4) { + format = GL_RGBA; + switch (hdr_type) { + case GPU_HDR_NONE: + internalformat = GL_RGBA8; + break; + case GPU_HDR_HALF_FLOAT: + internalformat = GL_RGBA16F; + break; + case GPU_HDR_FULL_FLOAT: + internalformat = GL_RGBA32F; + break; + default: + break; + } + } + else if (components == 2) { + format = GL_RG; + switch (hdr_type) { + case GPU_HDR_NONE: + internalformat = GL_RG8; + break; + case GPU_HDR_HALF_FLOAT: + internalformat = GL_RG16F; + break; + case GPU_HDR_FULL_FLOAT: + internalformat = GL_RG32F; + break; + default: + break; + } + } - if (fpixels) + if (fpixels && hdr_type == GPU_HDR_NONE) { + type = GL_UNSIGNED_BYTE; pixels = GPU_texture_convert_pixels(w*h, fpixels); + } } if (tex->target == GL_TEXTURE_1D) { @@ -651,7 +694,7 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) GPUTexture *GPU_texture_create_1D(int w, float *fpixels, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, 1, 1, fpixels, 0, err_out); + GPUTexture *tex = GPU_texture_create_nD(w, 1, 1, fpixels, 0, GPU_HDR_NONE, 4, err_out); if (tex) GPU_texture_unbind(tex); @@ -659,9 +702,9 @@ GPUTexture *GPU_texture_create_1D(int w, float *fpixels, char err_out[256]) return tex; } -GPUTexture *GPU_texture_create_2D(int w, int h, float *fpixels, char err_out[256]) +GPUTexture *GPU_texture_create_2D(int w, int h, float *fpixels, GPUHDRType hdr, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, err_out); + GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, hdr, 4, err_out); if (tex) GPU_texture_unbind(tex); @@ -671,7 +714,7 @@ GPUTexture *GPU_texture_create_2D(int w, int h, float *fpixels, char err_out[256 GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, err_out); + GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, GPU_HDR_NONE, 1, err_out); if (tex) GPU_texture_unbind(tex); @@ -684,13 +727,45 @@ GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]) */ GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(size, size, 2, NULL, 0, err_out); + GPUTexture *tex = GPU_texture_create_nD(size, size, 2, NULL, 0, GPU_HDR_FULL_FLOAT, 2, err_out); if (tex) { /* Now we tweak some of the settings */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, size, size, 0, GL_RG, GL_FLOAT, NULL); + + GPU_texture_unbind(tex); + } + + return tex; +} + +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, float *pixels, char err_out[256]) +{ + GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, err_out); + + if (tex) { + /* Now we tweak some of the settings */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + GPU_texture_unbind(tex); + } + + return tex; +} + +GPUTexture *GPU_texture_create_1D_procedural(int w, float *pixels, char err_out[256]) +{ + GPUTexture *tex = GPU_texture_create_nD(w, 0, 1, pixels, 0, GPU_HDR_HALF_FLOAT, 2, err_out); + + if (tex) { + /* Now we tweak some of the settings */ + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GPU_texture_unbind(tex); } @@ -702,7 +777,7 @@ void GPU_invalid_tex_init(void) { float color[4] = {1.0f, 0.0f, 1.0f, 1.0}; GG.invalid_tex_1D = GPU_texture_create_1D(1, color, NULL); - GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, color, NULL); + GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, color, GPU_HDR_NONE, NULL); GG.invalid_tex_3D = GPU_texture_create_3D(1, 1, 1, 4, color); } @@ -792,6 +867,45 @@ void GPU_texture_unbind(GPUTexture *tex) GPU_print_error("Post Texture Unbind"); } +void GPU_depth_texture_mode(GPUTexture *tex, bool compare, bool use_filter) +{ + GLenum arbnumber; + + if (tex->number >= GG.maxtextures) { + GPU_print_error("Not enough texture slots."); + return; + } + + if (!tex->depth) { + GPU_print_error("Not a depth texture."); + return; + } + + if (tex->number == -1) + return; + + GPU_print_error("Pre Texture Unbind"); + + arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); + if (tex->number != 0) glActiveTextureARB(arbnumber); + if (compare) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + + if (use_filter) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); + + GPU_print_error("Post Texture Unbind"); +} + void GPU_texture_free(GPUTexture *tex) { tex->refcount--; @@ -982,13 +1096,23 @@ void GPU_texture_bind_as_framebuffer(GPUTexture *tex) glPushMatrix(); } -void GPU_framebuffer_slot_bind(GPUFrameBuffer *fb, int slot) +void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot) { + int numslots = 0, i; + GLenum attachments[4]; + if (!fb->colortex[slot]) { fprintf(stderr, "Error, framebuffer slot empty!"); return; } - + + for (i = 0 ; i < 4; i++) { + if (fb->colortex[i]) { + attachments[numslots] = GL_COLOR_ATTACHMENT0_EXT + i; + numslots++; + } + } + /* push attributes */ glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); glDisable(GL_SCISSOR_TEST); @@ -997,7 +1121,7 @@ void GPU_framebuffer_slot_bind(GPUFrameBuffer *fb, int slot) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); /* last bound prevails here, better allow explicit control here too */ - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); + glDrawBuffers(numslots, attachments); glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); /* push matrices and set default viewport and matrix */ @@ -1023,6 +1147,18 @@ void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUS glPopAttrib(); } +void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot) +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + /* last bound prevails here, better allow explicit control here too */ + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); + + /* push matrices and set default viewport and matrix */ + glViewport(0, 0, fb->colortex[slot]->w, fb->colortex[slot]->h); + GG.currentfb = fb->object; + GG.currentfb = fb->object; +} bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) { @@ -1045,7 +1181,6 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) return true; } - void GPU_framebuffer_free(GPUFrameBuffer *fb) { int i; @@ -1080,8 +1215,8 @@ void GPU_framebuffer_restore(void) void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex) { - float scaleh[2] = {1.0f/GPU_texture_opengl_width(blurtex), 0.0f}; - float scalev[2] = {0.0f, 1.0f/GPU_texture_opengl_height(tex)}; + const float scaleh[2] = {1.0f / GPU_texture_opengl_width(blurtex), 0.0f}; + const float scalev[2] = {0.0f, 1.0f / GPU_texture_opengl_height(tex)}; GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); int scale_uniform, texture_source_uniform; @@ -1103,7 +1238,7 @@ void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *b GG.currentfb = blurfb->object; GPU_shader_bind(blur_shader); - GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, (float *)scaleh); + GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scaleh); GPU_shader_uniform_texture(blur_shader, texture_source_uniform, tex); glViewport(0, 0, GPU_texture_opengl_width(blurtex), GPU_texture_opengl_height(blurtex)); @@ -1126,7 +1261,7 @@ void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *b glTexCoord2d(1, 1); glVertex2f(-1, -1); glTexCoord2d(0, 1); glVertex2f(1, -1); glEnd(); - + /* Blurring vertically */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); @@ -1135,7 +1270,7 @@ void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *b GG.currentfb = fb->object; glViewport(0, 0, GPU_texture_opengl_width(tex), GPU_texture_opengl_height(tex)); - GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, (float *)scalev); + GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scalev); GPU_shader_uniform_texture(blur_shader, texture_source_uniform, blurtex); GPU_texture_bind(blurtex, 0); @@ -1180,7 +1315,7 @@ GPUOffScreen *GPU_offscreen_create(int width, int height, char err_out[256]) return NULL; } - ofs->color = GPU_texture_create_2D(width, height, NULL, err_out); + ofs->color = GPU_texture_create_2D(width, height, NULL, GPU_HDR_NONE, err_out); if (!ofs->color) { GPU_offscreen_free(ofs); return NULL; @@ -1214,15 +1349,20 @@ void GPU_offscreen_free(GPUOffScreen *ofs) MEM_freeN(ofs); } -void GPU_offscreen_bind(GPUOffScreen *ofs) +void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { glDisable(GL_SCISSOR_TEST); - GPU_texture_bind_as_framebuffer(ofs->color); + if (save) + GPU_framebuffer_slots_bind(ofs->fb, 0); + else { + GPU_framebuffer_bind_no_save(ofs->fb, 0); + } } -void GPU_offscreen_unbind(GPUOffScreen *ofs) +void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) { - GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); + if (restore) + GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); GPU_framebuffer_restore(); glEnable(GL_SCISSOR_TEST); } @@ -1250,6 +1390,7 @@ struct GPUShader { GLhandleARB fragment; /* handle for fragment shader */ GLhandleARB lib; /* handle for libment shader */ int totattrib; /* total number of attributes */ + int uniforms; /* required uniforms */ }; static void shader_print_errors(const char *task, char *log, const char **code, int totcode) @@ -1494,7 +1635,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name) return glGetUniformLocationARB(shader->object, name); } -void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, float *value) +void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) { if (location == -1) return; @@ -1588,8 +1729,64 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) return retval; } +#define MAX_DEFINES 100 + +GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp) +{ + int offset; + char defines[MAX_DEFINES] = ""; + /* avoid shaders out of range */ + if (effects >= MAX_FX_SHADERS) + return NULL; + + offset = 2 * effects; + + if (persp) { + offset += 1; + strcat(defines, "#define PERSP_MATRIX\n"); + } + + if (!GG.shaders.fx_shaders[offset]) { + switch(effects) { + case GPU_SHADER_FX_SSAO: + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_ssao_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: + strcat(defines, "#define FIRST_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: + strcat(defines, "#define SECOND_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: + strcat(defines, "#define THIRD_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: + strcat(defines, "#define FOURTH_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: + strcat(defines, "#define FIFTH_PASS\n"); + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, datatoc_gpu_shader_fx_lib_glsl, defines); + break; + } + } + + return GG.shaders.fx_shaders[offset]; +} + + void GPU_shader_free_builtin_shaders(void) { + int i; + if (GG.shaders.vsm_store) { MEM_freeN(GG.shaders.vsm_store); GG.shaders.vsm_store = NULL; @@ -1599,6 +1796,13 @@ void GPU_shader_free_builtin_shaders(void) MEM_freeN(GG.shaders.sep_gaussian_blur); GG.shaders.sep_gaussian_blur = NULL; } + + for (i = 0; i < 2 * MAX_FX_SHADERS; i++) { + if (GG.shaders.fx_shaders[i]) { + MEM_freeN(GG.shaders.fx_shaders[i]); + GG.shaders.fx_shaders[i] = NULL; + } + } } #if 0 diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl new file mode 100644 index 00000000000..d52ab2243fe --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl @@ -0,0 +1,206 @@ +/* amount of offset to move one pixel left-right. + * In second pass some dimensions are zero to control verical/horizontal convolution */ +uniform vec2 invrendertargetdim; +// color buffer +uniform sampler2D colorbuffer; +//blurred color buffer for DOF effect +uniform sampler2D blurredcolorbuffer; +// slightly blurred buffer +uniform sampler2D mblurredcolorbuffer; +// depth buffer +uniform sampler2D depthbuffer; + +// this includes focal distance in x and aperture size in y +uniform vec4 dof_params; + +// viewvectors for reconstruction of world space +uniform vec4 viewvecs[3]; + +// coordinates on framebuffer in normalized (0.0-1.0) uv space +varying vec4 uvcoordsvar; + +/* color texture coordinates, offset by a small amount */ +varying vec2 color_uv1; +varying vec2 color_uv2; + +varying vec2 depth_uv1; +varying vec2 depth_uv2; +varying vec2 depth_uv3; +varying vec2 depth_uv4; + + +float calculate_far_coc(in float zdepth) +{ + float coc = dof_params.x * max(1.0 - dof_params.y / zdepth, 0.0); + + /* multiply by 1.0 / sensor size to get the normalized size */ + return coc * dof_params.z; +} + +/* near coc only! when distance is nearer than focus plane first term is bigger than one */ +vec4 calculate_near_coc(in vec4 zdepth) +{ + vec4 coc = dof_params.x * max(vec4(dof_params.y) / zdepth - vec4(1.0), vec4(0.0)); + + /* multiply by 1.0 / sensor size to get the normalized size */ + return coc * dof_params.z; +} + +/* first pass blurs the color buffer heavily and gets the near coc only. + * There are many texture accesses here but they are done on a + * lower resolution image so overall bandwidth is not a concern */ +void first_pass() +{ + vec4 depth; + vec4 zdepth; + vec4 coc; + float final_coc; + + /* amount to add to uvs so that they move one row further */ + vec2 offset_row[3]; + offset_row[0] = vec2(0.0, invrendertargetdim.y); + offset_row[1] = 2.0 * offset_row[0]; + offset_row[2] = 3.0 * offset_row[0]; + + /* heavily blur the image */ + vec4 color = texture2D(colorbuffer, color_uv1); + color += texture2D(colorbuffer, color_uv1 + offset_row[1]); + color += texture2D(colorbuffer, color_uv2); + color += texture2D(colorbuffer, color_uv2 + offset_row[1]); + color /= 4.0; + + depth.r = texture2D(depthbuffer, depth_uv1).r; + depth.g = texture2D(depthbuffer, depth_uv2).r; + depth.b = texture2D(depthbuffer, depth_uv3).r; + depth.a = texture2D(depthbuffer, depth_uv4).r; + + zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), depth); + coc = calculate_near_coc(zdepth); + + depth.r = texture2D(depthbuffer, depth_uv1 + offset_row[0]).r; + depth.g = texture2D(depthbuffer, depth_uv2 + offset_row[0]).r; + depth.b = texture2D(depthbuffer, depth_uv3 + offset_row[0]).r; + depth.a = texture2D(depthbuffer, depth_uv4 + offset_row[0]).r; + + zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), depth); + coc = max(calculate_near_coc(zdepth), coc); + + depth.r = texture2D(depthbuffer, depth_uv1 + offset_row[1]).r; + depth.g = texture2D(depthbuffer, depth_uv2 + offset_row[1]).r; + depth.b = texture2D(depthbuffer, depth_uv3 + offset_row[1]).r; + depth.a = texture2D(depthbuffer, depth_uv4 + offset_row[1]).r; + + zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), depth); + coc = max(calculate_near_coc(zdepth), coc); + + depth.r = texture2D(depthbuffer, depth_uv1 + offset_row[2]).r; + depth.g = texture2D(depthbuffer, depth_uv2 + offset_row[2]).r; + depth.b = texture2D(depthbuffer, depth_uv3 + offset_row[2]).r; + depth.a = texture2D(depthbuffer, depth_uv4 + offset_row[2]).r; + + zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), depth); + coc = max(calculate_near_coc(zdepth), coc); + + final_coc = max(max(coc.x, coc.y), max(coc.z, coc.w)); + gl_FragColor = vec4(color.rgb, final_coc); +} + +/* second pass, gaussian blur the downsampled image */ +void second_pass() +{ + vec4 depth = vec4(texture2D(depthbuffer, uvcoordsvar.xy).r); + + /* clever sampling to sample 2 pixels at once. Of course it's not real gaussian sampling this way */ + vec4 color = texture2D(colorbuffer, uvcoordsvar.xy) * 0.3125; + color += texture2D(colorbuffer, uvcoordsvar.xy + invrendertargetdim) * 0.234375; + color += texture2D(colorbuffer, uvcoordsvar.xy + 2.5 * invrendertargetdim) * 0.09375; + color += texture2D(colorbuffer, uvcoordsvar.xy + 4.5 * invrendertargetdim) * 0.015625; + color += texture2D(colorbuffer, uvcoordsvar.xy -invrendertargetdim) * 0.234375; + color += texture2D(colorbuffer, uvcoordsvar.xy -2.5 * invrendertargetdim) * 0.09375; + color += texture2D(colorbuffer, uvcoordsvar.xy -4.5 * invrendertargetdim) * 0.015625; + + gl_FragColor = color; +} + + +/* third pass, calculate the final coc from blurred and unblurred images */ +void third_pass() +{ + vec4 color = texture2D(colorbuffer, uvcoordsvar.xy); + vec4 color_blurred = texture2D(blurredcolorbuffer, uvcoordsvar.xy); + float coc = 2.0 * max(color_blurred.a, color.a); - color.a; + gl_FragColor = vec4(color.rgb, coc); +} + + +/* fourth pass, blur the final coc once to get rid of discontinuities */ +void fourth_pass() +{ + vec4 color = texture2D(colorbuffer, uvcoordsvar.xz); + color += texture2D(colorbuffer, uvcoordsvar.yz); + color += texture2D(colorbuffer, uvcoordsvar.xw); + color += texture2D(colorbuffer, uvcoordsvar.yw); + + gl_FragColor = color / 4.0; +} + +vec4 small_sample_blur(in sampler2D colorbuffer, in vec2 uv, in vec4 color) +{ + float weight = 1.0/ 17.0; + vec4 result = weight * color; + weight *= 4.0; + + result += weight * texture2D(colorbuffer, uv + color_uv1.xy); + result += weight * texture2D(colorbuffer, uv - color_uv1.xy); + result += weight * texture2D(colorbuffer, uv + color_uv1.yx); + result += weight * texture2D(colorbuffer, uv - color_uv1.yx); + + return result; +} + + +/* fourth pass, just visualize the third pass contents */ +void fifth_pass() +{ + vec4 factors; + vec4 color_orig = texture2D(colorbuffer, uvcoordsvar.xy); + vec4 highblurred = texture2D(blurredcolorbuffer, uvcoordsvar.xy); + vec4 mediumblurred = texture2D(mblurredcolorbuffer, uvcoordsvar.xy); + vec4 smallblurred = small_sample_blur(colorbuffer, uvcoordsvar.xy, color_orig); + float depth = texture2D(depthbuffer, uvcoordsvar.xy).r; + + float zdepth = get_view_space_z_from_depth(vec4(viewvecs[0].z), vec4(viewvecs[1].z), vec4(depth)).r; + float coc_far = clamp(calculate_far_coc(zdepth), 0.0, 1.0); + + /* calculate final coc here */ + float coc = max(max(coc_far, mediumblurred.a), 0.0); + + float width = 2.5; + float radius = 0.2; + + factors.x = 1.0 - clamp(width * coc, 0.0, 1.0); + factors.y = 1.0 - clamp(abs(width * (coc - 2.0 * radius)), 0.0, 1.0); + factors.z = 1.0 - clamp(abs(width * (coc - 3.0 * radius)), 0.0, 1.0); + factors.w = 1.0 - clamp(abs(width * (coc - 4.0 * radius)), 0.0, 1.0); + /* blend! */ + vec4 color = factors.x * color_orig + factors.y * smallblurred + factors.z * mediumblurred + factors.w * highblurred; + + color /= dot(factors, vec4(1.0)); + gl_FragColor = vec4(color.rgb, 1.0); +} + + +void main() +{ +#ifdef FIRST_PASS + first_pass(); +#elif defined(SECOND_PASS) + second_pass(); +#elif defined(THIRD_PASS) + third_pass(); +#elif defined(FOURTH_PASS) + fourth_pass(); +#elif defined(FIFTH_PASS) + fifth_pass(); +#endif +} diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl new file mode 100644 index 00000000000..a2ef990c4e8 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl @@ -0,0 +1,68 @@ +uniform vec2 invrendertargetdim; + +//texture coordinates for framebuffer read +varying vec4 uvcoordsvar; + +/* color texture coordinates, offset by a small amount */ +varying vec2 color_uv1; +varying vec2 color_uv2; + +varying vec2 depth_uv1; +varying vec2 depth_uv2; +varying vec2 depth_uv3; +varying vec2 depth_uv4; + +//very simple shader for gull screen FX, just pass values on + +void vert_generic() +{ + uvcoordsvar = gl_MultiTexCoord0; + gl_Position = gl_Vertex; +} + +void vert_dof_first_pass() +{ + /* we offset the texture coordinates by 1.5 pixel, + * then we reuse that to sample the surrounding pixels */ + color_uv1 = gl_MultiTexCoord0.xy + vec2(-1.5, -1.5) * invrendertargetdim; + color_uv2 = gl_MultiTexCoord0.xy + vec2(0.5, -1.5) * invrendertargetdim; + + depth_uv1 = gl_MultiTexCoord0.xy + vec2(-1.5, -1.5) * invrendertargetdim; + depth_uv2 = gl_MultiTexCoord0.xy + vec2(-0.5, -1.5) * invrendertargetdim; + depth_uv3 = gl_MultiTexCoord0.xy + vec2(0.5, -1.5) * invrendertargetdim; + depth_uv4 = gl_MultiTexCoord0.xy + vec2(1.5, -1.5) * invrendertargetdim; + + gl_Position = gl_Vertex; +} + +void vert_dof_fourth_pass() +{ + vec4 halfpixel = vec4(-0.5, 0.5, -0.5, 0.5); + uvcoordsvar = gl_MultiTexCoord0.xxyy + halfpixel * vec4(invrendertargetdim.x, + invrendertargetdim.x, invrendertargetdim.y, invrendertargetdim.y); + + gl_Position = gl_Vertex; +} + +void vert_dof_fifth_pass() +{ + vec4 halfpixel = vec4(-0.5, 0.5, -0.5, 0.5); + color_uv1 = vec2(0.5, 1.5) * invrendertargetdim; + + uvcoordsvar = gl_MultiTexCoord0; + gl_Position = gl_Vertex; +} + +void main() +{ +#ifdef FIRST_PASS + vert_dof_first_pass(); +#elif defined(FOURTH_PASS) + vert_dof_fourth_pass(); +#elif defined(FIFTH_PASS) + vert_dof_fifth_pass(); +#else + vert_generic(); +#endif +} + diff --git a/source/blender/gpu/shaders/gpu_shader_fx_lib.glsl b/source/blender/gpu/shaders/gpu_shader_fx_lib.glsl new file mode 100644 index 00000000000..6c4bf3bb7a0 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_lib.glsl @@ -0,0 +1,47 @@ +vec3 calculate_view_space_normal(in vec3 viewposition) +{ + vec3 normal = cross(normalize(dFdx(viewposition)), + normalize(dFdy(viewposition))); + normalize(normal); + return normal; +} + +/* simple depth reconstruction, see http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer + * we change the factors from the article to fit the OpennGL model. */ +#ifdef PERSP_MATRIX + +/* perspective camera code */ + +vec3 get_view_space_from_depth(in vec2 uvcoords, in vec3 viewvec_origin, in vec3 viewvec_diff, in float depth) +{ + float d = 2.0 * depth - 1.0; + + float zview = -gl_ProjectionMatrix[3][2] / (d + gl_ProjectionMatrix[2][2]); + + return zview * (viewvec_origin + vec3(uvcoords, 0.0) * viewvec_diff); +} + +vec4 get_view_space_z_from_depth(in vec4 near, in vec4 range, in vec4 depth) +{ + vec4 d = 2.0 * depth - vec4(1.0); + + /* return positive value, so sign differs! */ + return vec4(gl_ProjectionMatrix[3][2]) / (d + vec4(gl_ProjectionMatrix[2][2])); +} + +#else +/* orthographic camera code */ + +vec3 get_view_space_from_depth(in vec2 uvcoords, in vec3 viewvec_origin, in vec3 viewvec_diff, in float depth) +{ + vec3 offset = vec3(uvcoords, depth); + + return vec3(viewvec_origin + offset * viewvec_diff); +} + +vec4 get_view_space_z_from_depth(in vec4 near, in vec4 range, in vec4 depth) +{ + return -(near + depth * range); +} + +#endif diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl new file mode 100644 index 00000000000..b33fda92a46 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -0,0 +1,82 @@ +// color buffer +uniform sampler2D colorbuffer; + +// jitter texture for ssao +uniform sampler2D jitter_tex; + +// concentric sample texture for ssao +uniform sampler1D ssao_concentric_tex; + +// depth buffer +uniform sampler2D depthbuffer; +// coordinates on framebuffer in normalized (0.0-1.0) uv space +varying vec4 uvcoordsvar; + +/* ssao_params.x : pixel scale for the ssao radious */ +/* ssao_params.y : factor for the ssao darkening */ +uniform vec4 ssao_params; +uniform vec4 ssao_sample_params; +uniform vec4 ssao_color; + +/* store the view space vectors for the corners of the view frustum here. + * It helps to quickly reconstruct view space vectors by using uv coordinates, + * see http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ +uniform vec4 viewvecs[3]; + +float calculate_ssao_factor(float depth) +{ + /* take the normalized ray direction here */ + vec2 rotX = texture2D(jitter_tex, uvcoordsvar.xy * ssao_sample_params.zw).rg; + vec2 rotY = vec2(-rotX.y, rotX.x); + + /* occlusion is zero in full depth */ + if (depth == 1.0) + return 0.0; + + vec3 position = get_view_space_from_depth(uvcoordsvar.xy, viewvecs[0].xyz, viewvecs[1].xyz, depth); + vec3 normal = calculate_view_space_normal(position); + + // find the offset in screen space by multiplying a point in camera space at the depth of the point by the projection matrix. + vec2 offset; + float homcoord = gl_ProjectionMatrix[2][3] * position.z + gl_ProjectionMatrix[3][3]; + offset.x = gl_ProjectionMatrix[0][0] * ssao_params.x / homcoord; + offset.y = gl_ProjectionMatrix[1][1] * ssao_params.x / homcoord; + /* convert from -1.0...1.0 range to 0.0..1.0 for easy use with texture coordinates */ + offset *= 0.5; + + float factor = 0.0; + int x; + int num_samples = int(ssao_sample_params.x); + + for (x = 0; x < num_samples; x++) { + vec2 dir_sample = texture1D(ssao_concentric_tex, (float(x) + 0.5) / ssao_sample_params.x).rg; + + /* rotate with random direction to get jittered result */ + vec2 dir_jittered = vec2(dot(dir_sample, rotX), dot(dir_sample, rotY)); + + vec2 uvcoords = uvcoordsvar.xy + dir_jittered * offset; + + float depth_new = texture2D(depthbuffer, uvcoords).r; + if (depth_new != 1.0) { + vec3 pos_new = get_view_space_from_depth(uvcoords, viewvecs[0].xyz, viewvecs[1].xyz, depth_new); + vec3 dir = pos_new - position; + float len = length(dir); + float f = dot(dir, normal); + + /* use minor bias here to avoid self shadowing */ + if (f > 0.05 * len + 0.0001) + factor += f * 1.0/(len * (1.0 + len * len * ssao_params.z)); + } + } + + factor /= ssao_sample_params.x; + + return clamp(factor * ssao_params.y, 0.0, 1.0); +} + +void main() +{ + float depth = texture2D(depthbuffer, uvcoordsvar.xy).r; + vec4 color = mix(texture2D(colorbuffer, uvcoordsvar.xy), ssao_color, calculate_ssao_factor(depth)); + gl_FragColor = vec4(color.rgb, 1.0); +} diff --git a/source/blender/gpu/shaders/gpu_shader_fx_vert.glsl b/source/blender/gpu/shaders/gpu_shader_fx_vert.glsl new file mode 100644 index 00000000000..5194e414520 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fx_vert.glsl @@ -0,0 +1,9 @@ +varying vec4 uvcoordsvar; + +//very simple shader for full screen FX, just pass values on + +void main() +{ + uvcoordsvar = gl_MultiTexCoord0; + gl_Position = gl_Vertex; +} diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index da7d98d2b22..c67a356a708 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -33,7 +33,7 @@ #define __DNA_CAMERA_TYPES_H__ #include "DNA_defs.h" - +#include "DNA_gpu_types.h" #include "DNA_ID.h" #ifdef __cplusplus @@ -65,6 +65,7 @@ typedef struct Camera { struct Ipo *ipo DNA_DEPRECATED; /* old animation system, deprecated for 2.5 */ struct Object *dof_ob; + struct GPUDOFSettings gpu_dof; char sensor_fit; char pad[7]; diff --git a/source/blender/makesdna/DNA_gpu_types.h b/source/blender/makesdna/DNA_gpu_types.h new file mode 100644 index 00000000000..b6009b3f899 --- /dev/null +++ b/source/blender/makesdna/DNA_gpu_types.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_gpu_types.h + * \ingroup DNA + */ + +#ifndef __DNA_GPU_TYPES_H__ +#define __DNA_GPU_TYPES_H__ + +/* properties for dof effect */ +typedef struct GPUDOFSettings { + float focus_distance; /* focal distance for depth of field */ + float fstop; + float focal_length; + float sensor; +} GPUDOFSettings; + +/* properties for SSAO effect */ +typedef struct GPUSSAOSettings { + float factor; + float color[3]; + float distance_max; + float attenuation; + int samples; /* ray samples, we use presets here for easy control instead of */ + int pad; +} GPUSSAOSettings; + +typedef struct GPUFXSettings { + GPUDOFSettings *dof; + GPUSSAOSettings *ssao; + char fx_flag; /* eGPUFXFlags */ + char pad[7]; +} GPUFXSettings; + +/* shaderfx enables */ +typedef enum eGPUFXFlags { + GPU_FX_FLAG_DOF = (1 << 0), + GPU_FX_FLAG_SSAO = (1 << 1), +} eGPUFXFlags; + +#endif /* __DNA_GPU_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 2d2ee4ab309..c20f5ff02f8 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -46,6 +46,7 @@ extern "C" { #include "DNA_listBase.h" #include "DNA_ID.h" #include "DNA_freestyle_types.h" +#include "DNA_gpu_types.h" struct CurveMapping; struct Object; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 543a95cc120..210deddc854 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -46,6 +46,7 @@ struct bGPdata; struct SmoothView3DStore; struct wmTimer; struct Material; +struct GPUFX; /* This is needed to not let VC choke on near and far... old * proprietary MS extensions... */ @@ -60,6 +61,7 @@ struct Material; #include "DNA_listBase.h" #include "DNA_image_types.h" #include "DNA_movieclip_types.h" +#include "DNA_gpu_types.h" /* ******************************** */ @@ -146,6 +148,7 @@ typedef struct RegionView3D { float rot_angle; float rot_axis[3]; + struct GPUFX *compositor; } RegionView3D; /* 3D ViewPort Struct */ @@ -210,11 +213,17 @@ typedef struct View3D { struct ListBase afterdraw_transp; struct ListBase afterdraw_xray; struct ListBase afterdraw_xraytransp; - + /* drawflags, denoting state */ char zbuf, transp, xray; + + /* built-in shader effects (eGPUFXFlags) */ char pad3[5]; + /* note, 'fx_settings.dof' is currently _not_ allocated, + * instead set (temporarily) from camera */ + struct GPUFXSettings fx_settings; + void *properties_storage; /* Nkey panel stores stuff here (runtime only!) */ struct Material *defmaterial; /* used by matcap now */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index ca8f56289ca..7f3e9b7c196 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -73,6 +73,7 @@ static const char *includefiles[] = { "DNA_key_types.h", "DNA_text_types.h", "DNA_packedFile_types.h", + "DNA_gpu_types.h", "DNA_camera_types.h", "DNA_image_types.h", "DNA_texture_types.h", diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 5704004b4b6..31e991dd2b6 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -284,8 +284,6 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Lens Unit", "Unit to edit lens in for the user interface"); /* pointers */ - rna_def_animdata_common(srna); - prop = RNA_def_property(srna, "dof_object", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_pointer_sdna(prop, NULL, "dof_ob"); @@ -293,6 +291,13 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "DOF Object", "Use this object to define the depth of field focal point"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + prop = RNA_def_property(srna, "gpu_dof", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPUDOFSettings"); + RNA_def_property_ui_text(prop, "GPU Depth Of Field", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + rna_def_animdata_common(srna); + /* Camera API */ RNA_api_camera(srna); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 1c133db37b7..b3151d5a844 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1692,6 +1692,28 @@ static void rna_FreestyleSettings_module_remove(ID *id, FreestyleSettings *confi WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); } +char *rna_GPUDOF_path(PointerRNA *ptr) +{ + /* if there is ID-data, resolve the path using the index instead of by name, + * since the name used is the name of the texture assigned, but the texture + * may be used multiple times in the same stack + */ + if (ptr->id.data) { + if (GS(((ID *)ptr->id.data)->name) == ID_CA) { + return BLI_strdup("gpu_dof"); + } + } + + return BLI_strdup("");; +} + +static void rna_GPUFXSettings_fx_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + GPUFXSettings *fx_settings = ptr->data; + + BKE_screen_gpu_fx_validate(fx_settings); +} + #else static void rna_def_transform_orientation(BlenderRNA *brna) @@ -3822,6 +3844,114 @@ static void rna_def_scene_game_data(BlenderRNA *brna) rna_def_scene_game_recast_data(brna); } +static void rna_def_gpu_dof_fx(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPUDOFSettings", NULL); + RNA_def_struct_ui_text(srna, "GPU DOF", "Settings for GPU based depth of field"); + RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + RNA_def_struct_path_func(srna, "rna_GPUDOF_path"); + + prop = RNA_def_property(srna, "focus_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text(prop, "Focus distance", "Viewport depth of field focus distance"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 5000.0f, 1, 2); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "focal_length", PROP_FLOAT, PROP_DISTANCE_CAMERA); + RNA_def_property_ui_text(prop, "Focal Length", "Foca Length for dof effect"); + RNA_def_property_range(prop, 1.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 1.0f, 5000.0f, 1, 2); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "sensor", PROP_FLOAT, PROP_DISTANCE_CAMERA); + RNA_def_property_ui_text(prop, "Sensor", "Size of sensor"); + RNA_def_property_range(prop, 1.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 1.0f, 5000.0f, 1, 2); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "fstop", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "F/Stop", "FStop for dof effect"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.1f, 128.0f, 10, 1); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); +} + +static void rna_def_gpu_ssao_fx(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPUSSAOSettings", NULL); + RNA_def_struct_ui_text(srna, "GPU SSAO", "Settings for GPU based screen space ambient occlusion"); + RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Strength", "Strength of the ssao effect"); + RNA_def_property_range(prop, 0.0f, 250.0f); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "distance_max", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Distance", "Distance of object that contribute to the SSAO effect"); + RNA_def_property_range(prop, 0.0f, 100000.0f); + RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "attenuation", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Attenuation", "Attenuation constant"); + RNA_def_property_range(prop, 1.0f, 100000.0f); + RNA_def_property_ui_range(prop, 1.0f, 100.0f, 1, 3); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Samples", "Number of samples. Final number is squared"); + RNA_def_property_range(prop, 1, 30); /* 0 is needed for compression. */ + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "SSAO Color", "Color for screen space ambient occlusion effect"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); +} + + +static void rna_def_gpu_fx(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + rna_def_gpu_ssao_fx(brna); + rna_def_gpu_dof_fx(brna); + + srna = RNA_def_struct(brna, "GPUFXSettings", NULL); + RNA_def_struct_ui_text(srna, "GPU FX Settings", "Settings for GPU based compositing"); + RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + + prop = RNA_def_property(srna, "dof", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "GPUDOFSettings"); + RNA_def_property_ui_text(prop, "Depth Of Field settings", ""); + + prop = RNA_def_property(srna, "use_dof", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "fx_flag", GPU_FX_FLAG_DOF); + RNA_def_property_ui_text(prop, "Depth Of Field", "Use depth of field on viewport using the values from active camera"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPUFXSettings_fx_update"); + + + prop = RNA_def_property(srna, "ssao", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "GPUSSAOSettings"); + RNA_def_property_ui_text(prop, "Screen Space Ambient Occlusion settings", ""); + + prop = RNA_def_property(srna, "use_ssao", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "fx_flag", GPU_FX_FLAG_SSAO); + RNA_def_property_ui_text(prop, "SSAO", "Use screen space ambient occlusion of field on viewport"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPUFXSettings_fx_update"); +} + + static void rna_def_scene_render_layer(BlenderRNA *brna) { StructRNA *srna; @@ -5862,7 +5992,9 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Animated *** */ rna_def_scene_render_data(brna); rna_def_scene_render_layer(brna); - + + rna_def_gpu_fx(brna); + /* Scene API */ RNA_api_scene(srna); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 70b9f18df8c..24c80ccdb02 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -34,6 +34,7 @@ #include "BKE_key.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_screen.h" #include "DNA_action_types.h" #include "DNA_key_types.h" @@ -2128,7 +2129,6 @@ static void rna_def_space_view3d(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; - srna = RNA_def_struct(brna, "SpaceView3D", "Space"); RNA_def_struct_sdna(srna, "View3D"); RNA_def_struct_ui_text(srna, "3D View Space", "3D View space data"); @@ -2470,6 +2470,10 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Matcap", "Image to use for Material Capture, active objects only"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_matcap_update"); + prop = RNA_def_property(srna, "fx_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "FX Options", "Options used for real time compositing"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* region */ srna = RNA_def_struct(brna, "RegionView3D", NULL); |