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

890 阅读3分钟

本系列文章是对 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上.

下次见!