# Commits

committed 8343d52

Started Tutorial 09 writeup.
Also moved the perspective correction to Tut 12.

• Participants
• Parent commits d4d0025

# File Documents/Illumination/Tutorial 09.xml

`+<?xml version="1.0" encoding="UTF-8"?>`
`+<?oxygen RNGSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng" type="xml"?>`
`+<?oxygen SCHSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng"?>`
`+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude"`
`+    xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0">`
`+    <?dbhtml filename="Tutorial 09.html" ?>`
`+    <title>Plane Lights</title>`
`+    <para>Directional lights are useful for representing light sources like the sun and so forth.`
`+        But most light sources are more likely to be represented as point lights.</para>`
`+    <para>A <glossterm>point light</glossterm> is a light source that has a position in the world`
`+        and shines with equal intensity in all directions. Our simple diffuse lighting equation is a`
`+        function of these properties:</para>`
`+    <itemizedlist>`
`+        <listitem>`
`+            <para>The surface normal at that point.</para>`
`+        </listitem>`
`+        <listitem>`
`+            <para>The direction from the point on the surface to the light.</para>`
`+        </listitem>`
`+    </itemizedlist>`
`+    <para>The direction to the light source from the point is a constant when dealing with`
`+        directional light. It is a parameter for lighting, but it is a constant value for all points`
`+        in the scene. The difference between directional lighting and point lights is only that this`
`+        direction must be computed for each position in the scene.</para>`
`+    <para>Computing this is quite simple. At the point of interest, we take the difference between`
`+        the point on the surface and the light's position. We normalize the result to produce a unit`
`+        vector direction to the light. Then we use the light direction as we did before. The surface`
`+        point, light position, and surface normal must all be in the same space for this equation to`
`+        make sense.</para>`
`+    <section>`
`+        <title>Vertex Point Lighting</title>`
`+        <para>Thus far, we have computed the lighting equation at each vertex and interpolated the`
`+            results across the surface of the triangle. We will continue to do so for point lights.`
`+            For the moment, at least.</para>`
`+        <para>We implement point lights per-vertex in the <phrase role="propername">Vertex Point`
`+                Lighting</phrase> tutorial. This tutorial has a moving point light that circles`
`+            around the cylinder.</para>`
`+        <!--TODO: Show a picture of the tutorial.-->`
`+        <para>It controls as follows:</para>`
`+        <!--TODO: Have a table explaining the tutorial's controls.-->`
`+        <para>Most of the code is nothing we haven't seen elsewhere. The main changes are at the top`
`+            of the rendering function.</para>`
`+        <example>`
`+            <title>Per-Vertex Point Light Rendering</title>`
`+            <programlisting language="cpp">Framework::MatrixStack modelMatrix;`
`+modelMatrix.SetMatrix(g_mousePole.CalcMatrix());`
`+`
`+const glm::vec4 &amp;worldLightPos = CalcLightPosition();`
`+`
`+glm::vec4 lightPosCameraSpace = modelMatrix.Top() * worldLightPos;`
`+`
`+glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`+glUniform3fv(g_WhiteAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));`
`+glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`+glUniform3fv(g_VertexAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));</programlisting>`
`+        </example>`
`+        <para>The light is computed initially in world space, then transformed into camera space.`
`+            The camera-space light position is given to both of the shaders. Rendering proceeds`
`+            normally from there.</para>`
`+        <para>Our vertex shader has had a few changes:</para>`
`+        <example>`
`+            <title>Per-Vertex Point Light Vertex Shader</title>`
`+            <programlisting>#version 330`
`+`
`+layout(location = 0) in vec3 position;`
`+layout(location = 1) in vec4 diffuseColor;`
`+layout(location = 2) in vec3 normal;`
`+`
`+smooth out vec4 interpColor;`
`+`
`+uniform vec3 lightPos;`
`+uniform vec4 lightIntensity;`
`+uniform vec4 ambientIntensity;`
`+`
`+uniform mat4 cameraToClipMatrix;`
`+uniform mat4 modelToCameraMatrix;`
`+`
`+uniform mat3 normalModelToCameraMatrix;`
`+`
`+void main()`
`+{`
`+    vec4 cameraPosition = (modelToCameraMatrix * vec4(position, 1.0));`
`+    gl_Position = cameraToClipMatrix * cameraPosition;`
`+    `
`+    vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);`
`+    `
`+    vec3 dirToLight = normalize(lightPos - vec3(cameraPosition));`
`+    `
`+    float cosAngIncidence = dot(normCamSpace, dirToLight);`
`+    cosAngIncidence = clamp(cosAngIncidence, 0, 1);`
`+    `
`+    interpColor = (diffuseColor * lightIntensity * cosAngIncidence) +`
`+        (diffuseColor * ambientIntensity);`
`+}</programlisting>`
`+        </example>`
`+        <para>The vertex shader takes a camera-space light position instead of a camera-space light`
`+            direction. It also stores the camera-space vertex position in a temporary in the first`
`+            line of <function>main</function>. This is used to compute the direction to the light.`
`+            From there, the computation proceeds normally.</para>`
`+        <para>Note the order of operations in computing <varname>dirToLight.</varname> The`
`+                <varname>lightPos</varname> is on the left and the <varname>cameraPosition</varname>`
`+            is on the right. Geometrically, this is correct. If you have two points, and you want to`
`+            find the direction from point A to point B, you compute B - A. The`
`+                <function>normalize</function> call is just to convert it into a unit vector.</para>`
`+    </section>`
`+    <section>`
`+        <title>Interpolation</title>`
`+        <para>As you can see, doing point lighting is quite simple. Unfortunately, the visual`
`+            results are not.</para>`
`+        <para>For example, use the controls to display the position of the point light source, then`
`+            position it near the ground plane. See anything wrong?</para>`
`+        <para>If everything were working correctly, one would expect to see a bright area directly`
`+            under the light. After all, geometrically, this situation looks like this:</para>`
`+        <!--TODO: Show a 2D diagram of a point light near a line surface.-->`
`+        <para>The area directly under the point should be very bright, but the area farther from the`
`+            point light should be darker. What we see is nothing of the sort. There is no bright`
`+            light directly under the light source. Why is that?</para>`
`+        <para>Well, consider what we are doing. We are computing the lighting at every triangle's`
`+                <emphasis>vertex</emphasis>, and then interpolating the results across the surface`
`+            of the triangle. The ground plane is made up of precisely four vertices: the four`
`+            corners. And those are all very far from the light position. Since none of the vertices`
`+            are close to the light, none of the colors that are interpolated across the surface are`
`+            bright.</para>`
`+        <para>You can see this is evident by putting the light position next to the cylinder. If the`
`+            light is at the top or bottom of the cylinder, then the area near the light will be`
`+            bright. But if you move the light to the middle of the cylinder, far the top or bottom`
`+            vertices, then the illumination will be much dimmer.</para>`
`+        <!--TODO: Show this tutorial with the light at the middle of the cylinder.-->`
`+        <para>This is not the only problem with doing per-vertex lighting. For example, run the`
`+            tutorial again and don't move the light. Just watch how the light behaves on the`
`+            cylinder's surface as it animates around. Unlike with directional lighting, you can very`
`+            easily see the triangles on the cylinder's surface. This has issues for similar reasons,`
`+            but it also introduces a new problem: interpolation artifacts.</para>`
`+        <para>If you move the light source farther away, you can see that the triangles smooth out.`
`+            But this is simply because, if the light source is far enough away, the results are`
`+            indistinguishable from a directional light. Each vertex's direction to the light is`
`+            almost the same as each other vertex's direction to the light.</para>`
`+        <para>Per-vertex lighting was reasonable when dealing with directional lights. But it simply`
`+            is not a good idea for point lighting. The question arises: why was per-vertex lighting`
`+            good with directional lights to begin with?</para>`
`+        <para>Remember that our diffuse lighting equation has two parameters: the direction to the`
`+            light and the surface normal. In directional lighting, the direction to the light is`
`+            always the same. Therefore, the only value that changes over a triangle's surface is the`
`+            surface normal.</para>`
`+        <para>The more physically correct method of lighting is to perform lighting at every`
`+            rendered pixel. To do that, we would have to interpolate the lighting parameters across`
`+            the triangle, and perform the lighting computation in the fragment shader.</para>`
`+        <para>Linear interpolation of vectors looks like this:</para>`
`+        <!--TODO: equation: Va& + Va(1-&)-->`
`+        <para>And our lighting equation is this:</para>`
`+        <!--TODO: eq: D * I * dot(L, N)-->`
`+        <para>If the surface normal N is being interpolated, then at any particular point on the`
`+            surface, we get this equation:</para>`
`+        <!--TODO: D * I * dot(L, Na& + Nb(1-&))-->`
`+        <para>The dot product is distributive, like scalar multiplication. So we can distribute the`
`+            L to both sides of the dot product term:</para>`
`+        <!--TODO: D * I * (dot(L, Na&) + dot(L, Nb(1-&)))-->`
`+        <para>We can extract the linear terms from the dot product. Remember that the dot product is`
`+            the cosine of the angle between two vectors, times the length of those vectors. The two`
`+            scaling terms directly modify the length of the vectors. So they can be pulled out to`
`+            give us:</para>`
`+        <!--TODO: D * I * (&dot(L, Na) + (1-&)dot(L, Nb))-->`
`+        <para>Vector multiplication happens to be distributive as well, so we get this:</para>`
`+        <!--TODO: (D * I * &dot(L, Na)) + (D * I * (1-&)dot(L, Nb))-->`
`+        <para>Or, rewritten to make things more clear:</para>`
`+        <!--TODO: (D * I * dot(L, Na))& + (D * I * dot(L, Nb))(1-&)-->`
`+        <para>This means that if L is constant, linearly interpolating N is exactly equivalent to`
`+            linearly interpolating the results of the lighting equation. And the addition of the`
`+            ambient term doesn't change this, since it is a constant and would not be affected by`
`+            linear interpolation.</para>`
`+        <para>When doing point lighting, you would have to interpolate both N and L. And that does`
`+            not yield the same results as linearly interpolating the two colors you get from the`
`+            lighting equation. This is a big part of the reason why the cylinder doesn't look`
`+            correct.</para>`
`+    </section>`
`+    <section>`
`+        <title>Fragment Lighting</title>`
`+        <para>So, in order to deal with interpolation artifacts, we need to interpolate the actual`
`+            light direction and normal, instead of just the results of the lighting equation. This`
`+            is called per-fragment lighting or just <glossterm>fragment lighting.</glossterm></para>`
`+        <para>There is a problem that needs to be dealt with first. Normals do not interpolate well.`
`+            Or rather, wildly different normals do not interpolate well. And light directions can be`
`+            very different.</para>`
`+        <para>Consider the large plane we have. The direction toward the light will be very`
`+            different at each vertex, so long as our light remains in relatively close proximity to`
`+            the plane.</para>`
`+        <para>Part of the problem is with interpolating values along the diagonal of our triangle.`
`+            Using two triangles to form a square plane does not mean that the values at the four`
`+            vertices interpolate the way you would expect. The plane is actually made of these two`
`+            triangles:</para>`
`+        <!--TODO: Show a picture of the two triangles composing the plane.-->`
`+        <para>The interpolation always happens between the three vertices of the particular`
`+            triangle. Which means that vertices near the diagonal will be basically doing a linear`
`+            interpolation between the two values on either end of that diagonal. This is not the`
`+            same thing as doing interpolation between all 4 values.</para>`
`+        <!--TODO: Show bilinear interpolation vs. triangular interpolation.-->`
`+        <para>In our case, this means that for points along the main diagonal, the light direction`
`+            will only be composed of the direction values from the two vertices on that diagonal.`
`+            This is not good. This wouldn't be much of a problem if the light direction did not`
`+            change much along the surface, but that is not the case here.</para>`
`+        <para>Since we cannot interpolate the light direction very well, we need to interpolate`
`+            something else. Something that does exhibit the characteristics we need.</para>`
`+        <para>Positions interpolate linearly quite well. So instead of interpolating the light`
`+            direction, we interpolate the components of the light direction. Namely, the two`
`+            positions. The light position is a constant, so we only need to interpolate the vertex`
`+            position.</para>`
`+        <para>Now, we could do this in any space. But for illustrative purposes, we will be doing`
`+            this in model space. That is, both the light position and vertex position will be in`
`+            model space.</para>`
`+        <para>One of the advantages of doing things in model space is that it gets rid of that pesky`
`+            matrix inverse/transpose we had to do to transform normals correctly. Indeed, normals`
`+            are not transformed at all. One of the disadvantages is that it requires computing an`
`+            inverse matrix for our light position, so that we can go from world space to model`
`+            space.</para>`
`+        <para>The <phrase role="propername">Fragment Point Lighting</phrase> tutorial shows off how`
`+            fragment lighting works.</para>`
`+    </section>`
`+    <section>`
`+        <title>Distance and Points</title>`
`+        <para/>`
`+    </section>`
`+    <section>`
`+        <?dbhtml filename="Tut09 In Review.html" ?>`
`+        <title>In Review</title>`
`+        <para/>`
`+        <section>`
`+            <title>Further Study</title>`
`+            <para>Try doing these things with the given programs.</para>`
`+            <itemizedlist>`
`+                <listitem>`
`+                    <para>When we used model space-based lighting computations, we had to perform an`
`+                        inverse on our matrix from the matrix stack to transform the light position`
`+                        from camera space to model space. However, it would be entirely possible to`
`+                        simply build an inverse matrix at the same time we build a regular matrix on`
`+                        our matrix stack. The inverse of a rotation matrix is just the rotation`
`+                        matrix with a negated angle; the inverse of a scale is just the`
`+                        multiplicative inverse of the scales, and the inverse of the translation is`
`+                        the negation of the translation vector.</para>`
`+                    <para>To do this, you will need to modify the <classname>MatrixStack</classname>`
`+                        class in a number of ways. It must store a second matrix representing the`
`+                        accumulated inverse matrix. When a transformation command is given to the`
`+                        stack, it must also generate the inverse matrix for this transform and`
`+                            <emphasis>left multiply</emphasis> this into the accumulated inverse.`
`+                        The push/pop will have to push/pop the inverse matrix as well. It can use`
`+                        the same stack, so long as the pop function puts the two matrices in the`
`+                        proper places.</para>`
`+                </listitem>`
`+            </itemizedlist>`
`+        </section>`
`+    </section>`
`+    <section>`
`+        <?dbhtml filename="Tut09 Glossary.html" ?>`
`+        <title>Glossary</title>`
`+        <glosslist>`
`+            <glossentry>`
`+                <glossterm>point light</glossterm>`
`+                <glossdef>`
`+                    <para/>`
`+                </glossdef>`
`+            </glossentry>`
`+            <glossentry>`
`+                <glossterm>fragment lighting</glossterm>`
`+                <glossdef>`
`+                    <para/>`
`+                </glossdef>`
`+            </glossentry>`
`+        </glosslist>`
`+    </section>`
`+    `
`+</chapter>`

