예제 #1
0
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)};

	GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
	int scale_uniform, texture_source_uniform;

	if (!blur_shader)
		return;

	scale_uniform = GPU_shader_get_uniform(blur_shader, "ScaleU");
	texture_source_uniform = GPU_shader_get_uniform(blur_shader, "textureSource");
		
	/* Blurring horizontally */

	/* We do the bind ourselves rather than using GPU_framebuffer_texture_bind() to avoid
	 * pushing unnecessary matrices onto the OpenGL stack. */
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, blurfb->object);

	GPU_shader_bind(blur_shader);
	GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, (float *)scaleh);
	GPU_shader_uniform_texture(blur_shader, texture_source_uniform, tex);
	glViewport(0, 0, GPU_texture_opengl_width(blurtex), GPU_texture_opengl_height(blurtex));

	/* Peparing to draw quad */
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	GPU_texture_bind(tex, 0);

	/* Drawing quad */
	glBegin(GL_QUADS);
	glTexCoord2d(0, 0); glVertex2f(1, 1);
	glTexCoord2d(1, 0); glVertex2f(-1, 1);
	glTexCoord2d(1, 1); glVertex2f(-1, -1);
	glTexCoord2d(0, 1); glVertex2f(1, -1);
	glEnd();
		
	/* Blurring vertically */

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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_texture(blur_shader, texture_source_uniform, blurtex);
	GPU_texture_bind(blurtex, 0);

	glBegin(GL_QUADS);
	glTexCoord2d(0, 0); glVertex2f(1, 1);
	glTexCoord2d(1, 0); glVertex2f(-1, 1);
	glTexCoord2d(1, 1); glVertex2f(-1, -1);
	glTexCoord2d(0, 1); glVertex2f(1, -1);
	glEnd();

	GPU_shader_unbind();
}
예제 #2
0
static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec,
                        bool use_fire, const float min[3],
                        const float ob_sizei[3], const float invsize[3])
{
	int invsize_location = GPU_shader_get_uniform(shader, "invsize");
	int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei");
	int min_location = GPU_shader_get_uniform(shader, "min_location");

	int soot_location;
	int stepsize_location;
	int densityscale_location;
	int spec_location, flame_location;
	int shadow_location, actcol_location;

	if (use_fire) {
		spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
		flame_location = GPU_shader_get_uniform(shader, "flame_texture");
	}
	else {
		shadow_location = GPU_shader_get_uniform(shader, "shadow_texture");
		actcol_location = GPU_shader_get_uniform(shader, "active_color");
		soot_location = GPU_shader_get_uniform(shader, "soot_texture");
		stepsize_location = GPU_shader_get_uniform(shader, "step_size");
		densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
	}

	GPU_shader_bind(shader);

	if (use_fire) {
		GPU_texture_bind(sds->tex_flame, 2);
		GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame);

		GPU_texture_bind(tex_spec, 3);
		GPU_shader_uniform_texture(shader, spec_location, tex_spec);
	}
	else {
		float density_scale = 10.0f;

		GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx);
		GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale);

		GPU_texture_bind(sds->tex, 0);
		GPU_shader_uniform_texture(shader, soot_location, sds->tex);

		GPU_texture_bind(sds->tex_shadow, 1);
		GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow);

		float active_color[3] = { 0.9, 0.9, 0.9 };
		if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
			mul_v3_v3(active_color, sds->active_color);
		GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
	}

	GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
	GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei);
	GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);
}
void GPU_pass_bind(GPUPass *pass, double time, int mipmap)
{
	GPUInput *input;
	GPUShader *shader = pass->shader;
	ListBase *inputs = &pass->inputs;

	if (!shader)
		return;

	GPU_shader_bind(shader);

	/* now bind the textures */
	for (input=inputs->first; input; input=input->next) {
		if (input->ima)
			input->tex = GPU_texture_from_blender(input->ima, input->iuser, input->image_isdata, time, mipmap);
		else if (input->prv)
			input->tex = GPU_texture_from_preview(input->prv, mipmap);

		if (input->tex && input->bindtex) {
			GPU_texture_bind(input->tex, input->texid);
			GPU_shader_uniform_texture(shader, input->shaderloc, input->tex);
		}
			
	}
}
예제 #4
0
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 */
	glBindBuffer(GL_ARRAY_BUFFER, fx->vbuffer);
	glVertexPointer(2, GL_FLOAT, 0, NULL);
	glTexCoordPointer(2, GL_FLOAT, 0, ((GLubyte *)NULL + 8 * sizeof(float)));
	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_texture_filter_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_texture_filter_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);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glPopAttrib();
}
예제 #5
0
void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
                       const float min[3], const float max[3],
                        const float viewnormal[3])
{
	if (!sds->tex || !sds->tex_shadow) {
		fprintf(stderr, "Could not allocate 3D texture for volume rendering!\n");
		return;
	}

	const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame;

	GPUShader *shader = GPU_shader_get_builtin_shader(
	                        (use_fire) ? GPU_SHADER_SMOKE_FIRE : GPU_SHADER_SMOKE);

	if (!shader) {
		fprintf(stderr, "Unable to create GLSL smoke shader.\n");
		return;
	}

	const float ob_sizei[3] = {
	    1.0f / fabsf(ob->size[0]),
	    1.0f / fabsf(ob->size[1]),
	    1.0f / fabsf(ob->size[2])
	};

	const float size[3] = { max[0] - min[0], max[1] - min[1], max[2] - min[2] };
	const float invsize[3] = { 1.0f / size[0], 1.0f / size[1], 1.0f / size[2] };

#ifdef DEBUG_DRAW_TIME
	TIMEIT_START(draw);
#endif

	/* setup smoke shader */

	int soot_location = GPU_shader_get_uniform(shader, "soot_texture");
	int spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
	int shadow_location = GPU_shader_get_uniform(shader, "shadow_texture");
	int flame_location = GPU_shader_get_uniform(shader, "flame_texture");
	int actcol_location = GPU_shader_get_uniform(shader, "active_color");
	int stepsize_location = GPU_shader_get_uniform(shader, "step_size");
	int densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
	int invsize_location = GPU_shader_get_uniform(shader, "invsize");
	int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei");
	int min_location = GPU_shader_get_uniform(shader, "min");

	GPU_shader_bind(shader);

	GPU_texture_bind(sds->tex, 0);
	GPU_shader_uniform_texture(shader, soot_location, sds->tex);

	GPU_texture_bind(sds->tex_shadow, 1);
	GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow);

	GPUTexture *tex_spec = NULL;

	if (use_fire) {
		GPU_texture_bind(sds->tex_flame, 2);
		GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame);

		tex_spec = create_flame_spectrum_texture();
		GPU_texture_bind(tex_spec, 3);
		GPU_shader_uniform_texture(shader, spec_location, tex_spec);
	}

	float active_color[3] = { 0.9, 0.9, 0.9 };
	float density_scale = 10.0f;
	if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
		mul_v3_v3(active_color, sds->active_color);

	GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
	GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx);
	GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale);
	GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
	GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei);
	GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);

	/* setup slicing information */

	const int max_slices = 256;
	const int max_points = max_slices * 12;

	VolumeSlicer slicer;
	copy_v3_v3(slicer.min, min);
	copy_v3_v3(slicer.max, max);
	copy_v3_v3(slicer.size, size);
	slicer.verts = MEM_mallocN(sizeof(float) * 3 * max_points, "smoke_slice_vertices");

	const int num_points = create_view_aligned_slices(&slicer, max_slices, viewnormal);

	/* setup buffer and draw */

	int gl_depth = 0, gl_blend = 0;
	glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend);
	glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

	GLuint vertex_buffer;
	glGenBuffers(1, &vertex_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer.verts[0][0], GL_STATIC_DRAW);

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, NULL);

	glDrawArrays(GL_TRIANGLES, 0, num_points);

	glDisableClientState(GL_VERTEX_ARRAY);

#ifdef DEBUG_DRAW_TIME
	printf("Draw Time: %f\n", (float)TIMEIT_VALUE(draw));
	TIMEIT_END(draw);
#endif

	/* cleanup */

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDeleteBuffers(1, &vertex_buffer);

	GPU_texture_unbind(sds->tex);
	GPU_texture_unbind(sds->tex_shadow);

	if (use_fire) {
		GPU_texture_unbind(sds->tex_flame);
		GPU_texture_unbind(tex_spec);
		GPU_texture_free(tex_spec);
	}

	MEM_freeN(slicer.verts);

	GPU_shader_unbind();

	if (!gl_blend) {
		glDisable(GL_BLEND);
	}

	if (gl_depth) {
		glEnable(GL_DEPTH_TEST);
	}
}
예제 #6
0
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;
}