diff options
Diffstat (limited to 'source/blender/gpu/intern/gpu_compositing.c')
-rw-r--r-- | source/blender/gpu/intern/gpu_compositing.c | 987 |
1 files changed, 987 insertions, 0 deletions
diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c new file mode 100644 index 00000000000..511167b775a --- /dev/null +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -0,0 +1,987 @@ +/* + * ***** 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; + GPUTexture *depth_buffer_xray; + + /* 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; +}; + +#if 0 +/* 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; +} +#endif + +static GPUTexture *create_spiral_sample_texture(int numsaples) +{ + GPUTexture *tex; + float (*texels)[2] = MEM_mallocN(sizeof(float[2]) * numsaples, "concentric_tex"); + const float numsaples_inv = 1.0f / numsaples; + int i; + /* arbitrary number to ensure we don't get conciding samples every circle */ + const float spirals = 7.357; + + for (i = 0; i < numsaples; i++) { + float r = (i + 0.5f) * numsaples_inv; + float phi = r * spirals * (float)(2.0 * M_PI); + texels[i][0] = r * cosf(phi); + texels[i][1] = r * sinf(phi); + } + + tex = GPU_texture_create_1D_procedural(numsaples, (float *)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; + } + + if (fx->depth_buffer_xray) { + GPU_framebuffer_texture_detach(fx->depth_buffer_xray); + GPU_texture_free(fx->depth_buffer_xray); + fx->depth_buffer_xray = 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.0f; + jitter[i][1] = 2.0f * BLI_frand() - 1.0f; + 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), h = BLI_rcti_size_y(rect); + char err_out[256]; + int num_passes = 0; + char fx_flag; + + fx->effects = 0; + + if (!fx_settings) { + cleanup_fx_gl_data(fx, true); + return false; + } + + fx_flag = fx_settings->fx_flag; + + /* 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; + } + + /* scissor is missing when drawing offscreen, in that case, dimensions match exactly. In opposite case + * add one to match viewport dimensions */ + if (scissor_rect) { + w++, h++; + } + + 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_spiral_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); + } +} + +void GPU_fx_compositor_setup_XRay_pass(GPUFX *fx, bool do_xray) +{ + char err_out[256]; + + if (do_xray) { + if (!fx->depth_buffer_xray && !(fx->depth_buffer_xray = GPU_texture_create_depth(fx->gbuffer_dim[0], fx->gbuffer_dim[1], err_out))) { + printf("%.256s\n", err_out); + cleanup_fx_gl_data(fx, true); + return; + } + } + else { + if (fx->depth_buffer_xray) { + GPU_framebuffer_texture_detach(fx->depth_buffer_xray); + GPU_texture_free(fx->depth_buffer_xray); + fx->depth_buffer_xray = NULL; + } + return; + } + + GPU_framebuffer_texture_detach(fx->depth_buffer); + + /* first depth buffer, because system assumes read/write buffers */ + if(!GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer_xray, 0, err_out)) + printf("%.256s\n", err_out); +} + + +void GPU_fx_compositor_XRay_resolve(GPUFX *fx) +{ + GPUShader *depth_resolve_shader; + GPU_framebuffer_texture_detach(fx->depth_buffer_xray); + + /* attach regular framebuffer */ + GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, NULL); + + /* full screen quad where we will always write to depth buffer */ + glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT); + glDepthFunc(GL_ALWAYS); + /* disable scissor from sculpt if any */ + glDisable(GL_SCISSOR_TEST); + /* disable writing to color buffer, it's depth only pass */ + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + /* 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); + + depth_resolve_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_RESOLVE, false); + + if (depth_resolve_shader) { + int depth_uniform; + + depth_uniform = GPU_shader_get_uniform(depth_resolve_shader, "depthbuffer"); + + GPU_shader_bind(depth_resolve_shader); + + GPU_texture_bind(fx->depth_buffer_xray, 0); + GPU_depth_texture_mode(fx->depth_buffer_xray, false, true); + GPU_shader_uniform_texture(depth_resolve_shader, depth_uniform, fx->depth_buffer_xray); + + /* draw */ + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* disable bindings */ + GPU_depth_texture_mode(fx->depth_buffer_xray, true, false); + GPU_texture_unbind(fx->depth_buffer_xray); + + GPU_shader_unbind(); + } + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glPopAttrib(); +} + + +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; + /* 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; + /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm + * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though + * because the shader reads coordinates in world space, which is in blender units. */ + float scale_camera = 0.001f / scale; + /* we want radius here for the aperture number */ + float aperture = 0.5f * scale_camera * fx_dof->focal_length / fx_dof->fstop; + + dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length / ((fx_dof->focus_distance / scale) - scale_camera * fx_dof->focal_length)); + dof_params[1] = fx_dof->focus_distance / scale; + 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 = 20; +} |