{"id":26,"date":"2011-12-15T22:11:50","date_gmt":"2011-12-15T14:11:50","guid":{"rendered":"https:\/\/teckartist.com\/?p=26"},"modified":"2011-12-15T22:27:10","modified_gmt":"2011-12-15T14:27:10","slug":"shader-fun-trading-accuracy-for-variety","status":"publish","type":"post","link":"https:\/\/teckartist.com\/?p=26","title":{"rendered":"Shader Fun: Trading Accuracy for Variety"},"content":{"rendered":"<p dir=\"ltr\">A little while ago, I was doing a bit of r&amp;d for a potential alternative to one of our shader solutions. This particular approach never made it past gestation, though, since it would have required a fair amount of texture reauthoring and rejiggering to be viable. Long story short, I was looking at ways to maximise available variation in a single 8-bit channel. Bang for buck, if you will.<\/p>\n<p dir=\"ltr\">Now, I&#8217;d recalled <a title=\"Shading a Bigger, Better Sequel Techniques in Left 4 Dead 2\" href=\"http:\/\/www.valvesoftware.com\/publications\/2010\/GDC10_ShaderTechniquesL4D2.pdf\">Bronwen Grimes&#8217; presentation on shader techniques in Left 4 Dead 2<\/a>, and in particular that they had split their zombie color variation masks by value range. In a nutshell, they would split the masks in half (page 26), and then remapped those via a split gradient map, which ultimately gave them four value ranges to work with (page 38). I didn&#8217;t really see the split gradient map working for my purposes, and I also felt there should be a cleaner (and more versatile) method of splitting the value range.<\/p>\n<p dir=\"ltr\">After a bit of experimentation, and cracking my skull over a bit of math, I ended up with the following, surprisingly straightforward, function:<\/p>\n<pre dir=\"ltr\">int bands = 4; \/\/this is the number of value bands to split the range\r\nfloat range = 1.0 \/ bands; \/\/range of each band\r\n\r\nfloat ReRamp(float val, int band)\r\n\/\/val is the pixel's luminance, band is the... band [index]\r\n{\r\n  float minRange = range * band;\r\n  float maxRange = range * (band + 1);\r\n  float c = min(saturate(val - minRange) \/ (maxRange - minRange), 1);\r\n  c = c &gt;= 1?0:c;\r\n  return c;\r\n}<\/pre>\n<p>This effectively allows you to split your value range into as many discrete bands as you please (however keeping in mind the need for a buffer between ranges to account for compression; page 39).<\/p>\n<p>The returned value has been reramped from its original value range (eg. 0.25, for 4 bands) to the full 0-1 range, allowing you to do whatever you please with it, be that as a color mask, uv distortion, etc.<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/teckartist.com\/wp-content\/uploads\/2011\/12\/reramp.gif\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-33 aligncenter\" title=\"reramp\" src=\"https:\/\/teckartist.com\/wp-content\/uploads\/2011\/12\/reramp.gif\" alt=\"\" width=\"256\" height=\"300\" \/><\/a><\/p>\n<p>You don&#8217;t even need to split a texture up to benefit from this approach, either. Assuming you have, say, 4 different UV warping algorithms modulated by this channel, you could have 4 different textures (for 4 different assets), just being sure to keep each one in the required value range.<\/p>\n<p>Now there are obviously drawbacks to this approach. As mentioned in the presentation, the various masked areas can&#8217;t overlap. Additionally, you&#8217;re sacrificing value range for file size (texture\/channel count). The more bands you want, the coarser the value ramp.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A little while ago, I was doing a bit of r&amp;d for a potential alternative to one of our shader solutions. This particular approach never made it past gestation, though, since it would have required a fair amount of texture reauthoring and rejiggering to be viable. Long story short, I was looking at ways to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,19,9],"tags":[],"class_list":["post-26","post","type-post","status-publish","format-standard","hentry","category-gamedev","category-shaders","category-techart"],"_links":{"self":[{"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/posts\/26","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/teckartist.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=26"}],"version-history":[{"count":10,"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/posts\/26\/revisions"}],"predecessor-version":[{"id":40,"href":"https:\/\/teckartist.com\/index.php?rest_route=\/wp\/v2\/posts\/26\/revisions\/40"}],"wp:attachment":[{"href":"https:\/\/teckartist.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=26"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/teckartist.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=26"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/teckartist.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=26"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}