Jason McKesson avatar Jason McKesson committed 78e43af

Tut13: Geometry shader code complete and tested on ATI. Needs NVIDIA test.

Comments (0)

Files changed (7)

Tut 13 Impostors/BasicImposter.cpp

-#include <string>
-#include <vector>
-#include <stack>
-#include <math.h>
-#include <glloader/gl_3_3_comp.h>
-#include <GL/freeglut.h>
-#include "../framework/framework.h"
-#include "../framework/Mesh.h"
-#include "../framework/MatrixStack.h"
-#include "../framework/MousePole.h"
-#include "../framework/ObjectPole.h"
-#include "../framework/Timer.h"
-#include "../framework/UniformBlockArray.h"
-#include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
-
-struct ProgramMeshData
-{
-	GLuint theProgram;
-
-	GLuint modelToCameraMatrixUnif;
-	GLuint normalModelToCameraMatrixUnif;
-};
-
-struct ProgramImposData
-{
-	GLuint theProgram;
-
-	GLuint sphereRadiusUnif;
-	GLuint cameraSpherePosUnif;
-};
-
-struct UnlitProgData
-{
-	GLuint theProgram;
-
-	GLuint objectColorUnif;
-	GLuint modelToCameraMatrixUnif;
-};
-
-float g_fzNear = 1.0f;
-float g_fzFar = 1000.0f;
-
-enum Impostors
-{
-	IMP_BASIC,
- 	IMP_PERSPECTIVE,
- 	IMP_DEPTH,
-
-	IMP_NUM_IMPOSTORS,
-};
-
-ProgramMeshData g_litMeshProg;
-ProgramImposData g_litImpProgs[IMP_NUM_IMPOSTORS];
-UnlitProgData g_Unlit;
-
-const int g_materialBlockIndex = 0;
-const int g_lightBlockIndex = 1;
-const int g_projectionBlockIndex = 2;
-
-UnlitProgData LoadUnlitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
-{
-	std::vector<GLuint> shaderList;
-
-	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
-	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
-
-	UnlitProgData data;
-	data.theProgram = Framework::CreateProgram(shaderList);
-	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
-	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");
-
-	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
-	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
-
-	return data;
-}
-
-ProgramMeshData LoadLitMeshProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
-{
-	std::vector<GLuint> shaderList;
-
-	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
-	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
-
-	ProgramMeshData data;
-	data.theProgram = Framework::CreateProgram(shaderList);
-	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
-
-	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
-
-	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
-	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
-	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
-
-	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
-	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
-	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
-
-	return data;
-}
-
-ProgramImposData LoadLitImposProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
-{
-	std::vector<GLuint> shaderList;
-
-	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
-	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
-
-	ProgramImposData data;
-	data.theProgram = Framework::CreateProgram(shaderList);
-	data.sphereRadiusUnif = glGetUniformLocation(data.theProgram, "sphereRadius");
-	data.cameraSpherePosUnif = glGetUniformLocation(data.theProgram, "cameraSpherePos");
-
-	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
-	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
-	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
-
-	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
-	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
-	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
-
-	return data;
-}
-
-const char *g_impShaderNames[IMP_NUM_IMPOSTORS * 2] =
-{
-	"BasicImpostor.vert", "BasicImpostor.frag",
- 	"PerspImpostor.vert", "PerspImpostor.frag",
- 	"DepthImpostor.vert", "DepthImpostor.frag",
-};
-
-void InitializePrograms()
-{
-	g_litMeshProg = LoadLitMeshProgram("PN.vert", "Lighting.frag");
-
-	for(int iLoop = 0; iLoop < IMP_NUM_IMPOSTORS; iLoop++)
-	{
-		g_litImpProgs[iLoop] = LoadLitImposProgram(
-			g_impShaderNames[iLoop * 2], g_impShaderNames[iLoop * 2 + 1]);
-	}
-
-	g_Unlit = LoadUnlitProgram("Unlit.vert", "Unlit.frag");
-}
-
-Framework::RadiusDef radiusDef = {10.0f, 3.0f, 70.0f, 3.5f, 1.5f};
-glm::vec3 objectCenter = glm::vec3(0.0f, 30.0f, 25.0f);
-
-Framework::MousePole g_mousePole(objectCenter, radiusDef);
-Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
-
-namespace
-{
-	void MouseMotion(int x, int y)
-	{
-		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
-		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-
-	void MouseButton(int button, int state, int x, int y)
-	{
-		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
-		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-
-	void MouseWheel(int wheel, int direction, int x, int y)
-	{
-		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
-		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
-		glutPostRedisplay();
-	}
-}
-
-struct ProjectionBlock
-{
-	glm::mat4 cameraToClipMatrix;
-};
-
-struct PerLight
-{
-	glm::vec4 cameraSpaceLightPos;
-	glm::vec4 lightIntensity;
-};
-
-const int NUMBER_OF_LIGHTS = 2;
-
-struct LightBlock
-{
-	glm::vec4 ambientIntensity;
-	float lightAttenuation;
-	float padding[3];
-	PerLight lights[NUMBER_OF_LIGHTS];
-};
-
-struct MaterialBlock
-{
-	glm::vec4 diffuseColor;
-	glm::vec4 specularColor;
-	float specularShininess;
-	float padding[3];
-};
-
-Framework::Mesh *g_pPlaneMesh = NULL;
-Framework::Mesh *g_pSphereMesh = NULL;
-Framework::Mesh *g_pCubeMesh = NULL;
-
-GLuint g_lightUniformBuffer = 0;
-GLuint g_projectionUniformBuffer = 0;
-GLuint g_materialUniformBuffer = 0;
-
-int g_materialBlockOffset = 0;
-
-enum MaterialNames
-{
-	MTL_TERRAIN = 0,
-	MTL_BLUE_SHINY,
-	MTL_GOLD_METAL,
-	MTL_DULL_GREY,
-	MTL_BLACK_SHINY,
-
-	NUM_MATERIALS,
-};
-
-void CreateMaterials()
-{
-	Framework::UniformBlockArray<MaterialBlock, NUM_MATERIALS> ubArray;
-	g_materialBlockOffset = ubArray.GetArrayOffset();
-
-	MaterialBlock mtl;
-	mtl.diffuseColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
-	mtl.specularColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
-	mtl.specularShininess = 0.6f;
-	ubArray[MTL_TERRAIN] = mtl;
-
-	mtl.diffuseColor = glm::vec4(0.1f, 0.1f, 0.8f, 1.0f);
-	mtl.specularColor = glm::vec4(0.8f, 0.8f, 0.8f, 1.0f);
-	mtl.specularShininess = 0.1f;
-	ubArray[MTL_BLUE_SHINY] = mtl;
-
-	mtl.diffuseColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f);
-	mtl.specularColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f) * 0.75;
-	mtl.specularShininess = 0.18f;
-	ubArray[MTL_GOLD_METAL] = mtl;
-
-	mtl.diffuseColor = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
-	mtl.specularColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
-	mtl.specularShininess = 0.8f;
-	ubArray[MTL_DULL_GREY] = mtl;
-
-	mtl.diffuseColor = glm::vec4(0.05f, 0.05f, 0.05f, 1.0f);
-	mtl.specularColor = glm::vec4(0.95f, 0.95f, 0.95f, 1.0f);
-	mtl.specularShininess = 0.3f;
-	ubArray[MTL_BLACK_SHINY] = mtl;
-
-	g_materialUniformBuffer = ubArray.CreateBufferObject();
-}
-
-GLuint g_imposterVAO;
-GLuint g_imposterVBO;
-
-//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
-void init()
-{
-	InitializePrograms();
-
-	try
-	{
-		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");
-		g_pSphereMesh = new Framework::Mesh("UnitSphere.xml");
-		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");
-	}
-	catch(std::exception &except)
-	{
-		printf(except.what());
-		throw;
-	}
-
-	glutMouseFunc(MouseButton);
-	glutMotionFunc(MouseMotion);
-	glutMouseWheelFunc(MouseWheel);
-
-	glEnable(GL_CULL_FACE);
-	glCullFace(GL_BACK);
-	glFrontFace(GL_CW);
-
-	const float depthZNear = 0.0f;
-	const float depthZFar = 1.0f;
-
-	glEnable(GL_DEPTH_TEST);
-	glDepthMask(GL_TRUE);
-	glDepthFunc(GL_LEQUAL);
-	glDepthRange(depthZNear, depthZFar);
-	glEnable(GL_DEPTH_CLAMP);
-
-	//Setup our Uniform Buffers
-	glGenBuffers(1, &g_lightUniformBuffer);
-	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
-	glBufferData(GL_UNIFORM_BUFFER, sizeof(LightBlock), NULL, GL_DYNAMIC_DRAW);
-
-	glGenBuffers(1, &g_projectionUniformBuffer);
-	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
-	glBufferData(GL_UNIFORM_BUFFER, sizeof(ProjectionBlock), NULL, GL_DYNAMIC_DRAW);
-
-	//Bind the static buffers.
-	glBindBufferRange(GL_UNIFORM_BUFFER, g_lightBlockIndex, g_lightUniformBuffer,
-		0, sizeof(LightBlock));
-
-	glBindBufferRange(GL_UNIFORM_BUFFER, g_projectionBlockIndex, g_projectionUniformBuffer,
-		0, sizeof(ProjectionBlock));
-
-	glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
-	glGenBuffers(1, &g_imposterVBO);
-	glBindBuffer(GL_ARRAY_BUFFER, g_imposterVBO);
-	glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), NULL, GL_STATIC_DRAW);
-
-	glGenVertexArrays(1, &g_imposterVAO);
-	glBindVertexArray(g_imposterVAO);
-	glEnableVertexAttribArray(0);
-	glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, (void*)(0));
-
-	glBindVertexArray(0);
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	CreateMaterials();
-}
-
-int g_currImpostor = IMP_BASIC;
-
-void DrawSphere(Framework::MatrixStack &modelMatrix,
-				const glm::vec3 &position, float radius, MaterialNames material,
-				bool bDrawImposter = false)
-{
-	glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialUniformBuffer,
-		material * g_materialBlockOffset, sizeof(MaterialBlock));
-
-	if(bDrawImposter)
-	{
-		glm::vec4 cameraSpherePos = modelMatrix.Top() * glm::vec4(position, 1.0f);
-		glUseProgram(g_litImpProgs[g_currImpostor].theProgram);
-		glUniform3fv(g_litImpProgs[g_currImpostor].cameraSpherePosUnif, 1, glm::value_ptr(cameraSpherePos));
-		glUniform1f(g_litImpProgs[g_currImpostor].sphereRadiusUnif, radius);
-
-		glBindVertexArray(g_imposterVAO);
-
-		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-		glBindVertexArray(0);
-		glUseProgram(0);
-	}
-	else
-	{
-		Framework::MatrixStackPusher push(modelMatrix);
-		modelMatrix.Translate(position);
-		modelMatrix.Scale(radius * 2.0f); //The unit sphere has a radius 0.5f.
-
-		glm::mat3 normMatrix(modelMatrix.Top());
-		normMatrix = glm::transpose(glm::inverse(normMatrix));
-
-		glUseProgram(g_litMeshProg.theProgram);
-		glUniformMatrix4fv(g_litMeshProg.modelToCameraMatrixUnif, 1, GL_FALSE,
-			glm::value_ptr(modelMatrix.Top()));
-		glUniformMatrix3fv(g_litMeshProg.normalModelToCameraMatrixUnif, 1, GL_FALSE,
-			glm::value_ptr(normMatrix));
-
-		g_pSphereMesh->Render("lit");
-
-		glUseProgram(0);
-	}
-
-	glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
-}
-
-void DrawSphereOrbit(Framework::MatrixStack &modelMatrix,
-					 const glm::vec3 &orbitCenter, const glm::vec3 &orbitAxis,
-					 float orbitRadius, float orbitAlpha, float sphereRadius, MaterialNames material,
-					 bool drawImposter = false)
-{
-	Framework::MatrixStackPusher push(modelMatrix);
-
-	modelMatrix.Translate(orbitCenter);
-	modelMatrix.Rotate(orbitAxis, 360.0f * orbitAlpha);
-
-	glm::vec3 offsetDir = glm::cross(orbitAxis, glm::vec3(0.0f, 1.0f, 0.0f));
-	if(glm::length(offsetDir) < 0.001f)
-		offsetDir = glm::cross(orbitAxis, glm::vec3(1.0f, 0.0f, 0.0f));
-
-	offsetDir = glm::normalize(offsetDir);
-
-	modelMatrix.Translate(offsetDir * orbitRadius);
-
-	DrawSphere(modelMatrix, glm::vec3(0.0f), sphereRadius, material, drawImposter);
-}
-
-bool g_bDrawCameraPos = false;
-bool g_bDrawLights = true;
-
-Framework::Timer g_sphereTimer(Framework::Timer::TT_LOOP, 6.0f);
-
-float g_lightHeight = 20.0f;
-
-glm::vec4 CalcLightPosition()
-{
-	const float fLoopDuration = 5.0f;
-	const float fScale = 3.14159f * 2.0f;
-
-	float timeThroughLoop = g_sphereTimer.GetAlpha();
-
-	glm::vec4 ret(0.0f, g_lightHeight, 0.0f, 1.0f);
-
-	ret.x = cosf(timeThroughLoop * fScale) * 20.0f;
-	ret.z = sinf(timeThroughLoop * fScale) * 20.0f;
-
-	return ret;
-}
-
-const float g_fHalfLightDistance = 25.0f;
-const float g_fLightAttenuation = 1.0f / (g_fHalfLightDistance * g_fHalfLightDistance);
-
-bool g_drawImposter[4] = { false, false, false, false };
-
-//Called to update the display.
-//You should call glutSwapBuffers after all of your rendering to display what you rendered.
-//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
-void display()
-{
-	g_sphereTimer.Update();
-
-	glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
-	glClearDepth(1.0f);
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-	if(g_pPlaneMesh && g_pSphereMesh && g_pCubeMesh)
-	{
-		Framework::MatrixStack modelMatrix;
-		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());
-		const glm::mat4 &worldToCamMat = modelMatrix.Top();
-
-		LightBlock lightData;
-
-		lightData.ambientIntensity = glm::vec4(0.2f, 0.2f, 0.2f, 1.0f);
-		lightData.lightAttenuation = g_fLightAttenuation;
-
-		lightData.lights[0].cameraSpaceLightPos = worldToCamMat * glm::vec4(0.707f, 0.707f, 0.0f, 0.0f);
-		lightData.lights[0].lightIntensity = glm::vec4(0.6f, 0.6f, 0.6f, 1.0f);
-
-		lightData.lights[1].cameraSpaceLightPos = worldToCamMat * CalcLightPosition();
-		lightData.lights[1].lightIntensity = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
-
-		glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
-		glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(lightData), &lightData);
-		glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
-		{
-			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialUniformBuffer,
-				MTL_TERRAIN * g_materialBlockOffset, sizeof(MaterialBlock));
-
-			glm::mat3 normMatrix(modelMatrix.Top());
-			normMatrix = glm::transpose(glm::inverse(normMatrix));
-
-			glUseProgram(g_litMeshProg.theProgram);
-			glUniformMatrix4fv(g_litMeshProg.modelToCameraMatrixUnif, 1, GL_FALSE,
-				glm::value_ptr(modelMatrix.Top()));
-			glUniformMatrix3fv(g_litMeshProg.normalModelToCameraMatrixUnif, 1, GL_FALSE,
-				glm::value_ptr(normMatrix));
-
-			g_pPlaneMesh->Render();
-
-			glUseProgram(0);
-			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
-		}
-
-		DrawSphere(modelMatrix, glm::vec3(0.0f, 10.0f, 0.0f), 4.0f, MTL_BLUE_SHINY,
-			g_drawImposter[0]);
-		DrawSphereOrbit(modelMatrix, glm::vec3(0.0f, 10.0f, 0.0f), glm::vec3(0.6f, 0.8f, 0.0f),
-			20.0f, g_sphereTimer.GetAlpha(), 2.0f, MTL_DULL_GREY, g_drawImposter[1]);
-		DrawSphereOrbit(modelMatrix, glm::vec3(-10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
-			10.0f, g_sphereTimer.GetAlpha(), 1.0f, MTL_BLACK_SHINY, g_drawImposter[2]);
-		DrawSphereOrbit(modelMatrix, glm::vec3(10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
-			10.0f, g_sphereTimer.GetAlpha() * 2.0f, 1.0f, MTL_GOLD_METAL, g_drawImposter[3]);
-
-		if(g_bDrawLights)
-		{
-			Framework::MatrixStackPusher push(modelMatrix);
-
-			modelMatrix.Translate(glm::vec3(CalcLightPosition()));
-			modelMatrix.Scale(0.5f);
-
-			glUseProgram(g_Unlit.theProgram);
-			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
-				glm::value_ptr(modelMatrix.Top()));
-
-			glm::vec4 lightColor(1.0f);
-			glUniform4fv(g_Unlit.objectColorUnif, 1, glm::value_ptr(lightColor));
-			g_pCubeMesh->Render("flat");
-		}
-
-		if(g_bDrawCameraPos)
-		{
-			Framework::MatrixStackPusher push(modelMatrix);
-
-			modelMatrix.SetIdentity();
-			modelMatrix.Translate(glm::vec3(0.0f, 0.0f, -g_mousePole.GetLookAtDistance()));
-
-			glDisable(GL_DEPTH_TEST);
-			glDepthMask(GL_FALSE);
-			glUseProgram(g_Unlit.theProgram);
-			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
-				glm::value_ptr(modelMatrix.Top()));
-			glUniform4f(g_Unlit.objectColorUnif, 0.25f, 0.25f, 0.25f, 1.0f);
-			g_pCubeMesh->Render("flat");
-			glDepthMask(GL_TRUE);
-			glEnable(GL_DEPTH_TEST);
-			glUniform4f(g_Unlit.objectColorUnif, 1.0f, 1.0f, 1.0f, 1.0f);
-			g_pCubeMesh->Render("flat");
-		}
-	}
-
-	glutPostRedisplay();
-	glutSwapBuffers();
-}
-
-//Called whenever the window is resized. The new window size is given, in pixels.
-//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
-void reshape (int w, int h)
-{
-	Framework::MatrixStack persMatrix;
-	persMatrix.Perspective(45.0f, (h / (float)w), g_fzNear, g_fzFar);
-
-	ProjectionBlock projData;
-	projData.cameraToClipMatrix = persMatrix.Top();
-
-	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
-	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
-	glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
-	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
-	glutPostRedisplay();
-}
-
-//Called whenever a key on the keyboard was pressed.
-//The key is given by the ''key'' parameter, which is in ASCII.
-//It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to 
-//exit the program.
-void keyboard(unsigned char key, int x, int y)
-{
-	switch (key)
-	{
-	case 27:
-		delete g_pPlaneMesh;
-		delete g_pSphereMesh;
-		g_pPlaneMesh = NULL;
-		g_pSphereMesh = NULL;
-		glutLeaveMainLoop();
-		break;
-
-	case 'p': g_sphereTimer.TogglePause(); break;
-	case '-': g_sphereTimer.Rewind(0.5f); break;
-	case '=': g_sphereTimer.Fastforward(0.5f); break;
-	case 't': g_bDrawCameraPos = !g_bDrawCameraPos; break;
-	case 'g': g_bDrawLights = !g_bDrawLights; break;
-
-	case '1': g_drawImposter[0] = !g_drawImposter[0]; break;
-	case '2': g_drawImposter[1] = !g_drawImposter[1]; break;
-	case '3': g_drawImposter[2] = !g_drawImposter[2]; break;
-	case '4': g_drawImposter[3] = !g_drawImposter[3]; break;
-
-	case 'l':
-		{
-			g_currImpostor += 1;
-			g_currImpostor %= IMP_NUM_IMPOSTORS;
-			const char *impostorNames[IMP_NUM_IMPOSTORS] =
-			{
-				"basic",
- 				"perspective-correct",
- 				"depth-accurate",
-			};
-
-			printf("Now using %s impostor.\n", impostorNames[g_currImpostor]);
-		}
-		break;
-
-	case 32: InitializePrograms(); break;
-
-	}
-
-	g_mousePole.GLUTKeyOffset(key, 5.0f, 1.0f);
-}
-

