[MetalKit]20-Using-MetalKit-part-14使用MetalKit14

蘋果API搬運工發表於2017-12-25

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.

MetalKit系統文章目錄


讓我們從第13部分 Part 13繼續.使用上次我們用的playground,我們今天將學習noise.摘自Wikipedia:

Noise refers to any random fluctuations of data that makes the perception of an expected signal, difficult. Value noise is a type of noise commonly used as a procedural texture primitive in computer graphics. This method consists of the creation of a lattice of points which are assigned random values. The noise function then returns the interpolated number based on the values of the surrounding lattice points. Multiple octaves of this noise can be generated and then summed together in order to create a form of fractal noise. Noise噪聲指任何隨機漲落的資料,它使感知期望訊號變得困難.Value noise值噪聲是一種經常用在計算機圖形學中作為程式紋理基元.這種方法由被賦於了隨機值的網格點建立而成.接著噪聲函式返回基於網格周圍點的插值資料.可以生成多個這種型別的噪聲並相加來建立一種分形噪聲.

噪聲最明顯的特徵就是隨機.因為MSL沒有提供隨機函式,所以我們自己建立一個.我們需要的是 [0,1] 之間的隨機數.我們可以用fract函式來返回一個數的小數部分:

float random(float2 p)
{
    return fract(sin(dot(p, float2(15.79, 81.93)) * 45678.9123));
}
複製程式碼

noise() 函式將雙線性插值一個格子(網格)並返回一個平滑的值.雙線性內插允許我們基於2D網格變換1D隨機函式到一個值:

float noise(float2 p)
{
    float2 i = floor(p);
    float2 f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    float bottom = mix(random(i + float2(0)), random(i + float2(1.0, 0.0)), f.x);
    float top = mix(random(i + float2(0.0, 1.0)), random(i + float2(1)), f.x);
    float t = mix(bottom, top, f.y);
    return t;
}
複製程式碼

我們首先用i來移動格點,並用f做為網格點間的偏移.然後我們用公式3f^2 - 2f^3計算一個Cubic Hermite Spline,它建立一個S型曲線,值在 [0,1] 之間.下一步我們沿著網格底部和頂部做內插值,最終內插出兩個水平點間的垂線,並得到我們的最終值作為噪聲.

下一步我們建立一個Fractional Brownian Motion微小布朗執行函式呼叫noise()函式若干次並將結果相加.

float fbm(float2 uv)
{
    float sum = 0;
    float amp = 0.7;
    for(int i = 0; i < 4; ++i)
    {
        sum += noise(uv) * amp;
        uv += uv * 1.2;
        amp *= 0.4;
    }
    return sum;
}
複製程式碼

通過新增若干(本例中為四)次噪聲在不同幅度(開頭解釋的那樣)的octaves倍頻,我們可以產生一個簡單的雲狀圖案.我們還有一件事要做:在核心中,用下面幾行替換distance定義後的所有行:

uv = fmod(uv + float2(timer * 0.2, 0), float2(width, height));
float t = fbm( uv * 3 );
output.write(distance < 0 ? float4(float3(t), 1) : float4(0), gid);
複製程式碼

為了好玩,我們新增timeruniform來讓內容動起來.輸出的影象看起來應該像這樣:

chapter14.gif

它看起來很酷,但是仍然不夠真實.為了讓它看起來更真實一些,我們需要學習並使用紋理.如果你感興趣的話,你可以閱讀更多關於 bilinear filtering, 關於value noise 以及關於Fractional Brownian motion的內容.也可以看一個 Cubic Hermit Spline的例子.

原始碼source code 已釋出在Github上.

下次見!

相關文章