int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) { GLenum status; GLenum attachment; GLenum error; if (slot >= GPU_FB_MAX_SLOTS) { fprintf(stderr, "Attaching to index %d framebuffer slot unsupported in blender use at most %d\n", slot, GPU_FB_MAX_SLOTS); return 0; } if (tex->number != -1) { fprintf(stderr, "Feedback loop warning!: Attempting to attach texture to framebuffer while still bound to texture unit for drawing!"); } if (tex->depth) attachment = GL_DEPTH_ATTACHMENT_EXT; else attachment = GL_COLOR_ATTACHMENT0_EXT + slot; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); GG.currentfb = fb->object; /* Clean glError buffer. */ while (glGetError() != GL_NO_ERROR) {} glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, tex->target, tex->bindcode, 0); error = glGetError(); if (error == GL_INVALID_OPERATION) { GPU_framebuffer_restore(); GPU_print_framebuffer_error(error, err_out); return 0; } status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { GPU_framebuffer_restore(); GPU_print_framebuffer_error(status, err_out); return 0; } if (tex->depth) fb->depthtex = tex; else fb->colortex[slot] = tex; tex->fb= fb; tex->fb_attachment = slot; return 1; }
int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, char err_out[256]) { GLenum status; GLenum attachment; GLenum error; if (tex->depth) attachment = GL_DEPTH_ATTACHMENT_EXT; else attachment = GL_COLOR_ATTACHMENT0_EXT; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); GG.currentfb = fb->object; glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, tex->target, tex->bindcode, 0); error = glGetError(); if (error == GL_INVALID_OPERATION) { GPU_framebuffer_restore(); GPU_print_framebuffer_error(error, err_out); return 0; } if (tex->depth) { glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); } else { glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); } status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { GPU_framebuffer_restore(); GPU_print_framebuffer_error(status, err_out); return 0; } if (tex->depth) fb->depthtex = tex; else fb->colortex = tex; tex->fb= fb; return 1; }
void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) { if (restore) GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); GPU_framebuffer_restore(); glEnable(GL_SCISSOR_TEST); }
GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256]) { GPUOffScreen *ofs; ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); ofs->fb = GPU_framebuffer_create(); if (!ofs->fb) { GPU_offscreen_free(ofs); return NULL; } if (samples) { if (!GLEW_EXT_framebuffer_multisample || !GLEW_ARB_texture_multisample || /* Only needed for GPU_offscreen_read_pixels. * We could add an arg if we intend to use multi-sample * offscreen buffers w/o reading their pixels */ !GLEW_EXT_framebuffer_blit || /* This is required when blitting from a multi-sampled buffers, * even though we're not scaling. */ !GLEW_EXT_framebuffer_multisample_blit_scaled) { samples = 0; } } ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, err_out); if (!ofs->depth) { GPU_offscreen_free(ofs); return NULL; } if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { GPU_offscreen_free(ofs); return NULL; } ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, GPU_HDR_NONE, samples, err_out); if (!ofs->color) { GPU_offscreen_free(ofs); return NULL; } if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { GPU_offscreen_free(ofs); return NULL; } /* check validity at the very end! */ if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { GPU_offscreen_free(ofs); return NULL; } GPU_framebuffer_restore(); return ofs; }
int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) { GLenum attachment; GLenum error; if (slot >= GPU_FB_MAX_SLOTS) { fprintf(stderr, "Attaching to index %d framebuffer slot unsupported. " "Use at most %d\n", slot, GPU_FB_MAX_SLOTS); return 0; } if ((G.debug & G_DEBUG)) { if (GPU_texture_bound_number(tex) != -1) { fprintf(stderr, "Feedback loop warning!: " "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n"); } } if (GPU_texture_depth(tex)) attachment = GL_DEPTH_ATTACHMENT_EXT; else attachment = GL_COLOR_ATTACHMENT0_EXT + slot; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); GG.currentfb = fb->object; /* Clean glError buffer. */ while (glGetError() != GL_NO_ERROR) {} glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, GPU_texture_target(tex), GPU_texture_opengl_bindcode(tex), 0); error = glGetError(); if (error == GL_INVALID_OPERATION) { GPU_framebuffer_restore(); gpu_print_framebuffer_error(error, err_out); return 0; } if (GPU_texture_depth(tex)) fb->depthtex = tex; else fb->colortex[slot] = tex; GPU_texture_framebuffer_set(tex, fb, slot); return 1; }
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); } }
GPUOffScreen *GPU_offscreen_create(int *width, int *height, char err_out[256]) { GPUOffScreen *ofs; ofs= MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); ofs->fb = GPU_framebuffer_create(); if(!ofs->fb) { GPU_offscreen_free(ofs); return NULL; } ofs->depth = GPU_texture_create_depth(*width, *height, err_out); if(!ofs->depth) { GPU_offscreen_free(ofs); return NULL; } if(*width!=ofs->depth->w || *height!=ofs->depth->h) { *width= ofs->depth->w; *height= ofs->depth->h; printf("Offscreen size differs from given size!\n"); } if(!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, err_out)) { GPU_offscreen_free(ofs); return NULL; } ofs->color = GPU_texture_create_2D(*width, *height, NULL, err_out); if(!ofs->color) { GPU_offscreen_free(ofs); return NULL; } if(!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, err_out)) { GPU_offscreen_free(ofs); return NULL; } GPU_framebuffer_restore(); return ofs; }
/* TODO: Creating, attaching texture, and destroying a framebuffer is quite slow. * Calling this function should be avoided during interactive drawing. */ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void *data) { DefaultTextureList *dtxl = (DefaultTextureList *)GPU_viewport_texture_list_get(viewport); GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(); GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0); GPU_framebuffer_bind(tmp_fb); glReadPixels(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), GL_DEPTH_COMPONENT, GL_FLOAT, data); GPU_framebuffer_restore(); GPU_framebuffer_free(tmp_fb); }
bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) { GLenum status; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); GG.currentfb = fb->object; /* Clean glError buffer. */ while (glGetError() != GL_NO_ERROR) {} status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { GPU_framebuffer_restore(); gpu_print_framebuffer_error(status, err_out); return false; } return true; }
GPUOffScreen *GPU_offscreen_create(int width, int height, char err_out[256]) { GPUOffScreen *ofs; ofs= MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); ofs->w= width; ofs->h= height; ofs->fb = GPU_framebuffer_create(); if (!ofs->fb) { GPU_offscreen_free(ofs); return NULL; } ofs->depth = GPU_texture_create_depth(width, height, err_out); if (!ofs->depth) { GPU_offscreen_free(ofs); return NULL; } if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, err_out)) { GPU_offscreen_free(ofs); return NULL; } ofs->color = GPU_texture_create_2D(width, height, NULL, err_out); if (!ofs->color) { GPU_offscreen_free(ofs); return NULL; } if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, err_out)) { GPU_offscreen_free(ofs); return NULL; } GPU_framebuffer_restore(); return ofs; }
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; }