{"id":12038,"date":"2011-06-16T05:14:11","date_gmt":"2011-06-16T10:14:11","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12038"},"modified":"2011-06-16T05:35:15","modified_gmt":"2011-06-16T10:35:15","slug":"project-frontier-6-growing-grass","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12038","title":{"rendered":"Project Frontier #6: Growing Grass"},"content":{"rendered":"<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_8.jpg' class='insetimage'   alt='frontier6_8.jpg' title='frontier6_8.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>I think we&#8217;re done mucking with the terrain for now.  There will be optimization to be done later, but for now it&#8217;s stable, fast and it accomplishes most of what I need it to do.  We&#8217;ll revisit it later when I add more geographical features, but for now it&#8217;s time to move on to putting stuff ON the terrain.<\/p>\n<p>Actually, no.  Let&#8217;s do something quick.  The sky is really bugging me.  Right now I&#8217;m just using a solid color to match the fog, so that things in the distance will fade into the sky.  That&#8217;s nice, but we can do a lot better with very little effort.<\/p>\n<p>Surprisingly enough, step one is to create a&#8230; cone?<\/p>\n<p><!--more--><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_9.jpg' class='insetimage'   alt='frontier6_9.jpg' title='frontier6_9.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>We color bottom rim the same as the fog color.  The tip is colored to be the same color at half brightness. This isn&#8217;t a texture map or anything.  Just a plain colored polygon.  Your graphics card is actually personally insulted when you render these.  It&#8217;s like asking Superman to bend a bar of Play-doh.  <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_10.jpg' class='insetimage'   alt='frontier6_10.jpg' title='frontier6_10.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>We render this so that the player&#8217;s head is always inside of the cone, with their eye even with the bottom rim.  The advantage is that this is fast and cheap.  The other advantage is that we have tricked the unwitting player into wearing a dunce cap.  If we render it so that everything else ends up being drawn in front of the cone, we end up with:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_11.jpg' class='insetimage'   alt='frontier6_11.jpg' title='frontier6_11.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>NOW we can put stuff on the terrain.  Let&#8217;s start with grass.<\/p>\n<p>The most common technique for making grass is to take two rectangles and intersect them at a right angle.<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_12.jpg' class='insetimage'   alt='frontier6_12.jpg' title='frontier6_12.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Then you just slap a picture of grass on them and call it a day. Actually, I guess this was the most common way of doing this ten years ago. Today, people use more polygons, and alpha-blending prevents them from intersecting to panels like this. But this project is all about building a rocket ship out of stone knives and bearskins, so that&#8217;s how we&#8217;re doing this.<\/p>\n<div class=\"dmnotes\">True story: The tufts of grass in Oblivion have <em>twenty eight polygons<\/em>.  For comparison, my little-two panel model is just four.  (Each rectangle is two triangles for the purposes of rendering.) <a href=\"?p=491\">Back when Oblivion was new<\/a>, I couldn&#8217;t turn on grass at ALL, because it would bring my poor little machine down into single-digit framerates. LOW single digits. <\/p>\n<p>Someone came out with a mod that was nothing more than an alternate grass model.  It replaced the twenty-eight polygon monster with a simple pair of panels like I have above.  The result?  The game ran about ten times faster. Visually, you could barely tell the difference. When you stood at a certain angle and saw a lump of grass in isolation (perhaps on the edge of a grassy area) you could tell the difference. A bit. And even that visual gap could have been fixed by simply moving from four polygons to eight. (So that from above, the panels form an asterisk shape.) Twenty-eight polygons is <em>insane<\/em>, and even on today&#8217;s hardware using the Crysis 2 engine,  I would still slap the artist that made a twenty-eight polygon tuft of grass.  There are just so many ways we can better use that power.<\/div>\n<p>So, I take a grass texture and slap it on my panels.  The result?<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_13.jpg' class='insetimage'   alt='frontier6_13.jpg' title='frontier6_13.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Looks okay. Now, I came to this spot for screenshots for a good reason.  I like the really stark difference between the bits of ground here.  My plan is to just render these tufts of grass to match the texture underneath them.  Let&#8217;s add this coloring, and add a few more bits of grass.<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_14.jpg' class='insetimage'   alt='frontier6_14.jpg' title='frontier6_14.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Hey! Why is the grass still greyscale? See, the grass texture is white, and my plan was to color the polygons to make it match the terrain.  I could use a texture with green grass on it, but then I&#8217;d be stuck with a single shade of green.  I have thousands of subtle shading changes all over the terrain to make it look diverse and vibrant, but that&#8217;s all for nothing if I&#8217;m going to carpet over it with a fixed shade of green! It defeats the entire purpose of what I&#8217;m trying to accomplish here. So why is the grass white, even I&#8217;m coloring the polygons different shades of green?<\/p>\n<p>Oh wait. Now I remember.<\/p>\n<p>Head. Hit. Keyboard.<\/p>\n<p>I&#8217;m using what&#8217;s called the &#8220;fixed function&#8221; rendering pipeline.  This is pretty much the default behavior for OpenGL.  See, how it works is there are two ways of coloring polygons.  One is using the color command.  This is like what I used on the sky.  You tell OpenGL what color you want the vertex, and it will do a nice neat fade from one vertex to the next. The other technique is to use the lighting system.  If you use that, then if you want a polygon to be green you need to aim a green light at it.  These two systems are mutually exclusive.  The sky works because I turn lighting off when I draw it.  (It wouldn&#8217;t make sense to have the sunlight shine ON the sky.  It would not look like sunshine.  It would just put a big vertical stripe up the sky.)<\/p>\n<p>If I want a lighting system that incorporates polygon coloring, I&#8217;ll have to make my own.  I can either do that with vertex shaders or by turning off all OpenGL lighting and doing all of the lighting calculations myself.  Both of these steps are complex and time-consuming, and might require changes to the low-level bits of my engine.  (Including the terrain system, which I just resolved to leave alone for a bit.) Can I use vertex shaders when I&#8217;m using OpenGL through SDL? Wait, now I have to read up on what functionality is available with SDL.  Oh, there&#8217;s a beta version of SDL that might offer&#8230;<\/p>\n<p>No. <em>No no no no.<\/em>  I will take this step eventually, but this is NOT the right time for it.  I started this project because I want to experiment with my ideas for procedural content, not so I can muck out implementing my own lighting model.  That will be a time-consuming distraction, and will lock me in to certain decisions that I&#8217;m just not ready to make yet. <\/p>\n<p>So we need a way to fake it. Let me think&#8230;<\/p>\n<p>(The next day&#8230;.)<\/p>\n<p>Okay, I think I&#8217;ve got it.  First, I render the grass, as before.  I have lighting enabled, so the grass is white.<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_14.jpg' class='insetimage'   alt='frontier6_14.jpg' title='frontier6_14.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Now, I turn off lighting and render all of the grass polygons <em>again<\/em>, but this time using the colors I want. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_15.jpg' class='insetimage'   alt='frontier6_15.jpg' title='frontier6_15.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Now, I can change a setting so that when it renders these polygons, it will ONLY render them where there is already a pixel at the exact same depth, and nowhere else. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_16.jpg' class='insetimage'   alt='frontier6_16.jpg' title='frontier6_16.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>I can also set it to blend the results of the second polygon, instead of just overwriting them.  If you&#8217;ve ever used Photoshop, this is almost identical to using the &#8220;multiply&#8221; feature on a layer. It changes the color of the stuff underneath.<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_17.jpg' class='insetimage'   alt='frontier6_17.jpg' title='frontier6_17.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Almost there.  But it&#8217;s too bright.  I fiddle with the filter a bit until I get the results I want.  <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_18.jpg' class='insetimage'   alt='frontier6_18.jpg' title='frontier6_18.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>It&#8217;s good. Up until now I&#8217;ve had the grass only once every five meters, for testing.  Let&#8217;s increase it to full density and see how it looks:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_19.jpg' class='insetimage'   alt='frontier6_19.jpg' title='frontier6_19.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Good enough.  There are drawbacks to my approach, though.  It won&#8217;t look right with fog.  It can&#8217;t.  If I have fog touching the grass, the affected grass will seem to glow.  If I use dark fog, the grass will turn very dark.  Basically, if the fog gets close enough to interact with the grass, the grass will no longer match the terrain and the two will look wrong together.  This is a small problem, and I&#8217;m willing to tolerate it until I decide on a lighting model.<\/p>\n<p>You know&#8230; this has me thinking.  The grass tufts are two intersecting panels.  What if I took those four points on top, and connected them?<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_20.jpg' class='insetimage'   alt='frontier6_20.jpg' title='frontier6_20.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Yeah, like that.  And now if I add another region to the texture and change the coloring of the top&#8230;<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_21.jpg' class='insetimage'   alt='frontier6_21.jpg' title='frontier6_21.jpg'\/><\/td><\/tr><\/table><\/p>\n<p><strong>Yeah.<\/strong><\/p>\n<p>Well, we&#8217;re now at the end of the features I&#8217;d already mapped out before I began. But from here on I&#8217;ll be making it up as I go. Which should be interesting.<\/p>\n<p>Wait.  One more:<\/p>\n<p><table width='600'  cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><a href='images\/frontier6_22_big.jpg'><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier6_22.jpg' class='insetimage' width='600' alt='Click for larger view.' title='Click for larger view.'\/><\/a><\/td><\/tr><tr><td class='insetcaption'>Click for larger view.<\/td><\/tr><\/table><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I think we&#8217;re done mucking with the terrain for now. There will be optimization to be done later, but for now it&#8217;s stable, fast and it accomplishes most of what I need it to do. We&#8217;ll revisit it later when I add more geographical features, but for now it&#8217;s time to move on to putting [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[],"class_list":["post-12038","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\/12038","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=12038"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/12038\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=12038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=12038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=12038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}