Commits

Jason McKesson committed 6237642

Added more to Tutorial 5.

Comments (0)

Files changed (10)

+require "lfs"
+
+local outputDir = ...;
+outputDir = outputDir or "website\\";
+
+if(not lfs.attributes(outputDir)) then
+	--Delete the directory.
+	lfs.rmdir(outputDir);
+end
+
+lfs.mkdir(outputDir);
+
+local mainDir = "Documents\\";
+
+--Copy the html files.
+local htmlDir = mainDir .. "web\\";
+local command = string.format([[copy "%s*.html" "%s"]], htmlDir, outputDir);
+print(command);
+os.execute(command);
+
+--Copy the .css file.
+command = string.format([[copy "%schunked.css" "%s" ]], mainDir, outputDir);
+print(command);
+os.execute(command);
+
+local function DoesDirHavePattern(theDir, ...)
+	local patterns = {...};
+	for dir in lfs.dir(theDir) do
+		for i, pattern in ipairs(patterns) do
+			if(dir:match(pattern)) then return true end
+		end
+	end
+	
+	return false;
+end
+
+--Copy the images.
+for dir in lfs.dir(mainDir) do
+	local sourceDir = mainDir .. dir;
+	if(lfs.attributes(sourceDir, "mode") == "directory" and dir ~= "." and dir ~= "..") then
+		if(DoesDirHavePattern(sourceDir, "%.svg$", "%.png$")) then
+			local destDir = outputDir .. dir;
+			lfs.mkdir(destDir);
+			
+			command = string.format([[copy "%s\*.svg" "%s" ]], sourceDir, destDir);
+			print(command);
+			os.execute(command);
+			
+			command = string.format([[copy "%s\*.png" "%s" ]], sourceDir, destDir);
+			print(command);
+			os.execute(command);
+		end
+	end
+end
+

Documents/Basics/Tutorial 00.xml

             <para>The specification is a very complicated and technical document. I do not suggest
                 that the novice graphics programmer read it. If you do however, the most important
                 thing to understand about it is this: it describes <emphasis>results</emphasis>, not
-                implementation. For example, the spec says that clipping of triangles happens before
-                transforming them from clip-space to normalized device coordinate space. Hardware
-                almost certainly does clipping in normalized device coordinate space, simply because
-                all the vertices are in the same space. It doesn't matter to the results, so it is
-                still a valid OpenGL implementation.</para>
+                implementation. Just because the spec says that X will happen does not mean that it
+                actually does. What it means is that the user should not be able to tell the
+                difference. If a piece of hardware can provide the same behavior in a different way,
+                then the specification allows this, so long as the user can never tell.</para>
         </section>
     </section>
     <section xml:id="Intro_Glossary">

Documents/Positioning/CameraToPerspective.svg