# File Documents/Outline.xml

`             <para>Concepts:</para>`
`             <itemizedlist>`
`                 <listitem>`
`-                    <para>Perspective-correct interpolation of vertex attributes.</para>`
`-                </listitem>`
`-                <listitem>`
`                     <para>Point lights.</para>`
`                 </listitem>`
`                 <listitem>`
`                     <para>Implementing per-fragment lighting.</para>`
`                 </listitem>`
`                 <listitem>`
`-                    <para>Tangent-space transform for per-fragment lighting.</para>`
`+                    <para>Talk about the artifact when lights get too close to the object. Talk`
`+                        about why it happens (surface being planar and not circular).</para>`
`+                </listitem>`
`+                <listitem>`
`+                    <para>Light attenuation.</para>`
`+                </listitem>`
`+                <listitem>`
`+                    <para>Distances in alternate spaces and how to compensate.</para>`
`+                </listitem>`
`+            </itemizedlist>`
`+        </section>`
`+        <section>`
`+            <title>Shininess</title>`
`+            <para>This tutorial introduces ways to make an object look shiny. Specifically specular`
`+                lighting and the Phong lighting model. There should be an animated light that shows`
`+                this off.</para>`
`+            <para>Concepts:</para>`
`+            <itemizedlist>`
`+                <listitem>`
`+                    <para>BDRFs: Lighting models that are a function of surface normal, angle to the`
`+                        light, and angle to the camera.</para>`
`+                </listitem>`
`+                <listitem>`
`+                    <para>The Phong specular lighting model. With both directional and point`
`+                        lights.</para>`
`                 </listitem>`
`             </itemizedlist>`
`         </section>`
`                 </listitem>`
`             </itemizedlist>`
`         </section>`
`-        <section>`
`-            <title>Shininess</title>`
`-            <para>This tutorial introduces ways to make an object look shiny. Specifically specular`
`-                lighting and the Phong lighting model. There should be an animated light or two that`
`-                shows this off.</para>`
`-            <para>Concepts:</para>`
`-            <itemizedlist>`
`-                <listitem>`
`-                    <para>BDRFs: Lighting models that are a function of surface normal, angle to the`
`-                        light, and angle to the camera.</para>`
`-                </listitem>`
`-                <listitem>`
`-                    <para>The Phong specular lighting model.</para>`
`-                </listitem>`
`-            </itemizedlist>`
`-        </section>`
`     </section>`
`     <section>`
`         <title>Texturing</title>`
`         <section>`
`-            <title>Texturing the World</title>`
`-            <para>This tutorial involves putting a texture on a simple, lit object.</para>`
`+            <title>Textures are not Pictures</title>`
`+            <para>This tutorial involves using a texture to define the specular value of an object`
`+                at a certain point.</para>`
`             <para>Concepts:</para>`
`             <itemizedlist>`
`                 <listitem>`
`+                    <para>Perspective-correct interpolation of vertex attributes.</para>`
`+                </listitem>`
`+                <listitem>`
`                     <para>Texture objects. An OpenGL object that holds images.</para>`
`                 </listitem>`
`                 <listitem>`
`                         units.</para>`
`                 </listitem>`
`                 <listitem>`
`-                    <para>Combining texture colors with the results of lighting.</para>`
`+                    <para>Using values from textures in lighting equations.</para>`
`                 </listitem>`
`             </itemizedlist>`
`         </section>`
`         <section>`
`             <title>More Images is Better</title>`
`-            <para>This tutorial shows a ground plane with a highly aliased texture. An animated`
`-                camera shows off the aliasing. Then we apply mipmapping and anisotropic filtering to`
`-                the surface to improve it.</para>`
`+            <para>This tutorial shows a ground plane with a highly aliased texture (as the diffuse`
`+                color). An animated camera shows off the aliasing. Then we apply mipmapping and`
`+                anisotropic filtering to the surface to improve it.</para>`
`             <para>Concepts:</para>`
`             <itemizedlist>`
`                 <listitem>`