Tut 13 Impostors/BasicImpostor.cpp

+#include <string>
+#include <vector>
+#include <stack>
+#include <math.h>
+#include <glloader/gl_3_3_comp.h>
+#include <GL/freeglut.h>
+#include "../framework/framework.h"
+#include "../framework/Mesh.h"
+#include "../framework/MatrixStack.h"
+#include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
+#include "../framework/Timer.h"
+#include "../framework/UniformBlockArray.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+struct ProgramMeshData
+{
+	GLuint theProgram;
+
+	GLuint modelToCameraMatrixUnif;
+	GLuint normalModelToCameraMatrixUnif;
+};
+
+struct ProgramImposData
+{
+	GLuint theProgram;
+
+	GLuint sphereRadiusUnif;
+	GLuint cameraSpherePosUnif;
+};
+
+struct UnlitProgData
+{
+	GLuint theProgram;
+
+	GLuint objectColorUnif;
+	GLuint modelToCameraMatrixUnif;
+};
+
+float g_fzNear = 1.0f;
+float g_fzFar = 1000.0f;
+
+enum Impostors
+{
+	IMP_BASIC,
+ 	IMP_PERSPECTIVE,
+ 	IMP_DEPTH,
+
+	IMP_NUM_IMPOSTORS,
+};
+
+ProgramMeshData g_litMeshProg;
+ProgramImposData g_litImpProgs[IMP_NUM_IMPOSTORS];
+UnlitProgData g_Unlit;
+
+const int g_materialBlockIndex = 0;
+const int g_lightBlockIndex = 1;
+const int g_projectionBlockIndex = 2;
+
+UnlitProgData LoadUnlitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	UnlitProgData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");
+
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+ProgramMeshData LoadLitMeshProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramMeshData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+
+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
+
+	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
+	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+
+	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
+	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+ProgramImposData LoadLitImposProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramImposData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.sphereRadiusUnif = glGetUniformLocation(data.theProgram, "sphereRadius");
+	data.cameraSpherePosUnif = glGetUniformLocation(data.theProgram, "cameraSpherePos");
+
+	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
+	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+
+	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
+	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+const char *g_impShaderNames[IMP_NUM_IMPOSTORS * 2] =
+{
+	"BasicImpostor.vert", "BasicImpostor.frag",
+ 	"PerspImpostor.vert", "PerspImpostor.frag",
+ 	"DepthImpostor.vert", "DepthImpostor.frag",
+};
+
+void InitializePrograms()
+{
+	g_litMeshProg = LoadLitMeshProgram("PN.vert", "Lighting.frag");
+
+	for(int iLoop = 0; iLoop < IMP_NUM_IMPOSTORS; iLoop++)
+	{
+		g_litImpProgs[iLoop] = LoadLitImposProgram(
+			g_impShaderNames[iLoop * 2], g_impShaderNames[iLoop * 2 + 1]);
+	}
+
+	g_Unlit = LoadUnlitProgram("Unlit.vert", "Unlit.frag");
+}
+
+Framework::RadiusDef radiusDef = {10.0f, 3.0f, 70.0f, 3.5f, 1.5f};
+glm::vec3 objectCenter = glm::vec3(0.0f, 30.0f, 25.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
+
+namespace
+{
+	void MouseMotion(int x, int y)
+	{
+		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+
+	void MouseButton(int button, int state, int x, int y)
+	{
+		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+
+	void MouseWheel(int wheel, int direction, int x, int y)
+	{
+		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+}
+
+struct ProjectionBlock
+{
+	glm::mat4 cameraToClipMatrix;
+};
+
+struct PerLight
+{
+	glm::vec4 cameraSpaceLightPos;
+	glm::vec4 lightIntensity;
+};
+
+const int NUMBER_OF_LIGHTS = 2;
+
+struct LightBlock
+{
+	glm::vec4 ambientIntensity;
+	float lightAttenuation;
+	float padding[3];
+	PerLight lights[NUMBER_OF_LIGHTS];
+};
+
+struct MaterialBlock
+{
+	glm::vec4 diffuseColor;
+	glm::vec4 specularColor;
+	float specularShininess;
+	float padding[3];
+};
+
+Framework::Mesh *g_pPlaneMesh = NULL;
+Framework::Mesh *g_pSphereMesh = NULL;
+Framework::Mesh *g_pCubeMesh = NULL;
+
+GLuint g_lightUniformBuffer = 0;
+GLuint g_projectionUniformBuffer = 0;
+GLuint g_materialUniformBuffer = 0;
+
+int g_materialBlockOffset = 0;
+
+enum MaterialNames
+{
+	MTL_TERRAIN = 0,
+	MTL_BLUE_SHINY,
+	MTL_GOLD_METAL,
+	MTL_DULL_GREY,
+	MTL_BLACK_SHINY,
+
+	NUM_MATERIALS,
+};
+
+void CreateMaterials()
+{
+	Framework::UniformBlockArray<MaterialBlock, NUM_MATERIALS> ubArray;
+	g_materialBlockOffset = ubArray.GetArrayOffset();
+
+	MaterialBlock mtl;
+	mtl.diffuseColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
+	mtl.specularColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
+	mtl.specularShininess = 0.6f;
+	ubArray[MTL_TERRAIN] = mtl;
+
+	mtl.diffuseColor = glm::vec4(0.1f, 0.1f, 0.8f, 1.0f);
+	mtl.specularColor = glm::vec4(0.8f, 0.8f, 0.8f, 1.0f);
+	mtl.specularShininess = 0.1f;
+	ubArray[MTL_BLUE_SHINY] = mtl;
+
+	mtl.diffuseColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f);
+	mtl.specularColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f) * 0.75;
+	mtl.specularShininess = 0.18f;
+	ubArray[MTL_GOLD_METAL] = mtl;
+
+	mtl.diffuseColor = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
+	mtl.specularColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
+	mtl.specularShininess = 0.8f;
+	ubArray[MTL_DULL_GREY] = mtl;
+
+	mtl.diffuseColor = glm::vec4(0.05f, 0.05f, 0.05f, 1.0f);
+	mtl.specularColor = glm::vec4(0.95f, 0.95f, 0.95f, 1.0f);
+	mtl.specularShininess = 0.3f;
+	ubArray[MTL_BLACK_SHINY] = mtl;
+
+	g_materialUniformBuffer = ubArray.CreateBufferObject();
+}
+
+GLuint g_imposterVAO;
+GLuint g_imposterVBO;
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializePrograms();
+
+	try
+	{
+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");
+		g_pSphereMesh = new Framework::Mesh("UnitSphere.xml");
+		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");
+	}
+	catch(std::exception &except)
+	{
+		printf(except.what());
+		throw;
+	}
+
+	glutMouseFunc(MouseButton);
+	glutMotionFunc(MouseMotion);
+	glutMouseWheelFunc(MouseWheel);
+
+	glEnable(GL_CULL_FACE);
+	glCullFace(GL_BACK);
+	glFrontFace(GL_CW);
+
+	const float depthZNear = 0.0f;
+	const float depthZFar = 1.0f;
+
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+	glDepthFunc(GL_LEQUAL);
+	glDepthRange(depthZNear, depthZFar);
+	glEnable(GL_DEPTH_CLAMP);
+
+	//Setup our Uniform Buffers
+	glGenBuffers(1, &g_lightUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(LightBlock), NULL, GL_DYNAMIC_DRAW);
+
+	glGenBuffers(1, &g_projectionUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(ProjectionBlock), NULL, GL_DYNAMIC_DRAW);
+
+	//Bind the static buffers.
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_lightBlockIndex, g_lightUniformBuffer,
+		0, sizeof(LightBlock));
+
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_projectionBlockIndex, g_projectionUniformBuffer,
+		0, sizeof(ProjectionBlock));
+
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+	glGenBuffers(1, &g_imposterVBO);
+	glBindBuffer(GL_ARRAY_BUFFER, g_imposterVBO);
+	glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), NULL, GL_STATIC_DRAW);
+
+	glGenVertexArrays(1, &g_imposterVAO);
+	glBindVertexArray(g_imposterVAO);
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, (void*)(0));
+
+	glBindVertexArray(0);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	CreateMaterials();
+}
+
+int g_currImpostor = IMP_BASIC;
+
+void DrawSphere(Framework::MatrixStack &modelMatrix,
+				const glm::vec3 &position, float radius, MaterialNames material,
+				bool bDrawImposter = false)
+{
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialUniformBuffer,
+		material * g_materialBlockOffset, sizeof(MaterialBlock));
+
+	if(bDrawImposter)
+	{
+		glm::vec4 cameraSpherePos = modelMatrix.Top() * glm::vec4(position, 1.0f);
+		glUseProgram(g_litImpProgs[g_currImpostor].theProgram);
+		glUniform3fv(g_litImpProgs[g_currImpostor].cameraSpherePosUnif, 1, glm::value_ptr(cameraSpherePos));
+		glUniform1f(g_litImpProgs[g_currImpostor].sphereRadiusUnif, radius);
+
+		glBindVertexArray(g_imposterVAO);
+
+		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+		glBindVertexArray(0);
+		glUseProgram(0);
+	}
+	else
+	{
+		Framework::MatrixStackPusher push(modelMatrix);
+		modelMatrix.Translate(position);
+		modelMatrix.Scale(radius * 2.0f); //The unit sphere has a radius 0.5f.
+
+		glm::mat3 normMatrix(modelMatrix.Top());
+		normMatrix = glm::transpose(glm::inverse(normMatrix));
+
+		glUseProgram(g_litMeshProg.theProgram);
+		glUniformMatrix4fv(g_litMeshProg.modelToCameraMatrixUnif, 1, GL_FALSE,
+			glm::value_ptr(modelMatrix.Top()));
+		glUniformMatrix3fv(g_litMeshProg.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+			glm::value_ptr(normMatrix));
+
+		g_pSphereMesh->Render("lit");
+
+		glUseProgram(0);
+	}
+
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
+}
+
+void DrawSphereOrbit(Framework::MatrixStack &modelMatrix,
+					 const glm::vec3 &orbitCenter, const glm::vec3 &orbitAxis,
+					 float orbitRadius, float orbitAlpha, float sphereRadius, MaterialNames material,
+					 bool drawImposter = false)
+{
+	Framework::MatrixStackPusher push(modelMatrix);
+
+	modelMatrix.Translate(orbitCenter);
+	modelMatrix.Rotate(orbitAxis, 360.0f * orbitAlpha);
+
+	glm::vec3 offsetDir = glm::cross(orbitAxis, glm::vec3(0.0f, 1.0f, 0.0f));
+	if(glm::length(offsetDir) < 0.001f)
+		offsetDir = glm::cross(orbitAxis, glm::vec3(1.0f, 0.0f, 0.0f));
+
+	offsetDir = glm::normalize(offsetDir);
+
+	modelMatrix.Translate(offsetDir * orbitRadius);
+
+	DrawSphere(modelMatrix, glm::vec3(0.0f), sphereRadius, material, drawImposter);
+}
+
+bool g_bDrawCameraPos = false;
+bool g_bDrawLights = true;
+
+Framework::Timer g_sphereTimer(Framework::Timer::TT_LOOP, 6.0f);
+
+float g_lightHeight = 20.0f;
+
+glm::vec4 CalcLightPosition()
+{
+	const float fLoopDuration = 5.0f;
+	const float fScale = 3.14159f * 2.0f;
+
+	float timeThroughLoop = g_sphereTimer.GetAlpha();
+
+	glm::vec4 ret(0.0f, g_lightHeight, 0.0f, 1.0f);
+
+	ret.x = cosf(timeThroughLoop * fScale) * 20.0f;
+	ret.z = sinf(timeThroughLoop * fScale) * 20.0f;
+
+	return ret;
+}
+
+const float g_fHalfLightDistance = 25.0f;
+const float g_fLightAttenuation = 1.0f / (g_fHalfLightDistance * g_fHalfLightDistance);
+
+bool g_drawImposter[4] = { false, false, false, false };
+
+//Called to update the display.
+//You should call glutSwapBuffers after all of your rendering to display what you rendered.
+//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
+void display()
+{
+	g_sphereTimer.Update();
+
+	glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	if(g_pPlaneMesh && g_pSphereMesh && g_pCubeMesh)
+	{
+		Framework::MatrixStack modelMatrix;
+		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());
+		const glm::mat4 &worldToCamMat = modelMatrix.Top();
+
+		LightBlock lightData;
+
+		lightData.ambientIntensity = glm::vec4(0.2f, 0.2f, 0.2f, 1.0f);
+		lightData.lightAttenuation = g_fLightAttenuation;
+
+		lightData.lights[0].cameraSpaceLightPos = worldToCamMat * glm::vec4(0.707f, 0.707f, 0.0f, 0.0f);
+		lightData.lights[0].lightIntensity = glm::vec4(0.6f, 0.6f, 0.6f, 1.0f);
+
+		lightData.lights[1].cameraSpaceLightPos = worldToCamMat * CalcLightPosition();
+		lightData.lights[1].lightIntensity = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
+
+		glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+		glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(lightData), &lightData);
+		glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+		{
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialUniformBuffer,
+				MTL_TERRAIN * g_materialBlockOffset, sizeof(MaterialBlock));
+
+			glm::mat3 normMatrix(modelMatrix.Top());
+			normMatrix = glm::transpose(glm::inverse(normMatrix));
+
+			glUseProgram(g_litMeshProg.theProgram);
+			glUniformMatrix4fv(g_litMeshProg.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+			glUniformMatrix3fv(g_litMeshProg.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(normMatrix));
+
+			g_pPlaneMesh->Render();
+
+			glUseProgram(0);
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
+		}
+
+		DrawSphere(modelMatrix, glm::vec3(0.0f, 10.0f, 0.0f), 4.0f, MTL_BLUE_SHINY,
+			g_drawImposter[0]);
+		DrawSphereOrbit(modelMatrix, glm::vec3(0.0f, 10.0f, 0.0f), glm::vec3(0.6f, 0.8f, 0.0f),
+			20.0f, g_sphereTimer.GetAlpha(), 2.0f, MTL_DULL_GREY, g_drawImposter[1]);
+		DrawSphereOrbit(modelMatrix, glm::vec3(-10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
+			10.0f, g_sphereTimer.GetAlpha(), 1.0f, MTL_BLACK_SHINY, g_drawImposter[2]);
+		DrawSphereOrbit(modelMatrix, glm::vec3(10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
+			10.0f, g_sphereTimer.GetAlpha() * 2.0f, 1.0f, MTL_GOLD_METAL, g_drawImposter[3]);
+
+		if(g_bDrawLights)
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			modelMatrix.Translate(glm::vec3(CalcLightPosition()));
+			modelMatrix.Scale(0.5f);
+
+			glUseProgram(g_Unlit.theProgram);
+			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+
+			glm::vec4 lightColor(1.0f);
+			glUniform4fv(g_Unlit.objectColorUnif, 1, glm::value_ptr(lightColor));
+			g_pCubeMesh->Render("flat");
+		}
+
+		if(g_bDrawCameraPos)
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			modelMatrix.SetIdentity();
+			modelMatrix.Translate(glm::vec3(0.0f, 0.0f, -g_mousePole.GetLookAtDistance()));
+
+			glDisable(GL_DEPTH_TEST);
+			glDepthMask(GL_FALSE);
+			glUseProgram(g_Unlit.theProgram);
+			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+			glUniform4f(g_Unlit.objectColorUnif, 0.25f, 0.25f, 0.25f, 1.0f);
+			g_pCubeMesh->Render("flat");
+			glDepthMask(GL_TRUE);
+			glEnable(GL_DEPTH_TEST);
+			glUniform4f(g_Unlit.objectColorUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+			g_pCubeMesh->Render("flat");
+		}
+	}
+
+	glutPostRedisplay();
+	glutSwapBuffers();
+}
+
+//Called whenever the window is resized. The new window size is given, in pixels.
+//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
+void reshape (int w, int h)
+{
+	Framework::MatrixStack persMatrix;
+	persMatrix.Perspective(45.0f, (h / (float)w), g_fzNear, g_fzFar);
+
+	ProjectionBlock projData;
+	projData.cameraToClipMatrix = persMatrix.Top();
+
+	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
+	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
+	glutPostRedisplay();
+}
+
+//Called whenever a key on the keyboard was pressed.
+//The key is given by the ''key'' parameter, which is in ASCII.
+//It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to 
+//exit the program.
+void keyboard(unsigned char key, int x, int y)
+{
+	switch (key)
+	{
+	case 27:
+		delete g_pPlaneMesh;
+		delete g_pSphereMesh;
+		g_pPlaneMesh = NULL;
+		g_pSphereMesh = NULL;
+		glutLeaveMainLoop();
+		break;
+
+	case 'p': g_sphereTimer.TogglePause(); break;
+	case '-': g_sphereTimer.Rewind(0.5f); break;
+	case '=': g_sphereTimer.Fastforward(0.5f); break;
+	case 't': g_bDrawCameraPos = !g_bDrawCameraPos; break;
+	case 'g': g_bDrawLights = !g_bDrawLights; break;
+
+	case '1': g_drawImposter[0] = !g_drawImposter[0]; break;
+	case '2': g_drawImposter[1] = !g_drawImposter[1]; break;
+	case '3': g_drawImposter[2] = !g_drawImposter[2]; break;
+	case '4': g_drawImposter[3] = !g_drawImposter[3]; break;
+
+	case 'l':
+		{
+			g_currImpostor += 1;
+			g_currImpostor %= IMP_NUM_IMPOSTORS;
+			const char *impostorNames[IMP_NUM_IMPOSTORS] =
+			{
+				"basic",
+ 				"perspective-correct",
+ 				"depth-accurate",
+			};
+
+			printf("Now using %s impostor.\n", impostorNames[g_currImpostor]);
+		}
+		break;
+
+	case 32: InitializePrograms(); break;
+
+	}
+
+	g_mousePole.GLUTKeyOffset(key, 5.0f, 1.0f);
+}
+

