Jason McKesson avatar Jason McKesson committed 0ddefa0

Added images for Tutorial 09.

Comments (0)

Files changed (33)

Add a comment to this file

Documents/Illumination/Ambient Lighting.png

Added
New image
Add a comment to this file

Documents/Illumination/BarycentricTriangle.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="301px" width="301px" >
+	<style type="text/css" ><![CDATA[.vertex
+{
+	fill: black;
+	stroke: black;
+}
+
+.label
+{
+	font-family: monospace;
+	stroke-width: 1px;
+	stroke: black;
+	text-anchor: middle;
+	font-size: 25px;
+}
+
+.vertex_label
+{
+	font-family: monospace;
+	stroke-width: 1px;
+	font-weight: bold;
+	stroke: black;
+	text-anchor: middle;
+	font-size: 25px;
+}
+
+.baryPts
+{
+	stroke-opacity: 0.5;
+	stroke: violet;
+	stroke-width: 4px;
+	fill: none;
+}
+
+.triangle
+{
+	fill: none;
+	stroke: black;
+}]]></style>
+	<defs >
+		<g id="g_point" >
+			<circle r="5" cy="0" cx="0" class="vertex" />
+		</g>
+	</defs>
+	<polygon points="180,45 255,195 45,240" class="triangle" />
+	<path d="M 159.9999999 159.9999999 L 180 45 M 159.9999999 159.9999999 L 255 195 M 159.9999999 159.9999999 L 45 240" class="baryPts" />
+	<use xlink:href="#g_point" y="45" x="180" height="10" width="10" />
+	<use xlink:href="#g_point" y="195" x="255" height="10" width="10" />
+	<use xlink:href="#g_point" y="240" x="45" height="10" width="10" />
+	<use xlink:href="#g_point" y="159.9999999" x="159.9999999" height="10" width="10" />
+	<text y="184.9999999" x="159.9999999" class="vertex_label" >P</text>
+	<text y="110.49999995" x="181.99999995" class="label" >&#x03b1;</text>
+	<text y="168.49999995" x="216.49999995" class="label" >&#x03b2;</text>
+	<text y="186.99999995" x="102.49999995" class="label" >&#x03b3;</text>
+	<text y="40" x="195" class="vertex_label" >A</text>
+	<text y="200" x="273" class="vertex_label" >B</text>
+	<text y="250" x="27" class="vertex_label" >C</text>
+</svg>
Add a comment to this file

Documents/Illumination/BarycentricTriangleEdge.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="301px" width="301px" >
+	<style type="text/css" ><![CDATA[.vertex
+{
+	fill: black;
+	stroke: black;
+}
+
+.label
+{
+	font-family: monospace;
+	stroke-width: 1px;
+	stroke: black;
+	text-anchor: middle;
+	font-size: 25px;
+}
+
+.vertex_label
+{
+	font-family: monospace;
+	stroke-width: 1px;
+	font-weight: bold;
+	stroke: black;
+	text-anchor: middle;
+	font-size: 25px;
+}
+
+.baryPts
+{
+	stroke-opacity: 0.5;
+	stroke: violet;
+	stroke-width: 4px;
+	fill: none;
+}
+
+.triangle
+{
+	fill: none;
+	stroke: black;
+}]]></style>
+	<defs >
+		<g id="g_point" >
+			<circle r="5" cy="0" cx="0" class="vertex" />
+		</g>
+	</defs>
+	<polygon points="180,45 255,195 45,240" class="triangle" />
+	<path d="M 150 217.5 L 180 45 M 150 217.5 L 255 195 M 150 217.5 L 45 240" class="baryPts" />
+	<use xlink:href="#g_point" y="45" x="180" height="10" width="10" />
+	<use xlink:href="#g_point" y="195" x="255" height="10" width="10" />
+	<use xlink:href="#g_point" y="240" x="45" height="10" width="10" />
+	<use xlink:href="#g_point" y="217.5" x="150" height="10" width="10" />
+	<text y="242.5" x="150" class="vertex_label" >P</text>
+	<text y="139.25" x="177" class="label" >&#x03b1;</text>
+	<text y="197.25" x="211.5" class="label" >&#x03b2;</text>
+	<text y="215.75" x="97.5" class="label" >&#x03b3;</text>
+	<text y="40" x="195" class="vertex_label" >A</text>
+	<text y="200" x="273" class="vertex_label" >B</text>
+	<text y="250" x="27" class="vertex_label" >C</text>
+</svg>
Add a comment to this file

Documents/Illumination/Basic Lighting.png

Added
New image
Add a comment to this file