# File Documents/Tutorial Documents.xpr

`         </folder>`
`         <folder name="Illumination">`
`             <file name="Illumination/Tutorial%2008.xml"/>`
`+            <file name="Illumination/Tutorial%2009.xml"/>`
`         </folder>`
`         <folder name="Positioning">`
`             <file name="Positioning/Tutorial%2003.xml"/>`

# File Tut 09 Plane Lights/Fragment Directional Lighting.cpp

`-#include <string>`
`-#include <vector>`
`-#include <stack>`
`-#include <math.h>`
`-#include <glloader/gl_3_2_comp.h>`
`-#include <GL/freeglut.h>`
`-#include "../framework/framework.h"`
`-#include "../framework/Mesh.h"`
`-#include "../framework/MatrixStack.h"`
`-#include "../framework/MousePole.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 ProgramData`
`-{`
`-	GLuint theProgram;`
`-`
`-	GLuint modelSpaceLightPosUnif;`
`-	GLuint lightIntensityUnif;`
`-	GLuint ambientIntensityUnif;`
`-`
`-	GLuint cameraToClipMatrixUnif;`
`-	GLuint modelToCameraMatrixUnif;`
`-	GLuint normalModelToCameraMatrixUnif;`
`-};`
`-`
`-struct UnlitProgData`
`-{`
`-	GLuint theProgram;`
`-`
`-	GLuint objectColorUnif;`
`-	GLuint cameraToClipMatrixUnif;`
`-	GLuint modelToCameraMatrixUnif;`
`-};`
`-`
`-float g_fzNear = 1.0f;`
`-float g_fzFar = 1000.0f;`
`-`
`-ProgramData g_WhiteAmbDiffuseColor;`
`-ProgramData g_VertexAmbDiffuseColor;`
`-ProgramData g_WhiteAmbFragDiffuseColor;`
`-ProgramData g_VertexAmbFragDiffuseColor;`
`-`
`-UnlitProgData g_Unlit;`
`-`
`-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.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`-	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");`
`-`
`-	return data;`
`-}`
`-`
`-ProgramData LoadLitProgram(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));`
`-`
`-	ProgramData data;`
`-	data.theProgram = Framework::CreateProgram(shaderList);`
`-	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");`
`-	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`-	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");`
`-	data.modelSpaceLightPosUnif = glGetUniformLocation(data.theProgram, "modelSpaceLightPos");`
`-	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");`
`-	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");`
`-`
`-	return data;`
`-}`
`-`
`-void InitializePrograms()`
`-{`
`-	g_WhiteAmbDiffuseColor = LoadLitProgram("ModelPosVertexLighting_PN.vert", "ColorPassthrough.frag");`
`-	g_VertexAmbDiffuseColor = LoadLitProgram("ModelPosVertexLighting_PCN.vert", "ColorPassthrough.frag");`
`-	g_WhiteAmbFragDiffuseColor = LoadLitProgram("FragmentLighting_PN.vert", "FragmentLighting.frag");`
`-	g_VertexAmbFragDiffuseColor = LoadLitProgram("FragmentLighting_PCN.vert", "FragmentLighting.frag");`
`-`
`-	g_Unlit = LoadUnlitProgram("PosTransform.vert", "UniformColor.frag");`
`-}`
`-`
`-Framework::Mesh *g_pCylinderMesh = NULL;`
`-Framework::Mesh *g_pPlaneMesh = NULL;`
`-Framework::Mesh *g_pCubeMesh = NULL;`
`-`
`-Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};`
`-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);`
`-`
`-namespace`
`-{`
`-	void MouseMotion(int x, int y)`
`-	{`
`-		g_mousePole.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));`
`-		glutPostRedisplay();`
`-	}`
`-`
`-	void MouseWheel(int wheel, int direction, int x, int y)`
`-	{`
`-		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));`
`-		glutPostRedisplay();`
`-	}`
`-}`
`-`
`-//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.`
`-void init()`
`-{`
`-	InitializePrograms();`
`-`
`-	try`
`-	{`
`-		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");`
`-		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");`
`-		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");`
`-	}`
`-	catch(std::exception &except)`
`-	{`
`-		printf(except.what());`
`-	}`
`-`
`- 	glutMouseFunc(MouseButton);`
`- 	glutMotionFunc(MouseMotion);`
`-	glutMouseWheelFunc(MouseWheel);`
`-`
`-	glEnable(GL_CULL_FACE);`
`-	glCullFace(GL_BACK);`
`-	glFrontFace(GL_CW);`
`-`
`-	glEnable(GL_DEPTH_TEST);`
`-	glDepthMask(GL_TRUE);`
`-	glDepthFunc(GL_LEQUAL);`
`-	glDepthRange(0.0f, 1.0f);`
`-	glEnable(GL_DEPTH_CLAMP);`
`-}`
`-`
`-static float g_fLightHeight = 1.5f;`
`-static float g_fLightRadius = 1.0f;`
`-`
`-glm::vec4 CalcLightPosition()`
`-{`
`-	const float fLoopDuration = 5.0f;`
`-	const float fScale = 3.14159f * 2.0f / fLoopDuration;`
`-`
`-	float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;`
`-`
`-	float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);`
`-`
`-	glm::vec4 ret(0.0f, g_fLightHeight, 0.0f, 1.0f);`
`-`
`-	ret.x = cosf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`-	ret.z = sinf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`-`
`-	return ret;`
`-}`
`-`
`-static float g_CylYaw = 0.0f;`
`-static float g_CylPitch = 0.0f;`
`-static float g_CylRoll = 0.0f;`
`-`
`-static bool g_bUseFragmentLighting = true;`
`-static bool g_bDrawColoredCyl = false;`
`-static bool g_bDrawLight = false;`
`-static bool g_bScaleCyl = 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()`
`-{`
`-	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);`
`-	glClearDepth(1.0f);`
`-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
`-`
`-	if(g_pPlaneMesh && g_pCylinderMesh)`
`-	{`
`-		Framework::MatrixStack modelMatrix;`
`-		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());`
`-`
`-		const glm::vec4 &worldLightPos = CalcLightPosition();`
`-`
`-		glm::vec4 lightPosCameraSpace = modelMatrix.Top() * worldLightPos;`
`-`
`-		ProgramData *pWhiteProgram = NULL;`
`-		ProgramData *pVertColorProgram = NULL;`
`-`
`-		if(g_bUseFragmentLighting)`
`-		{`
`-			pWhiteProgram = &g_WhiteAmbFragDiffuseColor;`
`-			pVertColorProgram = &g_VertexAmbFragDiffuseColor;`
`-		}`
`-		else`
`-		{`
`-			pWhiteProgram = &g_WhiteAmbDiffuseColor;`
`-			pVertColorProgram = &g_VertexAmbDiffuseColor;`
`-		}`
`-`
`-		glUseProgram(pWhiteProgram->theProgram);`
`-		glUniform4f(pWhiteProgram->lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`-		glUniform4f(pWhiteProgram->ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`-		glUseProgram(pVertColorProgram->theProgram);`
`-		glUniform4f(pVertColorProgram->lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`-		glUniform4f(pVertColorProgram->ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`-		glUseProgram(0);`
`-`
`-		{`
`-			Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-			//Render the ground plane.`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				glUseProgram(pWhiteProgram->theProgram);`
`-				glUniformMatrix4fv(pWhiteProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(modelMatrix.Top()));`
`-`
`-				glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`-				glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`-				glUniform3fv(pWhiteProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`-`
`-				glm::mat3 normMatrix(modelMatrix.Top());`
`-				glUniformMatrix3fv(pWhiteProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(normMatrix));`
`-				g_pPlaneMesh->Render();`
`-				glUseProgram(0);`
`-			}`
`-`
`-			//Render the Cylinder`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);`
`-`
`-				modelMatrix.RotateX(g_CylPitch);`
`-				modelMatrix.RotateY(g_CylYaw);`
`-				modelMatrix.RotateZ(g_CylRoll);`
`-`
`-				if(g_bScaleCyl)`
`-					modelMatrix.Scale(1.0f, 1.0f, 0.2f);`
`-`
`-				if(g_bDrawColoredCyl)`
`-				{`
`-					glUseProgram(pVertColorProgram->theProgram);`
`-					glUniformMatrix4fv(pVertColorProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(modelMatrix.Top()));`
`-`
`-					glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`-					glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`-					glUniform3fv(pVertColorProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`-`
`-					glm::mat3 normMatrix(modelMatrix.Top());`
`-					glUniformMatrix3fv(pVertColorProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(normMatrix));`
`-					g_pCylinderMesh->Render("tint");`
`-				}`
`-				else`
`-				{`
`-					glUseProgram(pWhiteProgram->theProgram);`
`-					glUniformMatrix4fv(pWhiteProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(modelMatrix.Top()));`
`-`
`-					glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`-					glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`-					glUniform3fv(pWhiteProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`-`
`-					glm::mat3 normMatrix(modelMatrix.Top());`
`-					glUniformMatrix3fv(pWhiteProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(normMatrix));`
`-					g_pCylinderMesh->Render("flat");`
`-				}`
`-				glUseProgram(0);`
`-			}`
`-`
`-			//Render the light`
`-			if(g_bDrawLight)`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				modelMatrix.Translate(glm::vec3(worldLightPos));`
`-				modelMatrix.Scale(0.1f, 0.1f, 0.1f);`
`-`
`-				glUseProgram(g_Unlit.theProgram);`
`-				glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(modelMatrix.Top()));`
`-				glUniform4f(g_Unlit.objectColorUnif, 0.8078f, 0.8706f, 0.9922f, 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);`
`-`
`-	glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_WhiteAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_VertexAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_WhiteAmbFragDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_WhiteAmbFragDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_VertexAmbFragDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_VertexAmbFragDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_Unlit.theProgram);`
`-	glUniformMatrix4fv(g_Unlit.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(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_pCylinderMesh;`
`-		delete g_pCubeMesh;`
`-		glutLeaveMainLoop();`
`-		break;`
`-	case 'w': g_CylPitch -= 11.25f; break;`
`-	case 's': g_CylPitch += 11.25f; break;`
`-	case 'd': g_CylRoll -= 11.25f; break;`
`-	case 'a': g_CylRoll += 11.25f; break;`
`-	case 'e': g_CylYaw -= 11.25f; break;`
`-	case 'q': g_CylYaw += 11.25f; break;`
`-	case 'W': g_CylPitch -= 4.0f; break;`
`-	case 'S': g_CylPitch += 4.0f; break;`
`-	case 'D': g_CylRoll -= 4.0f; break;`
`-	case 'A': g_CylRoll += 4.0f; break;`
`-	case 'E': g_CylYaw -= 4.0f; break;`
`-	case 'Q': g_CylYaw += 4.0f; break;`
`-		`
`-	case 32:`
`-		g_bDrawColoredCyl = !g_bDrawColoredCyl;`
`-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);`
`-		break;`
`-`
`-	case 'i': g_fLightHeight += 0.2f; break;`
`-	case 'k': g_fLightHeight -= 0.2f; break;`
`-	case 'l': g_fLightRadius += 0.2f; break;`
`-	case 'j': g_fLightRadius -= 0.2f; break;`
`-`
`-	case 'I': g_fLightHeight += 0.05f; break;`
`-	case 'K': g_fLightHeight -= 0.05f; break;`
`-	case 'L': g_fLightRadius += 0.05f; break;`
`-	case 'J': g_fLightRadius -= 0.05f; break;`
`-`
`-	case 'y': g_bDrawLight = !g_bDrawLight; break;`
`-	case 't': g_bScaleCyl = !g_bScaleCyl; break;`
`-	}`
`-`
`-	if(g_fLightRadius < 0.2f)`
`-		g_fLightRadius = 0.2f;`
`-`
`-	glutPostRedisplay();`
`-}`
`-`
`-`

# File Tut 09 Plane Lights/Fragment Point Lighting.cpp

`+#include <string>`
`+#include <vector>`
`+#include <stack>`
`+#include <math.h>`
`+#include <glloader/gl_3_2_comp.h>`
`+#include <GL/freeglut.h>`
`+#include "../framework/framework.h"`
`+#include "../framework/Mesh.h"`
`+#include "../framework/MatrixStack.h"`
`+#include "../framework/MousePole.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 ProgramData`
`+{`
`+	GLuint theProgram;`
`+`
`+	GLuint modelSpaceLightPosUnif;`
`+	GLuint lightIntensityUnif;`
`+	GLuint ambientIntensityUnif;`
`+`
`+	GLuint cameraToClipMatrixUnif;`
`+	GLuint modelToCameraMatrixUnif;`
`+	GLuint normalModelToCameraMatrixUnif;`
`+};`
`+`
`+struct UnlitProgData`
`+{`
`+	GLuint theProgram;`
`+`
`+	GLuint objectColorUnif;`
`+	GLuint cameraToClipMatrixUnif;`
`+	GLuint modelToCameraMatrixUnif;`
`+};`
`+`
`+float g_fzNear = 1.0f;`
`+float g_fzFar = 1000.0f;`
`+`
`+ProgramData g_WhiteAmbDiffuseColor;`
`+ProgramData g_VertexAmbDiffuseColor;`
`+ProgramData g_WhiteAmbFragDiffuseColor;`
`+ProgramData g_VertexAmbFragDiffuseColor;`
`+`
`+UnlitProgData g_Unlit;`
`+`
`+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.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`+	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");`
`+`
`+	return data;`
`+}`
`+`
`+ProgramData LoadLitProgram(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));`
`+`
`+	ProgramData data;`
`+	data.theProgram = Framework::CreateProgram(shaderList);`
`+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");`
`+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");`
`+	data.modelSpaceLightPosUnif = glGetUniformLocation(data.theProgram, "modelSpaceLightPos");`
`+	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");`
`+	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");`
`+`
`+	return data;`
`+}`
`+`
`+void InitializePrograms()`
`+{`
`+	g_WhiteAmbDiffuseColor = LoadLitProgram("ModelPosVertexLighting_PN.vert", "ColorPassthrough.frag");`
`+	g_VertexAmbDiffuseColor = LoadLitProgram("ModelPosVertexLighting_PCN.vert", "ColorPassthrough.frag");`
`+	g_WhiteAmbFragDiffuseColor = LoadLitProgram("FragmentLighting_PN.vert", "FragmentLighting.frag");`
`+	g_VertexAmbFragDiffuseColor = LoadLitProgram("FragmentLighting_PCN.vert", "FragmentLighting.frag");`
`+`
`+	g_Unlit = LoadUnlitProgram("PosTransform.vert", "UniformColor.frag");`
`+}`
`+`
`+Framework::Mesh *g_pCylinderMesh = NULL;`
`+Framework::Mesh *g_pPlaneMesh = NULL;`
`+Framework::Mesh *g_pCubeMesh = NULL;`
`+`
`+Framework::RadiusDef radiusDef = {5.0f, 3.0f, 200.0f, 1.5f, 0.5f};`
`+Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);`
`+`
`+namespace`
`+{`
`+	void MouseMotion(int x, int y)`
`+	{`
`+		g_mousePole.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));`
`+		glutPostRedisplay();`
`+	}`
`+`
`+	void MouseWheel(int wheel, int direction, int x, int y)`
`+	{`
`+		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));`
`+		glutPostRedisplay();`
`+	}`
`+}`
`+`
`+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.`
`+void init()`
`+{`
`+	InitializePrograms();`
`+`
`+	try`
`+	{`
`+		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");`
`+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");`
`+		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");`
`+	}`
`+	catch(std::exception &except)`
`+	{`
`+		printf(except.what());`
`+	}`
`+`
`+ 	glutMouseFunc(MouseButton);`
`+ 	glutMotionFunc(MouseMotion);`
`+	glutMouseWheelFunc(MouseWheel);`
`+`
`+	glEnable(GL_CULL_FACE);`
`+	glCullFace(GL_BACK);`
`+	glFrontFace(GL_CW);`
`+`
`+	glEnable(GL_DEPTH_TEST);`
`+	glDepthMask(GL_TRUE);`
`+	glDepthFunc(GL_LEQUAL);`
`+	glDepthRange(0.0f, 1.0f);`
`+	glEnable(GL_DEPTH_CLAMP);`
`+}`
`+`
`+static float g_fLightHeight = 1.5f;`
`+static float g_fLightRadius = 1.0f;`
`+`
`+glm::vec4 CalcLightPosition()`
`+{`
`+	const float fLoopDuration = 5.0f;`
`+	const float fScale = 3.14159f * 2.0f / fLoopDuration;`
`+`
`+	float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;`
`+`
`+	float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);`
`+`
`+	glm::vec4 ret(0.0f, g_fLightHeight, 0.0f, 1.0f);`
`+`
`+	ret.x = cosf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`+	ret.z = sinf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`+`
`+	return ret;`
`+}`
`+`
`+static float g_CylYaw = 0.0f;`
`+static float g_CylPitch = 0.0f;`
`+static float g_CylRoll = 0.0f;`
`+`
`+static bool g_bUseFragmentLighting = true;`
`+static bool g_bDrawColoredCyl = false;`
`+static bool g_bDrawLight = false;`
`+static bool g_bScaleCyl = 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()`
`+{`
`+	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);`
`+	glClearDepth(1.0f);`
`+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
`+`
`+	if(g_pPlaneMesh && g_pCylinderMesh)`
`+	{`
`+		Framework::MatrixStack modelMatrix;`
`+		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());`
`+`
`+		const glm::vec4 &worldLightPos = CalcLightPosition();`
`+`
`+		glm::vec4 lightPosCameraSpace = modelMatrix.Top() * worldLightPos;`
`+`
`+		ProgramData *pWhiteProgram = NULL;`
`+		ProgramData *pVertColorProgram = NULL;`
`+`
`+		if(g_bUseFragmentLighting)`
`+		{`
`+			pWhiteProgram = &g_WhiteAmbFragDiffuseColor;`
`+			pVertColorProgram = &g_VertexAmbFragDiffuseColor;`
`+		}`
`+		else`
`+		{`
`+			pWhiteProgram = &g_WhiteAmbDiffuseColor;`
`+			pVertColorProgram = &g_VertexAmbDiffuseColor;`
`+		}`
`+`
`+		glUseProgram(pWhiteProgram->theProgram);`
`+		glUniform4f(pWhiteProgram->lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`+		glUniform4f(pWhiteProgram->ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`+		glUseProgram(pVertColorProgram->theProgram);`
`+		glUniform4f(pVertColorProgram->lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`+		glUniform4f(pVertColorProgram->ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`+		glUseProgram(0);`
`+`
`+		{`
`+			Framework::MatrixStackPusher push(modelMatrix);`
`+`
`+			//Render the ground plane.`
`+			{`
`+				Framework::MatrixStackPusher push(modelMatrix);`
`+`
`+				glUseProgram(pWhiteProgram->theProgram);`
`+				glUniformMatrix4fv(pWhiteProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`+					glm::value_ptr(modelMatrix.Top()));`
`+`
`+				glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`+				glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`+				glUniform3fv(pWhiteProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`+`
`+				glm::mat3 normMatrix(modelMatrix.Top());`
`+				glUniformMatrix3fv(pWhiteProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`+					glm::value_ptr(normMatrix));`
`+				g_pPlaneMesh->Render();`
`+				glUseProgram(0);`
`+			}`
`+`
`+			//Render the Cylinder`
`+			{`
`+				Framework::MatrixStackPusher push(modelMatrix);`
`+`
`+				modelMatrix.Translate(0.0f, 0.5f, 0.0f);`
`+`
`+				modelMatrix.RotateX(g_CylPitch);`
`+				modelMatrix.RotateY(g_CylYaw);`
`+				modelMatrix.RotateZ(g_CylRoll);`
`+`
`+				if(g_bScaleCyl)`
`+					modelMatrix.Scale(1.0f, 1.0f, 0.2f);`
`+`
`+				if(g_bDrawColoredCyl)`
`+				{`
`+					glUseProgram(pVertColorProgram->theProgram);`
`+					glUniformMatrix4fv(pVertColorProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`+						glm::value_ptr(modelMatrix.Top()));`
`+`
`+					glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`+					glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`+					glUniform3fv(pVertColorProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`+`
`+					glm::mat3 normMatrix(modelMatrix.Top());`
`+					glUniformMatrix3fv(pVertColorProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`+						glm::value_ptr(normMatrix));`
`+					g_pCylinderMesh->Render("tint");`
`+				}`
`+				else`
`+				{`
`+					glUseProgram(pWhiteProgram->theProgram);`
`+					glUniformMatrix4fv(pWhiteProgram->modelToCameraMatrixUnif, 1, GL_FALSE,`
`+						glm::value_ptr(modelMatrix.Top()));`
`+`
`+					glm::mat4 invTransform = glm::inverse(modelMatrix.Top());`
`+					glm::vec4 lightPosModelSpace = invTransform * lightPosCameraSpace;`
`+					glUniform3fv(pWhiteProgram->modelSpaceLightPosUnif, 1, glm::value_ptr(lightPosModelSpace));`
`+`
`+					glm::mat3 normMatrix(modelMatrix.Top());`
`+					glUniformMatrix3fv(pWhiteProgram->normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`+						glm::value_ptr(normMatrix));`
`+					g_pCylinderMesh->Render("flat");`
`+				}`
`+				glUseProgram(0);`
`+			}`
`+`
`+			//Render the light`
`+			if(g_bDrawLight)`
`+			{`
`+				Framework::MatrixStackPusher push(modelMatrix);`
`+`
`+				modelMatrix.Translate(glm::vec3(worldLightPos));`
`+				modelMatrix.Scale(0.1f, 0.1f, 0.1f);`
`+`
`+				glUseProgram(g_Unlit.theProgram);`
`+				glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,`
`+					glm::value_ptr(modelMatrix.Top()));`
`+				glUniform4f(g_Unlit.objectColorUnif, 0.8078f, 0.8706f, 0.9922f, 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);`
`+`
`+	glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`+	glUniformMatrix4fv(g_WhiteAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`+		glm::value_ptr(persMatrix.Top()));`
`+	glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`+	glUniformMatrix4fv(g_VertexAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`+		glm::value_ptr(persMatrix.Top()));`
`+	glUseProgram(g_WhiteAmbFragDiffuseColor.theProgram);`
`+	glUniformMatrix4fv(g_WhiteAmbFragDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`+		glm::value_ptr(persMatrix.Top()));`
`+	glUseProgram(g_VertexAmbFragDiffuseColor.theProgram);`
`+	glUniformMatrix4fv(g_VertexAmbFragDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`+		glm::value_ptr(persMatrix.Top()));`
`+	glUseProgram(g_Unlit.theProgram);`
`+	glUniformMatrix4fv(g_Unlit.cameraToClipMatrixUnif, 1, GL_FALSE,`
`+		glm::value_ptr(persMatrix.Top()));`
`+	glUseProgram(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_pCylinderMesh;`
`+		delete g_pCubeMesh;`
`+		glutLeaveMainLoop();`
`+		break;`
`+	case 'w': g_CylPitch -= 11.25f; break;`
`+	case 's': g_CylPitch += 11.25f; break;`
`+	case 'd': g_CylRoll -= 11.25f; break;`
`+	case 'a': g_CylRoll += 11.25f; break;`
`+	case 'e': g_CylYaw -= 11.25f; break;`
`+	case 'q': g_CylYaw += 11.25f; break;`
`+	case 'W': g_CylPitch -= 4.0f; break;`
`+	case 'S': g_CylPitch += 4.0f; break;`
`+	case 'D': g_CylRoll -= 4.0f; break;`
`+	case 'A': g_CylRoll += 4.0f; break;`
`+	case 'E': g_CylYaw -= 4.0f; break;`
`+	case 'Q': g_CylYaw += 4.0f; break;`
`+		`
`+	case 32:`
`+		g_bDrawColoredCyl = !g_bDrawColoredCyl;`
`+		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);`
`+		break;`
`+`
`+	case 'i': g_fLightHeight += 0.2f; break;`
`+	case 'k': g_fLightHeight -= 0.2f; break;`
`+	case 'l': g_fLightRadius += 0.2f; break;`
`+	case 'j': g_fLightRadius -= 0.2f; break;`
`+`
`+	case 'I': g_fLightHeight += 0.05f; break;`
`+	case 'K': g_fLightHeight -= 0.05f; break;`
`+	case 'L': g_fLightRadius += 0.05f; break;`
`+	case 'J': g_fLightRadius -= 0.05f; break;`
`+`
`+	case 'y': g_bDrawLight = !g_bDrawLight; break;`
`+	case 't': g_bScaleCyl = !g_bScaleCyl; break;`
`+	}`
`+`
`+	if(g_fLightRadius < 0.2f)`
`+		g_fLightRadius = 0.2f;`
`+`
`+	glutPostRedisplay();`
`+}`
`+`
`+`

# File Tut 09 Plane Lights/Perspective Interpolation.cpp

`-#include <string>`
`-#include <vector>`
`-#include <stack>`
`-#include <math.h>`
`-#include <glloader/gl_3_2_comp.h>`
`-#include <GL/freeglut.h>`
`-#include "../framework/framework.h"`
`-#include "../framework/Mesh.h"`
`-#include "../framework/MatrixStack.h"`
`-#include "../framework/MousePole.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 ProgramData`
`-{`
`-	GLuint theProgram;`
`-`
`-	GLuint cameraToClipMatrixUnif;`
`-};`
`-`
`-float g_fzNear = 1.0f;`
`-float g_fzFar = 1000.0f;`
`-`
`-ProgramData g_SmoothInterp;`
`-ProgramData g_LinearInterp;`
`-`
`-ProgramData LoadProgram(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));`
`-`
`-	ProgramData data;`
`-	data.theProgram = Framework::CreateProgram(shaderList);`
`-	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`-`
`-	return data;`
`-}`
`-`
`-void InitializePrograms()`
`-{`
`-	g_SmoothInterp = LoadProgram("SmoothVertexColors.vert", "SmoothVertexColors.frag");`
`-	g_LinearInterp = LoadProgram("NoCorrectVertexColors.vert", "NoCorrectVertexColors.frag");`
`-`
`-	Framework::MatrixStack persMatrix;`
`-	persMatrix.Perspective(60.0f, 1.0f, g_fzNear, g_fzFar);`
`-`
`-	glUseProgram(g_SmoothInterp.theProgram);`
`-	glUniformMatrix4fv(g_SmoothInterp.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_LinearInterp.theProgram);`
`-	glUniformMatrix4fv(g_LinearInterp.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(0);`
`-}`
`-`
`-Framework::Mesh *g_pRealHallway = NULL;`
`-Framework::Mesh *g_pFauxHallway = NULL;`
`-`
`-//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.`
`-void init()`
`-{`
`-	InitializePrograms();`
`-`
`-	try`
`-	{`
`-		g_pRealHallway = new Framework::Mesh("RealHallway.xml");`
`-		g_pFauxHallway = new Framework::Mesh("FauxHallway.xml");`
`-	}`
`-	catch(std::exception &except)`
`-	{`
`-		printf(except.what());`
`-	}`
`-}`
`-`
`-static bool g_bUseFakeHallway = false;`
`-static bool g_bUseSmoothInterpolation = true;`
`-`
`-//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()`
`-{`
`-	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);`
`-	glClearDepth(1.0f);`
`-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
`-`
`-	if(g_pRealHallway && g_pFauxHallway)`
`-	{`
`-		if(g_bUseSmoothInterpolation)`
`-			glUseProgram(g_SmoothInterp.theProgram);`
`-		else`
`-			glUseProgram(g_LinearInterp.theProgram);`
`-`
`-		if(g_bUseFakeHallway)`
`-			g_pFauxHallway->Render();`
`-		else`
`-			g_pRealHallway->Render();`
`-`
`-		glUseProgram(0);`
`-	}`
`-`
`-	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)`
`-{`
`-	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_pRealHallway;`
`-		delete g_pFauxHallway;`
`-		glutLeaveMainLoop();`
`-		break;`
`-`
`-	case 's':`
`-		g_bUseFakeHallway = !g_bUseFakeHallway;`
`-		if(g_bUseFakeHallway)`
`-			printf("Fake Hallway.\n");`
`-		else`
`-			printf("Real Hallway.\n");`
`-		break;`
`-`
`-	case 'w':`
`-		g_bUseSmoothInterpolation = !g_bUseSmoothInterpolation;`
`-		if(g_bUseSmoothInterpolation)`
`-			printf("Perspective correct interpolation.\n");`
`-		else`
`-			printf("Just linear interpolation.\n");`
`-		break;`
`-`
`-	case 32:`
`-		//Reload.`
`-		delete g_pRealHallway;`
`-		delete g_pFauxHallway;`
`-		g_pRealHallway = new Framework::Mesh("RealHallway.xml");`
`-		g_pFauxHallway = new Framework::Mesh("FauxHallway.xml");`
`-		break;`
`-	}`
`-`
`-	glutPostRedisplay();`
`-}`
`-`
`-`

# File Tut 09 Plane Lights/Vertex Directional Lighting.cpp

`-#include <string>`
`-#include <vector>`
`-#include <stack>`
`-#include <math.h>`
`-#include <glloader/gl_3_2_comp.h>`
`-#include <GL/freeglut.h>`
`-#include "../framework/framework.h"`
`-#include "../framework/Mesh.h"`
`-#include "../framework/MatrixStack.h"`
`-#include "../framework/MousePole.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 ProgramData`
`-{`
`-	GLuint theProgram;`
`-`
`-	GLuint lightPosUnif;`
`-	GLuint lightIntensityUnif;`
`-	GLuint ambientIntensityUnif;`
`-`
`-	GLuint cameraToClipMatrixUnif;`
`-	GLuint modelToCameraMatrixUnif;`
`-	GLuint normalModelToCameraMatrixUnif;`
`-};`
`-`
`-struct UnlitProgData`
`-{`
`-	GLuint theProgram;`
`-`
`-	GLuint objectColorUnif;`
`-	GLuint cameraToClipMatrixUnif;`
`-	GLuint modelToCameraMatrixUnif;`
`-};`
`-`
`-float g_fzNear = 1.0f;`
`-float g_fzFar = 1000.0f;`
`-`
`-ProgramData g_WhiteAmbDiffuseColor;`
`-ProgramData g_VertexAmbDiffuseColor;`
`-`
`-UnlitProgData g_Unlit;`
`-`
`-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.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`-	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");`
`-`
`-	return data;`
`-}`
`-`
`-ProgramData LoadLitProgram(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));`
`-`
`-	ProgramData data;`
`-	data.theProgram = Framework::CreateProgram(shaderList);`
`-	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");`
`-	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`-	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");`
`-	data.lightPosUnif = glGetUniformLocation(data.theProgram, "lightPos");`
`-	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");`
`-	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");`
`-`
`-	return data;`
`-}`
`-`
`-void InitializePrograms()`
`-{`
`-	g_WhiteAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PN.vert", "ColorPassthrough.frag");`
`-	g_VertexAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PCN.vert", "ColorPassthrough.frag");`
`-	g_Unlit = LoadUnlitProgram("PosTransform.vert", "UniformColor.frag");`
`-}`
`-`
`-Framework::Mesh *g_pCylinderMesh = NULL;`
`-Framework::Mesh *g_pPlaneMesh = NULL;`
`-Framework::Mesh *g_pCubeMesh = NULL;`
`-`
`-Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};`
`-Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);`
`-`
`-namespace`
`-{`
`-	void MouseMotion(int x, int y)`
`-	{`
`-		g_mousePole.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));`
`-		glutPostRedisplay();`
`-	}`
`-`
`-	void MouseWheel(int wheel, int direction, int x, int y)`
`-	{`
`-		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));`
`-		glutPostRedisplay();`
`-	}`
`-}`
`-`
`-//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.`
`-void init()`
`-{`
`-	InitializePrograms();`
`-`
`-	try`
`-	{`
`-		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");`
`-		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");`
`-		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");`
`-	}`
`-	catch(std::exception &except)`
`-	{`
`-		printf(except.what());`
`-	}`
`-`
`- 	glutMouseFunc(MouseButton);`
`- 	glutMotionFunc(MouseMotion);`
`-	glutMouseWheelFunc(MouseWheel);`
`-`
`-	glEnable(GL_CULL_FACE);`
`-	glCullFace(GL_BACK);`
`-	glFrontFace(GL_CW);`
`-`
`-	glEnable(GL_DEPTH_TEST);`
`-	glDepthMask(GL_TRUE);`
`-	glDepthFunc(GL_LEQUAL);`
`-	glDepthRange(0.0f, 1.0f);`
`-	glEnable(GL_DEPTH_CLAMP);`
`-}`
`-`
`-static float g_fLightHeight = 1.5f;`
`-static float g_fLightRadius = 1.0f;`
`-`
`-glm::vec4 CalcLightPosition()`
`-{`
`-	const float fLoopDuration = 5.0f;`
`-	const float fScale = 3.14159f * 2.0f / fLoopDuration;`
`-`
`-	float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;`
`-`
`-	float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);`
`-`
`-	glm::vec4 ret(0.0f, g_fLightHeight, 0.0f, 1.0f);`
`-`
`-	ret.x = cosf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`-	ret.z = sinf(fCurrTimeThroughLoop * fScale) * g_fLightRadius;`
`-`
`-	return ret;`
`-}`
`-`
`-static float g_CylYaw = 0.0f;`
`-static float g_CylPitch = 0.0f;`
`-static float g_CylRoll = 0.0f;`
`-`
`-static bool g_bDrawColoredCyl = false;`
`-static bool g_bDrawLight = 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()`
`-{`
`-	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);`
`-	glClearDepth(1.0f);`
`-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
`-`
`-	if(g_pPlaneMesh && g_pCylinderMesh)`
`-	{`
`-		Framework::MatrixStack modelMatrix;`
`-		modelMatrix.SetMatrix(g_mousePole.CalcMatrix());`
`-`
`-		const glm::vec4 &worldLightPos = CalcLightPosition();`
`-`
`-		glm::vec4 lightPosCameraSpace = modelMatrix.Top() * worldLightPos;`
`-`
`-		glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-		glUniform4f(g_WhiteAmbDiffuseColor.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`-		glUniform4f(g_WhiteAmbDiffuseColor.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`-		glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`-		glUniform4f(g_VertexAmbDiffuseColor.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);`
`-		glUniform4f(g_VertexAmbDiffuseColor.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);`
`-`
`-		glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-		glUniform3fv(g_WhiteAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));`
`-		glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`-		glUniform3fv(g_VertexAmbDiffuseColor.lightPosUnif, 1, glm::value_ptr(lightPosCameraSpace));`
`-		glUseProgram(0);`
`-`
`-		{`
`-			Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-			//Render the ground plane.`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-				glUniformMatrix4fv(g_WhiteAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(modelMatrix.Top()));`
`-				glm::mat3 normMatrix(modelMatrix.Top());`
`-				glUniformMatrix3fv(g_WhiteAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(normMatrix));`
`-				g_pPlaneMesh->Render();`
`-				glUseProgram(0);`
`-			}`
`-`
`-			//Render the Cylinder`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				modelMatrix.Translate(0.0f, 0.5f, 0.0f);`
`-`
`-				modelMatrix.RotateX(g_CylPitch);`
`-				modelMatrix.RotateY(g_CylYaw);`
`-				modelMatrix.RotateZ(g_CylRoll);`
`-`
`-				if(g_bDrawColoredCyl)`
`-				{`
`-					glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`-					glUniformMatrix4fv(g_VertexAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(modelMatrix.Top()));`
`-					glm::mat3 normMatrix(modelMatrix.Top());`
`-					glUniformMatrix3fv(g_VertexAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(normMatrix));`
`-					g_pCylinderMesh->Render("tint");`
`-				}`
`-				else`
`-				{`
`-					glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-					glUniformMatrix4fv(g_WhiteAmbDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(modelMatrix.Top()));`
`-					glm::mat3 normMatrix(modelMatrix.Top());`
`-					glUniformMatrix3fv(g_WhiteAmbDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE,`
`-						glm::value_ptr(normMatrix));`
`-					g_pCylinderMesh->Render("flat");`
`-				}`
`-				glUseProgram(0);`
`-			}`
`-`
`-			//Render the light`
`-			if(g_bDrawLight)`
`-			{`
`-				Framework::MatrixStackPusher push(modelMatrix);`
`-`
`-				modelMatrix.Translate(glm::vec3(worldLightPos));`
`-				modelMatrix.Scale(0.1f, 0.1f, 0.1f);`
`-`
`-				glUseProgram(g_Unlit.theProgram);`
`-				glUniformMatrix4fv(g_Unlit.modelToCameraMatrixUnif, 1, GL_FALSE,`
`-					glm::value_ptr(modelMatrix.Top()));`
`-				glUniform4f(g_Unlit.objectColorUnif, 0.8078f, 0.8706f, 0.9922f, 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);`
`-`
`-	glUseProgram(g_WhiteAmbDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_WhiteAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_VertexAmbDiffuseColor.theProgram);`
`-	glUniformMatrix4fv(g_VertexAmbDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(g_Unlit.theProgram);`
`-	glUniformMatrix4fv(g_Unlit.cameraToClipMatrixUnif, 1, GL_FALSE,`
`-		glm::value_ptr(persMatrix.Top()));`
`-	glUseProgram(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_pCylinderMesh;`
`-		delete g_pCubeMesh;`
`-		glutLeaveMainLoop();`
`-		break;`
`-	case 'w': g_CylPitch -= 11.25f; break;`
`-	case 's': g_CylPitch += 11.25f; break;`
`-	case 'd': g_CylRoll -= 11.25f; break;`
`-	case 'a': g_CylRoll += 11.25f; break;`
`-	case 'e': g_CylYaw -= 11.25f; break;`
`-	case 'q': g_CylYaw += 11.25f; break;`
`-	case 'W': g_CylPitch -= 4.0f; break;`
`-	case 'S': g_CylPitch += 4.0f; break;`
`-	case 'D': g_CylRoll -= 4.0f; break;`
`-	case 'A': g_CylRoll += 4.0f; break;`
`-	case 'E': g_CylYaw -= 4.0f; break;`
`-	case 'Q': g_CylYaw += 4.0f; break;`
`-		`
`-	case 32:`
`-		g_bDrawColoredCyl = !g_bDrawColoredCyl;`
`-		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);`
`-		break;`
`-`
`-	case 'i': g_fLightHeight += 0.2f; break;`
`-	case 'k': g_fLightHeight -= 0.2f; break;`
`-	case 'l': g_fLightRadius += 0.2f; break;`
`-	case 'j': g_fLightRadius -= 0.2f; break;`
`-`
`-	case 'I': g_fLightHeight += 0.05f; break;`
`-	case 'K': g_fLightHeight -= 0.05f; break;`
`-	case 'L': g_fLightRadius += 0.05f; break;`
`-	case 'J': g_fLightRadius -= 0.05f; break;`
`-`
`-	case 'y': g_bDrawLight = !g_bDrawLight; break;`
`-	}`
`-`
`-	if(g_fLightRadius < 0.2f)`
`-		g_fLightRadius = 0.2f;`
`-`
`-	glutPostRedisplay();`
`-}`
`-`
`-`

