int main () {
	assert (restart_gl_log ());
	assert (start_gl ());
	/* set up framebuffer with texture attachment */
	assert (init_fb ());
	/* load the picking shaders */
	g_pick_sp = create_programme_from_files (PICK_VS, PICK_FS);
	g_pick_unique_id_loc = glGetUniformLocation (g_pick_sp, "unique_id");
	g_pick_P_loc = glGetUniformLocation (g_pick_sp, "P");
	g_pick_V_loc = glGetUniformLocation (g_pick_sp, "V");
	g_pick_M_loc = glGetUniformLocation (g_pick_sp, "M");
	assert (g_pick_P_loc > -1);
	assert (g_pick_V_loc > -1);
	assert (g_pick_M_loc > -1);
	/* load a mesh to draw in the main scene */
	load_sphere ();
	GLuint sphere_sp = create_programme_from_files (SPHERE_VS, SPHERE_FS);
	GLint sphere_P_loc = glGetUniformLocation (sphere_sp, "P");
	GLint sphere_V_loc = glGetUniformLocation (sphere_sp, "V");
	GLint sphere_M_loc = glGetUniformLocation (sphere_sp, "M");
	assert (sphere_P_loc > -1);
	assert (sphere_V_loc > -1);
	assert (sphere_M_loc > -1);
	/* set up view and projection matrices for sphere shader */
	mat4 P = perspective (
		67.0f, (float)g_gl_width / (float)g_gl_height, 0.1f, 100.0f);
	mat4 V = look_at (
		vec3 (0.0f, 0.0f, 5.0f), vec3 (0.0f, 0.0f, 0.0f), vec3 (0.0f, 1.0f, 0.0f));
	glUseProgram (sphere_sp);
	glUniformMatrix4fv (sphere_P_loc, 1, GL_FALSE, P.m);
	glUniformMatrix4fv (sphere_V_loc, 1, GL_FALSE, V.m);
	
	glViewport (0, 0, g_gl_width, g_gl_height);
	
	while (!glfwWindowShouldClose (g_window)) {
		_update_fps_counter (g_window);
		
		/* bind the 'render to a texture' framebuffer for main scene */
		glBindFramebuffer (GL_FRAMEBUFFER, 0);
		/* clear the framebuffer's colour and depth buffers */
		glClearColor (0.2, 0.2, 0.2, 1.0);
		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		// render an obj or something
		glUseProgram (sphere_sp);
		glBindVertexArray (g_sphere_vao);
		
		// model matrices for all 3 spheres
		mat4 Ms[3];
		// first sphere
		Ms[0] = identity_mat4 ();
		glUniformMatrix4fv (sphere_M_loc, 1, GL_FALSE, Ms[0].m);
		glDrawArrays (GL_TRIANGLES, 0, g_sphere_point_count);
		// 2nd sphere
		Ms[1] = translate (identity_mat4 (), vec3 (1.0, -1.0, -4.0));
		glUniformMatrix4fv (sphere_M_loc, 1, GL_FALSE, Ms[1].m);
		glDrawArrays (GL_TRIANGLES, 0, g_sphere_point_count);
		// 3rd sphere
		Ms[2] = translate (identity_mat4 (), vec3 (-0.50, 2.0, -2.0));
		glUniformMatrix4fv (sphere_M_loc, 1, GL_FALSE, Ms[2].m);
		glDrawArrays (GL_TRIANGLES, 0, g_sphere_point_count);
		
		/* bind framebuffer for pick */
		draw_picker_colours (P, V, Ms);
		
		// flip drawn framebuffer onto the display
		glfwSwapBuffers (g_window);
		glfwPollEvents ();
		if (GLFW_PRESS == glfwGetKey (g_window, GLFW_KEY_ESCAPE)) {
			glfwSetWindowShouldClose (g_window, 1);
		}
		debug_colours = glfwGetKey (g_window, GLFW_KEY_SPACE);
		if (glfwGetMouseButton (g_window, 0)) {
			glBindFramebuffer (GL_FRAMEBUFFER, g_fb);
			double xpos, ypos;
			glfwGetCursorPos (g_window, &xpos, &ypos);
			int mx = (int)xpos;
			int my = (int)ypos;
			unsigned char data[4] = {0, 0, 0, 0};
 			glReadPixels (
 				mx, g_gl_height - my, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &data);
		  int id = decode_id ((int)data[0], (int)data[1], (int)data[2]);
			int mid = -1;
			if (id == 255) {
				mid = 0;
			}
			if (id == 65280) {
				mid = 1;
			}
			if (id == 16711680) {
				mid = 2;
			}
			printf ("%i,%i,%i means -> id was %i, and monkey number is %i\n",
				data[0], data[1], data[2], id, mid);
			glBindFramebuffer (GL_FRAMEBUFFER, 0);
		}
	}
	return 0;
}
int main () {
	assert (restart_gl_log ());
	assert (start_gl ());
	/* set up framebuffer with texture attachment */
	assert (init_fb ());
	init_ss_quad ();
	/* load the post-processing effect shaders */
	GLuint post_sp = create_programme_from_files (POST_VS, POST_FS);
	/* load a mesh to draw in the main scene */
	load_sphere ();
	GLuint sphere_sp = create_programme_from_files (SPHERE_VS, SPHERE_FS);
	GLint sphere_P_loc = glGetUniformLocation (sphere_sp, "P");
	GLint sphere_V_loc = glGetUniformLocation (sphere_sp, "V");
	assert (sphere_P_loc > -1);
	assert (sphere_V_loc > -1);
	/* set up view and projection matrices for sphere shader */
	mat4 P = perspective (
		67.0f, (float)g_gl_width / (float)g_gl_height, 0.1f, 100.0f);
	mat4 V = look_at (
		vec3 (0.0f, 0.0f, 5.0f), vec3 (0.0f, 0.0f, 0.0f), vec3 (0.0f, 1.0f, 0.0f));
	glUseProgram (sphere_sp);
	glUniformMatrix4fv (sphere_P_loc, 1, GL_FALSE, P.m);
	glUniformMatrix4fv (sphere_V_loc, 1, GL_FALSE, V.m);
	
	glViewport (0, 0, g_gl_width, g_gl_height);
	
	while (!glfwWindowShouldClose (g_window)) {
		_update_fps_counter (g_window);
		
		/* bind the 'render to a texture' framebuffer for main scene */
		glFlush ();
		glFinish ();
        glActiveTexture (GL_TEXTURE0);
        glBindTexture (GL_TEXTURE_2D, 0);
		glBindFramebuffer (GL_FRAMEBUFFER, g_fb);
        /* clear the framebuffer's colour and depth buffers */
		glClearColor (0.2, 0.2, 0.2, 1.0);
		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		// render an obj or something
		glUseProgram (sphere_sp);
		glBindVertexArray (g_sphere_vao);
		glDrawArrays (GL_TRIANGLES, 0, g_sphere_point_count);
		
		/* bind default framebuffer for post-processing effects. sample texture
		from previous pass */

		glFlush ();
		glFinish ();
		glBindFramebuffer (GL_FRAMEBUFFER, 0);

		// clear the framebuffer's colour and depth buffers 
//		glClearColor (0.0, 0.0, 0.0, 1.0);
//		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		// use our post-processing shader for the screen-space quad
		glUseProgram (post_sp);
		// bind the quad's VAO
		glBindVertexArray (g_ss_quad_vao);
		// activate the first texture slot and put texture from previous pass in it
		glActiveTexture (GL_TEXTURE0);
		glBindTexture (GL_TEXTURE_2D, g_fb_tex);
		// draw the quad that covers the screen area
		glDrawArrays (GL_TRIANGLES, 0, 6);
		
		// flip drawn framebuffer onto the display
		glfwSwapBuffers (g_window);
		glfwPollEvents ();
		if (GLFW_PRESS == glfwGetKey (g_window, GLFW_KEY_ESCAPE)) {
			glfwSetWindowShouldClose (g_window, 1);
		}
	}
	return 0;
}
int main () {
	/* initialise GL context and window */
	assert (restart_gl_log ());
	assert (start_gl ());
	/* initialise framebuffer and G-buffer */
	assert (init_fb ());
	/* load pre-pass shaders that write to the g-buffer */
	g_first_pass_sp = create_programme_from_files (
		FIRST_PASS_VS, FIRST_PASS_FS);
	g_first_pass_P_loc = glGetUniformLocation (g_first_pass_sp, "P");
	g_first_pass_V_loc = glGetUniformLocation (g_first_pass_sp, "V");
	g_first_pass_M_loc = glGetUniformLocation (g_first_pass_sp, "M");
	/* load screen-space pass shaders that read from the g-buffer */
	g_second_pass_sp = create_programme_from_files (
		SECOND_PASS_VS, SECOND_PASS_FS);
	g_second_pass_P_loc = glGetUniformLocation (g_second_pass_sp, "P");
	g_second_pass_V_loc = glGetUniformLocation (g_second_pass_sp, "V");
	g_second_pass_M_loc = glGetUniformLocation (g_second_pass_sp, "M");
	g_second_pass_L_p_loc = glGetUniformLocation (g_second_pass_sp, "lp");
	g_second_pass_L_d_loc = glGetUniformLocation (g_second_pass_sp, "ld");
	g_second_pass_L_s_loc = glGetUniformLocation (g_second_pass_sp, "ls");
	g_second_pass_p_tex_loc = glGetUniformLocation (g_second_pass_sp, "p_tex");
	g_second_pass_n_tex_loc = glGetUniformLocation (g_second_pass_sp, "n_tex");
	glUseProgram (g_second_pass_sp);
	glUniform1i (g_second_pass_p_tex_loc, 0);
	glUniform1i (g_second_pass_n_tex_loc, 1);
	
	/* object positions and matrices */
	assert (load_plane ());
	g_plane_M = scale (identity_mat4 (), vec3 (200.0f, 1.0f, 200.0f));
	g_plane_M = translate (g_plane_M, vec3 (0.0f, -2.0f, 0.0f));
	
	/* load sphere mesh */
	assert (load_sphere ());
	/* light positions and matrices */
	for (int i = 0; i < NUM_LIGHTS; i++) {
		float x = -sinf ((float)i * 0.5f) * 25.0f; // between +- 10 x
		float y = 2.0f;
		float z = (float)-i * 2.0f + 10.0; // 1 light every 2 meters away on z
		g_L_p[i] = vec3 (x, y, z);
	}
	
	float light_radius = 10.0f;
	int redi = 0;
	int bluei = 1;
	int greeni = 2;
	for (int i = 0; i < NUM_LIGHTS; i++) {
		g_L_M[i] = scale (identity_mat4 (),
			vec3 (light_radius, light_radius, light_radius));
		g_L_M[i] = translate (g_L_M[i], g_L_p[i]);
		/* cycle different colours for each of the lights */
		g_L_d[i] = vec3 (
			(float)((redi + 1) / 3),
			(float)((greeni + 1) / 3),
			(float)((bluei + 1) / 3)
		);
		g_L_s[i] = vec3 (1.0, 1.0, 1.0);
		redi = (redi + 1) % 3;
		bluei = (bluei + 1) % 3;
		greeni = (greeni + 1) % 3;
	}
	
	/* set up virtual camera */
	float aspect = (float)g_gl_width / (float)g_gl_height;
	float near = 0.1f;
	float far = 1000.0f;
	float fovy = 67.0f;
	g_P = perspective (fovy, aspect, near, far);
	vec3 up (0.0f, 1.0f, 0.0f);
	vec3 targ_pos (0.0f, 0.0f, 0.0f);
	vec3 cam_pos (0.0f, 30.0f, 30.0f);
	g_V = look_at (cam_pos, targ_pos, up);
	
	glViewport (0, 0, g_gl_width, g_gl_height);
	glEnable (GL_CULL_FACE); // cull face
	glCullFace (GL_BACK); // cull back face
	glFrontFace (GL_CCW); // GL_CCW for counter clock-wise
	while (!glfwWindowShouldClose (g_window)) {
		_update_fps_counter (g_window);
		draw_first_pass ();
		draw_second_pass ();
		
		glfwSwapBuffers (g_window);
		glfwPollEvents ();
		if (GLFW_PRESS == glfwGetKey (g_window, GLFW_KEY_ESCAPE)) {
			glfwSetWindowShouldClose (g_window, 1);
		}
	}
	
	/* close GL context and any other GLFW resources */
	glfwTerminate();
	return 0;
}