{"id":26282,"date":"2015-04-05T07:08:06","date_gmt":"2015-04-05T12:08:06","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=26282"},"modified":"2015-04-05T08:10:28","modified_gmt":"2015-04-05T13:10:28","slug":"the-strange-evolution-of-opengl-part-2","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=26282","title":{"rendered":"The Strange Evolution of OpenGL Part 2"},"content":{"rendered":"<p>In case you missed the <a href=\"?p=26269\">first entry<\/a>: We&#8217;re here to talk about how OpenGL has changed and why that&#8217;s important<span class='snote' title='1'>To me, anyway.<\/span>. The odd colorful  screenshots are from one of my many half-baked OpenGL-based engine prototypes, presented here simply to break up the monotony of the words.<\/p>\n<p>Before we can talk about where OpenGL went, we have to talk about where it started. So let&#8217;s talk about how rendering works on a fundamental level.<\/p>\n<p><!--more--><\/p>\n<h3>The Basics<\/h3>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/opengl_triangles1.jpg' class='insetimage' width='800' alt='It&#8217;s a good thing Pythagoras is dead, or he&#8217;d be insufferably smug right now.' title='It&#8217;s a good thing Pythagoras is dead, or he&#8217;d be insufferably smug right now.'\/><\/td><\/tr><tr><td class='insetcaption'>It&#8217;s a good thing Pythagoras is dead, or he&#8217;d be insufferably smug right now.<\/td><\/tr><\/table><\/p>\n<p>Our videogames are based on triangles. <strong>Everything<\/strong> is triangles. Even cube-based Minecraft is made by creating rectangles from pairs of triangles. Even text and icons on-screen are made by putting pictures of words and symbols onto triangle pairs.<\/p>\n<p>I suppose there are a few other ways our graphics technology might have developed if history had played out just a little differently. We might have wound up with  <a href=\"?p=890\" title=\"Outcast: Something Different\">voxels<\/a>, for example. But triangles was always a likely path for us to take. <\/p>\n<p>Why triangles and not rectangles? Because triangles are mathematically more fundamental than rectangles. You can make rectangles<span class='snote' title='2'>Or any other 2D polygon, for that matter.<\/span> from triangles, but you can&#8217;t make triangles from rectangles. Computers hate ambiguity, and there&#8217;s a certain ambiguity to rendering with rectangles. Like your geometry teacher was busy telling you while you were drawing Power Rangers in your notebook, &#8220;3 points form a plane.&#8221; More informally, a 3-legged stool is inherently stable but a 4-legged stool might wobble. That wobble introduces a certain ambiguity. If you try to draw a rectangle and all 4 points aren&#8217;t on the same plane, that wobble needs to be resolved one way or another before it can begin drawing. And it turns out that the solution to that problem involves breaking the rectangle&#8230; into triangles.<\/p>\n<p>The point is: It&#8217;s triangles all the way down.<\/p>\n<p>3D rendering today consists of taking a bunch of 3D data and mushing it down to the 2D plane of your screen. From the Minecraft image above, you can see a cube is made from rectangles and rectangles are made from triangles. So while your brain uses its magical perspective detection to see the 3D world, to the computer it&#8217;s just a big pile of triangles sitting next to each other, with no more meaning than this lone triangle:<\/p>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/opengl_triangles2.jpg' class='insetimage' width='800' alt='For some reason nobody is interested in my design for a graphics card that&#8217;s based on rendering with tetradecagons.' title='For some reason nobody is interested in my design for a graphics card that&#8217;s based on rendering with tetradecagons.'\/><\/td><\/tr><tr><td class='insetcaption'>For some reason nobody is interested in my design for a graphics card that&#8217;s based on rendering with tetradecagons.<\/td><\/tr><\/table><\/p>\n<p>So whenever we draw polygons, we need to specify specify points in groups of threes. If you&#8217;re a hardcore trigonometry badass<span class='snote' title='3'>For the purposes of this discussion, this is not an oxymoron.<\/span> then I guess you can do it all yourself. Just draw nothing but 2D triangles. But over the years we&#8217;ve invented a bunch of techniques to do this for you, and the graphics hardware has been specially designed to do that sort of work really efficiently. <\/p>\n<p>So you give OpenGL 3 points. Assuming those points wind up on the screen<span class='snote' title='4'>And not off to one side or behind the camera.<\/span> then we get a triangle. Once the triangle is calculated, the graphics hardware fills in the space with pixels. This is called <a href=\"http:\/\/en.wikipedia.org\/wiki\/Rasterisation\">rasterization<\/a>.<\/p>\n<p>We don&#8217;t generally want to fill those pixels in with a solid color. I mean, you <em>can<\/em>, but you wind up with something like this:<\/p>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/bughunt10.jpg' class='insetimage' width='800' alt='I don&#8217;t want to come off like some kind of graphics snob, but this probably isn&#8217;t good enough to ship.' title='I don&#8217;t want to come off like some kind of graphics snob, but this probably isn&#8217;t good enough to ship.'\/><\/td><\/tr><tr><td class='insetcaption'>I don&#8217;t want to come off like some kind of graphics snob, but this probably isn&#8217;t good enough to ship.<\/td><\/tr><\/table><\/p>\n<p>So while you&#8217;re defining the positions of your vertices, you can also give each one a color.<\/p>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/opengl_triangles3.jpg' class='insetimage' width='800' alt='Here is how to meet the public&#8217;s insatiable demand for red\/green\/blue triangles. You&#8217;re welcome.' title='Here is how to meet the public&#8217;s insatiable demand for red\/green\/blue triangles. You&#8217;re welcome.'\/><\/td><\/tr><tr><td class='insetcaption'>Here is how to meet the public&#8217;s insatiable demand for red\/green\/blue triangles. You&#8217;re welcome.<\/td><\/tr><\/table><\/p>\n<p>That&#8217;s nice. Gradient colors are much better than flat colors. It&#8217;s enough to give you something that looks like <a href=\"https:\/\/www.youtube.com\/watch?v=QmjAg0e_YYU\">Race the Sun<\/a>. But if you&#8217;re not going for a minimalist style like that then you probably want to use a texture map. <\/p>\n<p>I imagine most people understand what a texture map is, even if they don&#8217;t get how it works. But for the sake of completeness: A texture map is when you take an image and use it to color your triangles like so:<\/p>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/opengl_triangles4.jpg' class='insetimage' width='800' alt='I wish I&#8217;d drawn this diagram a bit differently. Imagine the Mona Lisa not once, but as an infinite plane of that face repeating over and over like endless tiling wallpaper. Now picture putting the A, B, and C points anywhere you like on that plane. This will, of course, form a triangle. The image within that triangle will be mapped to the shape of the 3D triangle we&#8217;re drawing on screen. (Even if they&#8217;re wildly different proportions.)' title='I wish I&#8217;d drawn this diagram a bit differently. Imagine the Mona Lisa not once, but as an infinite plane of that face repeating over and over like endless tiling wallpaper. Now picture putting the A, B, and C points anywhere you like on that plane. This will, of course, form a triangle. The image within that triangle will be mapped to the shape of the 3D triangle we&#8217;re drawing on screen. (Even if they&#8217;re wildly different proportions.)'\/><\/td><\/tr><tr><td class='insetcaption'>I wish I&#8217;d drawn this diagram a bit differently. Imagine the Mona Lisa not once, but as an infinite plane of that face repeating over and over like endless tiling wallpaper. Now picture putting the A, B, and C points anywhere you like on that plane. This will, of course, form a triangle. The image within that triangle will be mapped to the shape of the 3D triangle we&#8217;re drawing on screen. (Even if they&#8217;re wildly different proportions.)<\/td><\/tr><\/table><\/p>\n<p>The math to do this is actually pretty straightforward, thanks to the use of triangles. <\/p>\n<p>It was common to combine the vertex coloring with texture mapping. Make some corners of the triangle light and some dark, and it will fade between the two. Thus you get &#8220;lighting&#8221; on your textured walls. <\/p>\n<p>Using nothing but these tools you could easily<span class='snote' title='5'>By &#8220;easily&#8221; I mean: Assuming you&#8217;ve got the budget, a skilled team, and you&#8217;re one of the few people at the time who understood all this stuff. And assuming you didn&#8217;t have anything else to do, I guess.<\/span> make a AAA game in the 1990&#8217;s. I&#8217;m pretty sure this is everything you need to make Quake work. (Although Quake did shadows by storing the shadows as texture maps. So it would draw the whole world at full brightness, and then draw over the same polygons again with the shadows, thus making the appropriate areas dark. That&#8217;s why the shadows often looked kind of rough and jagged. The shadow textures were very low resolution.)<\/p>\n<p>To be clear: The original Quake was actually a bit too early to ship with OpenGL support, and it wasn&#8217;t hardware accelerated at first. It&#8217;s complicated. But ignoring the strange way OpenGL was added to the game later, it serves as a really interesting snapshot of the technology of the day.<\/p>\n<p><table width='800' class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/opengl_quake1.jpg' class='insetimage' width='800' alt='The big blobby shadows in that corner are probably a couple of black pixels stretched over a couple of meters worth of wall. This image was taken using the much newer GL Quake. Modern graphics cards work pretty hard to smooth these edges out as much as possible. I remember the effect being a lot more obvious and ugly back in the day.' title='The big blobby shadows in that corner are probably a couple of black pixels stretched over a couple of meters worth of wall. This image was taken using the much newer GL Quake. Modern graphics cards work pretty hard to smooth these edges out as much as possible. I remember the effect being a lot more obvious and ugly back in the day.'\/><\/td><\/tr><tr><td class='insetcaption'>The big blobby shadows in that corner are probably a couple of black pixels stretched over a couple of meters worth of wall. This image was taken using the much newer GL Quake. Modern graphics cards work pretty hard to smooth these edges out as much as possible. I remember the effect being a lot more obvious and ugly back in the day.<\/td><\/tr><\/table><\/p>\n<p>And that&#8217;s it. That&#8217;s 90% of everything you need to know about how &#8220;classic&#8221; OpenGL worked. <\/p>\n<p>The code to do this was pretty simple:<\/p>\n<pre lang=\"c\" line=\"1\">\r\n\/\/  This will make a triangle shaped like so:\r\n\/\/  3---2\r\n\/\/  |  \/\r\n\/\/  | \/\r\n\/\/  |\/\r\n\/\/  1\r\nglBegin (GL_TRIANGLES);\r\nglVertex3f (0.0, 0.0, 0.0);\r\nglVertex3f (1.0, 1.0, 0.0);\r\nglVertex3f (0.0, 1.0, 0.0);\r\nglEnd ();\r\n<\/pre>\n<p>That code makes three vertices. Which makes one triangle. All I did was define three positions. Let&#8217;s add some color data:<\/p>\n<pre lang=\"c\" line=\"1\">\r\nglBegin (GL_TRIANGLES);\r\nglColor3f (1, 0, 0); \/\/red\r\nglVertex3f (0.0, 0.0, 0.0);\r\nglColor3f (0, 1, 0); \/\/green\r\nglVertex3f (1.0, 1.0, 0.0);\r\nglVertex3f (0.0, 1.0, 0.0);\r\nglEnd ();\r\n<\/pre>\n<p>In line two I tell OpenGL that I&#8217;m setting the color to red. Then in line 3 I give it a vertex. When it&#8217;s finally rendered, that vertex will be red. In line 4 I change the color to green. You&#8217;ll notice I didn&#8217;t set a color for the third vertex. When you set a color, it applies to every vertex from that point on, until you change it to something else.<\/p>\n<p>This probably looks like a very &#8220;raw&#8221; way of making graphics. Obviously we wouldn&#8217;t want to try and construct Lara Croft&#8217;s face like this, by manually defining thousands and thousands of triangle positions in code. <\/p>\n<p>Next time we&#8217;ll talk about how we got from this raw triangle access in the 90&#8217;s to the way we do things now.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In case you missed the first entry: We&#8217;re here to talk about how OpenGL has changed and why that&#8217;s importantTo me, anyway.. The odd colorful screenshots are from one of my many half-baked OpenGL-based engine prototypes, presented here simply to break up the monotony of the words. Before we can talk about where OpenGL went, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[],"class_list":["post-26282","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/26282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=26282"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/26282\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=26282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=26282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=26282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}