Old
Old image
New
New image
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="450px" width="900px" >
-	<style type="text/css" ><![CDATA[.fill_black
+	<style type="text/css" ><![CDATA[.wide_black
 {
-	fill: black;
+	stroke-width: 3px;
+	stroke: black;
+}
+
+.arrow_ended
+{
+	marker-end: url(#arrow);
 }
 
 .axis_label
 	font-family: monospace;
 }
 
+.fill_frustum
+{
+	fill: #E0E0E0;
+}
+
+.pointed
+{
+	marker: url(#point);
+}
+
+.fill_black
+{
+	fill: black;
+}
+
+.image_label
+{
+	font-size: 40px;
+	stroke: black;
+	text-anchor: middle;
+	font-family: serif;
+}
+
 .object_lines
 {
 	stroke: #00C000;
 	marker-start: url(#arrow);
 }
 
-.arrow_ended
+.text
 {
-	marker-end: url(#arrow);
+	font-size: 30px;
+	font-family: monospace;
 }
 
 .fill_none
 	fill: none;
 }
 
-.pointed
-{
-	marker: url(#point);
-}
-
-.stroke_none
-{
-	stroke: none;
-}
-
-.image_label
-{
-	font-size: 40px;
-	stroke: black;
-	text-anchor: middle;
-	font-family: serif;
-}
-
 .arrows
 {
 	marker-end: url(#arrow);
 	marker-mid: url(#arrow);
 }
 
-.wide_black
+.stroke_none
 {
-	stroke-width: 3px;
-	stroke: black;
-}
-
-.text
-{
-	font-size: 30px;
-	font-family: monospace;
-}
-
-.fill_frustum
-{
-	fill: #E0E0E0;
+	stroke: none;
 }
 
 .black
 	<line x2="250" y2="342" y1="358" x1="250" class="black" />
 	<line x2="300" y2="338" y1="362" x1="300" class="black" />
 	<line x2="350" y2="342" y1="358" x1="350" class="black" />
+	<line x2="700" y2="400" y1="200" x1="700" class="black arrow_ended" />
 	<line x2="700" y2="0" y1="200" x1="700" class="black arrow_ended" />
-	<line x2="700" y2="400" y1="200" x1="700" class="black arrow_ended" />
 	<line x2="500" y2="200" y1="200" x1="700" class="black arrow_ended" />
 	<line x2="900" y2="200" y1="200" x1="700" class="black arrow_ended" />
+	<line x2="692" y2="350" y1="350" x1="708" class="black" />
+	<line x2="688" y2="300" y1="300" x1="712" class="black" />
+	<line x2="692" y2="250" y1="250" x1="708" class="black" />
+	<line x2="688" y2="200" y1="200" x1="712" class="black" />
+	<line x2="692" y2="150" y1="150" x1="708" class="black" />
+	<line x2="688" y2="100" y1="100" x1="712" class="black" />
 	<line x2="692" y2="50" y1="50" x1="708" class="black" />
-	<line x2="688" y2="100" y1="100" x1="712" class="black" />
-	<line x2="692" y2="150" y1="150" x1="708" class="black" />
-	<line x2="688" y2="200" y1="200" x1="712" class="black" />
-	<line x2="692" y2="250" y1="250" x1="708" class="black" />
-	<line x2="688" y2="300" y1="300" x1="712" class="black" />
-	<line x2="692" y2="350" y1="350" x1="708" class="black" />
-	<line x2="550" y2="192" y1="208" x1="550" class="black" />
-	<line x2="600" y2="188" y1="212" x1="600" class="black" />
-	<line x2="650" y2="192" y1="208" x1="650" class="black" />
-	<line x2="700" y2="188" y1="212" x1="700" class="black" />
-	<line x2="750" y2="192" y1="208" x1="750" class="black" />
-	<line x2="800" y2="188" y1="212" x1="800" class="black" />
-	<line x2="850" y2="192" y1="208" x1="850" class="black" />
+	<line x2="550" y2="208" y1="192" x1="550" class="black" />
+	<line x2="600" y2="212" y1="188" x1="600" class="black" />
+	<line x2="650" y2="208" y1="192" x1="650" class="black" />
+	<line x2="700" y2="212" y1="188" x1="700" class="black" />
+	<line x2="750" y2="208" y1="192" x1="750" class="black" />
+	<line x2="800" y2="212" y1="188" x1="800" class="black" />
+	<line x2="850" y2="208" y1="192" x1="850" class="black" />
 	<path d="M 350 225 L 350 275 L 250 275 L 250 225 M 350 175 L 350 225 L 250 225 L 250 175 M 350 125 L 350 175 L 250 175 L 250 125 M 350 75 L 350 125 L 250 125 L 250 75 M 350 25 L 350 75 L 250 75 L 250 25 Z" class="object_lines" />
-	<path d="M 820 160 L 900 0 L 766.66666666667 0 L 740 160 M 785.71428571429 228.57142857143 L 820 160 L 740 160 L 728.57142857143 228.57142857143 M 766.66666666667 266.66666666667 L 785.71428571429 228.57142857143 L 728.57142857143 228.57142857143 L 722.22222222222 266.66666666667 M 754.54545454545 290.90909090909 L 766.66666666667 266.66666666667 L 722.22222222222 266.66666666667 L 718.18181818182 290.90909090909 M 746.15384615385 307.69230769231 L 754.54545454545 290.90909090909 L 718.18181818182 290.90909090909 L 715.38461538462 307.69230769231 Z" class="object_lines" />
+	<path d="M 820 240 L 900 400 L 766.66666666667 400 L 740 240 M 785.71428571429 171.42857142857 L 820 240 L 740 240 L 728.57142857143 171.42857142857 M 766.66666666667 133.33333333333 L 785.71428571429 171.42857142857 L 728.57142857143 171.42857142857 L 722.22222222222 133.33333333333 M 754.54545454545 109.09090909091 L 766.66666666667 133.33333333333 L 722.22222222222 133.33333333333 L 718.18181818182 109.09090909091 M 746.15384615385 92.307692307692 L 754.54545454545 109.09090909091 L 718.18181818182 109.09090909091 L 715.38461538462 92.307692307692 Z" class="object_lines" />
 	<circle r="5" cy="275" cx="350" class="object_circles" />
-	<circle r="5" cy="0" cx="900" class="object_circles" />
+	<circle r="5" cy="400" cx="900" class="object_circles" />
 	<circle r="5" cy="275" cx="250" class="object_circles" />
-	<circle r="5" cy="0" cx="766.66666666667" class="object_circles" />
+	<circle r="5" cy="400" cx="766.66666666667" class="object_circles" />
 	<circle r="5" cy="225" cx="350" class="object_circles" />
-	<circle r="5" cy="160" cx="820" class="object_circles" />
+	<circle r="5" cy="240" cx="820" class="object_circles" />
 	<circle r="5" cy="225" cx="250" class="object_circles" />
-	<circle r="5" cy="160" cx="740" class="object_circles" />
+	<circle r="5" cy="240" cx="740" class="object_circles" />
 	<circle r="5" cy="175" cx="350" class="object_circles" />
-	<circle r="5" cy="228.57142857143" cx="785.71428571429" class="object_circles" />
+	<circle r="5" cy="171.42857142857" cx="785.71428571429" class="object_circles" />
 	<circle r="5" cy="175" cx="250" class="object_circles" />
-	<circle r="5" cy="228.57142857143" cx="728.57142857143" class="object_circles" />
+	<circle r="5" cy="171.42857142857" cx="728.57142857143" class="object_circles" />
 	<circle r="5" cy="125" cx="350" class="object_circles" />
-	<circle r="5" cy="266.66666666667" cx="766.66666666667" class="object_circles" />
+	<circle r="5" cy="133.33333333333" cx="766.66666666667" class="object_circles" />
 	<circle r="5" cy="125" cx="250" class="object_circles" />
-	<circle r="5" cy="266.66666666667" cx="722.22222222222" class="object_circles" />
+	<circle r="5" cy="133.33333333333" cx="722.22222222222" class="object_circles" />
 	<circle r="5" cy="75" cx="350" class="object_circles" />
-	<circle r="5" cy="290.90909090909" cx="754.54545454545" class="object_circles" />
+	<circle r="5" cy="109.09090909091" cx="754.54545454545" class="object_circles" />
 	<circle r="5" cy="75" cx="250" class="object_circles" />
-	<circle r="5" cy="290.90909090909" cx="718.18181818182" class="object_circles" />
+	<circle r="5" cy="109.09090909091" cx="718.18181818182" class="object_circles" />
 	<circle r="5" cy="25" cx="350" class="object_circles" />
-	<circle r="5" cy="307.69230769231" cx="746.15384615385" class="object_circles" />
+	<circle r="5" cy="92.307692307692" cx="746.15384615385" class="object_circles" />
 	<circle r="5" cy="25" cx="250" class="object_circles" />
-	<circle r="5" cy="307.69230769231" cx="715.38461538462" class="object_circles" />
+	<circle r="5" cy="92.307692307692" cx="715.38461538462" class="object_circles" />
 	<text y="340" x="355" class="axis_label" >+X</text>
 	<text y="30" x="150" class="axis_label" >-Z</text>
 	<text y="190" x="855" class="axis_label" >+X</text>
-	<text y="30" x="650" class="axis_label" >-Z</text>
+	<text y="30" x="650" class="axis_label" >+Z</text>
 	<text y="440" x="200" class="image_label" >Camera Space</text>
 	<text y="440" x="700" class="image_label" >Norm. Device Coord.</text>
 </svg>

Documents/Positioning/GenCameraToPerspective.lua

 	end
 
 	local final = vmath.vec2(tPoint);
-	--final.y = -final.y
+	final.y = -final.y
 	final = final + (halfWorldWidth);
 	final = final * (subImageSize / worldWidth);
 	final = final + subImagePositions[2]
 
 local leftAxisLocations = { vec2(halfWorldWidth, 0), vec2(0, leftWorldVertRange[1]) };
 leftAxisLocations = TransformPointToLeftWnd(leftAxisLocations);
-local rightAxisLocations = { vec2(halfWorldWidth, 0), vec2(0, -halfWorldWidth) }
+local rightAxisLocations = { vec2(halfWorldWidth, 0), vec2(0, halfWorldWidth) }
 rightAxisLocations = TransformPointToRightWnd(rightAxisLocations);
 
 local axisLabelPixelOffsets = { vec2(-45, -10), vec2(-50, 30) };
 	writer:Text("+X", leftAxisLocations[1] + axisLabelPixelOffsets[1], {"axis_label"})
 	writer:Text("-Z", leftAxisLocations[2] + axisLabelPixelOffsets[2], {"axis_label"})
 	writer:Text("+X", rightAxisLocations[1] + axisLabelPixelOffsets[1], {"axis_label"})
-	writer:Text("-Z", rightAxisLocations[2] + axisLabelPixelOffsets[2], {"axis_label"})
+	writer:Text("+Z", rightAxisLocations[2] + axisLabelPixelOffsets[2], {"axis_label"})
 
 	--label the images
 	writer:Text("Camera Space", imageTitleLoc[1] + imageTitleOffset, {"image_label"});

Documents/Positioning/Tutorial 04.xml

                     </imageobject>
                 </mediaobject>
             </figure>
-            <para>Do note that the direction of viewing is flipped from camera space and normalized
-                device coordinate (NDC) space. Camera space is looking <quote>up</quote> in the left
-                diagram, along the -Z axis. While NDC space is looking <quote>down</quote>, along
-                the +Z axis. Thus, the meaning of front and back is reversed; the points at the top
-                of the left image correspond to the points at the <emphasis>bottom</emphasis> of the
-                right image.</para>
+            <para>Do note that this diagram has the Z axis flipped from camera space and normalized
+                device coordinate (NDC) space. This is because camera space and NDC space have
+                different viewing directions. In camera space, the camera looks down the -Z axis;
+                more negative Z values are farther. In NDC space, the camera looks down the +Z axis;
+                more positive Z values are farther. The diagram flips the axis so that the viewing
+                direction can remain the same between the two images.</para>
             <para>If you perform an orthographic projection from NDC space on the right (by dropping
                 the Z coordinate), then what you get is a perspective projection of the world on the
                 left. In effect, what we have done is transform objects into a three-dimensional

Documents/Positioning/Tutorial 05.xml

         <para>If the smaller object is truly behind the larger one, why is it being rendered on top
             of the larger one? Well, to answer that question, we need to remember what OpenGL
             is.</para>
-        <para>OpenGL defines a rasterization-based renderer. Rasterizers offer great opportunities
-            for optimizations and hardware implementation, and using them provides great power to
-            the programmer. However, they're very stupid. When you strip out all of the shaders and
-            other logic, a rasterizer is basically just a triangle drawer. That's all they know how
-            to do. And they're very good at it.</para>
+        <para>The OpenGL specification defines a rasterization-based renderer. Rasterizers offer
+            great opportunities for optimizations and hardware implementation, and using them
+            provides great power to the programmer. However, they're very stupid. When you strip out
+            all of the shaders and other logic, a rasterizer is basically just a triangle drawer.
+            That's all they know how to do. And they're very good at it.</para>
         <para>But rasterizers do exactly and only what the user says. They draw triangles in a given
             sequence. This means that, if there is overlap between multiple triangles in window
             space, the triangle that is rendered last will win.</para>
-        <para>Therefore, the easy way to solve this problem is to simply render the farther objects
-            first. This is called <glossterm>depth sorting.</glossterm> As you might imagine, this
-            solution scales incredibly poorly. Doing it for each triangle is prohibitive,
-            particularly with scenes with millions of triangles.</para>
-        <para>And the worst part is that, well, <emphasis>it doesn't work</emphasis>. Well, not
-            always. Many trivial cases can be solved via depth sorting, but non-trivial cases have
-            real problems. You can have an arrangement of 3 triangles where each overlaps the other,
-            such that there simply is no order you can render them in to achieve the right effect.
-            Clearly, we need something better.</para>
+        <para>The first thing you might think of when solving this problem is to simply render the
+            farther objects first. This is called <glossterm>depth sorting.</glossterm> As you might
+            imagine, this <quote>solution</quote> scales incredibly poorly. Doing it for each
+            triangle is prohibitive, particularly with scenes with millions of triangles.</para>
+        <para>And the worst part is that even if you put in all the effort, <emphasis>it doesn't
+                work</emphasis>. Well, not always. Many trivial cases can be solved via depth
+            sorting, but non-trivial cases have real problems. You can have an arrangement of 3
+            triangles where each overlaps the other, such that there simply is no order you can
+            render them in to achieve the right effect. Clearly, we need something better.</para>
         <para>One solution would be to tag fragments with the distance from the viewer. Then, if a
             fragment is about to be written is going to write a farther distance (ie: the fragment
             is behind what was already written), we simply do not write that fragment. That way, if
             a fragment ranges from 0 to 1, where 0 is the closest and 1 is the farthest.</para>
         <para>Colors output from the fragment shader are output into the color buffer. Therefore it
             naturally follows that depth values would be stored in a <glossterm>depth
-                buffer.</glossterm> The depth buffer is an image, the same size as the main color
-            buffer, that stores depth values as pixels rather than colors. Where a color is a
-            4-component vector, a depth is just a single floating-point value.</para>
+                buffer</glossterm> (also called a <glossterm>z buffer</glossterm>, because it stores
+            the Z value). The depth buffer is an image, the same size as the main color buffer, that
+            stores depth values as pixels rather than colors. Where a color is a 4-component vector,
+            a depth is just a single floating-point value.</para>
         <para>Like the color buffer, the depth buffer for the main window is created automatically
             by OpenGL when OpenGL is initialized. OpenGL can even be created without a depth buffer.
             Since FreeGLUT takes care of initializing OpenGL for us, we tell it in the standard
             <para>The range zNear can be greater than the range zFar; if it is, then the
                 window-space values will be reversed, in terms of what constitutes closest or
                 farthest from the viewer.</para>
+            <para>Earlier, it was said that the window-space Z value of 0 is closest and 1 is
+                farthest. However, if our clip-space Z values were negated, the depth of 1 would be
+                closest to the view and the depth of 0 would be farthest. Yet, if we flip the
+                direction of the depth test (GL_LESS to GL_GREATER, etc), we get the exact same
+                result. So it's really just a convention. Indeed, flipping the sign of Z and the
+                depth test was once a vital performance optimization for many games.</para>
         </section>
         <section>
             <title>Rendering with Depth</title>
             <para>These are the most common depth testing parameters. It turns on depth testing,
                 sets the test function to less than or equal to, and sets the range mapping to the
                 full accepted range.</para>
+            <para>It is comment to use <literal>GL_LEQUAL</literal> instead of
+                    <literal>GL_LESS</literal>. This allows for the use of multipass algorithms,
+                where you render the same geometry with the same vertex shader, but linked with a
+                different fragment shader. We'll look at those much, much later.</para>
             <para>There is one more issue. We know what the depth value is in the depth buffer after
                 a fragment is written to it. But what is its value before any rendering is done at
                 all? Depth buffers and color buffers are very similar; color buffers get their
             </example>
             <para>This will set all of the depth values in the depth buffer to 1.0, which is our
                 range zFar.</para>
+            <note>
+                <para>This is all that is necessary to do depth buffering, as far as OpenGL proper
+                    is concerned. However, in order to use depth buffering, the framebuffer must
+                    include a depth buffer in addition to an image buffer. This initialization code
+                    is platform-specific, but FreeGLUT takes care of it for us. If you do graduate
+                    from FreeGLUT, make sure that you use the appropriate initialization mechanism
+                    for your platform to create a depth buffer if you need to do depth
+                    buffering.</para>
+            </note>
         </section>
         <section>
             <title>Depth Precision</title>
             <para>There is one other thing that needs to be discussed with regard to depth buffers:
                 precision.</para>
-            <para>In the previous tutorial, </para>
+            <para>In the previous tutorial, we saw that the transform from camera space to
+                normalized device coordinate (<acronym>NDC</acronym>), in 2D, looked like
+                this:</para>
+            <figure>
+                <title>2D Camera to NDC Space</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="CameraToPerspective.svg"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
+            <para>This transformation used a special function to calculate the depth, one designed
+                to keep lines linear after performing the perspective divide. While it does do this,
+                it has a number of other effects. In particular, it changes the Z spacing between
+                points.</para>
+            <para>We can see that there is a lot of spacing between the points in NDC space at the
+                bottom (close to the view) and much less at the top (far from the view). The
+                third-nearest point to the viewer in camera space (Z = -1.75) maps to a point well
+                past halfway to the camera in NDC space.</para>
+            <para>Let us take just the front half of NDC space as an example. In NDC space, this is
+                the range [-1, 0]. In camera space, the exact range depends on the camera zNear and
+                zFar values. In the above example where the camera range is [-1, -3], the range that
+                maps to the front half of NDC space is [-1, -1.5], only a quarter of the range. The
+                equation to compute this for any camera range is pretty simple:</para>
+            <!--TODO: Add an image of the equation 2NF / F+N-->
+            <para>Where F and N are both positive values.</para>
+            <para>We can see from this equation that, the larger the difference between N and F, the
+                    <emphasis>smaller</emphasis> the half-space. If the camera range goes from
+                [-500, -1000], then half of NDC space represents the range from [-500, -666.67].
+                This is 33.3% of the camera space range mapping to 50% of the NDC range. However, if
+                the camera range goes from [-1, -1000], fully <emphasis>half</emphasis> of NDC space
+                will represent only [-1, -1.998]; less than 0.1% of the range.</para>
+            <para>This has real consequences for the precision of your depth buffer. Earlier, we
+                said that the depth buffer stores floating-point values. While this is conceptually
+                true, most depth buffers actually use fixed-point values and convert them into
+                floating-point values automatically. If you have a 16-bit depth buffer, you have
+                65536 possible depth values. Half of this is 32768 depth values, equivalent to a
+                15-bit depth buffer.</para>
+            <para>Even so, the difference between 16 bits and 15 bits is not that great. Instead of
+                looking at half of NDC space, let's look at half of the
+                    <emphasis>precision.</emphasis> So, what is the camera-space range at which you
+                lose half of your precision?</para>
+            <para>For a 16-bit depth buffer, half-precision is 8 bits. In fixed-point, if the near
+                value is 0 and the far is 65535 (representing 1.0), then half-precision happens when
+                the first 8 bits are all ones. This value is 65280 (65535 - 255). As a
+                floating-point value, this represents a value of ~0.996. In NDC space, this is a Z
+                value of ~0.992.</para>
+            <para>So what is the camera-space range at which you lose half precision? If the camera
+                depth range is [-500, -1000], then you get the half precision range of [-500, -996],
+                which is over 99% of the camera-space range. What about [-1, -1000]? This comes out
+                to [-1, -200], which is 20% of the range.</para>
+            <para>Before we can assess the consequences of this, we must first discuss what the
+                consequences are for low depth precision. Remember that the depth buffer exists to
+                allow each fragment to have a depth value, such that if an incoming fragment is
+                behind the already existing value, it is not written to the image.</para>
+            <para>If the available precision is too small, then what happens is that part of one
+                triangle will start showing through triangles that are supposed to be farther away.
+                If the camera or these objects are in motion, horrible flickering artifacts can be
+                seen. This is called <glossterm>z-fighting,</glossterm> as multiple objects appear
+                to be fighting each other.</para>
+            <!--TODO: Show an image of z-fighting.-->
+            <para>Most depth buffers are not 16-bit; these days, the default is 24-bit.
+                Half-precision of a 24-bit is 12-bit, which is not too far from a 16-bit depth
+                buffer in and of itself. If you use a 24-bit depth buffer, it turns out that you
+                lose half precision on a [-1, -1000] camera range at [-1, -891], which is 89% of the
+                range. At a 1:10,000 ratio, you have 45% of the camera range in most of the
+                precision. At 1:100,000 this drops to ~7%, and at 1:1,000,000 it is down to
+                0.8%.</para>
+            <para>The most important question to be asked is this: is this bad? Not really.</para>
+            <para>Let's take the 1:100,000 example. 7% may not sound like a lot, but this is still a
+                range of [-1, -7573]. If these units are conceptually in inches, then you've got
+                most of the precision sitting in the first 600+ feet.</para>
+            <para>And let's see what happens if we move the zNear plane forward just
+                    <emphasis>four</emphasis> inches, to 5:100,000. The percentage jumps to almost
+                30%, with half-precision happening at over 29,000 inches; that's a good half-mile.
+                Increase the zNear to a mere 10 inches, and you have the equivalent of 1:10,000
+                again: 45%. 10 inches may seem like a lot, but that's still less than a foot away
+                from the eye. Depending on what you are rendering, this may be a perfectly
+                legitimate trade-off.</para>
+            <para>What this teaches us is that the absolute numbers don't matter: it is the ratio of
+                zNear to zFar that dictates where you lose precision. 0.1:1000 is just as bad as
+                1:10,000. So push the zNear distance forward as far as you can. What happens if you
+                push it too far? That's the next section.</para>
+            <section>
+                <title>Large Camera Depth Ranges</title>
+                <para>You may ask what to do if you really need a wide camera depth range, like
+                    1:4,000,000 or something, where each unit represents an inch or something
+                    equally small.</para>
+                <para>First, it needs to be pointed out that a 24-bit depth buffer only goes from 0
+                    to 16,777,215. Even if the depth values were evenly distributed, you would only
+                    get a resolution of 4th of an inch.</para>
+                <para>Second, this range is starting to come perilously close to the issues with
+                        <emphasis>floating-point</emphasis> precision. Yes, this still provides a
+                    lot of precision, but remember: the depth range is for the current view. This
+                    means that your world is probably much larger than this. If you're getting
+                    numbers that large, you should be starting to worry about floating-point
+                    precision error in computing these positions. There are certainly ways around it
+                    (and we will discuss some later), but if you need a camera-space range
+                    that</para>
+                <para>Third, most applications render lower-quality models when objects are far
+                    away. This is mainly for the purpose of focusing performance where the user
+                    needs it: the things closest to him. By reducing detail in distant objects, you
+                    are also less likely to have z-fighting; so it helps here too.</para>
+                <para>Fourth, you usually really, <emphasis>really</emphasis> need that precision
+                    up-close. If you think z-fighting looks bad when it happens with a distant
+                    object, imagine how bad it will look if it's up in your face. Even if you could
+                    make the z-values linear, it could cause problems in near objects.</para>
+                <para>Fifth, if you really, <emphasis>really</emphasis> need a camera range this
+                    large, you can play some tricks with the depth range.</para>
+                <para>The camera range defines how the perspective matrix transforms the Z to
+                    clip-space and therefore NDC space. The <emphasis>depth</emphasis> range defines
+                    what part of the [0, 1] range of window coordinates that the NDC depth maps to.
+                    So you can draw the front half of your scene into the [0, 0.5] depth range with
+                    a camera range like [-1, -2,000,000]. Then, you can draw the back half of the
+                    scene in the [0.5, 1] depth range, with a camera range of [-2,000,000,
+                    -4,000,000]. Dividing it in half like this isn't very fair to your front
+                    objects, so it's more likely that you would want to use something like [-1,
+                    -10,000] for the front half and [-10,000, -4,000,000] for the second. Each of
+                    these would still map to half of the depth range.</para>
+                <para>Objects that lie on the border between the split would have to be rendered
+                    into both, just to make sure their depth values show up properly.</para>
+            </section>
         </section>
     </section>
     <section>
         <para/>
     </section>
     <section>
+        <title>Depth Clamping</title>
+        <para>That's all well and good, but this:</para>
+        <!--TODO: Same image from clipped tutorial as before-->
+        <para>This is never a good thing. Sure, it keeps the hardware from dividing by zero, but it
+            looks really bad. It's showing the inside of an object that has no insides. Plus, you
+            can also see that it has no backside (since we're doing face culling); you can see right
+            through to the object behind it.</para>
+        <para>If computer graphics is an elaborate illusion, then clipping utterly
+                <emphasis>shatters</emphasis> this illusion. What can we do about this?</para>
+        <para>The most common technique is to simply not allow it. That is, know how close objects
+            are getting to the near clipping plane (ie: the camera) and don't let them get close
+            enough to clip.</para>
+        <para>A more reasonable mechanism is <glossterm>depth clamping</glossterm>.</para>
+    </section>
+    <section>
         <?dbhtml filename="Tut05 In Review.html" ?>
         <title>In Review</title>
         <para/>
                         <para/>
                     </glossdef>
                 </glossentry>
+                <glossentry>
+                    <glossterm>glEnable/glDisable(GL_DEPTH_CLAMP)</glossterm>
+                    <glossdef>
+                        <para/>
+                    </glossdef>
+                </glossentry>
             </glosslist>
         </section>
     </section>
                 </glossdef>
             </glossentry>
             <glossentry>
-                <glossterm>depth buffer</glossterm>
+                <glossterm>depth buffer, z-buffer</glossterm>
                 <glossdef>
                     <para/>
                 </glossdef>
                     <para/>
                 </glossdef>
             </glossentry>
+            <glossentry>
+                <glossterm>z-fighting</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
+            <glossentry>
+                <glossterm>depth clamping</glossterm>
+                <glossdef>
+                    <para/>
+                </glossdef>
+            </glossentry>
         </glosslist>
     </section>
 </chapter>

Documents/Tutorial Documents.xpr

                         <scenarioAssociation-array>
                             <scenarioAssociation>
                                 <field name="name">
-                                    <String xml:space="preserve">Tutorial to HTML Chunked</String>
+                                    <String xml:space="preserve">Tutorial to Local HTML Chunked</String>
                                 </field>
                                 <field name="type">
                                     <String xml:space="preserve">XSL</String>
                             </scenarioAssociation>
                             <scenarioAssociation>
                                 <field name="name">
-                                    <String xml:space="preserve">Tutorial to HTML Chunked</String>
-                                </field>
-                                <field name="type">
-                                    <String xml:space="preserve">XSL</String>
-                                </field>
-                                <field name="url">
-                                    <String xml:space="preserve">Outline.xml</String>
-                                </field>
-                            </scenarioAssociation>
-                            <scenarioAssociation>
-                                <field name="name">
                                     <String xml:space="preserve">Tutorial to HTML</String>
                                 </field>
                                 <field name="type">
                                     <null/>
                                 </field>
                                 <field name="name">
-                                    <String xml:space="preserve">Tutorial to HTML Chunked</String>
-                                </field>
-                                <field name="baseURL">
-                                    <String xml:space="preserve"></String>
-                                </field>
-                                <field name="footerURL">
-                                    <String xml:space="preserve"></String>
-                                </field>
-                                <field name="fOPMethod">
-                                    <String xml:space="preserve">pdf</String>
-                                </field>
-                                <field name="fOProcessorName">
-                                    <String xml:space="preserve">Apache FOP</String>
-                                </field>
-                                <field name="headerURL">
-                                    <String xml:space="preserve"></String>
-                                </field>
-                                <field name="inputXSLURL">
-                                    <String xml:space="preserve">${frameworks}/docbook/xsl/html/chunkfast.xsl</String>
-                                </field>
-                                <field name="inputXMLURL">
-                                    <String xml:space="preserve">${currentFileURL}</String>
-                                </field>
-                                <field name="defaultScenario">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="isFOPPerforming">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="type">
-                                    <String xml:space="preserve">XSL</String>
-                                </field>
-                                <field name="saveAs">
-                                    <Boolean xml:space="preserve">true</Boolean>
-                                </field>
-                                <field name="openInBrowser">
-                                    <Boolean xml:space="preserve">true</Boolean>
-                                </field>
-                                <field name="outputFile">
-                                    <File xml:space="preserve">${cfd}/html/nothing.html</File>
-                                </field>
-                                <field name="openOtherLocationInBrowser">
-                                    <Boolean xml:space="preserve">true</Boolean>
-                                </field>
-                                <field name="locationToOpenInBrowserURL">
-                                    <String xml:space="preserve">html/index.html</String>
-                                </field>
-                                <field name="openInEditor">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="showInHTMLPane">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="showInXMLPane">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="showInSVGPane">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="showInResultSetPane">
-                                    <Boolean xml:space="preserve">false</Boolean>
-                                </field>
-                                <field name="useXSLTInput">
-                                    <Boolean xml:space="preserve">true</Boolean>
-                                </field>
-                                <field name="xsltParams">
-                                    <list>
-                                    <transformationParameter>
-                                    <field name="paramDescription">
-                                    <paramDescriptor>
-                                    <field name="localName">
-                                    <String xml:space="preserve">base.dir</String>
-                                    </field>
-                                    <field name="prefix">
-                                    <null/>
-                                    </field>
-                                    <field name="namespace">
-                                    <null/>
-                                    </field>
-                                    </paramDescriptor>
-                                    </field>
-                                    <field name="value">
-                                    <String xml:space="preserve">${cfd}/</String>
-                                    </field>
-                                    </transformationParameter>
-                                    <transformationParameter>
-                                    <field name="paramDescription">
-                                    <paramDescriptor>
-                                    <field name="localName">
-                                    <String xml:space="preserve">chunk.quietly</String>
-                                    </field>
-                                    <field name="prefix">
-                                    <null/>
-                                    </field>
-                                    <field name="namespace">
-                                    <null/>
-                                    </field>
-                                    </paramDescriptor>
-                                    </field>
-                                    <field name="value">
-                                    <String xml:space="preserve">1</String>
-                                    </field>
-                                    </transformationParameter>
-                                    <transformationParameter>
-                                    <field name="paramDescription">
-                                    <paramDescriptor>
-                                    <field name="localName">
-                                    <String xml:space="preserve">html.stylesheet</String>
-                                    </field>
-                                    <field name="prefix">
-                                    <null/>
-                                    </field>
-                                    <field name="namespace">
-                                    <null/>
-                                    </field>
-                                    </paramDescriptor>
-                                    </field>
-                                    <field name="value">
-                                    <String xml:space="preserve">../chunked.css</String>
-                                    </field>
-                                    </transformationParameter>
-                                    <transformationParameter>
-                                    <field name="paramDescription">
-                                    <paramDescriptor>
-                                    <field name="localName">
-                                    <String xml:space="preserve">keep.relative.image.uris</String>
-                                    </field>
-                                    <field name="prefix">
-                                    <null/>
-                                    </field>
-                                    <field name="namespace">
-                                    <null/>
-                                    </field>
-                                    </paramDescriptor>
-                                    </field>
-                                    <field name="value">
-                                    <String xml:space="preserve">0</String>
-                                    </field>
-                                    </transformationParameter>
-                                    </list>
-                                </field>
-                                <field name="cascadingStylesheets">
-                                    <String-array/>
-                                </field>
-                                <field name="xslTransformer">
-                                    <String xml:space="preserve">Saxon6.5.5</String>
-                                </field>
-                                <field name="extensionURLs">
-                                    <String-array/>
-                                </field>
-                            </scenario>
-                            <scenario>
-                                <field name="advancedOptionsMap">
-                                    <null/>
-                                </field>
-                                <field name="name">
                                     <String xml:space="preserve">Tutorial to Kindle size PDF</String>
                                 </field>
                                 <field name="baseURL">
                                     <null/>
                                 </field>
                                 <field name="name">
+                                    <String xml:space="preserve">Tutorial to Local HTML Chunked</String>
+                                </field>
+                                <field name="baseURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="footerURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="fOPMethod">
+                                    <String xml:space="preserve">pdf</String>
+                                </field>
+                                <field name="fOProcessorName">
+                                    <String xml:space="preserve">Apache FOP</String>
+                                </field>
+                                <field name="headerURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="inputXSLURL">
+                                    <String xml:space="preserve">${frameworks}/docbook/xsl/html/chunkfast.xsl</String>
+                                </field>
+                                <field name="inputXMLURL">
+                                    <String xml:space="preserve">${currentFileURL}</String>
+                                </field>
+                                <field name="defaultScenario">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="isFOPPerforming">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="type">
+                                    <String xml:space="preserve">XSL</String>
+                                </field>
+                                <field name="saveAs">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="openInBrowser">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="outputFile">
+                                    <File xml:space="preserve">${cfd}/html/nothing.html</File>
+                                </field>
+                                <field name="openOtherLocationInBrowser">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="locationToOpenInBrowserURL">
+                                    <String xml:space="preserve">html/index.html</String>
+                                </field>
+                                <field name="openInEditor">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInHTMLPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInXMLPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInSVGPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInResultSetPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="useXSLTInput">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="xsltParams">
+                                    <list>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">base.dir</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">${cfd}/html/</String>
+                                    </field>
+                                    </transformationParameter>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">chunk.quietly</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">1</String>
+                                    </field>
+                                    </transformationParameter>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">html.stylesheet</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">../chunked.css</String>
+                                    </field>
+                                    </transformationParameter>
+                                    </list>
+                                </field>
+                                <field name="cascadingStylesheets">
+                                    <String-array/>
+                                </field>
+                                <field name="xslTransformer">
+                                    <String xml:space="preserve">Saxon6.5.5</String>
+                                </field>
+                                <field name="extensionURLs">
+                                    <String-array/>
+                                </field>
+                            </scenario>
+                            <scenario>
+                                <field name="advancedOptionsMap">
+                                    <null/>
+                                </field>
+                                <field name="name">
+                                    <String xml:space="preserve">Tutorial to Web HTML Chunked</String>
+                                </field>
+                                <field name="baseURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="footerURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="fOPMethod">
+                                    <String xml:space="preserve">pdf</String>
+                                </field>
+                                <field name="fOProcessorName">
+                                    <String xml:space="preserve">Apache FOP</String>
+                                </field>
+                                <field name="headerURL">
+                                    <String xml:space="preserve"></String>
+                                </field>
+                                <field name="inputXSLURL">
+                                    <String xml:space="preserve">${frameworks}/docbook/xsl/html/chunkfast.xsl</String>
+                                </field>
+                                <field name="inputXMLURL">
+                                    <String xml:space="preserve">${currentFileURL}</String>
+                                </field>
+                                <field name="defaultScenario">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="isFOPPerforming">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="type">
+                                    <String xml:space="preserve">XSL</String>
+                                </field>
+                                <field name="saveAs">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="openInBrowser">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="outputFile">
+                                    <File xml:space="preserve">${cfd}/web/nothing.html</File>
+                                </field>
+                                <field name="openOtherLocationInBrowser">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="locationToOpenInBrowserURL">
+                                    <String xml:space="preserve">web/index.html</String>
+                                </field>
+                                <field name="openInEditor">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInHTMLPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInXMLPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInSVGPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="showInResultSetPane">
+                                    <Boolean xml:space="preserve">false</Boolean>
+                                </field>
+                                <field name="useXSLTInput">
+                                    <Boolean xml:space="preserve">true</Boolean>
+                                </field>
+                                <field name="xsltParams">
+                                    <list>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">base.dir</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">${cfd}/web/</String>
+                                    </field>
+                                    </transformationParameter>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">chunk.quietly</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">1</String>
+                                    </field>
+                                    </transformationParameter>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">html.stylesheet</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">chunked.css</String>
+                                    </field>
+                                    </transformationParameter>
+                                    <transformationParameter>
+                                    <field name="paramDescription">
+                                    <paramDescriptor>
+                                    <field name="localName">
+                                    <String xml:space="preserve">keep.relative.image.uris</String>
+                                    </field>
+                                    <field name="prefix">
+                                    <null/>
+                                    </field>
+                                    <field name="namespace">
+                                    <null/>
+                                    </field>
+                                    </paramDescriptor>
+                                    </field>
+                                    <field name="value">
+                                    <String xml:space="preserve">0</String>
+                                    </field>
+                                    </transformationParameter>
+                                    </list>
+                                </field>
+                                <field name="cascadingStylesheets">
+                                    <String-array/>
+                                </field>
+                                <field name="xslTransformer">
+                                    <String xml:space="preserve">Saxon6.5.5</String>
+                                </field>
+                                <field name="extensionURLs">
+                                    <String-array/>
+                                </field>
+                            </scenario>
+                            <scenario>
+                                <field name="advancedOptionsMap">
+                                    <null/>
+                                </field>
+                                <field name="name">
                                     <String xml:space="preserve">Execute XQuery</String>
                                 </field>
                                 <field name="baseURL">

Documents/Tutorials.xml

 <?oxygen RNGSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng" type="xml"?>
 <?oxygen SCHSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng"?>
 <book xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0">
-    <?dbhtml dir="html"?>
     <info>
         <title>OpenGL Tutorials</title>
     </info>
                 features of the API. This makes it impossible to gloss over seemingly minor yet
                 critical information about how various features of the language and graphics
                 hardware actually work.</para>
+            <note>
+                <para>As a side-effect, these tutorials all require OpenGL 3.2-capable hardware and
+                    drivers. This means any GeForce 8xxx or better, or any Radeon HD-class card.
+                    These are also called <quote>Direct3D 10</quote> cards, but you do not need
+                    Windows Vista or 7 to use their advanced features through OpenGL.</para>
+            </note>
             <para>This does not mean that you need to have read other tutorials first. As previously
                 stated, these tutorials are intended primarily for the beginning graphics
                 programmer. These tutorials are in-depth, but they are also designed to break

Tut 05 Objects in Depth/DepthClamping.cpp

+
+#include <string>
+#include <vector>
+#include <math.h>
+#include <glloader/gl_3_2_comp.h>
+#include <GL/freeglut.h>
+#include "../framework/framework.h"
+
+#define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
+
+GLuint theProgram;
+GLuint positionAttrib;
+GLuint colorAttrib;
+
+GLuint offsetUniform;
+GLuint perspectiveMatrixUnif;
+
+float perspectiveMatrix[16];
+const float fFrustumScale = 1.0f;
+
+void InitializeProgram()
+{
+	std::vector<GLuint> shaderList;
+
+	shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, "Standard.vert"));
+	shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, "Standard.frag"));
+
+	theProgram = Framework::CreateProgram(shaderList);
+
+	positionAttrib = glGetAttribLocation(theProgram, "position");
+	colorAttrib = glGetAttribLocation(theProgram, "color");
+
+	offsetUniform = glGetUniformLocation(theProgram, "offset");
+
+	perspectiveMatrixUnif = glGetUniformLocation(theProgram, "perspectiveMatrix");
+
+	float fzNear = 1.0f; float fzFar = 3.0f;
+
+	memset(perspectiveMatrix, 0, sizeof(float) * 16);
+
+	perspectiveMatrix[0] = fFrustumScale;
+	perspectiveMatrix[5] = fFrustumScale;
+	perspectiveMatrix[10] = (fzFar + fzNear) / (fzNear - fzFar);
+	perspectiveMatrix[14] = (2 * fzFar * fzNear) / (fzNear - fzFar);
+	perspectiveMatrix[11] = -1.0f;
+
+	glUseProgram(theProgram);
+	glUniformMatrix4fv(perspectiveMatrixUnif, 1, GL_FALSE, perspectiveMatrix);
+	glUseProgram(0);
+}
+
+const int numberOfVertices = 36;
+
+#define RIGHT_EXTENT 0.8f
+#define LEFT_EXTENT -RIGHT_EXTENT
+#define TOP_EXTENT 0.20f
+#define MIDDLE_EXTENT 0.0f
+#define BOTTOM_EXTENT -TOP_EXTENT
+#define FRONT_EXTENT -1.25f
+#define REAR_EXTENT -1.75f
+
+#define GREEN_COLOR 0.75f, 0.75f, 1.0f, 1.0f
+#define BLUE_COLOR 	0.0f, 0.5f, 0.0f, 1.0f
+#define RED_COLOR 1.0f, 0.0f, 0.0f, 1.0f
+#define GREY_COLOR 0.8f, 0.8f, 0.8f, 1.0f
+#define BROWN_COLOR 0.5f, 0.5f, 0.0f, 1.0f
+
+const float vertexData[] = {
+	//Object 1 positions
+	LEFT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+	LEFT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	RIGHT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	RIGHT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+
+	LEFT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+	LEFT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	RIGHT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	RIGHT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+
+	LEFT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+	LEFT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	LEFT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+
+	RIGHT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+	RIGHT_EXTENT,	MIDDLE_EXTENT,	FRONT_EXTENT,
+	RIGHT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+
+	LEFT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+	LEFT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+	RIGHT_EXTENT,	TOP_EXTENT,		REAR_EXTENT,
+	RIGHT_EXTENT,	BOTTOM_EXTENT,	REAR_EXTENT,
+
+	//	0, 2, 1,
+	//	3, 2, 0,
+
+	//Object 2 positions
+	TOP_EXTENT,		RIGHT_EXTENT,	REAR_EXTENT,
+	MIDDLE_EXTENT,	RIGHT_EXTENT,	FRONT_EXTENT,
+	MIDDLE_EXTENT,	LEFT_EXTENT,	FRONT_EXTENT,
+	TOP_EXTENT,		LEFT_EXTENT,	REAR_EXTENT,
+
+	BOTTOM_EXTENT,	RIGHT_EXTENT,	REAR_EXTENT,
+	MIDDLE_EXTENT,	RIGHT_EXTENT,	FRONT_EXTENT,
+	MIDDLE_EXTENT,	LEFT_EXTENT,	FRONT_EXTENT,
+	BOTTOM_EXTENT,	LEFT_EXTENT,	REAR_EXTENT,
+
+	TOP_EXTENT,		RIGHT_EXTENT,	REAR_EXTENT,
+	MIDDLE_EXTENT,	RIGHT_EXTENT,	FRONT_EXTENT,
+	BOTTOM_EXTENT,	RIGHT_EXTENT,	REAR_EXTENT,
+
+	TOP_EXTENT,		LEFT_EXTENT,	REAR_EXTENT,
+	MIDDLE_EXTENT,	LEFT_EXTENT,	FRONT_EXTENT,
+	BOTTOM_EXTENT,	LEFT_EXTENT,	REAR_EXTENT,
+
+	BOTTOM_EXTENT,	RIGHT_EXTENT,	REAR_EXTENT,
+	TOP_EXTENT,		RIGHT_EXTENT,	REAR_EXTENT,
+	TOP_EXTENT,		LEFT_EXTENT,	REAR_EXTENT,
+	BOTTOM_EXTENT,	LEFT_EXTENT,	REAR_EXTENT,
+
+	//Object 1 colors
+	GREEN_COLOR,
+	GREEN_COLOR,
+	GREEN_COLOR,
+	GREEN_COLOR,
+
+	BLUE_COLOR,
+	BLUE_COLOR,
+	BLUE_COLOR,
+	BLUE_COLOR,
+
+	RED_COLOR,
+	RED_COLOR,
+	RED_COLOR,
+
+	GREY_COLOR,
+	GREY_COLOR,
+	GREY_COLOR,
+
+	BROWN_COLOR,
+	BROWN_COLOR,
+	BROWN_COLOR,
+	BROWN_COLOR,
+
+	//Object 2 colors
+	RED_COLOR,
+	RED_COLOR,
+	RED_COLOR,
+	RED_COLOR,
+
+	BROWN_COLOR,
+	BROWN_COLOR,
+	BROWN_COLOR,
+	BROWN_COLOR,
+
+	BLUE_COLOR,
+	BLUE_COLOR,
+	BLUE_COLOR,
+
+	GREEN_COLOR,
+	GREEN_COLOR,
+	GREEN_COLOR,
+
+	GREY_COLOR,
+	GREY_COLOR,
+	GREY_COLOR,
+	GREY_COLOR,
+};
+
+const GLshort indexData[] =
+{
+	0, 2, 1,
+	3, 2, 0,
+
+	4, 5, 6,
+	6, 7, 4,
+
+	8, 9, 10,
+	11, 13, 12,
+
+	14, 16, 15,
+	17, 16, 14,
+};
+
+GLuint vertexBufferObject;
+GLuint indexBufferObject;
+GLuint vao;
+
+
+void InitializeVertexBuffer()
+{
+	glGenBuffers(1, &vertexBufferObject);
+
+	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
+	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	glGenBuffers(1, &indexBufferObject);
+
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData, GL_STATIC_DRAW);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+//Called after the window and OpenGL are initialized. Called exactly once, before the main loop.
+void init()
+{
+	InitializeProgram();
+	InitializeVertexBuffer();
+
+	glGenVertexArrays(1, &vao);
+	glBindVertexArray(vao);
+
+	size_t colorDataOffset = sizeof(float) * 3 * numberOfVertices;
+	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
+	glEnableVertexAttribArray(positionAttrib);
+	glEnableVertexAttribArray(colorAttrib);
+	glVertexAttribPointer(positionAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
+	glVertexAttribPointer(colorAttrib, 4, GL_FLOAT, GL_FALSE, 0, (void*)colorDataOffset);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);
+
+	glBindVertexArray(0);
+
+	glEnable(GL_CULL_FACE);
+	glCullFace(GL_BACK);
+	glFrontFace(GL_CW);
+
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LESS);
+	glDepthRange(0.0f, 1.0f);
+}
+
+//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);
+
+	glUseProgram(theProgram);
+	glBindVertexArray(vao);
+
+	glUniform3f(offsetUniform, 0.0f, 0.0f, 0.5f);
+	glDrawElements(GL_TRIANGLES, ARRAY_COUNT(indexData), GL_UNSIGNED_SHORT, 0);
+
+	glUniform3f(offsetUniform, 0.0f, 0.0f, -1.0f);
+	glDrawElementsBaseVertex(GL_TRIANGLES, ARRAY_COUNT(indexData),
+		GL_UNSIGNED_SHORT, 0, numberOfVertices / 2);
+
+	glBindVertexArray(0);
+	glUseProgram(0);
+
+	glutSwapBuffers();
+	glutPostRedisplay();
+}
+
+//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)
+{
+	perspectiveMatrix[0] = fFrustumScale * (h / (float)w);
+	perspectiveMatrix[5] = fFrustumScale;
+
+	glUseProgram(theProgram);
+	glUniformMatrix4fv(perspectiveMatrixUnif, 1, GL_FALSE, perspectiveMatrix);
+	glUseProgram(0);
+
+	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
+}
+
+//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)
+{
+	static bool bDepthClampingActive = false;
+	switch (key)
+	{
+	case 27:
+		glutLeaveMainLoop();
+		break;
+	case 32:
+		if(bDepthClampingActive)
+			glDisable(GL_DEPTH_CLAMP);
+		else
+			glEnable(GL_DEPTH_CLAMP);
+
+		bDepthClampingActive = !bDepthClampingActive;
+		break;
+	}
+}
+
+

Tut 05 Objects in Depth/premake4.lua

 	"data/Standard.frag", "data/Standard.vert")
 SetupProject("Vertex Clipping", "VertexClipping.cpp",
 	"data/Standard.frag", "data/Standard.vert")
+SetupProject("Depth Clamping", "DepthClamping.cpp",
+	"data/Standard.frag", "data/Standard.vert")