Commits

Sokka  committed d4d5366

Added images for scan conversion.

  • Participants
  • Parent commits 6b13243

Comments (0)

Files changed (6)

File Documents/Basics/GenSharedEdgeScanConvert.lua

+require "SvgWriter"
+require "vmath"
+
+--Parameters
+local subImageWidth, subImageHeight = 200, 300;
+local subImageSpacing = 100;
+local numSubImages = 3;
+
+local imageWidth = (subImageWidth * numSubImages) + (subImageSpacing * (numSubImages - 1));
+local imageHeight = subImageHeight;
+
+local subImageSize = {subImageWidth, imageHeight};
+
+local subImagePositions = {}
+
+for i = 1, numSubImages, 1 do
+	subImagePositions[i] = {(subImageWidth + subImageSpacing) * (i-1), 0};
+end
+
+local gridSize = subImageWidth / 8;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "trans_wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"):stroke_opacity(0.4));
+	
+styleLib:AddStyle(nil, "grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "wide_grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "trans_wide_grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("3px"):stroke_opacity(0.1));
+
+styleLib:AddStyle(nil, "no_stroke",
+	SvgWriter.Style():stroke("none"));
+	
+styleLib:AddStyle(nil, "dashed",
+	SvgWriter.Style():stroke_dasharray({3, 3}));
+	
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "fill_light_blue",
+	SvgWriter.Style():fill("#8080ff"));
+
+styleLib:AddStyle(nil, "fill_light_green",
+	SvgWriter.Style():fill("#80ff80"));
+
+styleLib:AddStyle(nil, "fill_light_red",
+	SvgWriter.Style():fill("#ff8080"));
+
+styleLib:AddStyle(nil, "pointed",
+	SvgWriter.Style():marker(SvgWriter.uriLocalElement("point")));
+
+-- Paths and other data.
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local gridPath = SvgWriter.Path();
+for x = (gridSize/2), subImageWidth, gridSize do
+	gridPath:M{x, 0}:V(subImageHeight)
+end
+
+for y = (gridSize/2), subImageHeight, gridSize do
+	gridPath:M{0, y}:H(subImageWidth)
+end
+
+local centerPositions = {}
+
+for x = gridSize, subImageWidth - 1, gridSize do
+	for y = gridSize, subImageHeight - 1, gridSize do
+		centerPositions[#centerPositions + 1] = {x, y}
+	end
+end
+
+local fillSquareIndices =
+{
+	{
+		{1, 8},
+		{1, 9},
+		{1, 10},
+
+		{2, 2},
+		{2, 3},
+		{2, 4},
+		{2, 5},
+		{2, 6},
+		{2, 7},
+		{2, 8},
+
+		{3, 4},
+		{3, 5},
+		{3, 6},
+		{3, 7},
+
+		{4, 6},
+	},
+	
+	{
+		{3, 2},
+		{3, 3},
+
+		{4, 2},
+		{4, 3},
+		{4, 4},
+		{4, 5},
+		
+		{5, 1},
+		{5, 2},
+		{5, 3},
+		{5, 4},
+		{5, 5},
+		
+		{6, 1},
+		{6, 2},
+		{6, 3},
+		
+		{7, 1},
+	},
+	
+	{
+		{2, 9},
+		{2, 10},
+		
+		{3, 8},
+		{3, 9},
+		{3, 10},
+		
+		{4, 7},
+		{4, 8},
+		{4, 9},
+		{4, 10},
+		{4, 11},
+		
+		{5, 8},
+		{5, 9},
+		{5, 10},
+		{5, 11},
+		
+		{6, 11},
+	},
+	
+}
+
+local fillSquarePositions = {}
+
+for j, indexList in ipairs(fillSquareIndices) do
+	local newPosList = {};
+	fillSquarePositions[#fillSquarePositions + 1] = newPosList;
+	
+	for i, index in ipairs(indexList) do
+		newPosList[#newPosList + 1] =
+			{((index[1] - 1) * gridSize) + (gridSize/2), ((index[2] - 1) * gridSize) + (gridSize/2)}
+	end
+end
+
+
+-- The SVG itself.
+local writer = SvgWriter.SvgWriter("SharedEdgeScanConvert.svg", {imageWidth .."px", imageHeight .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({gridSize/2, gridSize/2}, {gridSize/4, gridSize/4}, "auto", true, nil, "point");
+			writer:Circle({gridSize/4, gridSize/4}, gridSize/4, {"black", "fill_black"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_triangle");
+			writer:Polygon({{50, 30}, {110, 160}, {15, 260}})
+			writer:Polygon({{50, 30}, {185, 15}, {110, 160}})
+			writer:Polygon({{160, 285}, {110, 160}, {15, 260}})
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_grid");
+			writer:Path(gridPath);
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_gridCenters");
+			for i, pos in ipairs(centerPositions) do
+				writer:Circle(pos, gridSize/7);
+			end
+		writer:EndGroup();
+		for i, posList in ipairs(fillSquarePositions) do
+			writer:BeginGroup(nil, "g_scanConvert" .. i);
+				for j, pos in ipairs(posList) do
+					writer:Rect(pos, {gridSize, gridSize});
+				end
+			writer:EndGroup();
+		end
+	writer:EndDefinitions();
+
+	--First subimage: just the triangle.
+	writer:Use("g_triangle", subImagePositions[1], subImageSize, {"black", "fill_none"});
+	
+	--Second subimage: triangle + grid + circles
+	writer:Use("g_grid", subImagePositions[2], subImageSize, {"black", "fill_none"});
+	writer:Use("g_gridCenters", subImagePositions[2], subImageSize, {"black", "fill_none"});
+	writer:Use("g_triangle", subImagePositions[2], subImageSize, {"trans_wide_black", "fill_none"});
+
+	--Third subimage: triangle + grid + fill.
+	writer:Use("g_scanConvert1", subImagePositions[3], subImageSize, {"no_stroke", "fill_light_blue"});
+	writer:Use("g_scanConvert2", subImagePositions[3], subImageSize, {"no_stroke", "fill_light_green"});
+	writer:Use("g_scanConvert3", subImagePositions[3], subImageSize, {"no_stroke", "fill_light_red"});
+	writer:Use("g_grid", subImagePositions[3], subImageSize, {"black", "fill_none"});
+	writer:Use("g_triangle", subImagePositions[3], subImageSize, {"trans_wide_black", "fill_none"});
+
+	
+writer:Close();
+
+
+

File Documents/Basics/GenTriangleScanConvert.lua

+require "SvgWriter"
+require "vmath"
+
+--Parameters
+local subImageWidth, subImageHeight = 200, 300;
+local subImageSpacing = 100;
+local numSubImages = 3;
+
+local imageWidth = (subImageWidth * numSubImages) + (subImageSpacing * (numSubImages - 1));
+local imageHeight = subImageHeight;
+
+local subImageSize = {subImageWidth, imageHeight};
+
+local subImagePositions = {}
+
+for i = 1, numSubImages, 1 do
+	subImagePositions[i] = {(subImageWidth + subImageSpacing) * (i-1), 0};
+end
+
+local gridSize = subImageWidth / 8;
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "trans_wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"):stroke_opacity(0.4));
+	
+styleLib:AddStyle(nil, "grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "wide_grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "trans_wide_grey",
+	SvgWriter.Style():stroke("#C0C0C0"):stroke_width("3px"):stroke_opacity(0.1));
+
+styleLib:AddStyle(nil, "no_stroke",
+	SvgWriter.Style():stroke("none"));
+	
+styleLib:AddStyle(nil, "dashed",
+	SvgWriter.Style():stroke_dasharray({3, 3}));
+	
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "fill_blue",
+	SvgWriter.Style():fill("blue"));
+
+styleLib:AddStyle(nil, "pointed",
+	SvgWriter.Style():marker(SvgWriter.uriLocalElement("point")));
+
+-- Paths and other data.
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local gridPath = SvgWriter.Path();
+for x = (gridSize/2), subImageWidth, gridSize do
+	gridPath:M{x, 0}:V(subImageHeight)
+end
+
+for y = (gridSize/2), subImageHeight, gridSize do
+	gridPath:M{0, y}:H(subImageWidth)
+end
+
+local centerPositions = {}
+
+for x = gridSize, subImageWidth - 1, gridSize do
+	for y = gridSize, subImageHeight - 1, gridSize do
+		centerPositions[#centerPositions + 1] = {x, y}
+	end
+end
+
+local fillSquareIndices =
+{
+	{3, 6},
+	{3, 7},
+	{3, 8},
+	{3, 9},
+
+	{4, 3},
+	{4, 4},
+	{4, 5},
+	{4, 6},
+	{4, 7},
+	{4, 8},
+	{4, 9},
+
+	{5, 5},
+	{5, 6},
+	{5, 7},
+	{5, 8},
+
+	{6, 7},
+}
+
+local fillSquarePositions = {}
+
+for i, index in ipairs(fillSquareIndices) do
+	fillSquarePositions[#fillSquarePositions + 1] =
+		{((index[1] - 1) * gridSize) + (gridSize/2), ((index[2] - 1) * gridSize) + (gridSize/2)}
+end
+
+
+-- The SVG itself.
+local writer = SvgWriter.SvgWriter("TriangleScanConvert.svg", {imageWidth .."px", imageHeight .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({gridSize/2, gridSize/2}, {gridSize/4, gridSize/4}, "auto", true, nil, "point");
+			writer:Circle({gridSize/4, gridSize/4}, gridSize/4, {"black", "fill_black"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_triangle");
+			writer:Polygon({{95, 50}, {160, 190}, {50, 260}})
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_grid");
+			writer:Path(gridPath);
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_gridCenters");
+			for i, pos in ipairs(centerPositions) do
+				writer:Circle(pos, gridSize/7);
+			end
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_scanConvert");
+			for i, pos in ipairs(fillSquarePositions) do
+				writer:Rect(pos, {gridSize, gridSize});
+			end
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--First subimage: just the triangle.
+	writer:Use("g_triangle", subImagePositions[1], subImageSize, {"black", "fill_none"});
+	
+	--Second subimage: triangle + grid + circles
+	writer:Use("g_grid", subImagePositions[2], subImageSize, {"black", "fill_none"});
+	writer:Use("g_gridCenters", subImagePositions[2], subImageSize, {"black", "fill_none"});
+	writer:Use("g_triangle", subImagePositions[2], subImageSize, {"trans_wide_black", "fill_none"});
+
+	--Third subimage: triangle + grid + fill.
+	writer:Use("g_scanConvert", subImagePositions[3], subImageSize, {"no_stroke", "fill_blue"});
+	writer:Use("g_grid", subImagePositions[3], subImageSize, {"black", "fill_none"});
+	writer:Use("g_triangle", subImagePositions[3], subImageSize, {"trans_wide_black", "fill_none"});
+
+	
+writer:Close();
+
+
+

File Documents/Basics/SharedEdgeScanConvert.svg

Added
New image

File Documents/Basics/TriangleScanConvert.svg

Added
New image

File Documents/Basics/Tutorial 00.xml

                     process takes the triangle and breaks it up based on the arrangement of window
                     pixels over the output image that the triangle covers.</para>
             </formalpara>
-            <!--TODO: Show a series of images, starting with a triangle, then overlying it with a pixel grid, followed by one showing
-which pixels get filled in.-->
-            <para>The specifics of which pixels get used and which do not for a triangle is not
-                important for the time being. What matters more than that is the fact that if two
-                triangles are perfectly adjacent, such that they share the same input vertex
-                positions, the output rasterization will never have holes or double coverage. Along
-                the shared line, there will be no overlap or holes between the two triangles.</para>
-            <!--TODO: Show an image of two adjacent triangles, then show their scan-converted pixels as above. The pixels of one
-triangle should be a different color from the pixels of the other.-->
+            <example>
+                <title>Scan Converted Triangle</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="TriangleScanConvert.svg"/>
+                    </imageobject>
+                </mediaobject>
+            </example>
+            <para>The center image shows the digital grid of output pixels; the circles represent
+                the center of each pixel. The center of each pixel represents a
+                    <glossterm>sample</glossterm>: a discrete location within the area of a pixel.
+                During scan conversion, a triangle will produce a <glossterm>fragment</glossterm>
+                for every pixel sample that is within the 2D area of the triangle.</para>
+            <para>The image on the right shows the fragments generated by the scan conversion of the
+                triangle. This creates a rough approximation of the triangle's general shape.</para>
+            <para>It is very often the case that triangles are rendered that share edges. OpenGL
+                offers a guarantee that, so long as the shared edge vertex positions are
+                    <emphasis>identical</emphasis>, there will be no sample gaps during scan
+                conversion.</para>
+            <example>
+                <title>Shared Edge Scan Conversion</title>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata fileref="SharedEdgeScanConvert.svg"/>
+                    </imageobject>
+                </mediaobject>
+            </example>
+            <para>To make it easier to use this, OpenGL also offers the guarantee that if you pass
+                the same input vertex data through the vertex processing stage, you will get
+                identical output. So the onus is on the user to use the same input vertices if you
+                want to ensure gap-less scan conversion.</para>
             <para>Scan conversion is an inherently 2D operation. This process only uses the X and Y
-                position of the triangle in window coordinates.</para>
-            <para>The result of scan converting a triangle is a sequence of boxes along the area of
-                the triangle. These boxes are called <glossterm>fragments.</glossterm></para>
-            <para>Each fragment has certain data associated with it. This data contains the 2D
-                location of the fragment in window coordinates, and the Z value of the fragment.
-                This Z value is known as the depth of the fragment. There may be other information
-                that is part of a fragment, and we will expand on that in later tutorials.</para>
+                position of the triangle in window coordinates to determine which fragments to
+                generate. The Z value is not forgotten, but it is directly part of the actual
+                process of scan converting the triangle.</para>
+            <para>The result of scan converting a triangle is a sequence of fragments that cover the
+                shape of the triangle. Each fragment has certain data associated with it. This data
+                contains the 2D location of the fragment in window coordinates, as well as the Z
+                position of the fragment. This Z value is known as the depth of the fragment. There
+                may be other information that is part of a fragment, and we will expand on that in
+                later tutorials.</para>
             <formalpara>
                 <title>Fragment Processing</title>
                 <para>This phase takes a fragment from a scan converted triangle and transforms it
             <note>
                 <title>Direct3D Note</title>
                 <para>Direct3D prefers to call this stage <quote>pixel processing</quote> or
-                        <quote>pixel shading</quote>. This is a misnomer, because as we will see in
-                    tutorials on antialiasing, multiple fragments from a single triangle can be
-                    combined together to form a single output pixel. Also, the fragment has not been
-                    written to the image, so it isn't a pixel. Indeed, this step can conditionally
-                    prevent rendering of a fragment based on arbitrary computations. Thus a
-                        <quote>pixel</quote> in D3D parlance may never actually become a pixel at
-                    all.</para>
+                        <quote>pixel shading</quote>. This is a misnomer, for several reasons.
+                    First, a pixel's final color can be composed of the results of multiple
+                    fragments generated by multiple <emphasis>samples</emphasis> within a single
+                    pixel. This is a common antialiasing technique. Also, the fragment data has not
+                    been written to the image, so it isn't a pixel yet. Indeed, the fragment
+                    processing step can conditionally prevent rendering of a fragment based on
+                    arbitrary computations. Thus a <quote>pixel</quote> in D3D parlance may never
+                    actually become a pixel at all.</para>
             </note>
             <formalpara>
                 <title>Fragment Writing</title>
             </glossdef>
         </glossentry>
         <glossentry>
+            <glossterm>sample</glossterm>
+            <glossdef>
+                <para>A discrete location within the bounds of a pixel that determines whether to
+                    generate a fragment from scan converting the triangle. The area of a single
+                    pixel can have multiple samples, which can generate multiple fragments.</para>
+            </glossdef>
+        </glossentry>
+        <glossentry>
             <glossterm>fragment</glossterm>
             <glossdef>
                 <para>A single element of a scan converted triangle. A fragment can contain

File Documents/Outline.xml

                         state.</para>
                 </listitem>
                 <listitem>
-                    <para>Indexed vertex arrays.</para>
+                    <para>Indexed vertex arrays. Include mention of the fact that there is only one
+                        index for all of the attributes.</para>
+                </listitem>
+                <listitem>
+                    <para>glDrawElementsBaseVertex, as an optimization.</para>
                 </listitem>
                 <listitem>
                     <para>Depth buffers. How to use them to hide surfaces.</para>