Commits

Jason McKesson  committed 988c826

Ambient Lighting done.

  • Participants
  • Parent commits 84960c2

Comments (0)

Files changed (5)

File Documents/Illumination/Tutorial 08.xml

 glUseProgram(0);</programlisting>
         </example>
         <para>It's pretty self-explanatory.</para>
+        <!--TODO: Show a picture of the tutorial, with scale applied.-->
         <para>One more thing to note before we move on. Doing the inverse-transpose is only really
             necessary if you are using a <emphasis>non-uniform</emphasis> scale. In practice, it's
             actually somewhat rare to use this kind of scale factor. We do it in these tutorials, so
             transforming it with the model-to-camera matrix.</para>
     </section>
     <section>
-        <title>Interreflection</title>
-        <para/>
+        <title>Global Illumination</title>
+        <para>You may notice something very unrealistic about the results of this tutorial. For
+            example, take this image:</para>
+        <!--TODO: Show an image of the cylinder with half lit and half unlit-->
+        <para>The unlit portions of the cylinder are completely, 100% black. This almost never
+            happens in real life. The reason for this is somewhat complicated.</para>
+        <para>Consider a scene of the outdoors. In normal daylight, there is exactly one light
+            source: the sun. Objects that are in direct sunlight appear to be bright, and objects
+            that have some object between them and the sun are in shadow.</para>
+        <para>But think about what those shadows look like. They're not 100% black. They're
+            certainly darker than the surrounding area, but they still have some color. And
+            remember: we only see anything because our eyes detect light. This means, in order to
+            see an object in the shadow of a light source, that object must either be emitting light
+            directly or reflecting light that came from somewhere else. Grass is not known for
+            emitting light, so where does the light come from?</para>
+        <para>Think about it. We see because an object reflects light into our eyes. But our eyes
+            are not special; the object does not reflect light <emphasis>only</emphasis> into our
+            eyes. It reflects light in all directions. Not necessarily at the same intensity, but
+            objects that reflect light tend to do so in all directions to some degree. What happens
+            when that light hits another surface?</para>
+        <para>The same thing that happens when light hits any surface: some of it is absorbed, and
+            some is reflected in some way.</para>
+        <para>The light being cast in shadows from the sun comes from many places. Part of it is an
+            atmospheric effect; the sun is so bright that the weakly reflective atmosphere reflects
+            enough light to shine a color. Typically, this is a pale blue. Part of the light comes
+            from other objects. The sun gives off so much light that the light reflected from other
+            objects is bright enough to be a substantial contributer to the overall lighting in a
+            scene.</para>
+        <para>This phenomenon is called <glossterm>interreflection.</glossterm> A lighting model
+            that handles interreflection is said to handle <glossterm>global
+                illumination.</glossterm> It represents light that bounces from object to object
+            before hitting the eye's of the person viewing the scene. Modelling only lighting
+            directly from a light-emitting surface is called <glossterm>local
+                illumination</glossterm> or <glossterm>direct illumination,</glossterm> and it is
+            what we have been doing up until this point.</para>
+        <para>As you might imagine, modelling global illumination is hard. <emphasis>Very</emphasis>
+            hard. It is typically a subtle effect, but in many scenes, particularly outdoor scenes,
+            it is almost a necessity to some at least basic global illumination modelling in order
+            to achieve a decent degree of photorealism. Incidentally, this is a good part of the
+            reason why most games tend to avoid outdoor scenes or light outdoor scenes as though the
+            sky were cloudy or overcast. This neatly avoids needing to do difficult global
+            illumination modelling, by damping down the brightness of the sun to levels when
+            interreflection would be difficult to notice.</para>
+        <para>Having this completely black area in our rendering looks incredibly fake. Since doing
+            actual global illumination modelling is hard, we will instead do a time-tested
+                <quote>model</quote>: <glossterm>ambient lighting.</glossterm></para>
+        <para>The ambient lighting <quote>model</quote><footnote>
+                <para>I put model in quotations because it is so divorced from anything in reality
+                    that it doesn't really deserve to be called a model.</para>
+            </footnote> is quite simple. We assume that, on every object in the scene, there is a
+            light of a certain intensity that emanates from everywhere. It comes from all directions
+            equally, so there is no angle of incidence in our diffuse calculation. It is simply the
+            ambient light intensity * the diffuse surface color.</para>
+        <para>We do this in the <phrase role="propername">Ambient Lighting</phrase> tutorial. The
+            controls are the same as the last tutorial, except that the space bar swaps between the
+            two cylinders (red and white), and that the <keycap>T</keycap> key toggles ambient
+            lighting on and off (defaults to off).</para>
+        <!--TODO: Show a picture of the ambient lighting.-->
+        <para>The rendering code now uses four of vertex shaders instead of two. Two of them are
+            used for non-ambient lighting, and use the same shaders we have seen before, and the
+            other two use ambient lighting.</para>
+        <para>The ambient vertex shader that uses per-vertex colors is called
+                <filename>DirAmbVertexLighting_PCN.vert</filename> and reads as follows:</para>
+        <example>
+            <title>Ambient Vertex Lighting</title>
+            <programlisting language="glsl">#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 dirToLight;
+uniform vec4 lightIntensity;
+uniform vec4 ambientIntensity;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+uniform mat3 normalModelToCameraMatrix;
+
+void main()
+{
+    gl_Position = cameraToClipMatrix * (modelToCameraMatrix * vec4(position, 1.0));
+    
+    vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);
+    
+    float cosAngIncidence = dot(normCamSpace, dirToLight);
+    cosAngIncidence = clamp(cosAngIncidence, 0, 1);
+    
+    interpColor = (diffuseColor * lightIntensity * cosAngIncidence) +
+        (diffuseColor * ambientIntensity);
+}</programlisting>
+        </example>
+        <para>It takes two uniforms that specify lighting intensity. One specifies the intensity for
+            the diffuse lighting, and the other for the ambient lighting. The only other change is
+            to the last line in the shader. The usual diffuse lighting result has its value added to
+            the ambient lighting computation.</para>
+        <para>Of particular note is the difference between the lighting intensities in the
+            pure-diffuse case and the diffuse+ambient case:</para>
+        <example>
+            <title>Lighting Intensity Settings</title>
+            <programlisting language="cpp">if(g_bShowAmbient)
+{
+    glUseProgram(whiteDiffuse.theProgram);
+    glUniform4f(whiteDiffuse.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+    glUniform4f(whiteDiffuse.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+    glUseProgram(vertexDiffuse.theProgram);
+    glUniform4f(vertexDiffuse.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+    glUniform4f(vertexDiffuse.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+}
+else
+{
+    glUseProgram(whiteDiffuse.theProgram);
+    glUniform4f(whiteDiffuse.lightIntensityUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+    glUseProgram(vertexDiffuse.theProgram);
+    glUniform4f(vertexDiffuse.lightIntensityUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+}</programlisting>
+        </example>
+        <para>In the pure-diffuse case, the light intensity is full white. But in the ambient case,
+            we deliberately set the diffuse intensity to less than full white. This is very
+            intensional.</para>
+        <para>We will talk more about this issue in the near future, but it is very critical that
+            light intensity values not exceed 1.0. This includes <emphasis>combined</emphasis>
+            lighting intensity values. OpenGL clamps colors that it writes to the output image to
+            the range [0, 1]. So any light intensity that exceeds 1.0, whether alone or combined
+            with other lights, can cause unpleasant visual effects.</para>
+        <para>There are ways around this, and those ways will be discussed in the eventual
+            future.</para>
     </section>
     <section>
         <title>Positional Lights</title>
     </section>
     <section>
         <title>Intensity of Light</title>
-        <para/>
+        <para>There are many, many things wrong with the very primitive lighting model introduced
+            here. But one of the most important is the treatment of the lighting intensity.</para>
+        <para>Thus far, we have shown</para>
     </section>
     <section>
         <title>In Review</title>
                     <para/>
                 </glossdef>
             </glossentry>
+            <glossentry>
+                <glossterm>interreflection</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
+            <glossentry>
+                <glossterm>global illumination</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
+            <glossentry>
+                <glossterm>local illumination, direct illumination</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
+            <glossentry>
+                <glossterm>ambient lighting</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
         </glosslist>
     </section>
 </chapter>

File Tut 08 Lights on/Ambient 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 <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 dirToLightUnif;
+	GLuint lightIntensityUnif;
+	GLuint ambientIntensityUnif;
+
+	GLuint cameraToClipMatrixUnif;
+	GLuint modelToCameraMatrixUnif;
+	GLuint normalModelToCameraMatrixUnif;
+};
+
+float g_fzNear = 1.0f;
+float g_fzFar = 1000.0f;
+
+ProgramData g_WhiteDiffuseColor;
+ProgramData g_VertexDiffuseColor;
+ProgramData g_WhiteAmbDiffuseColor;
+ProgramData g_VertexAmbDiffuseColor;
+
+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.modelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "modelToCameraMatrix");
+	data.cameraToClipMatrixUnif = glGetUniformLocation(data.theProgram, "cameraToClipMatrix");
+	data.normalModelToCameraMatrixUnif = glGetUniformLocation(data.theProgram, "normalModelToCameraMatrix");
+	data.dirToLightUnif = glGetUniformLocation(data.theProgram, "dirToLight");
+	data.lightIntensityUnif = glGetUniformLocation(data.theProgram, "lightIntensity");
+	data.ambientIntensityUnif = glGetUniformLocation(data.theProgram, "ambientIntensity");
+
+	return data;
+}
+
+void InitializeProgram()
+{
+	g_WhiteDiffuseColor = LoadProgram("DirVertexLighting_PN.vert", "ColorPassthrough.frag");
+	g_VertexDiffuseColor = LoadProgram("DirVertexLighting_PCN.vert", "ColorPassthrough.frag");
+	g_WhiteAmbDiffuseColor = LoadProgram("DirAmbVertexLighting_PN.vert", "ColorPassthrough.frag");
+	g_VertexAmbDiffuseColor = LoadProgram("DirAmbVertexLighting_PCN.vert", "ColorPassthrough.frag");
+}
+
+glm::mat4 CalcLookAtMatrix(const glm::vec3 &cameraPt, const glm::vec3 &lookPt, const glm::vec3 &upPt)
+{
+	glm::vec3 lookDir = glm::normalize(lookPt - cameraPt);
+	glm::vec3 upDir = glm::normalize(upPt);
+
+	glm::vec3 rightDir = glm::normalize(glm::cross(lookDir, upDir));
+	glm::vec3 perpUpDir = glm::cross(rightDir, lookDir);
+
+	glm::mat4 rotMat(1.0f);
+	rotMat[0] = glm::vec4(rightDir, 0.0f);
+	rotMat[1] = glm::vec4(perpUpDir, 0.0f);
+	rotMat[2] = glm::vec4(-lookDir, 0.0f);
+
+	rotMat = glm::transpose(rotMat);
+
+	glm::mat4 transMat(1.0f);
+	transMat[3] = glm::vec4(-cameraPt, 1.0f);
+
+	return rotMat * transMat;
+}
+
+Framework::Mesh *g_pCylinderMesh = NULL;
+Framework::Mesh *g_pPlaneMesh = NULL;
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializeProgram();
+
+	try
+	{
+		g_pCylinderMesh = new Framework::Mesh("UnitCylinder.xml");
+		g_pPlaneMesh = new Framework::Mesh("LargePlane.xml");
+	}
+	catch(std::exception &except)
+	{
+		printf(except.what());
+	}
+
+	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 glm::vec3 g_camTarget(0.0f, 0.5f, 0.0f);
+
+//In spherical coordinates.
+static glm::vec3 g_sphereCamRelPos(67.5f, -46.0f, 5.0f);
+
+glm::vec3 ResolveCamPosition()
+{
+	float rho = Framework::DegToRad(g_sphereCamRelPos.x);
+	float theta = Framework::DegToRad(g_sphereCamRelPos.y + 90.0f);
+
+	float fSinTheta = sinf(theta);
+	float fCosTheta = cosf(theta);
+	float fCosRho = cosf(rho);
+	float fSinRho = sinf(rho);
+
+	glm::vec3 dirToCamera(fSinTheta * fCosRho, fCosTheta, fSinTheta * fSinRho);
+	return (dirToCamera * g_sphereCamRelPos.z) + g_camTarget;
+}
+
+glm::vec4 g_lightDirection(0.866f, 0.5f, 0.0f, 0.0f);
+
+static float g_CylYaw = 0.0f;
+static float g_CylPitch = 0.0f;
+static float g_CylRoll = 0.0f;
+
+static bool g_bDrawColoredCyl = true;
+static bool g_bShowAmbient = 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)
+	{
+		const glm::vec3 &camPos = ResolveCamPosition();
+
+		Framework::MatrixStack modelMatrix;
+		modelMatrix.SetMatrix(CalcLookAtMatrix(camPos, g_camTarget, glm::vec3(0.0f, 1.0f, 0.0f)));
+
+		glm::vec4 lightDirCameraSpace = modelMatrix.Top() * g_lightDirection;
+
+		ProgramData &whiteDiffuse = g_bShowAmbient ? g_WhiteAmbDiffuseColor : g_WhiteDiffuseColor;
+		ProgramData &vertexDiffuse = g_bShowAmbient ? g_VertexAmbDiffuseColor : g_VertexDiffuseColor;
+
+		if(g_bShowAmbient)
+		{
+			glUseProgram(whiteDiffuse.theProgram);
+			glUniform4f(whiteDiffuse.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+			glUniform4f(whiteDiffuse.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+			glUseProgram(vertexDiffuse.theProgram);
+			glUniform4f(vertexDiffuse.lightIntensityUnif, 0.8f, 0.8f, 0.8f, 1.0f);
+			glUniform4f(vertexDiffuse.ambientIntensityUnif, 0.2f, 0.2f, 0.2f, 1.0f);
+		}
+		else
+		{
+			glUseProgram(whiteDiffuse.theProgram);
+			glUniform4f(whiteDiffuse.lightIntensityUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+			glUseProgram(vertexDiffuse.theProgram);
+			glUniform4f(vertexDiffuse.lightIntensityUnif, 1.0f, 1.0f, 1.0f, 1.0f);
+		}
+
+		glUseProgram(whiteDiffuse.theProgram);
+		glUniform3fv(whiteDiffuse.dirToLightUnif, 1, glm::value_ptr(lightDirCameraSpace));
+		glUseProgram(vertexDiffuse.theProgram);
+		glUniform3fv(vertexDiffuse.dirToLightUnif, 1, glm::value_ptr(lightDirCameraSpace));
+		glUseProgram(0);
+
+		{
+			Framework::MatrixStackPusher push(modelMatrix);
+
+			//Render the ground plane.
+			{
+				Framework::MatrixStackPusher push(modelMatrix);
+
+				glUseProgram(whiteDiffuse.theProgram);
+				glUniformMatrix4fv(whiteDiffuse.modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(modelMatrix.Top()));
+				glm::mat3 normMatrix(modelMatrix.Top());
+				glUniformMatrix3fv(whiteDiffuse.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(vertexDiffuse.theProgram);
+					glUniformMatrix4fv(vertexDiffuse.modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(modelMatrix.Top()));
+					glm::mat3 normMatrix(modelMatrix.Top());
+					glUniformMatrix3fv(vertexDiffuse.normalModelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(normMatrix));
+					g_pCylinderMesh->Render("tint");
+				}
+				else
+				{
+					glUseProgram(whiteDiffuse.theProgram);
+					glUniformMatrix4fv(whiteDiffuse.modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(modelMatrix.Top()));
+					glm::mat3 normMatrix(modelMatrix.Top());
+					glUniformMatrix3fv(whiteDiffuse.normalModelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(normMatrix));
+					g_pCylinderMesh->Render("flat");
+				}
+				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)
+{
+	Framework::MatrixStack persMatrix;
+	persMatrix.Perspective(45.0f, (h / (float)w), g_fzNear, g_fzFar);
+
+	glUseProgram(g_WhiteDiffuseColor.theProgram);
+	glUniformMatrix4fv(g_WhiteDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE, glm::value_ptr(persMatrix.Top()));
+	glUseProgram(g_VertexDiffuseColor.theProgram);
+	glUniformMatrix4fv(g_VertexDiffuseColor.cameraToClipMatrixUnif, 1, GL_FALSE, glm::value_ptr(persMatrix.Top()));
+	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(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;
+		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 'i': g_sphereCamRelPos.y -= 11.25f; break;
+	case 'k': g_sphereCamRelPos.y += 11.25f; break;
+	case 'j': g_sphereCamRelPos.x += 11.25f; break;
+	case 'l': g_sphereCamRelPos.x -= 11.25f; break;
+	case 'o': g_sphereCamRelPos.z -= 1.5f; break;
+	case 'u': g_sphereCamRelPos.z += 1.5f; break;
+	case 'I': g_sphereCamRelPos.y -= 1.125f; break;
+	case 'K': g_sphereCamRelPos.y += 1.125f; break;
+	case 'J': g_sphereCamRelPos.x += 1.125f; break;
+	case 'L': g_sphereCamRelPos.x -= 1.125f; break;
+	case 'O': g_sphereCamRelPos.z -= 0.25f; break;
+	case 'U': g_sphereCamRelPos.z += 0.25f; break;
+		
+	case 32:
+		g_bDrawColoredCyl = !g_bDrawColoredCyl;
+		printf("Position: %f, %f, %f\n", g_sphereCamRelPos.x, g_sphereCamRelPos.y, g_sphereCamRelPos.z);
+		printf("Yaw: %f, Pitch: %f, Roll: %f\n", g_CylYaw, g_CylPitch, g_CylRoll);
+		break;
+
+	case 't':
+	case 'T':
+		g_bShowAmbient = !g_bShowAmbient;
+		if(g_bShowAmbient)
+			printf("Ambient Lighting On.\n");
+		else
+			printf("Ambient Lighting Off.\n");
+
+		break;
+	}
+
+	g_sphereCamRelPos.y = glm::clamp(g_sphereCamRelPos.y, -78.75f, 78.75f);
+	g_camTarget.y = g_camTarget.y > 0.0f ? g_camTarget.y : 0.0f;
+	g_sphereCamRelPos.z = g_sphereCamRelPos.z > 2.0f ? g_sphereCamRelPos.z : 2.0f;
+
+	glutPostRedisplay();
+}
+
+

File Tut 08 Lights on/data/DirAmbVertexLighting_PCN.vert

+#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 dirToLight;
+uniform vec4 lightIntensity;
+uniform vec4 ambientIntensity;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+uniform mat3 normalModelToCameraMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * (modelToCameraMatrix * vec4(position, 1.0));
+
+	vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);
+	
+	float cosAngIncidence = dot(normCamSpace, dirToLight);
+	cosAngIncidence = clamp(cosAngIncidence, 0, 1);
+	
+	interpColor = (diffuseColor * lightIntensity * cosAngIncidence) +
+		(diffuseColor * ambientIntensity);
+}

File Tut 08 Lights on/data/DirAmbVertexLighting_PN.vert

+#version 330
+
+layout(location = 0) in vec3 position;
+layout(location = 2) in vec3 normal;
+
+smooth out vec4 interpColor;
+
+uniform vec3 dirToLight;
+uniform vec4 lightIntensity;
+uniform vec4 ambientIntensity;
+
+uniform mat4 cameraToClipMatrix;
+uniform mat4 modelToCameraMatrix;
+
+uniform mat3 normalModelToCameraMatrix;
+
+void main()
+{
+	gl_Position = cameraToClipMatrix * (modelToCameraMatrix * vec4(position, 1.0));
+
+	vec3 normCamSpace = normalize(normalModelToCameraMatrix * normal);
+	
+	float cosAngIncidence = dot(normCamSpace, dirToLight);
+	cosAngIncidence = clamp(cosAngIncidence, 0, 1);
+	
+	interpColor = (lightIntensity * cosAngIncidence) + ambientIntensity;
+}

File Tut 08 Lights on/tutorials.lua

 	"data/DirVertexLighting_PN.vert",
 	"data/ColorPassthrough.frag")
 
+SetupProject("Tut 08 Ambient Lighting", "Ambient Lighting.cpp",
+	"data/DirVertexLighting_PCN.vert",
+	"data/DirVertexLighting_PN.vert",
+	"data/DirAmbVertexLighting_PCN.vert",
+	"data/DirAmbVertexLighting_PN.vert",
+	"data/ColorPassthrough.frag")
+