Scape Terrain Editor - Giliam de Carpentier

今天介紹的是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仁讨。

Permutation Texture

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挑辆。

Gradient Texture

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)的輸出效果。

Procedural erosion
Procedural ridged turbulence

用來實(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效果从藤。

Step 1: Basic 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)行模擬了黄鳍。

Step 2: Distort by gradient

前面說過,山谷地形應(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输玷。

Step 3: Smoothen valleys

另外队丝,為了使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就夠了)衔憨。

Step 4: Distort by noise

下面給出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噪聲的平方粉渠,效果就跟下面一樣。

Step 1: Squared noise 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控制的渡嚣。

Step 2: Gradient-based damping

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ì)余下所有噪聲的作用功咒。

Step 3: Gradient-based warping

這里的所有步驟的操作方式跟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中波峰距離比較接近的瑕疵溅呢。

Step 4: Adding 2D noise to p

2.8 筆刷編輯管線

Scape的編輯管線主要包含三個(gè)階段:

  1. 捕捉用戶輸入锉走,并將之轉(zhuǎn)化為筆刷操作存入到一個(gè)操作隊(duì)列中
  • 用戶的輸入經(jīng)過投影后被翻譯成cubic spline上的一系列采樣點(diǎn)
  • 根據(jù)用戶配置,對(duì)cubic spline以一定的間隔進(jìn)行采樣
  • 上一步中的采樣點(diǎn)會(huì)被當(dāng)成筆刷操作實(shí)例添加到隊(duì)列中
  1. 對(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
  1. 更新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

[4]. Scape - Brush pipeline

[5]. Scape - Overview and downloads

[6]. Effective GPU-based synthesis and editing of realistic heightfields - Giliam de Carpentier

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市八秃,隨后出現(xiàn)的幾起案子碱妆,更是在濱河造成了極大的恐慌,老刑警劉巖昔驱,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疹尾,死亡現(xiàn)場離奇詭異,居然都是意外死亡骤肛,警方通過查閱死者的電腦和手機(jī)纳本,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腋颠,“玉大人繁成,你說我怎么就攤上這事∈缑担” “怎么了巾腕?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長絮蒿。 經(jīng)常有香客問我尊搬,道長,這世上最難降的妖魔是什么土涝? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任毁嗦,我火速辦了婚禮,結(jié)果婚禮上回铛,老公的妹妹穿的比我還像新娘狗准。我一直安慰自己,他們只是感情好茵肃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布腔长。 她就那樣靜靜地躺著,像睡著了一般验残。 火紅的嫁衣襯著肌膚如雪捞附。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音鸟召,去河邊找鬼胆绊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛欧募,可吹牛的內(nèi)容都是我干的压状。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼跟继,長吁一口氣:“原來是場噩夢啊……” “哼种冬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舔糖,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤娱两,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后金吗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體十兢,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年摇庙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纪挎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跟匆,死狀恐怖异袄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玛臂,我是刑警寧澤烤蜕,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站迹冤,受9級(jí)特大地震影響讽营,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泡徙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一橱鹏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堪藐,春花似錦莉兰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至模捂,卻和暖如春捶朵,著一層夾襖步出監(jiān)牢的瞬間蜘矢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工综看, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留品腹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓红碑,卻偏偏與公主長得像舞吭,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子句喷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容