Documents/Illumination/CircleNormalScaling.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="251px" width="851px" >
+	<style type="text/css" ><![CDATA[.normal
+{
+	marker-end: url(#arrow_normal);
+	stroke: red;
+	stroke-width: 1px;
+	fill: red;
+}
+
+.normal_head
+{
+	stroke: red;
+	stroke-width: 1px;
+	fill: red;
+}
+
+.circle
+{
+	fill: none;
+	stroke: black;
+}]]></style>
+	<defs >
+		<marker markerWidth="10" markerHeight="8" refX="0" refY="4" markerUnits="userSpaceOnUse" orient="auto" id="arrow_normal" >
+			<path d="M 8 4 L 0 0 L 0 8 Z" class="normal_head" />
+		</marker>
+		<g id="g_circle" >
+			<circle r="87.5" cy="125" cx="125" class="circle" />
+		</g>
+		<g id="g_circle_normals" >
+			<line x2="125" y2="18.75" y1="37.5" x1="125" class="normal" />
+			<line x2="78.899852718759" y2="29.272057785368" y1="46.165224058538" x1="87.035172827214" class="normal" />
+			<line x2="41.930404987772" y2="58.75420855251" y1="70.444642337361" x1="56.589745284047" class="normal" />
+			<line x2="21.413909330681" y2="101.35715076714" y1="105.52941827882" x1="39.69380768409" class="normal" />
+			<line x2="21.413909330681" y2="148.64284923286" y1="144.47058172118" x1="39.69380768409" class="normal" />
+			<line x2="41.930404987772" y2="191.24579144749" y1="179.55535766264" x1="56.589745284047" class="normal" />
+			<line x2="78.899852718759" y2="220.72794221463" y1="203.83477594146" x1="87.035172827214" class="normal" />
+			<line x2="125" y2="231.25" y1="212.5" x1="125" class="normal" />
+			<line x2="171.10014728124" y2="220.72794221463" y1="203.83477594146" x1="162.96482717279" class="normal" />
+			<line x2="208.06959501223" y2="191.24579144749" y1="179.55535766264" x1="193.41025471595" class="normal" />
+			<line x2="228.58609066932" y2="148.64284923286" y1="144.47058172118" x1="210.30619231591" class="normal" />
+			<line x2="228.58609066932" y2="101.35715076714" y1="105.52941827882" x1="210.30619231591" class="normal" />
+			<line x2="208.06959501223" y2="58.75420855251" y1="70.444642337361" x1="193.41025471595" class="normal" />
+			<line x2="171.10014728124" y2="29.272057785368" y1="46.165224058538" x1="162.96482717279" class="normal" />
+		</g>
+		<g id="g_bad_normals" >
+			<line x2="125" y2="77.375" y1="96.125" x1="125" class="normal" />
+			<line x2="71.568173164107" y2="88.385730544716" y1="98.984523939318" x1="87.035172827214" class="normal" />
+			<line x2="38.457134856536" y2="102.22484145665" y1="106.99673197133" x1="56.589745284047" class="normal" />
+			<line x2="20.996768274523" y2="117.16644054425" y1="118.57470803201" x1="39.69380768409" class="normal" />
+			<line x2="20.996768274523" y2="132.83355945575" y1="131.42529196799" x1="39.69380768409" class="normal" />
+			<line x2="38.457134856536" y2="147.77515854335" y1="143.00326802867" x1="56.589745284047" class="normal" />
+			<line x2="71.568173164107" y2="161.61426945528" y1="151.01547606068" x1="87.035172827214" class="normal" />
+			<line x2="125" y2="172.625" y1="153.875" x1="125" class="normal" />
+			<line x2="178.43182683589" y2="161.61426945528" y1="151.01547606068" x1="162.96482717279" class="normal" />
+			<line x2="211.54286514346" y2="147.77515854335" y1="143.00326802867" x1="193.41025471595" class="normal" />
+			<line x2="229.00323172548" y2="132.83355945575" y1="131.42529196799" x1="210.30619231591" class="normal" />
+			<line x2="229.00323172548" y2="117.16644054425" y1="118.57470803201" x1="210.30619231591" class="normal" />
+			<line x2="211.54286514346" y2="102.22484145665" y1="106.99673197133" x1="193.41025471595" class="normal" />
+			<line x2="178.43182683589" y2="88.385730544716" y1="98.984523939318" x1="162.96482717279" class="normal" />
+		</g>
+		<g id="g_good_normals" >
+			<line x2="125" y2="77.375" y1="96.125" x1="125" class="normal" />
+			<line x2="84.092359197344" y2="80.466901307353" y1="98.984523939318" x1="87.035172827214" class="normal" />
+			<line x2="49.420444917683" y2="89.671500883207" y1="106.99673197133" x1="56.589745284047" class="normal" />
+			<line x2="24.272936656021" y2="107.90891051211" y1="118.57470803201" x1="39.69380768409" class="normal" />
+			<line x2="24.272936656021" y2="142.09108948789" y1="131.42529196799" x1="39.69380768409" class="normal" />
+			<line x2="49.420444917683" y2="160.32849911679" y1="143.00326802867" x1="56.589745284047" class="normal" />
+			<line x2="84.092359197344" y2="169.53309869265" y1="151.01547606068" x1="87.035172827214" class="normal" />
+			<line x2="125" y2="172.625" y1="153.875" x1="125" class="normal" />
+			<line x2="165.90764080266" y2="169.53309869265" y1="151.01547606068" x1="162.96482717279" class="normal" />
+			<line x2="200.57955508232" y2="160.32849911679" y1="143.00326802867" x1="193.41025471595" class="normal" />
+			<line x2="225.72706334398" y2="142.09108948789" y1="131.42529196799" x1="210.30619231591" class="normal" />
+			<line x2="225.72706334398" y2="107.90891051211" y1="118.57470803201" x1="210.30619231591" class="normal" />
+			<line x2="200.57955508232" y2="89.671500883207" y1="106.99673197133" x1="193.41025471595" class="normal" />
+			<line x2="165.90764080266" y2="80.466901307353" y1="98.984523939318" x1="162.96482717279" class="normal" />
+		</g>
+		<g id="g_ellipse" >
+			<ellipse ry="28.875" rx="87.5" cy="125" cx="125" class="circle" />
+		</g>
+	</defs>
+	<use xlink:href="#g_circle" y="0" x="0" height="250" width="250" />
+	<use xlink:href="#g_circle_normals" y="0" x="0" height="250" width="250" />
+	<use xlink:href="#g_ellipse" y="0" x="300" height="250" width="250" />
+	<use xlink:href="#g_bad_normals" y="0" x="300" height="250" width="250" />
+	<use xlink:href="#g_ellipse" y="0" x="600" height="250" width="250" />
+	<use xlink:href="#g_good_normals" y="0" x="600" height="250" width="250" />
+</svg>
Add a comment to this file

Documents/Illumination/CircleScaling.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="251px" width="551px" >
+	<style type="text/css" ><![CDATA[.clip_line
+{
+	stroke: black;
+	stroke-width: 2px;
+	fill: none;
+}
+
+.clip_region
+{
+	fill: #E0E0FF;
+	stroke: none;
+}
+
+.main_triangle
+{
+	stroke: black;
+	stroke-width: 1px;
+	fill: none;
+}
+
+.new_point
+{
+	fill: red;
+	stroke: none;
+}
+
+.circle
+{
+	fill: none;
+	stroke: black;
+}
+
+.border
+{
+	fill: none;
+	stroke: black;
+}
+
+.new_edge
+{
+	stroke-opacity: 0.4;
+	stroke-width: 4px;
+	stroke-linejoin: round;
+	stroke: red;
+	stroke-linecap: round;
+	fill: none;
+}]]></style>
+	<defs >
+		<marker markerWidth="9" markerHeight="9" refX="4.5" refY="4.5" markerUnits="userSpaceOnUse" orient="auto" id="m_point" >
+			<circle r="4.5" cy="4.5" cx="4.5" class="point" />
+		</marker>
+		<marker markerWidth="10" markerHeight="8" refX="10" refY="4" markerUnits="userSpaceOnUse" orient="auto" id="arrow" >
+			<path d="M 10 4 L 0 0 L 0 8 Z" class="fill_black thin" />
+		</marker>
+		<marker markerWidth="10" markerHeight="20" refX="10" refY="10" markerUnits="userSpaceOnUse" orient="auto" id="big_arrow_y_axis" >
+			<path d="M 0 0 L 10 10 L 0 20" class="y_axis_marker" />
+		</marker>
+		<marker markerWidth="10" markerHeight="20" refX="10" refY="10" markerUnits="userSpaceOnUse" orient="auto" id="big_arrow_x_axis" >
+			<path d="M 0 0 L 10 10 L 0 20" class="x_axis_marker" />
+		</marker>
+		<g id="g_circle" >
+			<circle r="87.5" cy="125" cx="125" class="circle" />
+		</g>
+		<g id="g_ellipse" >
+			<ellipse ry="21.875" rx="87.5" cy="125" cx="125" class="circle" />
+		</g>
+		<g id="g_new_point" >
+			<circle r="4.5" cy="0" cx="0" class="new_point" />
+		</g>
+	</defs>
+	<use xlink:href="#g_circle" y="0" x="0" height="250" width="250" />
+	<use xlink:href="#g_ellipse" y="0" x="300" height="250" width="250" />
+</svg>
Add a comment to this file

Documents/Illumination/Cylinder Close Light.png

Added
New image
Add a comment to this file

Documents/Illumination/FacetedCircle.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="301px" width="301px" >
+	<style type="text/css" ><![CDATA[.facets
+{
+	fill: none;
+	stroke: black;
+	stroke-width: 1px;
+	stroke-linejoin: round;
+}
+
+.circle
+{
+	fill: none;
+	stroke: black;
+}]]></style>
+	<defs >
+		<g id="g_faceted_circle" >
+			<circle r="120" cy="150" cx="150" class="circle" />
+			<path d="M 150 30 L 65.147186257614 65.147186257614 L 30 150 L 65.147186257614 234.85281374239 L 150 270 L 234.85281374239 234.85281374239 L 270 150 L 234.85281374239 65.147186257614 Z" class="facets" />
+		</g>
+	</defs>
+	<use xlink:href="#g_faceted_circle" y="0" x="0" height="300" width="300" />
+</svg>
Add a comment to this file

Documents/Illumination/FacetedNearVsFar.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="300px" width="400px" >
+	<style type="text/css" ><![CDATA[.__grid1
+{
+	stroke: #CCC;
+	stroke-width: 1px;
+	fill: none;
+}
+
+.ray
+{
+	marker-end: url(#arrow);
+	stroke: darkblue;
+	stroke-dasharray: 3,3;
+	stroke-width: 1px;
+}
+
+.__axis_arrow_end1
+{
+	marker-end: url(#__axis_arrow1);
+}
+
+.__axis1
+{
+	stroke: black;
+	stroke-width: 2px;
+	fill: none;
+}
+
+.light
+{
+	fill: yellow;
+	stroke: none;
+}
+
+.facets
+{
+	fill: lightblue;
+	stroke: black;
+	stroke-width: 1px;
+	stroke-linejoin: round;
+}
+
+.circle
+{
+	stroke: black;
+	stroke-width: 2px;
+	fill: none;
+}
+
+.show_clip
+{
+	fill: none;
+	stroke: black;
+}
+
+.surface
+{
+	fill: lightblue;
+	stroke: none;
+}
+
+.arrowhead
+{
+	fill: darkblue;
+	stroke: darkblue;
+}
+
+.clip_to_area
+{
+	clip-path: url(#area_clip);
+}]]></style>
+	<defs >
+		<marker markerWidth="10" markerHeight="8" refX="10" refY="4" markerUnits="userSpaceOnUse" orient="auto" id="arrow" >
+			<path d="M 10 4 L 0 0 L 0 8 Z" class="arrowhead" />
+		</marker>
+		<g id="g_surface" >
+			<rect y="0" x="325" height="150" width="75" class="surface" />
+		</g>
+		<g id="g_faceted_circle" >
+			<circle r="200" cy="250" cx="375" class="circle" />
+			<path d="M 375 50 L 218.63370350639 125.30203962825 L 180.01441756364 294.50418679126 L 288.22325217649 430.19377358048 L 461.77674782351 430.19377358048 L 569.98558243636 294.50418679126 L 531.36629649361 125.30203962825 Z" class="facets" />
+		</g>
+		<clipPath id="area_clip" >
+			<rect y="0" x="0" height="150" width="400" class="show_clip" />
+		</clipPath>
+	</defs>
+	<circle r="10" cy="75" cx="50" class="light" />
+	<circle r="10" cy="225" cx="225" class="light" />
+	<use xlink:href="#g_faceted_circle" y="0" x="0" height="150" width="400" class="clip_to_area" />
+	<use xlink:href="#g_faceted_circle" y="150" x="0" height="150" width="400" class="clip_to_area" />
+	<line x2="213.2410708021" y2="71.440997454968" y1="69.806226419516" x1="288.22325217649" class="ray" />
+	<line x2="221.91517993333" y2="83.811806536858" y1="87.651019814127" x1="296.8168517532" class="ray" />
+	<line x2="250.84915116786" y2="222.87649901276" y1="219.80622641952" x1="288.22325217649" class="ray" />
+	<line x2="259.88548610599" y2="231.14531220986" y1="237.65101981413" x1="296.8168517532" class="ray" />
+</svg>
Add a comment to this file

Documents/Illumination/Fragment Attenuation.png

Added
New image
Add a comment to this file

Documents/Illumination/Fragment Point Lighting.png

Added
New image

Documents/Illumination/GenBarycentricTriangle.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 300;
+
+local subImages = SubImage.SubImage(1, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local scaleFactor = 0.25;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+local textSize = 25;
+
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+styleLib:AddStyle(nil, "baryPts",
+	SvgWriter.Style():stroke("violet"):stroke_width("4px"):fill("none"):stroke_opacity(0.5));
+	
+styleLib:AddStyle(nil, "vertex",
+	SvgWriter.Style():stroke("black"):fill("black"));
+	
+styleLib:AddStyle(nil, "label",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"):font_size(textSize .. "px")
+		:font_family("monospace"):text_anchor("middle"));
+
+styleLib:AddStyle(nil, "vertex_label",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"):font_size(textSize .. "px")
+		:font_family("monospace"):font_weight("bold"):text_anchor("middle"));
+
+local function Output(convertName, barycoords)
+	--Triangle
+	local trianglePts =
+	{
+		vmath.vec2(0.2, 0.7),
+		vmath.vec2(0.7, -0.3),
+		vmath.vec2(-0.7, -0.6),
+	};
+
+	local baryPt = vmath.vec2(0, 0);
+
+	for i, pt in ipairs(trianglePts) do
+		baryPt = baryPt + (pt * barycoords[i]);
+	end
+
+	baryPt = vp:Transform(baryPt);
+
+	trianglePts = vp:Transform(trianglePts);
+
+	--Bary-lines
+	local baryLines = SvgWriter.Path();
+	for i, pt in ipairs(trianglePts) do
+		baryLines:M(baryPt):L(pt);
+	end
+
+	--Labels
+	local vertexLableOffsets =
+	{
+		vmath.vec2(15, -5),
+		vmath.vec2(18, 5),
+		vmath.vec2(-18, 10),
+	}
+
+	local lineLabelOffsets = 
+	{
+		vmath.vec2(12, 8),
+		vmath.vec2(9, -9),
+		vmath.vec2(0, -13),
+	}
+
+	local labels =
+	{
+		"A",
+		"B",
+		"C",
+	}
+
+	local baryLabels =
+	{
+		"&#x03b1;",
+		"&#x03b2;",
+		"&#x03b3;",
+	}
+
+	local baryPtOffset = vmath.vec2(0, 25);
+		
+
+	--Vertices
+	local pointRadius = 5;
+	local radiusPt = vmath.vec2(pointRadius, pointRadius);
+
+	local writer = SvgWriter.SvgWriter(ConstructSVGName(convertName), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+		writer:StyleLibrary(styleLib);
+		writer:BeginDefinitions();
+			writer:BeginGroup(nil, "g_point");
+				writer:Circle(vmath.vec2(0, 0), pointRadius, {"vertex"});
+			writer:EndGroup();
+		writer:EndDefinitions();
+
+		writer:Polygon(trianglePts, {"triangle"});
+		
+		writer:Path(baryLines, {"baryPts"});
+		
+		for i, pt in ipairs(trianglePts) do
+			writer:Use("g_point", pt, radiusPt * 2);
+		end
+
+		writer:Use("g_point", baryPt, radiusPt * 2);
+		
+		writer:Text("P", baryPt + baryPtOffset, {"vertex_label"});
+
+		for i = 1, #trianglePts do
+			local labelPt = baryPt + trianglePts[i];
+			labelPt = labelPt / 2.0;
+			writer:Text(baryLabels[i], labelPt + lineLabelOffsets[i], {"label"});
+		end
+
+		for i = 1, #vertexLableOffsets do
+			writer:Text(labels[i], trianglePts[i] + vertexLableOffsets[i], {"vertex_label"});
+		end
+	writer:Close();
+end
+
+	local barycoords =
+
+
+Output(arg[0],
+	{
+		0.33333333,
+		0.33333333,
+		0.33333333,
+	});
+
+Output(arg[0]:gsub("%.lua", "Edge.lua"),
+	{
+		0.0,
+		0.5,
+		0.5,
+	});

Documents/Illumination/GenCircleNormalScaling.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 250;
+
+local subImages = SubImage.SubImage(3, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local scaleFactor = 0.33;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+
+local coordAxisWidth = "2px";
+local xCoordAxisColor = "green";
+local yCoordAxisColor = "red";
+local objectColor = "#E0E0FF";
+local normalColor = "red";
+
+styleLib:AddStyle(nil, "circle",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+styleLib:AddStyle(nil, "normal",
+	SvgWriter.Style():stroke(normalColor):stroke_width("1px"):
+		fill(normalColor):marker_end(SvgWriter.uriLocalElement("arrow_normal")));
+	
+styleLib:AddStyle(nil, "normal_head",
+	SvgWriter.Style():stroke(normalColor):stroke_width("1px"):fill(normalColor));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{8, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Cell Border
+local borderPath = SvgWriter.Path();
+borderPath:M(vp:Transform(vmath.vec2(1, 1)));
+borderPath:L(vp:Transform(vmath.vec2(1, -1)));
+borderPath:L(vp:Transform(vmath.vec2(-1, -1)));
+borderPath:L(vp:Transform(vmath.vec2(-1, 1)));
+borderPath:Z();
+
+--Circle/Ellipse radius
+local circleRadius = 0.7;
+local ellipseRadius = circleRadius * scaleFactor;
+
+local vpCircleRadius = vp:Transform(vmath.vector(circleRadius, circleRadius, 0.0));
+vpCircleRadius = math.abs(vpCircleRadius.x);
+
+local vpEllipseRadius = vpCircleRadius * scaleFactor;
+
+local circleCenterPt = vmath.vec2(0, 0);
+
+--Normals for the circles.
+local numNormals = 14;
+local normalLen = 0.15;
+
+local circleNormals = {};
+local badNormals = {};
+local goodNormals = {};
+
+local vectorScale = vmath.vec2(1, scaleFactor);
+
+for i = 1, numNormals do
+	local angle = ((i - 1) / numNormals) * math.pi * 2 + (math.pi / 2);
+	local tail = vmath.vec2(math.cos(angle), math.sin(angle));
+	tail = (tail * circleRadius) + circleCenterPt;
+	
+	local dir = vmath.vec2(math.cos(angle), math.sin(angle));
+	
+	circleNormals[#circleNormals + 1] = tail;
+	circleNormals[#circleNormals + 1] = tail + (dir * normalLen);
+	
+	tail = tail * vectorScale;
+	local badDir = vmath.normalize(dir * vectorScale);
+	badNormals[#badNormals + 1] = tail;
+	badNormals[#badNormals + 1] = tail + (badDir * normalLen);
+	
+	local goodDir = vmath.normalize(dir / vectorScale);
+	goodNormals[#goodNormals + 1] = tail;
+	goodNormals[#goodNormals + 1] = tail + (goodDir * normalLen);
+end
+
+circleNormals = vp:Transform(circleNormals);
+badNormals = vp:Transform(badNormals);
+goodNormals = vp:Transform(goodNormals);
+
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({10, 8}, {0, 4}, "auto", true, nil, "arrow_normal");
+			writer:Path(arrowheadPath, {"normal_head"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_circle");
+			writer:Circle(vp:Transform(circleCenterPt), vpCircleRadius, {"circle"});
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_circle_normals");
+			for i = 2, numNormals * 2, 2 do
+				writer:Line(circleNormals[i - 1], circleNormals[i], {"normal"});
+			end
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_bad_normals");
+			for i = 2, numNormals * 2, 2 do
+				writer:Line(badNormals[i - 1], badNormals[i], {"normal"});
+			end
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_good_normals");
+			for i = 2, numNormals * 2, 2 do
+				writer:Line(goodNormals[i - 1], goodNormals[i], {"normal"});
+			end
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_ellipse");
+			writer:Ellipse(vp:Transform(circleCenterPt),
+				{vpCircleRadius, vpEllipseRadius}, {"circle"});
+		writer:EndGroup();
+	writer:EndDefinitions();
+	
+	local xpoints = nil;
+	--Left
+	writer:Use("g_circle", subImages:Offset(1, 1), subImages:SubSize());
+	writer:Use("g_circle_normals", subImages:Offset(1, 1), subImages:SubSize());
+	
+	--Middle
+	writer:Use("g_ellipse", subImages:Offset(2, 1), subImages:SubSize());
+	writer:Use("g_bad_normals", subImages:Offset(2, 1), subImages:SubSize());
+	
+	--Right
+	writer:Use("g_ellipse", subImages:Offset(3, 1), subImages:SubSize());
+	writer:Use("g_good_normals", subImages:Offset(3, 1), subImages:SubSize());
+writer:Close();

Documents/Illumination/GenCircleScaling.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 250;
+
+local subImages = SubImage.SubImage(2, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local scaleFactor = 0.25;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+
+styleLib:AddStyle(nil, "circle",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Cell Border
+local borderPath = SvgWriter.Path();
+borderPath:M(vp:Transform(vmath.vec2(1, 1)));
+borderPath:L(vp:Transform(vmath.vec2(1, -1)));
+borderPath:L(vp:Transform(vmath.vec2(-1, -1)));
+borderPath:L(vp:Transform(vmath.vec2(-1, 1)));
+borderPath:Z();
+
+--Circle/Ellipse radius
+local circleRadius = 0.7;
+local ellipseRadius = circleRadius * scaleFactor;
+
+local vpCircleRadius = vp:Transform(vmath.vector(circleRadius, circleRadius, 0.0));
+vpCircleRadius = math.abs(vpCircleRadius.x);
+
+local vpEllipseRadius = vpCircleRadius * scaleFactor;
+
+local circleCenterPt = vmath.vec2(0, 0);
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginGroup(nil, "g_circle");
+			writer:Circle(vp:Transform(circleCenterPt), vpCircleRadius, {"circle"});
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_ellipse");
+			writer:Ellipse(vp:Transform(circleCenterPt),
+				{vpCircleRadius, vpEllipseRadius}, {"circle"});
+		writer:EndGroup();
+	writer:EndDefinitions();
+	
+	local xpoints = nil;
+	--TopLeft
+	writer:Use("g_circle", subImages:Offset(1, 1), subImages:SubSize());
+	
+	--TopRight
+	writer:Use("g_ellipse", subImages:Offset(2, 1), subImages:SubSize());
+	
+writer:Close();

Documents/Illumination/GenFacetedCircle.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 300;
+
+local subImages = SubImage.SubImage(1, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+local textSize = 25;
+
+styleLib:AddStyle(nil, "facets",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"):stroke_linejoin("round"):fill("none"));
+
+styleLib:AddStyle(nil, "circle",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+
+--Vertices
+local circleRadius = 0.8;
+
+local vpCircleRadius = vp:Transform(vmath.vector(circleRadius, circleRadius, 0.0));
+vpCircleRadius = math.abs(vpCircleRadius.x);
+
+local circleCenterPt = vmath.vec2(0, 0);
+
+--Facets
+local numFacets = 8;
+
+local facetPath = SvgWriter:Path();
+
+for i=1, numFacets do
+	local angle = ((i - 1) / numFacets) * math.pi * 2 + (math.pi / 2);
+	local point = vmath.vec2(math.cos(angle), math.sin(angle));
+	point = (point * circleRadius) + circleCenterPt;
+	
+	point = vp:Transform(point);
+
+	if(i == 1) then
+		facetPath:M(point);
+	else
+		facetPath:L(point);
+	end
+end
+
+facetPath:Z();
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginGroup(nil, "g_faceted_circle");
+			writer:Circle(vp:Transform(circleCenterPt), vpCircleRadius, {"circle"});
+			writer:Path(facetPath, {"facets"});
+		writer:EndGroup();
+	writer:EndDefinitions();
+	
+	writer:Use("g_faceted_circle", subImages:Offset(1, 1), subImages:SubSize());
+
+writer:Close();

Documents/Illumination/GenFacetedNearVsFar.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "GridAxis"
+require "_utils"
+
+local imageSize = vmath.vec2(400, 150);
+
+local subImages = SubImage.SubImage(1, 2, imageSize.x, imageSize.y, 0, 0);
+
+local coordSize = 6;
+local coordWidth = coordSize * (imageSize.x / imageSize.y);
+
+local vp = Viewport.Viewport(imageSize, {-5, 0}, {coordWidth, coordSize})
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local styleLib = SvgWriter.StyleLibrary();
+
+local lightSourceColor = "yellow";
+local surfaceColor = "lightblue";
+local rayColor = "darkblue";
+
+styleLib:AddStyle(nil, "surface", SvgWriter.Style():stroke("none"):fill(surfaceColor));
+styleLib:AddStyle(nil, "light", SvgWriter.Style():stroke("none"):fill(lightSourceColor));
+styleLib:AddStyle(nil, "arrowhead",
+	SvgWriter.Style():stroke(rayColor):fill(rayColor));
+	
+styleLib:AddStyle(nil, "ray",
+	SvgWriter.Style():stroke(rayColor):stroke_width("1px"):stroke_dasharray({3, 3}):marker_end(SvgWriter.uriLocalElement("arrow")));
+	
+styleLib:AddStyle(nil, "facets",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"):stroke_linejoin("round"):fill(surfaceColor));
+
+styleLib:AddStyle(nil, "circle",
+	SvgWriter.Style():stroke("black"):stroke_width("2px"):fill("none"));
+	
+styleLib:AddStyle(nil, "show_clip",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+styleLib:AddStyle(nil, "clip_to_area",
+	SvgWriter.Style():clip_path(SvgWriter.uriLocalElement("area_clip")));
+
+	
+local axisData = GridAxis.GridAxis2D(vp, nil, styleLib, true, nil);
+
+--Surface and light.
+local surfaceDim =
+{
+	vmath.vec2(0, coordSize / 2),
+	vmath.vec2(3, -coordSize / 2),
+};
+
+local lightSourcePos = 
+{
+	vmath.vec2(-11, 0),
+	vmath.vec2(-4, 0);
+}
+
+local lightSourceRadius = 0.4;
+
+lightSourceRadius = vp:Transform(vmath.vector(lightSourceRadius, lightSourceRadius, 0.0));
+lightSourceRadius = math.abs(lightSourceRadius.x);
+
+
+--Vertices
+local circleRadius = 8;
+
+local vpCircleRadius = vp:Transform(vmath.vector(circleRadius, circleRadius, 0.0));
+vpCircleRadius = math.abs(vpCircleRadius.x);
+
+local circleCenterPt = vmath.vec2(2, -7);
+
+--Facets
+local numFacets = 7;
+
+local facetPath = SvgWriter:Path();
+
+for i=1, numFacets do
+	local angle = ((i - 1) / numFacets) * math.pi * 2 + (math.pi / 2);
+	local point = vmath.vec2(math.cos(angle), math.sin(angle));
+	point = (point * circleRadius) + circleCenterPt;
+	
+	point = vp:Transform(point);
+
+	if(i == 1) then
+		facetPath:M(point);
+	else
+		facetPath:L(point);
+	end
+end
+
+facetPath:Z();
+
+--Diffuse Rays
+local function CalcRays(lightPos, rayLen)
+	rayLen = rayLen or 3;
+	
+	local rayLines = {};
+
+	local startAngle = (0 / numFacets) * math.pi * 2 + (math.pi / 2);
+	local endAngle = (1 / numFacets) * math.pi * 2 + (math.pi / 2);
+	local facetAngle = startAngle + (2 * math.pi / numFacets / 2);
+	
+	local startLoc = vmath.vec2(math.cos(facetAngle), math.sin(facetAngle));
+	startLoc = (startLoc * circleRadius) + circleCenterPt;
+	rayLines[#rayLines + 1] = startLoc;
+	
+	local dir = vmath.norm(lightPos - startLoc);
+	rayLines[#rayLines + 1] = startLoc + (dir * rayLen);
+
+	local startPt = vmath.vec2(math.cos(startAngle), math.sin(startAngle));
+	local endPt = vmath.vec2(math.cos(endAngle), math.sin(endAngle));
+	startPt = (startPt * circleRadius) + circleCenterPt;
+	endPt = (endPt * circleRadius) + circleCenterPt;
+	
+	local midPt = (startPt + endPt) / 2;
+	rayLines[#rayLines + 1] = midPt;
+
+	dir = vmath.norm(lightPos - midPt);
+	rayLines[#rayLines + 1] = midPt + (dir * rayLen);
+	
+	return rayLines;
+end
+
+local rays =
+{
+	CalcRays(lightSourcePos[1]),
+	CalcRays(lightSourcePos[2], 1.5),
+}
+
+surfaceDim = vp:Transform(surfaceDim);
+lightSourcePos = vp:Transform(lightSourcePos);
+rays[1] = subImages:Transform({1, 1}, vp:Transform(rays[1]));
+rays[2] = subImages:Transform({1, 2}, vp:Transform(rays[2]));
+
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		WriteStandardArrowhead(writer, "arrow", {"arrowhead"});
+		
+		writer:BeginGroup(nil, "g_surface");
+			--Draw surface and light.
+			writer:Rect2Pt(surfaceDim[1], surfaceDim[2], nil, {"surface"});
+		writer:EndGroup();
+		
+		writer:BeginGroup(nil, "g_faceted_circle");
+			writer:Circle(vp:Transform(circleCenterPt), vpCircleRadius, {"circle"});
+			writer:Path(facetPath, {"facets"});
+		writer:EndGroup();
+		
+		writer:BeginClipPath(nil, "area_clip");
+			writer:Rect(subImages:Offset(1, 1), subImages:SubSize(), nil, {"show_clip"});
+		writer:EndClipPath();
+		
+	writer:EndDefinitions();
+
+--	axisData:AddDefinitions(writer, "g_axes");
+--	writer:Use("g_axes", subImages:Offset(1, 1), subImages:SubSize());
+--	writer:Use("g_axes", subImages:Offset(1, 2), subImages:SubSize());
+
+
+	lightSourcePos[1] = subImages:Transform({1, 1}, lightSourcePos[1]);
+	lightSourcePos[2] = subImages:Transform({1, 2}, lightSourcePos[2]);
+
+	writer:Circle(lightSourcePos[1], lightSourceRadius, {"light"});
+	writer:Circle(lightSourcePos[2], lightSourceRadius, {"light"});
+	
+	writer:Use("g_faceted_circle", subImages:Offset(1, 1), subImages:SubSize(), {"clip_to_area"});
+	writer:Use("g_faceted_circle", subImages:Offset(1, 2), subImages:SubSize(), {"clip_to_area"});
+
+	for i=1, #rays[1], 2 do
+		writer:Line(rays[1][i], rays[1][i + 1], {"ray"});
+	end
+	for i=1, #rays[2], 2 do
+		writer:Line(rays[2][i], rays[2][i + 1], {"ray"});
+	end
+	
+
+
+
+writer:Close();

Documents/Illumination/GenLightNearSurface.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "GridAxis"
+require "_utils"
+
+local imageSize = vmath.vec2(500, 200);
+
+local subImages = SubImage.SubImage(1, 1, imageSize.x, imageSize.y, 0, 50);
+
+local coordSize = 6;
+local coordWidth = coordSize * (imageSize.x / imageSize.y);
+
+local vp = Viewport.Viewport(imageSize, {0, 0}, {coordWidth, coordSize})
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local styleLib = SvgWriter.StyleLibrary();
+
+local lightSourceColor = "yellow";
+local surfaceColor = "lightblue";
+local rayColor = "darkblue";
+
+styleLib:AddStyle(nil, "surface", SvgWriter.Style():stroke("none"):fill(surfaceColor));
+styleLib:AddStyle(nil, "light", SvgWriter.Style():stroke("none"):fill(lightSourceColor));
+styleLib:AddStyle(nil, "arrowhead",
+	SvgWriter.Style():stroke(rayColor):fill(rayColor));
+	
+styleLib:AddStyle(nil, "ray",
+	SvgWriter.Style():stroke(rayColor):stroke_width("1px"):stroke_dasharray({3, 3}):marker_end(SvgWriter.uriLocalElement("arrow")));
+	
+local axisData = GridAxis.GridAxis2D(vp, nil, styleLib, true, nil);
+
+--Surface and light.
+local surfaceDim =
+{
+	vmath.vec2(coordWidth / 2, -coordSize / 5), vmath.vec2(-coordWidth / 2, -coordSize / 2),
+};
+
+local lightSourcePos = 
+{
+	vmath.vec2(0, 1.5),
+}
+
+local lightSourceRadius = 10;
+
+
+--Diffuse Rays
+local function CalcRays(lightPos, iNumRays, rayLen)
+	rayLen = rayLen or 3;
+
+	local rayLines = {};
+	for i = 1, iNumRays do
+		local startLoc = (-coordWidth / 2) + ((coordWidth / (iNumRays + 1)) * i);
+		startLoc = vmath.vec2(startLoc, surfaceDim[1].y);
+		rayLines[#rayLines + 1] = startLoc;
+		
+		local dir = vmath.norm(lightPos - startLoc);
+		rayLines[#rayLines + 1] = startLoc + (dir * rayLen);
+	end
+	
+	return rayLines;
+end
+
+local rays =
+{
+	CalcRays(lightSourcePos[1], 15, 1),
+}
+
+local iNumRays = 7;
+
+surfaceDim = vp:Transform(surfaceDim);
+lightSourcePos = vp:Transform(lightSourcePos);
+rays[1] = subImages:Transform({1, 1}, vp:Transform(rays[1]));
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		WriteTipArrowhead(writer, "arrow", {"arrowhead"});
+		
+		writer:BeginGroup(nil, "g_surface");
+			--Draw surface and light.
+			writer:Rect2Pt(surfaceDim[1], surfaceDim[2], nil, {"surface"});
+		writer:EndGroup();
+
+	writer:EndDefinitions();
+
+	writer:Use("g_surface", subImages:Offset(1, 1), subImages:SubSize());
+
+	lightSourcePos[1] = subImages:Transform({1, 1}, lightSourcePos[1]);
+	writer:Circle(lightSourcePos[1], lightSourceRadius, {"light"});
+	
+	for i=1, #rays[1], 2 do
+		writer:Line(rays[1][i], rays[1][i + 1], {"ray"});
+	end
+	
+
+writer:Close();

Documents/Illumination/GenTwoTriangleInterpolation.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 300;
+
+local subImages = SubImage.SubImage(1, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local scaleFactor = 0.25;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+local textSize = 25;
+
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke("black"):fill("none"));
+	
+styleLib:AddStyle(nil, "triangle_top",
+	SvgWriter.Style():stroke("grey"):stroke_width("2px"):stroke_linejoin("round"):fill(SvgWriter.uriLocalElement("top_grad")));
+	
+styleLib:AddStyle(nil, "triangle_bottom",
+	SvgWriter.Style():stroke("grey"):stroke_width("2px"):stroke_linejoin("round"):fill(SvgWriter.uriLocalElement("bottom_grad")));
+	
+
+--Vertices
+local quadPts =
+{
+	vmath.vec2(-0.8, 0.8),
+	vmath.vec2(0.8, 0.8),
+	vmath.vec2(0.8, -0.8),
+	vmath.vec2(-0.8, -0.8),
+};
+
+quadPts = vp:Transform(quadPts);
+
+
+--Vertices
+local pointRadius = 5;
+local radiusPt = vmath.vec2(pointRadius, pointRadius);
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:LinearGradient("top_grad",
+			{
+				{offset="0%", color="black"},
+				{offset="100%", color="#0F0"},
+			},
+			{"0%", "100%"}, {"50%", "50%"}
+		);
+		writer:LinearGradient("bottom_grad",
+			{
+				{offset="0%", color="black"},
+				{offset="100%", color="#0F0"},
+			},
+			{"100%", "0%"}, {"50%", "50%"}
+		);
+	writer:EndDefinitions();
+
+	writer:Polygon({quadPts[1], quadPts[2], quadPts[3]}, {"triangle_bottom"});
+	writer:Polygon({quadPts[1], quadPts[3], quadPts[4]}, {"triangle_top"});
+writer:Close();

Documents/Illumination/GenTwoTriangleQuad.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+require "_utils"
+
+local subImageSize = 300;
+
+local subImages = SubImage.SubImage(1, 1, subImageSize, subImageSize, 50, 0);
+
+local coordSize = 2;
+
+local vp = Viewport.Viewport(subImages:SubSize(), {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local scaleFactor = 0.25;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 9;
+local textSize = 25;
+
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke("black"):stroke_width("2px"):stroke_linejoin("round"):fill("none"));
+	
+
+--Vertices
+local quadPts =
+{
+	vmath.vec2(-0.8, 0.8),
+	vmath.vec2(0.8, 0.8),
+	vmath.vec2(0.8, -0.8),
+	vmath.vec2(-0.8, -0.8),
+};
+
+quadPts = vp:Transform(quadPts);
+
+
+--Vertices
+local pointRadius = 5;
+local radiusPt = vmath.vec2(pointRadius, pointRadius);
+
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {(subImages:Size().x + 1) .."px", (subImages:Size().y + 1) .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+	writer:EndDefinitions();
+
+	writer:Polygon({quadPts[1], quadPts[2], quadPts[3]}, {"triangle"});
+	writer:Polygon({quadPts[1], quadPts[3], quadPts[4]}, {"triangle"});
+writer:Close();
Add a comment to this file

Documents/Illumination/High Light.png

Added
New image
Add a comment to this file

Documents/Illumination/LightNearSurface.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="200px" width="500px" >
+	<style type="text/css" ><![CDATA[.arrowhead
+{
+	fill: darkblue;
+	stroke: darkblue;
+}
+
+.__grid1
+{
+	stroke: #CCC;
+	stroke-width: 1px;
+	fill: none;
+}
+
+.__axis_arrow_end1
+{
+	marker-end: url(#__axis_arrow1);
+}
+
+.light
+{
+	fill: yellow;
+	stroke: none;
+}
+
+.surface
+{
+	fill: lightblue;
+	stroke: none;
+}
+
+.__axis1
+{
+	stroke: black;
+	stroke-width: 2px;
+	fill: none;
+}
+
+.ray
+{
+	marker-end: url(#arrow);
+	stroke: darkblue;
+	stroke-dasharray: 3,3;
+	stroke-width: 1px;
+}]]></style>
+	<defs >
+		<marker markerWidth="10" markerHeight="8" refX="0" refY="4" markerUnits="userSpaceOnUse" orient="auto" id="arrow" >
+			<path d="M 10 4 L 0 0 L 0 8 Z" class="arrowhead" />
+		</marker>
+		<g id="g_surface" >
+			<rect y="140" x="-2.8421709430404e-014" height="60" width="500" class="surface" />
+		</g>
+	</defs>
+	<use xlink:href="#g_surface" y="0" x="0" height="200" width="500" />
+	<circle r="10" cy="50" cx="250" class="light" />
+	<line x2="62.076250086125" y2="127.31719996457" y1="140" x1="31.25" class="ray" />
+	<line x2="92.550768582276" y2="125.57563108051" y1="140" x1="62.5" class="ray" />
+	<line x2="122.63438686989" y2="123.36259316294" y1="140" x1="93.75" class="ray" />
+	<line x2="152.05114471505" y2="120.52317580516" y1="140" x1="125" class="ray" />
+	<line x2="180.29624403437" y2="116.91560572701" y1="140" x1="156.25" class="ray" />
+	<line x2="206.51319364984" y2="112.62100114422" y1="140" x1="187.5" class="ray" />
+	<line x2="229.68372296568" y2="108.51087785884" y1="140" x1="218.75" class="ray" />
+	<line x2="250" y2="106.66666666667" y1="140" x1="250" class="ray" />
+	<line x2="270.31627703432" y2="108.51087785884" y1="140" x1="281.25" class="ray" />
+	<line x2="293.48680635016" y2="112.62100114422" y1="140" x1="312.5" class="ray" />
+	<line x2="319.70375596563" y2="116.91560572701" y1="140" x1="343.75" class="ray" />
+	<line x2="347.94885528495" y2="120.52317580516" y1="140" x1="375" class="ray" />
+	<line x2="377.36561313011" y2="123.36259316294" y1="140" x1="406.25" class="ray" />
+	<line x2="407.44923141772" y2="125.57563108051" y1="140" x1="437.5" class="ray" />
+	<line x2="437.92374991388" y2="127.31719996457" y1="140" x1="468.75" class="ray" />
+</svg>
Add a comment to this file

Documents/Illumination/No Ambient.png

Added
New image
Add a comment to this file

Documents/Illumination/Scale and Lighting.png

Added
New image

Documents/Illumination/Tutorial 08.xml

                 </mediaobject>
             </figure>
             <para>If the light source is sufficiently distant, relative to the size of the scene
-                being rendered, then the direction towards the light is the same for every point on
-                every object you render. Since the direction is the same everywhere, the light can
-                be represented as just a single direction given to all of the objects. There is no
-                need to compute the direction based on the position of the point being
+                being rendered, then the direction towards the light is nearly the same for every
+                point on every object you render. Since the direction is the same everywhere, the
+                light can be represented as just a single direction given to all of the objects.
+                There is no need to compute the direction based on the position of the point being
                 illuminated.</para>
             <para>This situation is called a <glossterm>directional light source.</glossterm> Light
                 from such a source effectively comes from a particular direction as a wall of
                 source illuminating both objects. One of the nice things about a cylinder is that it
                 has both curved and flat surfaces, thus making an adequate demonstration of how
                 light interacts with a surface.</para>
-            <!--TODO: Show an image of the tutorial output.-->
+            <figure>
+                <title>Basic Lighting</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="Basic%20Lighting.png"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
             <para>The light is at a fixed direction; the model and camera both can be
                 rotated.</para>
             <formalpara>
                 the cosine of it. But both computing that angle and taking its cosine are quite
                 expensive. Instead, we elect to use a vector math trick: the <glossterm>vector dot
                     product.</glossterm></para>
-            <para>Geometrically, the vector dot product of two direction vectors represents the
-                length of the projection of one vector onto the other.</para>
-            <!--TODO: Show two vectors, and the projection onto each other.-->
-            <para>This effectively means that the dot product of two vectors is as follows:</para>
+            <para>The vector dot product between two vectors can be mathematically computed as
+                follows:</para>
             <equation>
-                <title>Vector Dot Product Lengths</title>
+                <title>Dot Product</title>
                 <mediaobject>
                     <imageobject>
                         <imagedata format="SVG" fileref="DotProductLength.svg"/>
                     </imageobject>
                 </mediaobject>
             </equation>
-            <para>If both vectors have a length of one, and thus are unit vectors, then the result
-                of a dot product is just the cosine of the angle between the vectors.</para>
+            <para>If both vectors have a length of one, if they are unit vectors, then the result of
+                a dot product is just the cosine of the angle between the vectors.</para>
             <para>This is also part of the reason why the light direction is the direction
                     <emphasis>towards</emphasis> the light rather than from the light. Otherwise we
                 would have to negate the vector before performing the dot product.</para>
             <para>What makes this faster than taking the cosine of the angle directly is that, while
-                the dot product is geometrically the cosine of the angle between the two vectors,
-                mathematically computing the dot product is very simple:</para>
+                the dot product is geometrically the cosine of the angle between the two unit
+                vectors, computing the dot product via vector math is very simple:</para>
             <equation>
-                <title>Vector Dot Product Equation</title>
+                <title>Dot Product from Vector Math</title>
                 <mediaobject>
                     <imageobject>
                         <imagedata format="SVG" fileref="DotProductEquation.svg"/>
             arbitrary matrix; there is no guarantee that this transformation will not apply scaling
             or other transformations to the vector that will result in a non-unit vector.</para>
         <para>Of course, it is easy enough to correct this. The GLSL function
-                <function>normalize</function> will return a vector that is of unit length.</para>
+                <function>normalize</function> will return a vector that is of unit length without
+            changing the direction of the input vector.</para>
         <para>And while mathematically this would function, geometrically, it would be nonsense. For
             example, consider a 2D circle. We can apply a non-uniform scale (different scales in
             different axes) to the positions on this circle that will transform it into an
             ellipse:</para>
-        <!--TODO: Show a circle, then show the squashed ellipse-->
+        <figure>
+            <title>Circle Scaling</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="CircleScaling.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>This is all well and good, but consider the normals in this transformation:</para>
-        <!--TODO: Show the circle with normals, then show the ellipse with squashed normals.
-Also show an ellipse with proper normals.-->
+        <figure>
+            <title>Circle Scaling with Normals</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="CircleNormalScaling.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>The ellipse in the middle has the normals that you would expect if you transformed the
             normals from the circle by the same matrix the circle was transformed by. They may be
             unit length, but they no longer reflect the <emphasis>shape</emphasis> of the ellipse.
 glUseProgram(0);</programlisting>
         </example>
         <para>It's pretty self-explanatory.</para>
-        <!--TODO: Show a picture of the tutorial, with scale applied.-->
+        <figure>
+            <title>Lighting and Scale</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="Scale%20and%20Lighting.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <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
         <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-->
+        <figure>
+            <title>Half Lit</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="No%20Ambient.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>The unlit portions of the cylinder are completely, 100% black. This almost never
             happens in real life, even for objects we perceive as being <quote>black</quote> in
             color. The reason for this is somewhat complicated.</para>
             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 tutorial.-->
+        <figure>
+            <title>Ambient Lighting</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="Ambient%20Lighting.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>The detail seen in the dark portion of the cylinder only comes from the diffuse color.
+            And because the ambient is fairly weak, the diffuse color of the surface appears muted
+            in the dark areas.</para>
         <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>

Documents/Illumination/Tutorial 09.xml

         <para>We implement point lights per-vertex in the <phrase role="propername">Vertex Point
                 Lighting</phrase> tutorial. This tutorial has a moving point light that circles
             around the cylinder.</para>
-        <!--TODO: Show a picture of the tutorial.-->
+        <figure>
+            <title>Vertex Point Lighting</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="Vertex%20Point%20Lighting.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>It controls as follows:</para>
         <!--TODO: Have a table explaining the tutorial's controls.-->
         <para>Most of the code is nothing we haven't seen elsewhere. The main changes are at the top
             position it near the ground plane. See anything wrong?</para>
         <para>If everything were working correctly, one would expect to see a bright area directly
             under the light. After all, geometrically, this situation looks like this:</para>
-        <!--TODO: Show a 2D diagram of a point light near a line surface.-->
-        <para>The area directly under the point should be very bright, but the area farther from the
-            point light should be darker. What we see is nothing of the sort. There is no bright
-            light directly under the light source. Why is that?</para>
+        <figure>
+            <title>Light Near Surface</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="LightNearSurface.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>The surface normal for the areas directly under the light are almost the same as the
+            direction towards the light. This means that the angle of incidence is small, so the
+            cosine of this angle is close to 1. That should translate to having a bright area under
+            the light, but darker areas farther away. What we see is nothing of the sort. Why is
+            that?</para>
         <para>Well, consider what we are doing. We are computing the lighting at every triangle's
                 <emphasis>vertex</emphasis>, and then interpolating the results across the surface
             of the triangle. The ground plane is made up of precisely four vertices: the four
-            corners. And those are all very far from the light position and have a very angle of
-            incidence. Since none of them have a small angle of incidence, none of the colors that
-            are interpolated across the surface are bright.</para>
+            corners. And those are all very far from the light position and have a very large angle
+            of incidence. Since none of them have a small angle of incidence, none of the colors
+            that are interpolated across the surface are bright.</para>
         <para>You can see this is evident by putting the light position next to the cylinder. If the
             light is at the top or bottom of the cylinder, then the area near the light will be
             bright. But if you move the light to the middle of the cylinder, far the top or bottom
             vertices, then the illumination will be much dimmer.</para>
-        <!--TODO: Show this tutorial with the light at the middle of the cylinder.-->
         <para>This is not the only problem with doing per-vertex lighting. For example, run the
             tutorial again and don't move the light. Just watch how the light behaves on the
             cylinder's surface as it animates around. Unlike with directional lighting, you can very
             light and the surface normal. In directional lighting, the direction to the light is
             always the same. Therefore, the only value that changes over a triangle's surface is the
             surface normal.</para>
-        <para>The more physically correct method of lighting is to perform lighting at every
-            rendered pixel. To do that, we would have to interpolate the lighting parameters across
-            the triangle, and perform the lighting computation in the fragment shader.</para>
         <para>Linear interpolation of vectors looks like this:</para>
         <informalequation>
             <mediaobject>
             not yield the same results as linearly interpolating the two colors you get from the
             lighting equation. This is a big part of the reason why the cylinder doesn't look
             correct.</para>
+        <para>The more physically correct method of lighting is to perform lighting at every rendered
+            pixel. To do that, we would have to interpolate the lighting parameters across the
+            triangle, and perform the lighting computation in the fragment shader.</para>
     </section>
     <section>
         <?dbhtml filename="Tut09 Fragment Lighting.html" ?>
         <para>So, in order to deal with interpolation artifacts, we need to interpolate the actual
             light direction and normal, instead of just the results of the lighting equation. This
             is called per-fragment lighting or just <glossterm>fragment lighting.</glossterm></para>
+        <para>This is pretty simple, conceptually. We simply need to do the lighting computations in
+            the fragment shader. So the fragment shader needs the position of the fragment, the
+            light's position (or the direction to the light from that position), and the surface
+            normal of the fragment at that position. And all of these values need to be in the same
+            coordinate space.</para>
         <para>There is a problem that needs to be dealt with first. Normals do not interpolate well.
             Or rather, wildly different normals do not interpolate well. And light directions can be
             very different if the light source is close to the triangle relative to that triangle's
             different at each vertex, so long as our light remains in relatively close proximity to
             the plane.</para>
         <para>Part of the problem is with interpolating values along the diagonal of our triangle.
-            Using two triangles to form a square plane does not mean that the values at the four
-            vertices interpolate the way you would expect. The plane is actually made of these two
-            triangles:</para>
-        <!--TODO: Show a picture of the two triangles composing the plane.-->
-        <para>The interpolation always happens between the three vertices of the particular
-            triangle. Which means that vertices near the diagonal will be basically doing a linear
-            interpolation between the two values on either end of that diagonal. This is not the
-            same thing as doing interpolation between all 4 values.</para>
-        <!--TODO: Show bilinear interpolation vs. barycentric interpolation.-->
+            Interpolation within a triangle works like this. For any position within the area of the
+            triangle, that position can be expressed as a weighted sum of the positions of the three
+            vertices.</para>
+        <figure>
+            <title>Triangle Interpolation</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="BarycentricTriangle.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <informalequation>
+            <mathphrase>P = αA + βB + γC, where α + β + γ = 1.0</mathphrase>
+        </informalequation>
+        <para>The α, β, and γ values are not the distances from their respective points to the point
+            of interest. In the above case, the point P is in the exact center of the triangle.
+            Thus, the three values are each ⅓.</para>
+        <para>If the point of interest is along an edge of the triangle, then the contribution of
+            the vertex not sharing that edge is zero.</para>
+        <figure>
+            <title>Triangle Edge Interpolation</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="BarycentricTriangleEdge.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>Here, point P is exactly halfway between points C and B. Therefore,  β, and γ are both
+            0.5, but α is 0.0. If point P is anywhere along the edge of a triangle, it gets none of
+            its final interpolated value from the third vertex. So along a triangle's edge, it acts
+            like the kind of linear interpolation we have seen before.</para>
+        <para>This is how OpenGL interpolates the vertex shader outputs. It takes the α, β, and γ
+            coordinates for the fragment's position and combines them with the vertex output value
+            for the three vertices in the same way it does for the fragment's position. There is
+            slightly more to it than that, but we will discuss that later.</para>
+        <para>The ground plane in our example is made of two large triangles. They look like
+            this:</para>
+        <figure>
+            <title>Two Triangle Quadrilateral</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="TwoTriangleQuad.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>What happens if we put the color black on the top-right and bottom-left points, and
+            put the color green on the top-left and bottom-right points? If you interpolate these
+            across the surface, you would get this:</para>
+        <figure>
+            <title>Two Triangle Interpolation</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="TwoTriangleInterpolation.svg" format="SVG"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
+        <para>The color is pure green along the diagonal. That is because along a triangle's edge,
+            the value interpolated will only be the color of the two vertices along that edge. The
+            value is interpolated based only on each triangle individually, not on extra data from
+            another neighboring triangle.</para>
         <para>In our case, this means that for points along the main diagonal, the light direction
             will only be composed of the direction values from the two vertices on that diagonal.
             This is not good. This wouldn't be much of a problem if the light direction did not
-            change much along the surface, but that is not the case here.</para>
+            change much along the surface, but with large triangles (relative to how close the light
+            is to them), that is simply not the case.</para>
         <para>Since we cannot interpolate the light direction very well, we need to interpolate
             something else. Something that does exhibit the characteristics we need when
             interpolated.</para>
-        <para>Positions interpolate linearly quite well. So instead of interpolating the light
-            direction, we interpolate the components of the light direction. Namely, the two
-            positions. The light position is a constant, so we only need to interpolate the vertex
-            position.</para>
+        <para>Positions interpolate quite well. Interpolating the top-left position and bottom-right
+            positions gets an accurate position value along the diagonal. So instead of
+            interpolating the light direction, we interpolate the components of the light direction.
+            Namely, the two positions. The light position is a constant, so we only need to
+            interpolate the vertex position.</para>
         <para>Now, we could do this in any space. But for illustrative purposes, we will be doing
             this in model space. That is, both the light position and vertex position will be in
             model space.</para>
             space.</para>
         <para>The <phrase role="propername">Fragment Point Lighting</phrase> tutorial shows off how
             fragment lighting works.</para>
-        <!--TODO: Show a picture of the tutorial.-->
+        <figure>
+            <title>Fragment Point Lighting</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="Fragment%20Point%20Lighting.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>Much better.</para>
         <para>This tutorial is controlled as before, with a few exceptions. Pressing the
                 <keycap>t</keycap> key will toggle a scale factor onto to be applied to the
                     <keycap>J</keycap>
                 </keycombo> key to move the light really close to the cylinder, but without putting
                 the light inside the cylinder. You should see something like this:</para>
-            <!--TODO: Picture of the cylinder with a close light.-->
-            <para>This looks like the same interpolation problem we had before. Wasn't doing
-                lighting at the fragment level supposed to fix this?</para>
+            <figure>
+                <title>Close Lit Cylinder</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="Cylinder%20Close%20Light.png"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
+            <para>Notice the vertical bands on the cylinder. This are reminiscent of the same
+                interpolation problem we had before. Wasn't doing lighting at the fragment level
+                supposed to fix this?</para>
             <para>Actually, this is a completely different problem. And it is one that is
-                essentially impossible to solve. Well, not without changing our geometry.</para>
+                essentially impossible to solve. Well, not without changing our rendered
+                mesh.</para>
             <para>The source of the problem is this: the light finally caught us in our lie.
                 Remember what we are actually doing. We are not rendering a true cylinder; we are
                 rendering a group of flat triangles that we attempt to make
                 light is very close, as it is here, it reveals our fakery for what it is.
                 Why?</para>
             <para>Let's take a top-down view of our cylinder approximation, but let's also draw what
-                the actual cylinder would look like:</para>
-            <!--TODO: Show a diagram of a faceted cylinder inscribed within a circle.-->
+                the actual cylinder would look like from the top down:</para>
+            <figure>
+                <title>Faceted Cylinder</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="FacetedCircle.svg" format="SVG"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
             <para>Now, consider our lighting equation at a particular point on the fake
                 cylinder:</para>
-            <!--TODO: Extend the previous diagram with two lights, one close and one far. Also show a point on the fake
-cylinder, and a normal. And show the corresponding point/normal.-->
+            <figure>
+                <title>Light Directions on Faceted Cylinder</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="FacetedNearVsFar.svg" format="SVG"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
             <para>The problem comes from the difference between the actual position being rendered
                 and the corresponding position on the circle that has the normal that we are
                 pretending our point has. When the light is somewhat distant, the difference between
-                the actual light direction we use, and the one we would use on a real cylinder is
+                the actual light direction we use and the one we would use on a real cylinder is
                 pretty small. But when the light is very close, the difference is
                 substantial.</para>
             <para>The lighting computations are correct for the vertices near the edges of a
             <para>The key point is that there isn't much we can do about this problem. The only
                 solution is to add more vertices to the approximation of the cylinder. It should
                 also be noted that adding more triangles would also make per-vertex lighting look
-                more correct. Thus making the whole exercise in using per-fragment lighting somewhat
-                pointless; if the mesh is finely-divided enough so that each vertex effectively
-                becomes a single fragment, then there is no difference between the two
-                techniques.</para>
+                more correct. If you add infinitely many vertices to the cylinder, then the results
+                of per-vertex lighting are indistinguishable from per-fragment lighting.</para>
             <para>For our simple case, adding triangles is easy, since a cylinder is a
                 mathematically defined object. For a complicated case like a human being, this is
                 usually rather more difficult. It also takes up more performance, since there are
         <para>There is another issue with our current example. Use the <keycap>i</keycap> key to
             raise the light up really high. Notice how bright all of the upwardly-facing surfaces
             get:</para>
-        <!--TODO: Show a picture of a highlight's effect on the surface.-->
+        <figure>
+            <title>High Light</title>
+            <mediaobject>
+                <imageobject>
+                    <imagedata fileref="High%20Light.png"/>
+                </imageobject>
+            </mediaobject>
+        </figure>
         <para>You probably have no experience with this in real life. Holding a light farther from
             the surface in reality does not make the light brighter. So obviously something is
             happening in reality that our simple lighting model is not accounting for.</para>
             <title>Applied Attenuation</title>
             <para>The <phrase role="propername">Fragment Attenuation</phrase> tutorial performs
                 per-fragment attenuation, both with linear and quadratic attenuation.</para>
-            <!--TODO: Show a picture of the tutorial.-->
+            <figure>
+                <title>Fragment Attenuation</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="Fragment%20Attenuation.png"/>
+                    </imageobject>
+                </mediaobject>
+            </figure>
             <para>This tutorial controls as before, with the following exceptions. The
                     <keycap>O</keycap> and <keycap>U</keycap> keys increase and decrease the
                 attenuation constant. However, remember that decreasing the constant makes the
Add a comment to this file

Documents/Illumination/TwoTriangleInterpolation.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="301px" width="301px" >
+	<style type="text/css" ><![CDATA[.triangle
+{
+	fill: none;
+	stroke: black;
+}
+
+.triangle_top
+{
+	fill: url(#top_grad);
+	stroke: grey;
+	stroke-width: 2px;
+	stroke-linejoin: round;
+}
+
+.triangle_bottom
+{
+	fill: url(#bottom_grad);
+	stroke: grey;
+	stroke-width: 2px;
+	stroke-linejoin: round;
+}]]></style>
+	<defs >
+		<linearGradient y1="100%" x1="0%" x2="50%" y2="50%" id="top_grad" >
+			<stop offset="0%" stop-color="black" />
+			<stop offset="100%" stop-color="#0F0" />
+		</linearGradient>
+		<linearGradient y1="0%" x1="100%" x2="50%" y2="50%" id="bottom_grad" >
+			<stop offset="0%" stop-color="black" />
+			<stop offset="100%" stop-color="#0F0" />
+		</linearGradient>
+	</defs>
+	<polygon points="30,30 270,30 270,270" class="triangle_bottom" />
+	<polygon points="30,30 270,270 30,270" class="triangle_top" />
+</svg>
Add a comment to this file

Documents/Illumination/TwoTriangleQuad.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!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="301px" width="301px" >
+	<style type="text/css" ><![CDATA[.triangle
+{
+	fill: none;
+	stroke: black;
+}]]></style>
+	<defs />
+	<polygon points="30,30 270,30 270,270" class="triangle" />
+	<polygon points="30,30 270,270 30,270" class="triangle" />
+</svg>
Add a comment to this file

Documents/Illumination/Vertex Point Lighting.png

Added
New image

Documents/Positioning/GenTriangleFan.lua

 
 local worldPoints = vp:Transform(trianglePoints);
 
-local writer = SvgWriter.SvgWriter("TriangleFan.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+local writer = SvgWriter.SvgWriter(ConstructSVGName(arg[0]), {subImages:Size().x .."px", subImages:Size().y .. "px"});
 	writer:StyleLibrary(styleLib);
 	writer:BeginDefinitions();
 		writer:BeginGroup(nil, "g_point");

Documents/Tools/SubImage.lua

 	return vmath.vec2(points) + offset;
 end
 
+--Returns the top-left position and a size, for the image in question.
+function ClassMembers:GetRect(image)
+	return self:Offset(image[1], image[2]), self.subImageSize;
+end
 
 
 function SubImage(subImagesX, subImagesY, subImageWidth, subImageHeight,

Documents/Tools/Viewport.lua

 	end
 	
 	local point = points;
+
 	if(self.transform) then