# File Tut 09 Plane Lights/Vertex Point Lighting.cpp

`+#include <string>`
`+#include <vector>`
`+#include <stack>`
`+#include <math.h>`
`+#include <glloader/gl_3_2_comp.h>`
`+#include <GL/freeglut.h>`
`+#include "../framework/framework.h"`
`+#include "../framework/Mesh.h"`
`+#include "../framework/MatrixStack.h"`
`+#include "../framework/MousePole.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 ProgramData`
`+{`
`+	GLuint theProgram;`
`+`
`+	GLuint lightPosUnif;`
`+	GLuint lightIntensityUnif;`
`+	GLuint ambientIntensityUnif;`
`+`
`+	GLuint cameraToClipMatrixUnif;`
`+	GLuint modelToCameraMatrixUnif;`
`+	GLuint normalModelToCameraMatrixUnif;`
`+};`
`+`
`+struct UnlitProgData`
`+{`
`+	GLuint theProgram;`
`+`
`+	GLuint objectColorUnif;`
`+	GLuint cameraToClipMatrixUnif;`
`+	GLuint modelToCameraMatrixUnif;`
`+};`
`+`
`+float g_fzNear = 1.0f;`
`+float g_fzFar = 1000.0f;`
`+`
`+ProgramData g_WhiteAmbDiffuseColor;`
`+ProgramData g_VertexAmbDiffuseColor;`
`+`
`+UnlitProgData g_Unlit;`
`+`
`+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.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`+	data.objectColorUnif = glGetUniformLocation(data.theProgram, "objectColor");`
`+`
`+	return data;`
`+}`
`+`
`+ProgramData LoadLitProgram(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));`
`+`
`+	ProgramData data;`
`+	data.theProgram = Framework::CreateProgram(shaderList);`
`+	data.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");`
`+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");`
`+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");`
`+	data.lightPosUnif = glGetUniformLocation(data.theProgram, "lightPos");`
`+	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");`
`+	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");`
`+`
`+	return data;`
`+}`
`+`
`+void InitializePrograms()`
`+{`
`+	g_WhiteAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PN.vert", "ColorPassthrough.frag");`
`+	g_VertexAmbDiffuseColor = LoadLitProgram("PosVertexLighting_PCN.vert", "ColorPassthrough.frag");`
`+	g_Unlit = LoadUnlitProgram("PosTransform.vert", "UniformColor.frag");`
`+}`
`+`
`+Framework::Mesh *g_pCylinderMesh = NULL;`
`+Framework::Mesh *g_pPlaneMesh = NULL;`
`+Framework::Mesh *g_pCubeMesh = NULL;`
`+`
`+Framework::RadiusDef radiusDef = {5.0f, 3.0f, 20.0f, 1.5f, 0.5f};`
`+Framework::MousePole g_mousePole(glm::vec3(0.0f, 0.5f, 0.0f), radiusDef);`
`+`
`+namespace`
`+{`
`+	void MouseMotion(int x, int y)`
`+	{`
`+		g_mousePole.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));`
`+		glutPostRedisplay();`
`+	}`
`+`
`+	void MouseWheel(int wheel, int direction, int x, int y)`
`+	{`
`+		g_mousePole.GLUTMouseWheel(direction, glm::ivec2(x, y));`
`+		glutPostRedisplay();`
`+	}`
`+}`
`+`
`+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.`
`+void init()`
`+{`
`+	InitializePrograms();`
`+`
`+	try`
`+	{`
`+		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");`
`+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");`
`+		g_pCubeMesh = new Framework::Mesh("UnitCube.xml");`
`+	}`
`+	catch(std::exception &except)`
`+	{`
`+		printf(except.what());`
`+	}`
`+`
`+ 	glutMouseFunc(MouseButton);`
`+ 	glutMotionFunc(MouseMotion);`
`+	glutMouseWheelFunc(MouseWheel);`
`+`
`+	glEnable(GL_CULL_FACE);`
`+	glCullFace(GL_BACK);`
`+	glFrontFace(GL_CW);`
`+`
`+	glEnable(GL_DEPTH_TEST);`
`+	glDepthMask(GL_TRUE);`
`+	glDepthFunc(GL_LEQUAL);`
`+	glDepthRange(0.0f, 1.0f);`
`+	glEnable(GL_DEPTH_CLAMP);`
`+}`
`+`
`+static float g_fLightHeight = 1.5f;`
`+static float g_fLightRadius = 1.0f;`