<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ranmantaru Games &#187; code</title>
	<atom:link href="http://ranmantaru.com/blog/tag/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://ranmantaru.com</link>
	<description></description>
	<lastBuildDate>Sun, 24 Apr 2016 12:50:43 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Soft object shadows with deferred rendering</title>
		<link>http://ranmantaru.com/blog/2012/04/17/soft-object-shadows-with-deferred-rendering/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=soft-object-shadows-with-deferred-rendering</link>
		<comments>http://ranmantaru.com/blog/2012/04/17/soft-object-shadows-with-deferred-rendering/#comments</comments>
		<pubDate>Tue, 17 Apr 2012 16:25:26 +0000</pubDate>
		<dc:creator>e-dog</dc:creator>
				<category><![CDATA[Arcane Worlds]]></category>
		<category><![CDATA[article]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[technobabble]]></category>

		<guid isPermaLink="false">http://ranmantaru.com/?p=236</guid>
		<description><![CDATA[Technobabble warning. In this article I assume you&#8217;re fairly familiar with deferred rendering/shading. If not, google it. There are numerous articles and presentations about what it is how it&#8217;s done in various games. That&#8217;s a lot more technobabble though. Deferred rendering in Arcane Worlds I use three ARGB8 textures for the G-buffer, so it&#8217;s 96 [...]]]></description>
			<content:encoded><![CDATA[<p>Technobabble warning. In this article I assume you&#8217;re fairly familiar with deferred rendering/shading. If not, google it. There are numerous articles and presentations about what it is how it&#8217;s done in various games. That&#8217;s a lot more technobabble though.</p>
<p><center>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2012_04_15_02_25_27.png" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/44__512x288_shot2012_04_15_02_25_27.png" alt="Land and object shadows" title="Land and object shadows" />
</a>
</center></p>
<p><span id="more-236"></span></p>
<h2>Deferred rendering in Arcane Worlds</h2>
<p>I use three ARGB8 textures for the G-buffer, so it&#8217;s 96 bits per pixel. There&#8217;s also a shadow buffer and light buffer textures. All of them are reused later in the anti-aliasing post-effect, so deferred rendering has the same video memory requirements as before.</p>
<p>I use stencil when rendering geometry, so I can draw sky <b>after</b> the scene to save fillrate.</p>
<p>In the G-buffer I store:</p>
<ul>
<li>24-bit depth, packed into 3 channels.</li>
<li>World-space normal, simply stored in 3 channels.</li>
<li>Water factor (8 bit). Currently it&#8217;s either 0 or 1, but it&#8217;ll be used later to blend water based on its depth.</li>
<li>Gloss factor (8 bit). Currently only used for water foam, but it&#8217;ll be used later for glossy parts of objects and buildings.</li>
<li>Surface color (24-bit RGB). That&#8217;s surface albedo, not used for water.</li>
<li>Glow factor (8 bit). Not used for now, it&#8217;s reserved for lava and other glowing things (like monster eyes).</li>
</ul>
<p>So, world space pixel position can be reconstructed from depth, and we got normal and some other stuff.</p>
<h2>Soft shadows</h2>
<p>The huge sun in Arcane Worlds should cast very soft shadows that fade out quite fast with distance from the caster. This means the shadow density along the light ray is <b>not</b> monotonic, which is a problem with shadow maps. Also, I wanted sky shadows (ambient occlusion actually) because it becomes very important at night without direct sun/moon light.</p>
<p>So, after experimenting a bit with shadow maps, I decided to do screen-space shadows using deferred rendering.</p>
<p>With deferred rendering, you can apply any number of lights in screen space, as a post-processing effect. With world position and normal in each pixel, and knowing light properties (position, color etc) it&#8217;s easy to compute lighting and accumulate it in the light buffer. But the same can be done to compute shadows.</p>
<p>Knowing caster and light properties, one can compute the amount of shadowing for that particular light in the particular pixel, and accumulate it in the shadow buffer. The simplest object is sphere, it&#8217;s defined by four numbers: 3 for position and 1 for radius. Fits nicely in a single float4 shader register.</p>
<p>This means I need to add special &#8220;shadow-casting spheres&#8221; to each object I model, but I&#8217;m fine with that since I was ready to make special shadow-casting meshes for thin objects anyway. And on the plus side, far LODs can use less number of bigger spheres or no spheres at all. Filling object shape with spheres is an approximation of course, but with very blurry shadows it works fine even with a few spheres per object. The stones in the 0.04 have 3 spheres per object.</p>
<p>Computing sky shadow aka ambient occlusion from sphere caster is simple and intuitive, while being <a href="http://iquilezles.org/www/articles/sphereao/sphereao.htm" title="sphere ambient occlusion" target="_blank">mathematically accurate</a>.</p>
<p>Sun shadow is a bit more complex, but can be done with a variant of aperture lighting (see <a href="http://ranmantaru.com/blog/2012/02/25/lighting-in-arcane-worlds-technobabble/">previous technobabble article</a>). Looking from the surface point (the pixel being shadowed) you see two circles: the sun and the sphere caster. The amount of intersection of these circles defines the amount of shadowing. It can be computed approximately of course, since shadows are very blurry anyway.</p>
<p>The accumulated shadow buffer texture is used then in the full-screen lighting pass to modulate sky and sun light respectively.</p>
<h2>Implementation details</h2>
<p>I store shadows as light scaling factors, so 1 is no shadow at all and 0 is full shadow (no light). Sun and sky (AO) shadows are stored in separate 8-bit channels, and I have 2 more channels free for 2 more lights. That&#8217;s sky, two suns/moons and one lightning strike (or other effect light) total, all with shadows. Should be enough for this game.</p>
<p>The shadow accumulation is done just by multiplying those factors, both in the shader and as render target blending.</p>
<p>I started the implementation with a simple full-screen pass for each shadow sphere. When I got it working, I started optimizing the application of the shader.</p>
<p>The screen is divided into cells (16&#215;9 currently, but that&#8217;s easy to change) and each cell has a list of data to pass to the shader (float4 for each shadow sphere). When a shadow sphere is &#8220;rendered&#8221; its affected volume is projected to the screen and its data is added to all cells that the projected area covers. Then the data accumulated in cells is passed to the shader.</p>
<p>You can pass up to 10 float4 registers via interpolants in shader model 3, but I chose to pass 8 at most to simplify the implementation. In fact, I pass either 8, 4, 2 or 1 registers at once. First, I scan all cells and get data from those which have 8 or more entries in the list, storing the data and the cell coordinates in the dynamic vertex buffer. So after this pass, every cell has 7 or less entries in the list and I scan them again to pass data in packs of 4. Then it&#8217;s packs of 2 and finally 1 for odd number of entries in the list.</p>
<p>The technique is known and is usually used in deferred rendering to apply many lights which affect small screen area each. I&#8217;ll reuse it later to apply lights too, for spells, burning trees and the like.</p>
<p>To limit affected screen area, I have to limit the shadow range. For sky shadow, 4 times the radius is fine without any extra tricks, but the sun shadows are longer. I found that 8 times the radius range is acceptable if the end of the shadow is faded smoothly. I use a sphere to approximate affected volume, with center shifted away from the light by 3 times the radius of the shadow sphere, and its radius set to 5 times the shadow sphere radius. That way, I capture 8x radius shadow range away from the light and 2x radius around the caster for noticeable (in daylight) sky shadow.</p>
<h2>Code</h2>
<p>Here comes the shader code:</p>
<p><code><br />
#include&nbsp;"common.sh"</p>
<p>#define&nbsp;MAX_RK&nbsp;8<br />
#define&nbsp;FADE_RANGE&nbsp;4</p>
<p>#define&nbsp;MAX_RK2&nbsp;(MAX_RK*MAX_RK)<br />
#define&nbsp;FADE_RANGE2&nbsp;(FADE_RANGE*FADE_RANGE)</p>
<p>struct&nbsp;VsInput<br />
{<br />
&nbsp;&nbsp;float2&nbsp;dp&nbsp;&nbsp;:&nbsp;POSITION0;<br />
&nbsp;&nbsp;float2&nbsp;org&nbsp;:&nbsp;POSITION1;<br />
&nbsp;&nbsp;float4&nbsp;data[NUMVEC]&nbsp;:&nbsp;TEXCOORD0;<br />
};</p>
<p>struct&nbsp;VsOutput<br />
{<br />
&nbsp;&nbsp;float4&nbsp;pos&nbsp;&nbsp;:&nbsp;POSITION;<br />
&nbsp;&nbsp;float4&nbsp;data[NUMVEC]&nbsp;:&nbsp;TEXCOORD0;<br />
&nbsp;&nbsp;float2&nbsp;tc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;TEXCOORD9;<br />
&nbsp;&nbsp;float3&nbsp;wvec&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;TEXCOORD10;<br />
};</p>
<p>float4&nbsp;postm&nbsp;:&nbsp;register(c15);<br />
float4&nbsp;texsz&nbsp;:&nbsp;register(c16);<br />
float4&nbsp;viewx&nbsp;:&nbsp;register(c17);<br />
float4&nbsp;viewy&nbsp;:&nbsp;register(c18);<br />
float4&nbsp;viewz&nbsp;:&nbsp;register(c19);</p>
<p>VsOutput&nbsp;vsh(VsInput&nbsp;I)<br />
{<br />
&nbsp;&nbsp;VsOutput&nbsp;O;<br />
&nbsp;&nbsp;float2&nbsp;pos=(I.org+I.dp)*postm.xy+postm.zw;<br />
&nbsp;&nbsp;O.pos=float4(pos,&nbsp;1,&nbsp;1);<br />
&nbsp;&nbsp;O.tc=pos.xy*texsz.xy+texsz.zw;<br />
&nbsp;&nbsp;O.wvec=pos.x*viewx.xyz+pos.y*viewy.xyz+viewz.xyz;<br />
&nbsp;&nbsp;O.data=I.data;<br />
&nbsp;&nbsp;return&nbsp;O;<br />
}</p>
<p>float3&nbsp;viewPos&nbsp;:&nbsp;register(c16);</p>
<p>float4&nbsp;psh(VsOutput&nbsp;I)&nbsp;:&nbsp;COLOR<br />
{<br />
&nbsp;&nbsp;UNPACK_GBUF(I.tc.xy)</p>
<p>&nbsp;&nbsp;float3&nbsp;eyeVec=I.wvec*depth;<br />
&nbsp;&nbsp;float3&nbsp;pos=eyeVec+viewPos;</p>
<p>&nbsp;&nbsp;float2&nbsp;shadow=1;</p>
<p>&nbsp;&nbsp;[unroll]<br />
&nbsp;&nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&lt;NUMVEC;&nbsp;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;float4&nbsp;ball=I.data[i];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float3&nbsp;bpos=ball.xyz;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;r=ball.w;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;r2=r*r;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float3&nbsp;dp=bpos-pos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;d2=dot(dp,&nbsp;dp);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float3&nbsp;dpn=dp*rsqrt(d2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;dn=saturate(dot(dpn,&nbsp;norm));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;sky&nbsp;/&nbsp;ambient<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;sin2=r2/d2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;sky=sin2*dn;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;sun<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;c=dot(dpn,&nbsp;sun_dir.xyz);<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;ca=sqrt(saturate(1-sin2)),&nbsp;cb=sun_dir.w;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;cc=ca*cb,&nbsp;ss=sqrt((1-ca*ca)*(1-cb*cb));<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;maxc=cc+ss;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;sun=smoothstep(cc-ss,&nbsp;maxc,&nbsp;c)*(1-ca)/(1-cb);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;fade&nbsp;with&nbsp;distance<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;fk=saturate((r2*MAX_RK2-d2)/(r2*FADE_RANGE2));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;shadow*=saturate(1-float2(sky,&nbsp;sun)*fk);<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;return&nbsp;float4(shadow,&nbsp;0,&nbsp;0);<br />
}<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://ranmantaru.com/blog/2012/04/17/soft-object-shadows-with-deferred-rendering/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Water erosion on heightmap terrain</title>
		<link>http://ranmantaru.com/blog/2011/10/08/water-erosion-on-heightmap-terrain/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=water-erosion-on-heightmap-terrain</link>
		<comments>http://ranmantaru.com/blog/2011/10/08/water-erosion-on-heightmap-terrain/#comments</comments>
		<pubDate>Sat, 08 Oct 2011 16:01:39 +0000</pubDate>
		<dc:creator>e-dog</dc:creator>
				<category><![CDATA[Arcane Worlds]]></category>
		<category><![CDATA[article]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[screenshots]]></category>

		<guid isPermaLink="false">http://ranmantaru.com/?p=77</guid>
		<description><![CDATA[I&#8217;m back from vacation, and I decided to write an article as a warm-up exercise. This article is for those who know what a heightmap is, and who are familiar with some basic heightmap generation. If you aren’t one, you can still enjoy the screenshots and skip most of the technobabble here Examples Here is [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m back from vacation, and I decided to write an article as a warm-up exercise.</p>
<p>This article is for those who know what a heightmap is, and who are familiar with some basic heightmap generation. If you aren’t one, you can still enjoy the screenshots and skip most of the technobabble here <img src='http://ranmantaru.com/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Examples</h2>
<p>Here is some fault-formed terrain:</p>
<table>
<tr>
<td>before erosion</td>
<td>after erosion</td>
</tr>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_06_15_57_03.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/19__256x_shot2011_10_06_15_57_03.jpg" alt="shot2011_10_06_15_57_03" title="shot2011_10_06_15_57_03" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_06_15_57_08.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/20__256x_shot2011_10_06_15_57_08.jpg" alt="shot2011_10_06_15_57_08" title="shot2011_10_06_15_57_08" />
</a>
</td>
</tr>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_06_15_56_26.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/17__256x_shot2011_10_06_15_56_26.jpg" alt="shot2011_10_06_15_56_26" title="shot2011_10_06_15_56_26" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_06_15_56_29.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/18__256x_shot2011_10_06_15_56_29.jpg" alt="shot2011_10_06_15_56_29" title="shot2011_10_06_15_56_29" />
</a>
</td>
</tr>
</table>
<p><span id="more-77"></span></p>
<table>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_17_06_26.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/7__256x_shot2011_10_05_17_06_26.jpg" alt="shot2011_10_05_17_06_26" title="shot2011_10_05_17_06_26" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_17_06_32.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/8__256x_shot2011_10_05_17_06_32.jpg" alt="shot2011_10_05_17_06_32" title="shot2011_10_05_17_06_32" />
</a>
</td>
</tr>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_17_11_28.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/9__256x_shot2011_10_05_17_11_28.jpg" alt="shot2011_10_05_17_11_28" title="shot2011_10_05_17_11_28" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_17_11_33.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/10__256x_shot2011_10_05_17_11_33.jpg" alt="shot2011_10_05_17_11_33" title="shot2011_10_05_17_11_33" />
</a>
</td>
</tr>
</table>
<p>And here is what my erosion method can do to a simple classic fractal-noise-generated terrain:</p>
<table>
<tr>
<td>before erosion</td>
<td>after erosion</td>
</tr>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_18_44_58.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/11__256x_shot2011_10_05_18_44_58.jpg" alt="shot2011_10_05_18_44_58" title="shot2011_10_05_18_44_58" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_18_45_15.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/12__256x_shot2011_10_05_18_45_15.jpg" alt="shot2011_10_05_18_45_15" title="shot2011_10_05_18_45_15" />
</a>
</td>
</tr>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_18_52_26.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/13__256x_shot2011_10_05_18_52_26.jpg" alt="shot2011_10_05_18_52_26" title="shot2011_10_05_18_52_26" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_18_52_29.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/14__256x_shot2011_10_05_18_52_29.jpg" alt="shot2011_10_05_18_52_29" title="shot2011_10_05_18_52_29" />
</a>
</td>
</tr>
</table>
<p>It also turns artificial forms into more natural looking ones:</p>
<table>
<tr>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_19_03_09.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/15__256x_shot2011_10_05_19_03_09.jpg" alt="shot2011_10_05_19_03_09" title="shot2011_10_05_19_03_09" />
</a>
</td>
<td>
<a href="http://ranmantaru.com/wordpress/wp-content/gallery/shots1/shot2011_10_05_19_03_11.jpg" title=""  >
	<img class="ngg-singlepic" src="http://ranmantaru.com/wordpress/wp-content/gallery/cache/16__256x_shot2011_10_05_19_03_11.jpg" alt="shot2011_10_05_19_03_11" title="shot2011_10_05_19_03_11" />
</a>
</td>
</tr>
</table>
<h2>Applications</h2>
<p>As you see from the examples above, erosion can make generated terrain look much more interesting and natural. It looks even better when you add grass by slope and/or ground type.<br />
It&#8217;s very straightforward to integrate since it modifies existing heightmap. It can be applied several times too, both for increased effect or after other terrain modifiers.<br />
It&#8217;s useful for gameplay as well, since it makes lower areas more flat and thus easier to walk/navigate/build on.</p>
<p>It has some drawbacks of course.<br />
It&#8217;s a full-terrain method, not suitable for “infinite” generated worlds. It works on maps of limited size.<br />
It can be rather slow on large heightmaps. Especially if you want heavy erosion. So it&#8217;s probably better as offline method in your editor if your heightmap is large.</p>
<h2>Understanding water erosion</h2>
<p>The water starts somewhere on the terrain (as rain, water spring, whatever), then flows downhill, dissolving the soil, carrying it, and depositing it as sediment. Eventually it gets to a low point and evaporates.<br />
The trick here is understanding (and carefully implementing) the erosion/deposition process. This involves sediment carry capacity of the flow.<br />
At any time, the water flow can only carry a limited amount of dissolved soil. This amount depends on the surface slope, the speed of the flow and the amount of water.<br />
If less is carried than possible, erosion happens, removing soil from the terrain and adding it to the flow.<br />
If more is carried, deposition happens, dropping extra carried soil as sediment.<br />
As you can see, the process can switch fast between erosion and deposition as the flow accelerates, or drops down a steep slope, or comes to a flat area.<br />
The formula I use:<br />
<code>q=max(slope, minSlope)*v*w*Kq</code><br />
Where <em>slope</em> is just a tangent, <em>minSlope</em> and <em>Kq</em> are constant parameters, <em>v</em> is flow speed and <em>w</em> is amount of water in the flow.</p>
<h2>Droplets</h2>
<p>I use droplet model to simulate water erosion. That is, I pick a starting point on the terrain, start with zero velocity and carried soil, and proceed moving this point, changing its dynamic variables and the terrain in the process, until all its water evaporates or it flows to a pit it can&#8217;t get out of.<br />
Some tricky things here are:</p>
<ul>
<li>I use height gradient as the downhill direction. If the gradient is zero, I pick a random direction. I also add some inertia just for fun.</li>
<li>I then sample next height in the downhill direction. If it happens to be higher than the current one, we&#8217;re in the pit and should either drop sediment to fill it and flow on, or die trying. When the flow goes on after filling the pit, its speed is reset to zero.</li>
<li>When erosion happens, don&#8217;t remove more than the height difference (between current and next position). That is, don&#8217;t dig a pit in the place the water flows from, cut it flat at most. Digging a pit makes the process unstable, creating nasty spikes.</li>
<li>I deposit to heightmap using bilinear weights. This way it fills pits better while being somewhat smooth.</li>
<li>I erode the heightmap with a bell-like “brush”, to make smoother (yet somewhat wider) channels. That&#8217;s to make the final result look better too.</li>
<li>A single droplet effect on the terrain is hardly noticeable (unless you use some crazy parameters). Use at least a thousand droplets for testing. Their power is in numbers.</li>
<li>Erosion and deposition speed is affected by special parameters. So only some (not all) extra soil is dropped when carry capacity is exceeded, for example.</li>
<li>The flow speed is accelerated by height delta on each step, and some water is evaporated as well.</li>
</ul>
<h2>Droplet sources</h2>
<p>I use “rain” erosion in the examples, starting droplets randomly all over the terrain.<br />
However, other effects can be achieved with non-uniform droplet distribution, like creating streams from sources, or raining more/less in certain areas.</p>
<h2>Code sample</h2>
<p>Here is the code &#8220;as is&#8221;. It won&#8217;t compile since it uses some external stuff, but I hope it&#8217;s easy enough to figure it out.<br />
You can do with it whatever you want.</p>
<p><code><br />
void&nbsp;HeightmapData::genDropletErosion(unsigned&nbsp;iterations,&nbsp;ErosionParams&nbsp;&amp;params)<br />
{<br />
&nbsp;&nbsp;float&nbsp;Kq=params.Kq,&nbsp;Kw=params.Kw,&nbsp;Kr=params.Kr,&nbsp;Kd=params.Kd,&nbsp;Ki=params.Ki,<br />
&nbsp;&nbsp;&nbsp;&nbsp;minSlope=params.minSlope,&nbsp;Kg=params.g*2;</p>
<p>&nbsp;&nbsp;TempData&lt;Point2[HMAP_SIZE*HMAP_SIZE]&gt;&nbsp;erosion;</p>
<p>&nbsp;&nbsp;float&nbsp;flt=0;<br />
&nbsp;&nbsp;erosion.fillMem32(*(uint32_t*)&amp;flt);</p>
<p>&nbsp;&nbsp;static&nbsp;const&nbsp;unsigned&nbsp;MAX_PATH_LEN=HMAP_SIZE*4;</p>
<p>&nbsp;&nbsp;int64_t&nbsp;t0=get_ref_time();</p>
<p>&nbsp;&nbsp;#define&nbsp;DEPOSIT_AT(X,&nbsp;Z,&nbsp;W)&nbsp;\<br />
&nbsp;&nbsp;{&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;delta=ds*(W);&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;erosion[HMAP_INDEX((X),&nbsp;(Z))].y+=delta;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;hmap&nbsp;&nbsp;&nbsp;[HMAP_INDEX((X),&nbsp;(Z))]&nbsp;&nbsp;+=delta;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;params.deposit(scolor,&nbsp;surface[HMAP_INDEX((X),&nbsp;(Z))],&nbsp;delta);&nbsp;\<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;#if&nbsp;1<br />
&nbsp;&nbsp;&nbsp;&nbsp;#define&nbsp;DEPOSIT(H)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi&nbsp;&nbsp;,&nbsp;zi&nbsp;&nbsp;,&nbsp;(1-xf)*(1-zf))&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi+1,&nbsp;zi&nbsp;&nbsp;,&nbsp;&nbsp;&nbsp;&nbsp;xf&nbsp;*(1-zf))&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi&nbsp;&nbsp;,&nbsp;zi+1,&nbsp;(1-xf)*&nbsp;&nbsp;&nbsp;zf&nbsp;)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi+1,&nbsp;zi+1,&nbsp;&nbsp;&nbsp;&nbsp;xf&nbsp;*&nbsp;&nbsp;&nbsp;zf&nbsp;)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(H)+=ds;<br />
&nbsp;&nbsp;#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;#define&nbsp;DEPOSIT(H)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi&nbsp;&nbsp;,&nbsp;zi&nbsp;&nbsp;,&nbsp;0.25f)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi+1,&nbsp;zi&nbsp;&nbsp;,&nbsp;0.25f)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi&nbsp;&nbsp;,&nbsp;zi+1,&nbsp;0.25f)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT_AT(xi+1,&nbsp;zi+1,&nbsp;0.25f)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(H)+=ds;<br />
&nbsp;&nbsp;#endif</p>
<p>&nbsp;&nbsp;uint64_t&nbsp;longPaths=0,&nbsp;randomDirs=0,&nbsp;sumLen=0;</p>
<p>&nbsp;&nbsp;for&nbsp;(unsigned&nbsp;iter=0;&nbsp;iter&lt;iterations;&nbsp;++iter)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((iter&amp;0x3FFF)==0&nbsp;&amp;&amp;&nbsp;iter!=0)&nbsp;show_splash("Calculating&nbsp;erosion",&nbsp;(iter+0.5f)/iterations);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;xi=game_rnd()&amp;(HMAP_SIZE-1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;zi=game_rnd()&amp;(HMAP_SIZE-1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;xp=xi,&nbsp;zp=zi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;xf=&nbsp;0,&nbsp;zf=&nbsp;0;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;h=HMAP(xi,&nbsp;zi);<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;s=0,&nbsp;v=0,&nbsp;w=1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;vec4f&nbsp;scolor=zero4f();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;h00=h;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;h10=HMAP(xi+1,&nbsp;zi&nbsp;&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;h01=HMAP(xi&nbsp;&nbsp;,&nbsp;zi+1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;h11=HMAP(xi+1,&nbsp;zi+1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;dx=0,&nbsp;dz=0;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;numMoves=0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(;&nbsp;numMoves&lt;MAX_PATH_LEN;&nbsp;++numMoves)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;calc&nbsp;gradient<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;gx=h00+h01-h10-h11;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;gz=h00+h10-h01-h11;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//==&nbsp;better&nbsp;interpolated&nbsp;gradient?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;calc&nbsp;next&nbsp;pos<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=(dx-gx)*Ki+gx;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dz=(dz-gz)*Ki+gz;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;dl=sqrtf(dx*dx+dz*dz);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(dl&lt;=FLT_EPSILON)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;pick&nbsp;random&nbsp;dir<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;a=frnd()*TWOPI;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=cosf(a);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dz=sinf(a);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++randomDirs;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx/=dl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dz/=dl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nxp=xp+dx;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nzp=zp+dz;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;sample&nbsp;next&nbsp;height<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;nxi=intfloorf(nxp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;nzi=intfloorf(nzp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nxf=nxp-nxi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nzf=nzp-nzi;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nh00=HMAP(nxi&nbsp;&nbsp;,&nbsp;nzi&nbsp;&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nh10=HMAP(nxi+1,&nbsp;nzi&nbsp;&nbsp;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nh01=HMAP(nxi&nbsp;&nbsp;,&nbsp;nzi+1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nh11=HMAP(nxi+1,&nbsp;nzi+1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;nh=(nh00*(1-nxf)+nh10*nxf)*(1-nzf)+(nh01*(1-nxf)+nh11*nxf)*nzf;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;if&nbsp;higher&nbsp;than&nbsp;current,&nbsp;try&nbsp;to&nbsp;deposit&nbsp;sediment&nbsp;up&nbsp;to&nbsp;neighbour&nbsp;height<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(nh&gt;=h)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;ds=(nh-h)+0.001f;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ds&gt;=s)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;deposit&nbsp;all&nbsp;sediment&nbsp;and&nbsp;stop<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds=s;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT(h)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s=0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT(h)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s-=ds;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v=0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;compute&nbsp;transport&nbsp;capacity<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;dh=h-nh;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;slope=dh;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//float&nbsp;slope=dh/sqrtf(dh*dh+1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;q=maxval(slope,&nbsp;minSlope)*v*w*Kq;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;deposit/erode&nbsp;(don't&nbsp;erode&nbsp;more&nbsp;than&nbsp;dh)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;ds=s-q;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ds&gt;=0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;deposit<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds*=Kd;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//ds=minval(ds,&nbsp;1.0f);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEPOSIT(dh)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s-=ds;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;erode<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds*=-Kr;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds=minval(ds,&nbsp;dh*0.99f);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#define&nbsp;ERODE(X,&nbsp;Z,&nbsp;W)&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;delta=ds*(W);&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hmap&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[HMAP_INDEX((X),&nbsp;(Z))]-=delta;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point2&nbsp;&amp;e=erosion[HMAP_INDEX((X),&nbsp;(Z))];&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;r=e.x,&nbsp;d=e.y;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(delta&lt;=d)&nbsp;d-=delta;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;{&nbsp;r+=delta-d;&nbsp;d=0;&nbsp;}&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.x=r;&nbsp;e.y=d;&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scolor=params.erode(scolor,&nbsp;surface[HMAP_INDEX((X),&nbsp;(Z))],&nbsp;s,&nbsp;delta);&nbsp;\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#if&nbsp;1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;z=zi-1;&nbsp;z&lt;=zi+2;&nbsp;++z)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;zo=z-zp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;zo2=zo*zo;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;x=xi-1;&nbsp;x&lt;=xi+2;&nbsp;++x)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;xo=x-xp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;w=1-(xo*xo+zo2)*0.25f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(w&lt;=0)&nbsp;continue;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;w*=0.1591549430918953f;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERODE(x,&nbsp;z,&nbsp;w)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERODE(xi&nbsp;&nbsp;,&nbsp;zi&nbsp;&nbsp;,&nbsp;(1-xf)*(1-zf))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERODE(xi+1,&nbsp;zi&nbsp;&nbsp;,&nbsp;&nbsp;&nbsp;&nbsp;xf&nbsp;*(1-zf))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERODE(xi&nbsp;&nbsp;,&nbsp;zi+1,&nbsp;(1-xf)*&nbsp;&nbsp;&nbsp;zf&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ERODE(xi+1,&nbsp;zi+1,&nbsp;&nbsp;&nbsp;&nbsp;xf&nbsp;*&nbsp;&nbsp;&nbsp;zf&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#endif</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dh-=ds;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#undef&nbsp;ERODE</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s+=ds;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;move&nbsp;to&nbsp;the&nbsp;neighbour<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v=sqrtf(v*v+Kg*dh);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;w*=1-Kw;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xp=nxp;&nbsp;zp=nzp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xi=nxi;&nbsp;zi=nzi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xf=nxf;&nbsp;zf=nzf;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h=nh;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h00=nh00;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h10=nh10;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h01=nh01;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h11=nh11;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(numMoves&gt;=MAX_PATH_LEN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debug("droplet&nbsp;#%d&nbsp;path&nbsp;is&nbsp;too&nbsp;long!",&nbsp;iter);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++longPaths;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;sumLen+=numMoves;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;#undef&nbsp;DEPOSIT<br />
&nbsp;&nbsp;#undef&nbsp;DEPOSIT_AT</p>
<p>&nbsp;&nbsp;int64_t&nbsp;t1=get_ref_time();<br />
&nbsp;&nbsp;debug("computed&nbsp;%7d&nbsp;erosion&nbsp;droplets&nbsp;in&nbsp;%6u&nbsp;ms,&nbsp;%.0f&nbsp;droplets/s",<br />
&nbsp;&nbsp;&nbsp;&nbsp;iterations,&nbsp;get_time_msec(t1-t0),&nbsp;double(iterations)/get_time_sec(t1-t0));</p>
<p>&nbsp;&nbsp;debug("&nbsp;&nbsp;%.2f&nbsp;average&nbsp;path&nbsp;length,&nbsp;%I64u&nbsp;long&nbsp;paths&nbsp;cut,&nbsp;%I64u&nbsp;random&nbsp;directions&nbsp;picked",<br />
&nbsp;&nbsp;&nbsp;&nbsp;double(sumLen)/iterations,&nbsp;longPaths,&nbsp;randomDirs);<br />
}<br />
</code></p>
<h2>About parameters</h2>
<p>Here are the default parameters I use:<br />
<code><br />
ErosionParams()<br />
{<br />
&nbsp;&nbsp;Kq=10; Kw=0.001f; Kr=0.9f; Kd=0.02f; Ki=0.1f; minSlope=0.05f; g=20;<br />
}<br />
</code></p>
<p>Kq and minSlope are for soil carry capacity (see the formula above).<br />
Kw is water evaporation speed.<br />
Kr is erosion speed (how fast the soil is removed).<br />
Kd is deposition speed (how fast the extra sediment is dropped).<br />
Ki is direction inertia. Higher values make channel turns smoother.<br />
g is gravity that accelerates the flows.</p>
<h2>Other simulation methods</h2>
<p>Droplet model is not the only one. The same flow process can be simulated with iterations on the grid, for example. However I find droplets results more interesting.<br />
Also, there are methods to fake the erosion result. The fake isn&#8217;t as good as a &#8220;real&#8221; one, but it can be much faster. See for example “Realtime Procedural Terrain Generation” by Jacob Olsen.</p>
<h2>Other erosion types</h2>
<p>This article covers some water erosion only. There are other erosion types though. Rivers can do some interesting things to the terrain, for one. Coastal erosion is completely different, as well as glacial and thermal ones.<br />
Wind erosion can be done with droplet-like method, but is very different in nature too.</p>
]]></content:encoded>
			<wfw:commentRss>http://ranmantaru.com/blog/2011/10/08/water-erosion-on-heightmap-terrain/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