Tut 13 Impostors/GeomImpostor.cpp

+#include <string>
+#include <vector>
+#include <stack>
+#include <math.h>
+#include <glloader/gl_3_3_comp.h>
+#include <GL/freeglut.h>
+#include "../framework/framework.h"
+#include "../framework/Mesh.h"
+#include "../framework/MatrixStack.h"
+#include "../framework/MousePole.h"
+#include "../framework/ObjectPole.h"
+#include "../framework/Timer.h"
+#include "../framework/UniformBlockArray.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+struct ProgramMeshData
+{
+	GLuint theProgram;
+
+	GLuint modelToCameraMatrixUnif;
+	GLuint normalModelToCameraMatrixUnif;
+};
+
+struct ProgramImposData
+{
+	GLuint theProgram;
+};
+
+struct UnlitProgData
+{
+	GLuint theProgram;
+
+	GLuint objectColorUnif;
+	GLuint modelToCameraMatrixUnif;
+};
+
+float g_fzNear = 1.0f;
+float g_fzFar = 1000.0f;
+
+ProgramMeshData g_litMeshProg;
+ProgramImposData g_litImpProg;
+UnlitProgData g_Unlit;
+
+const int g_materialBlockIndex = 0;
+const int g_lightBlockIndex = 1;
+const int g_projectionBlockIndex = 2;
+
+UnlitProgData LoadUnlitProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	UnlitProgData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");
+
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+ProgramMeshData LoadLitMeshProgram(const std::string &strVertexShader, const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramMeshData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+
+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
+
+	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
+	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+
+	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
+	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+ProgramImposData LoadLitImposProgram(const std::string &strVertexShader,
+									 const std::string &strGeometryShader,
+									 const std::string &strFragmentShader)
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, strVertexShader));
+	shaderList.push_back(Framework::LoadShader(GL_GEOMETRY_SHADER, strGeometryShader));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, strFragmentShader));
+
+	ProgramImposData data;
+	data.theProgram = Framework::CreateProgram(shaderList);
+
+	GLuint materialBlock = glGetUniformBlockIndex(data.theProgram, "Material");
+	GLuint lightBlock = glGetUniformBlockIndex(data.theProgram, "Light");
+	GLuint projectionBlock = glGetUniformBlockIndex(data.theProgram, "Projection");
+
+	glUniformBlockBinding(data.theProgram, materialBlock, g_materialBlockIndex);
+	glUniformBlockBinding(data.theProgram, lightBlock, g_lightBlockIndex);
+	glUniformBlockBinding(data.theProgram, projectionBlock, g_projectionBlockIndex);
+
+	return data;
+}
+
+void InitializePrograms()
+{
+	g_litMeshProg = LoadLitMeshProgram("PN.vert", "Lighting.frag");
+
+	g_litImpProg = LoadLitImposProgram("GeomImpostor.vert", "GeomImpostor.geom",
+		"GeomImpostor.frag");
+
+	g_Unlit = LoadUnlitProgram("Unlit.vert", "Unlit.frag");
+}
+
+Framework::RadiusDef radiusDef = {10.0f, 3.0f, 70.0f, 3.5f, 1.5f};
+glm::vec3 objectCenter = glm::vec3(0.0f, 30.0f, 25.0f);
+
+Framework::MousePole g_mousePole(objectCenter, radiusDef);
+Framework::ObjectPole g_objectPole(objectCenter, &g_mousePole);
+
+namespace
+{
+	void MouseMotion(int x, int y)
+	{
+		g_mousePole.GLUTMouseMove(glm::ivec2(x, y));
+		g_objectPole.GLUTMouseMove(glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+
+	void MouseButton(int button, int state, int x, int y)
+	{
+		g_mousePole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseButton(button, state, glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+
+	void MouseWheel(int wheel, int direction, int x, int y)
+	{
+		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		g_objectPole.GLUTMouseWheel(direction, glm::ivec2(x, y));
+		glutPostRedisplay();
+	}
+}
+
+struct ProjectionBlock
+{
+	glm::mat4 cameraToClipMatrix;
+};
+
+struct PerLight
+{
+	glm::vec4 cameraSpaceLightPos;
+	glm::vec4 lightIntensity;
+};
+
+const int NUMBER_OF_LIGHTS = 2;
+
+struct LightBlock
+{
+	glm::vec4 ambientIntensity;
+	float lightAttenuation;
+	float padding[3];
+	PerLight lights[NUMBER_OF_LIGHTS];
+};
+
+struct MaterialBlock
+{
+	glm::vec4 diffuseColor;
+	glm::vec4 specularColor;
+	float specularShininess;
+	float padding[3];
+};
+
+Framework::Mesh *g_pPlaneMesh = NULL;
+Framework::Mesh *g_pSphereMesh = NULL;
+Framework::Mesh *g_pCubeMesh = NULL;
+
+GLuint g_lightUniformBuffer = 0;
+GLuint g_projectionUniformBuffer = 0;
+GLuint g_materialArrayUniformBuffer = 0;
+GLuint g_materialTerrainUniformBuffer = 0;
+
+const int NUMBER_OF_SPHERES = 4;
+
+void CreateMaterials()
+{
+	std::vector<MaterialBlock> ubArray(NUMBER_OF_SPHERES);
+
+	ubArray[0].diffuseColor = glm::vec4(0.1f, 0.1f, 0.8f, 1.0f);
+	ubArray[0].specularColor = glm::vec4(0.8f, 0.8f, 0.8f, 1.0f);
+	ubArray[0].specularShininess = 0.1f;
+
+	ubArray[1].diffuseColor = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
+	ubArray[1].specularColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
+	ubArray[1].specularShininess = 0.8f;
+
+	ubArray[2].diffuseColor = glm::vec4(0.05f, 0.05f, 0.05f, 1.0f);
+	ubArray[2].specularColor = glm::vec4(0.95f, 0.95f, 0.95f, 1.0f);
+	ubArray[2].specularShininess = 0.3f;
+
+	ubArray[3].diffuseColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f);
+	ubArray[3].specularColor = glm::vec4(0.803f, 0.709f, 0.15f, 1.0f) * 0.75;
+	ubArray[3].specularShininess = 0.18f;
+
+	glGenBuffers(1, &g_materialArrayUniformBuffer);
+	glGenBuffers(1, &g_materialTerrainUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_materialArrayUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(MaterialBlock) * ubArray.size(), &ubArray[0], GL_STATIC_DRAW);
+
+	glBindBuffer(GL_UNIFORM_BUFFER, g_materialTerrainUniformBuffer);
+	MaterialBlock mtl;
+	mtl.diffuseColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
+	mtl.specularColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f);
+	mtl.specularShininess = 0.6f;
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(MaterialBlock), &mtl, GL_STATIC_DRAW);
+
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
+GLuint g_imposterVAO;
+GLuint g_imposterVBO;
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializePrograms();
+
+	try
+	{
+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");
+		g_pSphereMesh = new Framework::Mesh("UnitSphere.xml");
+		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");
+	}
+	catch(std::exception &except)
+	{
+		printf(except.what());
+		throw;
+	}
+
+	glutMouseFunc(MouseButton);
+	glutMotionFunc(MouseMotion);
+	glutMouseWheelFunc(MouseWheel);
+
+	glEnable(GL_CULL_FACE);
+	glCullFace(GL_BACK);
+	glFrontFace(GL_CW);
+
+	const float depthZNear = 0.0f;
+	const float depthZFar = 1.0f;
+
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+	glDepthFunc(GL_LEQUAL);
+	glDepthRange(depthZNear, depthZFar);
+	glEnable(GL_DEPTH_CLAMP);
+
+	//Setup our Uniform Buffers
+	glGenBuffers(1, &g_lightUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(LightBlock), NULL, GL_DYNAMIC_DRAW);
+
+	glGenBuffers(1, &g_projectionUniformBuffer);
+	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
+	glBufferData(GL_UNIFORM_BUFFER, sizeof(ProjectionBlock), NULL, GL_DYNAMIC_DRAW);
+
+	//Bind the static buffers.
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_lightBlockIndex, g_lightUniformBuffer,
+		0, sizeof(LightBlock));
+
+	glBindBufferRange(GL_UNIFORM_BUFFER, g_projectionBlockIndex, g_projectionUniformBuffer,
+		0, sizeof(ProjectionBlock));
+
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+	glGenBuffers(1, &g_imposterVBO);
+	glBindBuffer(GL_ARRAY_BUFFER, g_imposterVBO);
+	glBufferData(GL_ARRAY_BUFFER, NUMBER_OF_SPHERES * 4 * sizeof(float), NULL, GL_STREAM_DRAW);
+
+	glGenVertexArrays(1, &g_imposterVAO);
+	glBindVertexArray(g_imposterVAO);
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 16, (void*)(0));
+	glEnableVertexAttribArray(1);
+	glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 16, (void*)(12));
+
+	glBindVertexArray(0);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	glEnable(GL_PROGRAM_POINT_SIZE);
+
+	CreateMaterials();
+}
+
+glm::vec3 GetSphereOrbitPos(Framework::MatrixStack &modelMatrix,
+							const glm::vec3 &orbitCenter, const glm::vec3 &orbitAxis,
+							float orbitRadius, float orbitAlpha)
+{
+	Framework::MatrixStackPusher push(modelMatrix);
+
+	modelMatrix.Translate(orbitCenter);
+	modelMatrix.Rotate(orbitAxis, 360.0f * orbitAlpha);
+
+	glm::vec3 offsetDir = glm::cross(orbitAxis, glm::vec3(0.0f, 1.0f, 0.0f));
+	if(glm::length(offsetDir) < 0.001f)
+		offsetDir = glm::cross(orbitAxis, glm::vec3(1.0f, 0.0f, 0.0f));
+
+	offsetDir = glm::normalize(offsetDir);
+
+	modelMatrix.Translate(offsetDir * orbitRadius);
+
+	return glm::vec3(modelMatrix.Top() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+}
+
+bool g_bDrawCameraPos = false;
+bool g_bDrawLights = true;
+
+Framework::Timer g_sphereTimer(Framework::Timer::TT_LOOP, 6.0f);
+
+float g_lightHeight = 20.0f;
+
+glm::vec4 CalcLightPosition()
+{
+	const float fLoopDuration = 5.0f;
+	const float fScale = 3.14159f * 2.0f;
+
+	float timeThroughLoop = g_sphereTimer.GetAlpha();
+
+	glm::vec4 ret(0.0f, g_lightHeight, 0.0f, 1.0f);
+
+	ret.x = cosf(timeThroughLoop * fScale) * 20.0f;
+	ret.z = sinf(timeThroughLoop * fScale) * 20.0f;
+
+	return ret;
+}
+
+const float g_fHalfLightDistance = 25.0f;
+const float g_fLightAttenuation = 1.0f / (g_fHalfLightDistance * g_fHalfLightDistance);
+
+struct VertexData
+{
+	glm::vec3 cameraPosition;
+	float sphereRadius;
+};
+
+//Called to update the display.
+//You should call glutSwapBuffers after all of your rendering to display what you rendered.
+//If you need continuous updates of the screen, call glutPostRedisplay() at the end of the function.
+void display()
+{
+	g_sphereTimer.Update();
+
+	glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
+	glClearDepth(1.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	if(g_pPlaneMesh && g_pSphereMesh && g_pCubeMesh)
+	{
+		Framework::MatrixStack modelMatrix;
+		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());
+		const glm::mat4 &worldToCamMat = modelMatrix.Top();
+
+		LightBlock lightData;
+
+		lightData.ambientIntensity = glm::vec4(0.2f, 0.2f, 0.2f, 1.0f);
+		lightData.lightAttenuation = g_fLightAttenuation;
+
+		lightData.lights[0].cameraSpaceLightPos = worldToCamMat * glm::vec4(0.707f, 0.707f, 0.0f, 0.0f);
+		lightData.lights[0].lightIntensity = glm::vec4(0.6f, 0.6f, 0.6f, 1.0f);
+
+		lightData.lights[1].cameraSpaceLightPos = worldToCamMat * CalcLightPosition();
+		lightData.lights[1].lightIntensity = glm::vec4(0.4f, 0.4f, 0.4f, 1.0f);
+
+		glBindBuffer(GL_UNIFORM_BUFFER, g_lightUniformBuffer);
+		glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(lightData), &lightData);
+		glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+		{
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialTerrainUniformBuffer,
+				0, sizeof(MaterialBlock));
+
+			glm::mat3 normMatrix(modelMatrix.Top());
+			normMatrix = glm::transpose(glm::inverse(normMatrix));
+
+			glUseProgram(g_litMeshProg.theProgram);
+			glUniformMatrix4fv(g_litMeshProg.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+			glUniformMatrix3fv(g_litMeshProg.normalModelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(normMatrix));
+
+			g_pPlaneMesh->Render();
+
+			glUseProgram(0);
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
+		}
+
+		{
+			VertexData posSizeArray[NUMBER_OF_SPHERES];
+
+			posSizeArray[0].cameraPosition = glm::vec3(worldToCamMat * glm::vec4(0.0f, 10.0f, 0.0f, 1.0f));
+			posSizeArray[0].sphereRadius = 4.0f;
+
+			posSizeArray[1].cameraPosition = GetSphereOrbitPos(modelMatrix,
+				glm::vec3(0.0f, 10.0f, 0.0f), glm::vec3(0.6f, 0.8f, 0.0f), 20.0f,
+				g_sphereTimer.GetAlpha());
+			posSizeArray[1].sphereRadius = 2.0f;
+
+			posSizeArray[2].cameraPosition = GetSphereOrbitPos(modelMatrix,
+				glm::vec3(-10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
+				10.0f, g_sphereTimer.GetAlpha());
+			posSizeArray[2].sphereRadius = 1.0f;
+
+			posSizeArray[3].cameraPosition = GetSphereOrbitPos(modelMatrix,
+				glm::vec3(10.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f),
+				10.0f, g_sphereTimer.GetAlpha() * 2.0f);
+			posSizeArray[3].sphereRadius = 1.0f;
+
+			glBindBuffer(GL_ARRAY_BUFFER, g_imposterVBO);
+			glBufferData(GL_ARRAY_BUFFER, NUMBER_OF_SPHERES * sizeof(VertexData), posSizeArray,
+				GL_STREAM_DRAW);
+			glBindBuffer(GL_ARRAY_BUFFER, 0);
+		}
+
+		{
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, g_materialArrayUniformBuffer,
+				0, sizeof(MaterialBlock) * NUMBER_OF_SPHERES);
+			
+			glUseProgram(g_litImpProg.theProgram);
+			glBindVertexArray(g_imposterVAO);
+			glDrawArrays(GL_POINTS, 0, NUMBER_OF_SPHERES);
+			glBindVertexArray(0);
+			glUseProgram(0);
+
+			glBindBufferRange(GL_UNIFORM_BUFFER, g_materialBlockIndex, 0, 0, 0);
+		}
+
+		if(g_bDrawLights)
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			modelMatrix.Translate(glm::vec3(CalcLightPosition()));
+			modelMatrix.Scale(0.5f);
+
+			glUseProgram(g_Unlit.theProgram);
+			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+
+			glm::vec4 lightColor(1.0f);
+			glUniform4fv(g_Unlit.objectColorUnif, 1, glm::value_ptr(lightColor));
+			g_pCubeMesh->Render("flat");
+		}
+
+		if(g_bDrawCameraPos)
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			modelMatrix.SetIdentity();
+			modelMatrix.Translate(glm::vec3(0.0f, 0.0f, -g_mousePole.GetLookAtDistance()));
+
+			glDisable(GL_DEPTH_TEST);
+			glDepthMask(GL_FALSE);
+			glUseProgram(g_Unlit.theProgram);
+			glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,
+				glm::value_ptr(modelMatrix.Top()));
+			glUniform4f(g_Unlit.objectColorUnif, 0.25f, 0.25f, 0.25f, 1.0f);
+			g_pCubeMesh->Render("flat");
+			glDepthMask(GL_TRUE);
+			glEnable(GL_DEPTH_TEST);
+			glUniform4f(g_Unlit.objectColorUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+			g_pCubeMesh->Render("flat");
+		}
+	}
+
+	glutPostRedisplay();
+	glutSwapBuffers();
+}
+
+//Called whenever the window is resized. The new window size is given, in pixels.
+//This is an opportunity to call glViewport or glScissor to keep up with the change in size.
+void reshape (int w, int h)
+{
+	Framework::MatrixStack persMatrix;
+	persMatrix.Perspective(45.0f, (h / (float)w), g_fzNear, g_fzFar);
+
+	ProjectionBlock projData;
+	projData.cameraToClipMatrix = persMatrix.Top();
+
+	glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
+	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
+	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
+	glutPostRedisplay();
+}
+
+//Called whenever a key on the keyboard was pressed.
+//The key is given by the ''key'' parameter, which is in ASCII.
+//It's often a good idea to have the escape key (ASCII value 27) call glutLeaveMainLoop() to 
+//exit the program.
+void keyboard(unsigned char key, int x, int y)
+{
+	switch (key)
+	{
+	case 27:
+		delete g_pPlaneMesh;
+		delete g_pSphereMesh;
+		g_pPlaneMesh = NULL;
+		g_pSphereMesh = NULL;
+		glutLeaveMainLoop();
+		break;
+
+	case 'p': g_sphereTimer.TogglePause(); break;
+	case '-': g_sphereTimer.Rewind(0.5f); break;
+	case '=': g_sphereTimer.Fastforward(0.5f); break;
+	case 't': g_bDrawCameraPos = !g_bDrawCameraPos; break;
+	case 'g': g_bDrawLights = !g_bDrawLights; break;
+
+	case 32: InitializePrograms(); break;
+
+	}
+
+	g_mousePole.GLUTKeyOffset(key, 5.0f, 1.0f);
+}
+

