今天介紹的是Giliam de Carpentier的程序化地形方案,原文鏈接在文末有給出。
Scape是Carpentier為其學(xué)位論文而編寫的地形編輯器夭问,在這個(gè)編輯器中咪啡,允許用戶使用程序化筆刷(Procedural Brushes,相對(duì)于傳統(tǒng)的拉高壓低等操作灵再,程序化可以根據(jù)選擇的地形類型的不同,生成不同細(xì)節(jié)的地形,比如山脈等)對(duì)地形進(jìn)行編輯雕刻弛车。
整個(gè)Terrain Editor系列包含了地形的渲染與地形數(shù)據(jù)的生成,渲染部分就是借助高度圖加上一些傳統(tǒng)的網(wǎng)格處理技術(shù)完成蒲每,沒有太多值得介紹的內(nèi)容纷跛,因此這里的主要內(nèi)容就集中在地形數(shù)據(jù)的程序化生成算法上面,即如何通過程序化生成實(shí)現(xiàn)對(duì)自然地形的模擬邀杏。
1. 地形渲染
1.1 Shading
地形的著色是通過程序化的方式完成的贫奠,地形著色總共由四張貼圖完成,通過高度以及地形梯度來控制不同位置的貼圖混合權(quán)重望蜡,為了避免重復(fù)的紋樣唤崭,這里還支持通過一張?jiān)肼曎N圖為不同的位置設(shè)置不同的混合方式,從而實(shí)現(xiàn)更為自然的混合效果脖律。
為了解決根據(jù)頂點(diǎn)XZ坐標(biāo)計(jì)算貼圖采樣UV坐標(biāo)導(dǎo)致的拉伸浩姥,這里采用了經(jīng)典的Tri-Planar采樣方法
從而可以得到如下的效果:
具體的算法給出如下,大致思路就是使用法線平方作為混合權(quán)重状您,將三個(gè)方向軸采樣之后的結(jié)果進(jìn)行混合勒叠。
float3 uvwPos = uvwScaleFactor * worldPosition;
float3 weights = worldNormal * worldNormal;
float3 blendedColor = weights.xxx * tex2D(texSampler, uvwPos.yz).rgb +
weights.yyy * tex2D(texSampler, uvwPos.zx).rgb +
weights.zzz * tex2D(texSampler, uvwPos.xy).rgb;
解決投影扭曲的方案有多種兜挨,除了上面使用法線平方作為權(quán)重的方式,還有GPU Gem3的使用法線abs作為混合權(quán)重的方案眯分,多種方案之間的效果對(duì)比可以參考如下的圖片:
2. 地形生成
Scape的地形編輯器跟其他引擎的地形編輯一樣拌汇,都是通過筆刷控制的,不同的是弊决,這里的筆刷包含了內(nèi)外兩層噪舀,內(nèi)層使用full strength,外層strength則從1.0過渡到0.0飘诗,此外這里還提供了不太常見的程序化筆刷功能与倡,而程序化筆刷則是這篇文章的主要關(guān)注點(diǎn)。
Scape的程序化筆刷提供了眾多的程序化算法昆稿,但是這些算法都是基于2D Perlin噪聲的纺座,這是因?yàn)镻erlin噪聲具有如下的一些優(yōu)點(diǎn):
- 噪聲是band-limited的
- 計(jì)算方便快捷
- 可以實(shí)現(xiàn)局部以及并行計(jì)算(即無過多關(guān)聯(lián)數(shù)據(jù)需要考慮)
2.1 Perlin噪聲
Perlin噪聲支持任意維度的輸入與輸出,對(duì)于地形生成溉潭,我們只需要傳入一個(gè)2D坐標(biāo)p净响,外加一個(gè)1D的噪聲種子,就能夠收獲一個(gè)對(duì)應(yīng)位置的1D輸出喳瓣,其算法給出如下:
uniform sampler2D samplerPerlinPerm2D;
uniform sampler2D samplerPerlinGrad2D;
float perlinNoise(float2 p, float seed)
{
// Calculate 2D integer coordinates i and fraction p.
float2 i = floor(p);
float2 f = p - i;
// Get weights from the coordinate fraction
float2 w = f * f * f * (f * (f * 6 - 15) + 10);
float4 w4 = float4(1, w.x, w.y, w.x * w.y);
// Get the four randomly permutated indices from the noise lattice nearest to
// p and offset these numbers with the seed number.
float4 perm = tex2D(samplerPerlinPerm2D, i / 256) + seed;
// Permutate the four offseted indices again and get the 2D gradient for each
// of the four permutated coordinates-seed pairs.
float4 g1 = tex2D(samplerPerlinGrad2D, perm.xy) * 2 - 1;
float4 g2 = tex2D(samplerPerlinGrad2D, perm.zw) * 2 - 1;
// Evaluate the four lattice gradients at p
float a = dot(g1.xy, f);
float b = dot(g2.xy, f + float2(-1, 0));
float c = dot(g1.zw, f + float2( 0, -1));
float d = dot(g2.zw, f + float2(-1, -1));
// Bi-linearly blend between the gradients, using w4 as blend factors.
float4 grads = float4(a, b - a, c - a, a - b - c + d);
float n = dot(grads, w4);
// Return the noise value, roughly normalized in the range [-1, 1]
return n * 1.5;
}
samplerPerlinPerm2D用于對(duì)一個(gè)精心設(shè)計(jì)的4通道彩色貼圖(分辨率為256x256馋贤,如下圖所示)進(jìn)行采樣。這張貼圖是一張LUT畏陕,對(duì)于一個(gè)給定的整數(shù)坐標(biāo)i配乓,會(huì)返回四個(gè)坐標(biāo)位置(i.x, i.y), of (i.x, i.y + 1), of (i.x + 1, i.y) and of (i.x + 1, i.y + 1) 的hash值,這四個(gè)hash值就分別存儲(chǔ)于四個(gè)通道中惠毁。 這張貼圖采樣模式是點(diǎn)采樣犹芹,而非雙線性采樣,在采樣的過程中需要關(guān)閉mipmap仁讨。
samplerPerlinGrad2D 的使用邏輯類似,不同的是對(duì)于某個(gè)采樣點(diǎn)实昨,會(huì)將上一個(gè)貼圖的采樣結(jié)果根據(jù)seed對(duì)其整數(shù)坐標(biāo)偏移洞豁,從而得到兩個(gè)相鄰的整數(shù)坐標(biāo),用這兩個(gè)坐標(biāo)作為輸入分別對(duì)gradient貼圖進(jìn)行一次lookup荒给,得到兩個(gè)梯度向量丈挟。對(duì)于包含四個(gè)頂點(diǎn)的cell來說,我們只需要兩個(gè)梯度向量就能夠計(jì)算出對(duì)應(yīng)cell的梯度. 根據(jù)這些數(shù)據(jù)就可以計(jì)算p點(diǎn)的高度志电,最后各點(diǎn)的高度會(huì)使用quintic插值算法實(shí)現(xiàn)插值并歸一化曙咽。跟perm貼圖一樣,gradient貼圖也是需要repeating+point sampling + no mipmapping挑辆。
2.2 FBM
FBM (fractional Brownian motion)是對(duì)Perlin噪聲進(jìn)行組合的最簡單算法例朱,一些更為復(fù)雜的turbulence算法都是以這個(gè)算法為基礎(chǔ)完成的孝情。其基本原理就是對(duì)不同頻率的噪聲以不同的振幅進(jìn)行累加,這種算法在很多方面都有應(yīng)用洒嗤,比如在體積云實(shí)現(xiàn)中箫荡,用于對(duì)Voronoi噪聲進(jìn)行處理以實(shí)現(xiàn)更為自然的效果。
FBM累加中的每一個(gè)不同頻率的噪聲被稱為一個(gè)Octave渔隶,不同的Octave負(fù)責(zé)不同尺寸的feature羔挡,算法實(shí)現(xiàn)給出如下:
float turbulence(float2 p, float seed, int octaves, float lacunarity = 2.0, float gain = 0.5)
{
float sum = 0;
float freq = 1.0, amp = 1.0;
for (int i=0; i < octaves; i++)
{
float n = perlinNoise(p*freq, seed + i / 256.0);
sum += n*amp;
freq *= lacunarity;
amp *= gain;
}
return sum;
}
算法中的seed通常是一個(gè)[0, 1]范圍內(nèi)的浮點(diǎn)數(shù),而為了避免重復(fù)的pattern间唉,lacunarity數(shù)值通常會(huì)被設(shè)定為一個(gè)低于2.0的浮點(diǎn)數(shù)(比如1.92)绞灼,為了能夠覆蓋基本上所有的feature,octave數(shù)目通常會(huì)少于log(terrain_width) / log(lacunarity)呈野,對(duì)于一個(gè)1024x1024尺寸的地形而言低矮,我們通常就需要大約10個(gè)octave的perlin噪聲累加;gain數(shù)值是不同octave噪聲之間的振幅差異际跪,用于控制不同的粗糙粒度商佛。
2.3 Billowy turbulence
如果用下面的billowedNoise替換之前的PerlinNoise的函數(shù)實(shí)現(xiàn),我們就可以得到一些凸起部分具有billow(鼓鼓的)效果而凹陷部分帶有腐蝕Erosion效果且具有較為尖銳褶痕(crease)的地形結(jié)果姆打,這個(gè)噪聲在很多文獻(xiàn)中就直接被稱為turbulence function良姆,實(shí)際上就是Perlin噪聲的絕對(duì)值。
float billowedNoise(float2 p, float seed)
{
return abs(perlinNoise(p, seed));
}
2.4 Ridged turbulence
通過對(duì)Turbulence Function取反幔戏,就可以得到山脈一樣尖銳的山脊效果
float ridgedNoise(float2 p, float seed)
{
return 1.0f-abs(perlinNoise(p, seed));
}
2.5 IQ Noise
上面的算法是通過對(duì)Perlin噪聲進(jìn)行累加得到的玛追,而這種做法的弊端在于波峰跟波谷的尺寸都是近似的,而這跟自然中的地形風(fēng)貌不太一致闲延。
解決這個(gè)問題的一種思路是痊剖,在對(duì)多個(gè)octave的噪聲進(jìn)行累加前,先根據(jù)粗糙的octave噪聲的輸出來確定更為精細(xì)的octave噪聲的振幅垒玲,從而得到更為異構(gòu)的地形效果陆馁,這個(gè)算法最開始是由I. Quillez提出的:
float iqTurbulence(float2 p, float seed, int octaves, float lacunarity = 2.0, float gain = 0.5)
{
float sum = 0.5;
float freq = 1.0, amp = 1.0;
float2 dsum = float2(0,0);
for (int i=0; i < octaves; i++)
{
float3 n = perlinNoisePseudoDeriv(p*freq, seed + i / 256.0);
dsum += n.yz;
// the higher the octave, the smaller the amplitude
sum += amp * n.x / (1 + dot(dsum, dsum));
freq *= lacunarity;
amp *= gain;
}
return sum;
}
上述算法中,n是一個(gè)通過perlinNoisePseudoDeriv 函數(shù)計(jì)算得到的三維向量合愈,其中n.x就是此前算法中輸出的Perlin噪聲叮贩,也就是此前Turbulence Function中的n。而n.yz則有點(diǎn)類似于當(dāng)前采樣點(diǎn)下Perlin噪聲的局部微分佛析,這里說有點(diǎn)類似益老,是因?yàn)樵髡咴谕茖?dǎo)微分計(jì)算公式的時(shí)候存在錯(cuò)誤,但是輸出的效果卻歪打正著寸莫,正好是我們所需要的效果捺萌。
在octave迭代的過程中,這個(gè)微分?jǐn)?shù)值會(huì)累加起來對(duì)振幅進(jìn)行抑制膘茎,從而減少斜坡(高微分值)區(qū)域的細(xì)節(jié)凹凸效果桃纯。下面給出perlinNoisePseudoDeriv 算法的具體實(shí)現(xiàn):
float3 perlinNoisePseudoDeriv(float2 p, float seed)
{
// Calculate 2D integer coordinates i and fraction p.
float2 i = floor(p);
float2 f = p - i;
// Get weights from the coordinate fraction
float2 w = f * f * f * (f * (f * 6 - 15) + 10);
float4 w4 = float4(1, w.x, w.y, w.x * w.y);
// Get pseudo derivative weights
float2 dw = f * f * (f * (30 * f - 60) + 30);
// Get the four randomly permutated indices from the noise lattice nearest to
// p and offset these numbers with the seed number.
float4 perm = tex2D(samplerPerlinPerm2D, i / 256) + seed;
// Permutate the four offseted indices again and get the 2D gradient for each
// of the four permutated coordinates-seed pairs.
float4 g1 = tex2D(samplerPerlinGrad2D, perm.xy) * 2 - 1;
float4 g2 = tex2D(samplerPerlinGrad2D, perm.zw) * 2 - 1;
// Evaluate the four lattice gradients at p
float a = dot(g1.xy, f);
float b = dot(g2.xy, f + float2(-1, 0));
float c = dot(g1.zw, f + float2( 0, -1));
float d = dot(g2.zw, f + float2(-1, -1));
// Bi-linearly blend between the gradients, using w4 as blend factors.
float4 grads = float4(a, b - a, c - a, a - b - c + d);
float n = dot(grads, w4);
// Calculate pseudo derivates
float dx = dw.x * (grads.y + grads.w*w.y);
float dy = dw.y * (grads.z + grads.w*w.x);
// Return the noise value, roughly normalized in the range [-1, 1]
// Also return the pseudo dn/dx and dn/dy, scaled by the same factor
return float3(n, dx, dy) * 1.5;
}
2.6 Swiss turbulence
下面兩張圖中酷誓,Procedural erosion給出的是通過gidged turbulence函數(shù)生成的地形效果,這種地形看起來并不太差慈参,但是缺少了地形成型的那種歷史演變信息呛牲。通常來說,自然地形中會(huì)在地形的斜坡上生成槽溝驮配,而在山谷中的地形會(huì)較為平緩娘扩,而這些效果在這個(gè)地形中均沒有,雖然我們可以通過對(duì)heightfield進(jìn)行處理來模擬這種侵蝕效果壮锻,但是需要經(jīng)過大量迭代琐旁,效率較低,幸運(yùn)的是猜绣,我們可以通過算法進(jìn)行fake灰殴,下面Procedural ridged turbulence就給出了對(duì)應(yīng)的輸出效果。
用來實(shí)現(xiàn)這個(gè)效果的算法叫做SwissTurbulence掰邢,這個(gè)算法依然是通過將多個(gè)Perlin噪聲組合起來實(shí)現(xiàn)的牺陶,具體算法邏輯給出如下:
float swissTurbulence(float2 p, float seed, int octaves,
float lacunarity = 2.0, float gain = 0.5,
float warp = 0.15>)
{
float sum = 0;
float freq = 1.0, amp = 1.0;
float2 dsum = float2(0,0);
for(int i=0; i < octaves; i++)
{
float3 n = perlinNoiseDeriv((p + warp * dsum)*freq, seed + i);
sum += amp * (1 - abs(n.x));
// specific modifications
dsum += amp * n.yz * -n.x;
freq *= lacunarity;
amp *= gain * saturate(sum);
}
return sum;
}
可以看到,相對(duì)于此前的Ridged Turbulence函數(shù)辣之,這里只是增加了一個(gè)dsum的計(jì)算邏輯掰伸,這部分后面會(huì)介紹。perlinNoiseDeriv 函數(shù)跟perlinNoisePseudoDeriv 函數(shù)很相似怀估,不同的是狮鸭,這里的yz分量返回是的正確推導(dǎo)的微分結(jié)果,而x分量還是Perlin噪聲多搀。
整個(gè)算法結(jié)構(gòu)還是跟之前一樣歧蕉,通過對(duì)噪聲應(yīng)用FBM來完成,不同的是康铭,由于這里希望生成帶有Ridge效果的地形惯退,因此這里的累加是針對(duì)1-abs(n.x)而非n.x的,通過這種方式可以生成基礎(chǔ)的ridged turbulence效果从藤。
dsum跟sum一樣催跪,都是一個(gè)加權(quán)求和項(xiàng),不同的是sum是針對(duì)噪聲的求和呛哟,而dsum則是針對(duì)微分?jǐn)?shù)值的求和叠荠。最終這個(gè)2D的求和項(xiàng)會(huì)被更精一級(jí)octave用作噪聲offset匿沛,這個(gè)offset將被更精一級(jí)octave中的采樣點(diǎn)用來查找最近的ridge數(shù)據(jù)扫责,從而導(dǎo)致如下圖所示的在ridge在斜坡上的拉長效果。這個(gè)拉長效果的幅度受warp參數(shù)的影響逃呼,需要注意的是鳖孤,按照chain rule(復(fù)合求導(dǎo))來說者娱,1-abs(n.x)的梯度公式應(yīng)該寫成n.xy-sign(n.x) 而非n.yz-n.x,不過為了避免sign函數(shù)的不連續(xù)導(dǎo)致的異常苏揣,這里就直接使用后者進(jìn)行模擬了黄鳍。
前面說過,山谷地形應(yīng)該要比較平緩平匈,為了實(shí)現(xiàn)這個(gè)效果框沟,這里的amp計(jì)算方式做了輕微修正,amp不再是只乘上每個(gè)octave的gain增炭,還會(huì)乘上一個(gè)臨時(shí)的sum變量(即上面算法中的saturate(sum))忍燥,而這個(gè)做法會(huì)使得更精細(xì)的octave的細(xì)節(jié)數(shù)據(jù)在地形高度降低的時(shí)候fade out,而在地形高度較高區(qū)域則繼續(xù)維持甚至更為突出隙姿。而為了對(duì)整體振幅縮小的補(bǔ)償梅垄,這里使用的gain參數(shù)應(yīng)該要比正常情況下要稍大,比如從0.5改成0.6输玷。
另外队丝,為了使ridge以及slope效果具有更為隨機(jī)自然的效果,這里還可以對(duì)輸入的p做一次distortion欲鹏。這里的做法是將p分別傳入到兩個(gè)不同的turbulence function(跟這里的turbulence function不一樣的兩個(gè)函數(shù))机久,將輸出的結(jié)果組合成一個(gè)新的p。這里對(duì)p進(jìn)行distortion擾動(dòng)的函數(shù)所使用的gain貌虾,octave吞加,scale等參數(shù)都不必跟SwissTurbulence函數(shù)中一致,且為了得到較好的效果尽狠,這里擾動(dòng)函數(shù)中的octave數(shù)目不能太高(通常4就夠了)衔憨。
下面給出perlinNoiseDeriv函數(shù)的實(shí)施邏輯。
float3 perlinNoiseDeriv(float2 p, float seed)
{
// Calculate 2D integer coordinates i and fraction p.
float2 i = floor(p);
float2 f = p - i;
// Get weights from the coordinate fraction
float2 w = f * f * f * (f * (f * 6 - 15) + 10); // 6f^5 - 15f^4 + 10f^3
float4 w4 = float4(1, w.x, w.y, w.x * w.y);
// Get the derivative dw/df
float2 dw = f * f * (f * (f * 30 - 60) + 30); // 30f^4 - 60f^3 + 30f^2
// Get the derivative d(w*f)/df
float2 dwp = f * f * f * (f * (f * 36 - 75) + 40); // 36f^5 - 75f^4 + 40f^3
// Get the four randomly permutated indices from the noise lattice nearest to
// p and offset these numbers with the seed number.
float4 perm = tex2D(samplerPerlinPerm2D, i / 256) + seed;
// Permutate the four offseted indices again and get the 2D gradient for each
// of the four permutated coordinates-seed pairs.
float4 g1 = tex2D(samplerPerlinGrad2D, perm.xy) * 2 - 1;
float4 g2 = tex2D(samplerPerlinGrad2D, perm.zw) * 2 - 1;
// Evaluate the four lattice gradients at p
float a = dot(g1.xy, f);
float b = dot(g2.xy, f + float2(-1, 0));
float c = dot(g1.zw, f + float2( 0, -1));
float d = dot(g2.zw, f + float2(-1, -1));
// Bi-linearly blend between the gradients, using w4 as blend factors.
float4 grads = float4(a, b - a, c - a, a - b - c + d);
float n = dot(grads, w4);
// Calculate the derivatives dn/dx and dn/dy
float dx = (g1.x + (g1.z-g1.x)*w.y) + ((g2.y-g1.y)*f.y - g2.x +
((g1.y-g2.y-g1.w+g2.w)*f.y + g2.x + g1.w - g2.z - g2.w)*w.y)*
dw.x + ((g2.x-g1.x) + (g1.x-g2.x-g1.z+g2.z)*w.y)*dwp.x;
float dy = (g1.y + (g2.y-g1.y)*w.x) + ((g1.z-g1.x)*f.x - g1.w + ((g1.x-
g2.x-g1.z+g2.z)*f.x + g2.x + g1.w - g2.z - g2.w)*w.x)*dw.y +
((g1.w-g1.y) + (g1.y-g2.y-g1.w+g2.w)*w.x)*dwp.y;
// Return the noise value, roughly normalized in the range [-1, 1]
// Also return the pseudo dn/dx and dn/dy, scaled by the same factor
return float3(n, dx, dy) * 1.5;
}
2.7 Jordan turbulence
下面兩張圖給出的高度圖效果依然是通過對(duì)Perlin噪聲進(jìn)行混合輸出的袄膏,沒有使用任何積分方式的迭代算法践图。
跟SwissTurbulence函數(shù)一樣,這里也同樣使用了perlinNoiseDeriv 函數(shù)對(duì)octave進(jìn)行擾動(dòng)跟扭曲沉馆,但是卻輸出了一個(gè)完全不同的地形效果:
float jordanTurbulence(float2 p, float seed, int octaves, float lacunarity = 2.0,
float gain1 = 0.8, float gain = 0.5,
float warp0 = 0.4, float warp = 0.35,
float damp0 = 1.0, float damp = 0.8,
float damp_scale = 1.0)
{
float3 n = perlinNoiseDeriv(p, seed);
float3 n2 = n * n.x;
float sum = n2.x;
float2 dsum_warp = warp0*n2.yz;
float2 dsum_damp = damp0*n2.yz;
float amp = gain1;
float freq = lacunarity;
float damped_amp = amp * gain;
for(int i=1; i < octaves; i++)
{
n = perlinNoiseDeriv(p * freq + dsum_warp.xy, seed + i / 256.0);
n2 = n * n.x;
sum += damped_amp * n2.x;
dsum_warp += warp * n2.yz;
dsum_damp += damp * n2.yz;
freq *= lacunarity;
amp *= gain;
damped_amp = amp * (1-damp_scale/(1+dot(dsum_damp,dsum_damp)));
}
return sum;
}
跟其他擾動(dòng)算法不同的是码党,這里最粗糙一級(jí)的octave噪聲是在for循環(huán)之外計(jì)算的。這種做法的好處是最粗糙一級(jí)octave的振幅將跟其他octave的噪聲獨(dú)立開來斥黑,當(dāng)然更好的一種控制方式是將每一級(jí)octave的權(quán)重都分開處理揖盘,通過一個(gè)數(shù)組進(jìn)行控制,從而可以節(jié)省GPU cycle锌奴,同時(shí)提供更為靈活的控制兽狭。
首先,最終輸出的是噪聲之和,這里的噪聲之和是n2.x箕慧,也就是(n.x)^2服球,通過這個(gè)平方,使得輸出的噪聲具有billowy的效果颠焦,而這也是JordanTurbulence跟SwissTurbulence不同的原因之一了斩熊。當(dāng)warp/warp0/damp/damp0都是0的時(shí)候,這時(shí)候dsum_warp/dsum_damp也都是0伐庭,這時(shí)候噪聲效果就相當(dāng)于普通Turbulence噪聲的平方粉渠,效果就跟下面一樣。
根據(jù)復(fù)合求導(dǎo)算法圾另,(n.x)^2的梯度是2.0n.xn.yz或2.0*n2.yz渣叛,因此dsum_damp會(huì)與疊加上一個(gè)scale因子的梯度進(jìn)行累加,而因?yàn)樯弦患?jí)的噪聲結(jié)果比較平緩盯捌,因此這個(gè)結(jié)果會(huì)用于對(duì)下一級(jí)的octave的振幅進(jìn)行平滑處理淳衙,這個(gè)效果看起來就像是與thermal erosion的組合。
跟SwissTurbulence不同饺著,JordanTurbulence根據(jù)梯度而非高度來對(duì)下一級(jí)噪聲的振幅進(jìn)行dampen(平滑處理)箫攀,這也就意味著不論是山峰還是山谷,平緩區(qū)域處的噪聲細(xì)節(jié)都會(huì)比較少幼衰。其中damp0控制著最粗糙一級(jí)噪聲對(duì)后續(xù)噪聲damping效果的強(qiáng)度靴跛,而damp則控制著當(dāng)前一級(jí)噪聲對(duì)后面噪聲的damping效果強(qiáng)度。被damping的振幅大小是受damp_scale控制的渡嚣。
dsum_warp變量的計(jì)算方式跟dsum_damp類似梢睛,不過其使用目的不一樣。這個(gè)變量的作用是用于創(chuàng)建類似下斜槽溝的下過识椰,用于模擬fluvial erosion绝葡,因此其控制參數(shù)需要保持跟dsum_damp不同。作為副作用腹鹉,這個(gè)變量還會(huì)導(dǎo)致一個(gè)對(duì)所有feature的一個(gè)squash(壓擠)效果藏畅。
warp0用于控制最粗糙一級(jí)的噪聲對(duì)所有其他級(jí)數(shù)噪聲的作用,而warp則控制著上一級(jí)噪聲對(duì)余下所有噪聲的作用功咒。
這里的所有步驟的操作方式跟SwissTurbulence差不多愉阎,不過最終輸出了完全不同的地形效果。前面說過SwissTurbulence中會(huì)需要對(duì)輸入的p進(jìn)行擾動(dòng)處理力奋,這里也是一樣的榜旦,同樣需要通過兩個(gè)不同Turbulence函數(shù)對(duì)p進(jìn)行擾動(dòng)并組合成新的p來計(jì)算后續(xù)效果,其中一個(gè)可選的Turbulence函數(shù)可以使用前面的SwissTurbulence景殷,這個(gè)函數(shù)可以緩解最終JordanTurbulence中波峰距離比較接近的瑕疵溅呢。
2.8 筆刷編輯管線
Scape的編輯管線主要包含三個(gè)階段:
- 捕捉用戶輸入锉走,并將之轉(zhuǎn)化為筆刷操作存入到一個(gè)操作隊(duì)列中
- 用戶的輸入經(jīng)過投影后被翻譯成cubic spline上的一系列采樣點(diǎn)
- 根據(jù)用戶配置,對(duì)cubic spline以一定的間隔進(jìn)行采樣
- 上一步中的采樣點(diǎn)會(huì)被當(dāng)成筆刷操作實(shí)例添加到隊(duì)列中
- 對(duì)筆刷操作實(shí)例進(jìn)行處理
- 每個(gè)筆刷操作實(shí)例被分割成一個(gè)或者多個(gè)paged筆刷實(shí)例
- 通過一個(gè)scheduler對(duì)待處理的paged筆刷實(shí)例batch進(jìn)行處理
- 對(duì)page的處理會(huì)將對(duì)應(yīng)的地形tile設(shè)置為invalidation
- 當(dāng)某次編輯對(duì)應(yīng)的所有筆刷操作實(shí)例都處理完成后藕届,老的地形就會(huì)被壓縮存儲(chǔ),同時(shí)更新一下undo stack
- 更新terrain geometry
- 使用新的heightfield page作為輸入亭饵,完成之前設(shè)置為invalidation的tile的重新生成
Page是一個(gè)具有固定尺寸的方塊休偶,Page跟Tile不是一個(gè)概念,最大的區(qū)別是Page是沒有LOD概念的辜羊,通過Page可以避免地形整體尺寸過大導(dǎo)致的操作的不方便(如Undo/Redo等的不方便)踏兜。
參考資料
[1]. Scape - Rendering the terrain
[2]. Scape - Procedural basics
[3]. Scape - Procedural extensions
[5]. Scape - Overview and downloads
[6]. Effective GPU-based synthesis and editing of realistic heightfields - Giliam de Carpentier