Tut 13 Impostors/data/GeomImpostor.frag

+#version 330
+
+in FragData
+{
+	flat vec3 cameraSpherePos;
+	flat float sphereRadius;
+	smooth vec2 mapping;
+};
+
+out vec4 outputColor;
+
+layout(std140) uniform;
+
+struct MaterialEntry
+{
+	vec4 diffuseColor;
+	vec4 specularColor;
+	vec4 specularShininess;		//ATI Array Bug fix. Not really a vec4.
+};
+
+const int NUMBER_OF_SPHERES = 4;
+
+uniform Material
+{
+	MaterialEntry material[NUMBER_OF_SPHERES];
+} Mtl;
+
+struct PerLight
+{
+	vec4 cameraSpaceLightPos;
+	vec4 lightIntensity;
+};
+
+const int numberOfLights = 2;
+
+uniform Light
+{
+	vec4 ambientIntensity;
+	float lightAttenuation;
+	PerLight lights[numberOfLights];
+} Lgt;
+
+
+float CalcAttenuation(in vec3 cameraSpacePosition,
+	in vec3 cameraSpaceLightPos,
+	out vec3 lightDirection)
+{
+	vec3 lightDifference =  cameraSpaceLightPos - cameraSpacePosition;
+	float lightDistanceSqr = dot(lightDifference, lightDifference);
+	lightDirection = lightDifference * inversesqrt(lightDistanceSqr);
+	
+	return (1 / ( 1.0 + Lgt.lightAttenuation * lightDistanceSqr));
+}
+
+uniform Projection
+{
+	mat4 cameraToClipMatrix;
+};
+
+vec4 ComputeLighting(in PerLight lightData, in vec3 cameraSpacePosition,
+	in vec3 cameraSpaceNormal, in MaterialEntry material)
+{
+	vec3 lightDir;
+	vec4 lightIntensity;
+	if(lightData.cameraSpaceLightPos.w == 0.0)
+	{
+		lightDir = vec3(lightData.cameraSpaceLightPos);
+		lightIntensity = lightData.lightIntensity;
+	}
+	else
+	{
+		float atten = CalcAttenuation(cameraSpacePosition,
+			lightData.cameraSpaceLightPos.xyz, lightDir);
+		lightIntensity = atten * lightData.lightIntensity;
+	}
+	
+	vec3 surfaceNormal = normalize(cameraSpaceNormal);
+	float cosAngIncidence = dot(surfaceNormal, lightDir);
+	cosAngIncidence = cosAngIncidence < 0.0001 ? 0.0 : cosAngIncidence;
+	
+	vec3 viewDirection = normalize(-cameraSpacePosition);
+	
+	vec3 halfAngle = normalize(lightDir + viewDirection);
+	float angleNormalHalf = acos(dot(halfAngle, surfaceNormal));
+	float exponent = angleNormalHalf / material.specularShininess.x;
+	exponent = -(exponent * exponent);
+	float gaussianTerm = exp(exponent);
+
+	gaussianTerm = cosAngIncidence != 0.0 ? gaussianTerm : 0.0;
+	
+	vec4 lighting = material.diffuseColor * lightIntensity * cosAngIncidence;
+	lighting += material.specularColor * lightIntensity * gaussianTerm;
+	
+	return lighting;
+}
+
+void Impostor(out vec3 cameraPos, out vec3 cameraNormal)
+{
+	vec3 cameraPlanePos = vec3(mapping * sphereRadius, 0.0) + cameraSpherePos;
+	vec3 rayDirection = normalize(cameraPlanePos);
+	
+	float B = 2.0 * dot(rayDirection, -cameraSpherePos);
+	float C = dot(cameraSpherePos, cameraSpherePos) - (sphereRadius * sphereRadius);
+	
+	float det = (B * B) - (4 * C);
+	if(det < 0.0)
+		discard;
+		
+	float sqrtDet = sqrt(det);
+	float posT = (-B + sqrtDet)/2;
+	float negT = (-B - sqrtDet)/2;
+	
+	float intersectT = min(posT, negT);
+	cameraPos = rayDirection * intersectT;
+	cameraNormal = normalize(cameraPos - cameraSpherePos);
+}
+
+void main()
+{
+
+	vec3 cameraPos;
+	vec3 cameraNormal;
+	
+	Impostor(cameraPos, cameraNormal);
+	
+	//Set the depth based on the new cameraPos.
+	vec4 clipPos = cameraToClipMatrix * vec4(cameraPos, 1.0);
+	float ndcDepth = clipPos.z / clipPos.w;
+	gl_FragDepth = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;
+	
+	vec4 accumLighting = Mtl.material[gl_PrimitiveID].diffuseColor * Lgt.ambientIntensity;
+	for(int light = 0; light < numberOfLights; light++)
+	{
+		accumLighting += ComputeLighting(Lgt.lights[light],
+			cameraPos, cameraNormal, Mtl.material[gl_PrimitiveID]);
+	}
+	
+	outputColor = sqrt(accumLighting); //2.0 gamma correction
+}

Tut 13 Impostors/data/GeomImpostor.geom

+#version 330
+
+layout(std140) uniform;
+layout(points) in;
+layout(triangle_strip, max_vertices=4) out;
+
+uniform Projection
+{
+	mat4 cameraToClipMatrix;
+};
+
+out FragData
+{
+	flat vec3 cameraSpherePos;
+	flat float sphereRadius;
+	smooth vec2 mapping;
+};
+
+const float g_boxCorrection = 1.5;
+
+void main()
+{
+	vec4 cameraCornerPos;
+	//Bottom-left
+	mapping = vec2(-1.0, -1.0) * g_boxCorrection;
+	cameraSpherePos = vec3(gl_in[0].gl_Position);
+	sphereRadius = gl_in[0].gl_PointSize;
+	cameraCornerPos = gl_in[0].gl_Position;
+	cameraCornerPos.xy += vec2(-gl_in[0].gl_PointSize, -gl_in[0].gl_PointSize) * g_boxCorrection;
+	gl_Position = cameraToClipMatrix * cameraCornerPos;
+	gl_PrimitiveID = gl_PrimitiveIDIn;
+	EmitVertex();
+	
+	//Top-left
+	mapping = vec2(-1.0, 1.0) * g_boxCorrection;
+	cameraSpherePos = vec3(gl_in[0].gl_Position);
+	sphereRadius = gl_in[0].gl_PointSize;
+	cameraCornerPos = gl_in[0].gl_Position;
+	cameraCornerPos.xy += vec2(-gl_in[0].gl_PointSize, gl_in[0].gl_PointSize) * g_boxCorrection;
+	gl_Position = cameraToClipMatrix * cameraCornerPos;
+	gl_PrimitiveID = gl_PrimitiveIDIn;
+	EmitVertex();
+	
+	//Bottom-right
+	mapping = vec2(1.0, -1.0) * g_boxCorrection;
+	cameraSpherePos = vec3(gl_in[0].gl_Position);
+	sphereRadius = gl_in[0].gl_PointSize;
+	cameraCornerPos = gl_in[0].gl_Position;
+	cameraCornerPos.xy += vec2(gl_in[0].gl_PointSize, -gl_in[0].gl_PointSize) * g_boxCorrection;
+	gl_Position = cameraToClipMatrix * cameraCornerPos;
+	gl_PrimitiveID = gl_PrimitiveIDIn;
+	EmitVertex();
+	
+	//Top-right
+	mapping = vec2(1.0, 1.0) * g_boxCorrection;
+	cameraSpherePos = vec3(gl_in[0].gl_Position);
+	sphereRadius = gl_in[0].gl_PointSize;
+	cameraCornerPos = gl_in[0].gl_Position;
+	cameraCornerPos.xy += vec2(gl_in[0].gl_PointSize, gl_in[0].gl_PointSize) * g_boxCorrection;
+	gl_Position = cameraToClipMatrix * cameraCornerPos;
+	gl_PrimitiveID = gl_PrimitiveIDIn;
+	EmitVertex();
+}

Tut 13 Impostors/data/GeomImpostor.vert

+#version 330
+
+layout(std140) uniform;
+
+layout(location = 0) in vec3 cameraSpherePos;
+layout(location = 1) in float sphereRadius;
+
+uniform Projection
+{
+	mat4 cameraToClipMatrix;
+};
+
+void main()
+{
+	gl_Position = vec4(cameraSpherePos, 1.0);
+	gl_PointSize = sphereRadius;
+}

Tut 13 Impostors/tutorials.lua

 
-SetupProject("Tut 13 Basic Imposter", "BasicImposter.cpp",
-	"data/BasicImposter.vert",
-	"data/BasicImposter.frag",
-	"data/DepthImposter.vert",
-	"data/DepthImposter.frag",
-	"data/PerspImposter.vert",
-	"data/PerspImposter.frag",
+SetupProject("Tut 13 Basic Impostor", "BasicImpostor.cpp",
+	"data/BasicImpostor.vert",
+	"data/BasicImpostor.frag",
+	"data/DepthImpostor.vert",
+	"data/DepthImpostor.frag",
+	"data/PerspImpostor.vert",
+	"data/PerspImpostor.frag",
 	"data/PN.vert",
 	"data/Lighting.frag")
 
+SetupProject("Tut 13 Geometry Impostor", "GeomImpostor.cpp",
+	"data/GeomImpostor.vert",
+	"data/GeomImpostor.geom",
+	"data/GeomImpostor.frag")
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.