将2D噪声转换为3D

时间:2014-08-18 07:41:34

标签: opengl glsl fragment-shader

我最近开始尝试使用噪音(简单的perlin噪音),并且在设置动画时遇到了一些小问题。到目前为止,我已经看到了一个令人敬畏的3D噪音(https://github.com/ashima/webgl-noise),我可以在我的项目中使用但是我对此一无所知,还有一些教程解释了如何创建简单的2d噪音。< / p>

对于2d噪音,我最初使用以下片段着色器:

uniform sampler2D al_tex;
varying vec4 varying_pos; //Actual coords
varying vec2 varying_texcoord; //Normalized coords

uniform float time;

float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); }

float ease(float p) { return 3*p*p - 2*p*p*p; }

float cnoise(vec2 p, int wavelength)
{
    int ix1 = (int(varying_pos.x) / wavelength) * wavelength;
    int iy1 = (int(varying_pos.y) / wavelength) * wavelength;
    int ix2 = (int(varying_pos.x) / wavelength) * wavelength + wavelength;
    int iy2 = (int(varying_pos.y) / wavelength) * wavelength + wavelength;

    float x1 = ix1 / 1280.0f;
    float y1 = iy1 / 720.0f;
    float x2 = ix2 / 1280.0f;
    float y2 = iy2 / 720.0f;

    float xOffset = (varying_pos.x - ix1) / wavelength;
    float yOffset = (varying_pos.y - iy1) / wavelength;

    xOffset = ease(xOffset);
    yOffset = ease(yOffset);

    float t1 = rand(vec2(x1, y1));
    float t2 = rand(vec2(x2, y1));
    float t3 = rand(vec2(x2, y2));
    float t4 = rand(vec2(x1, y2));

    float tt1 = mix(t1, t2, xOffset);
    float tt2 = mix(t4, t3, xOffset);

    return mix(tt1, tt2, yOffset);
}

void main()
{
    float t = 0;

    int minFreq = 0;
    int noIterations = 8;

    for (int i = 0; i < noIterations; i++)
        t += cnoise(varying_texcoord, int(pow(2, i + minFreq))) / pow(2, noIterations - i);

    gl_FragColor = vec4(vec3(t), 1);
}

我得到的结果是: Sample result of shader

现在,我希望随着时间的推移制作动画。我的第一个想法是改变rand函数以取vec3而不是vec2,然后相应地改变我的cnoise函数,以便在z方向上插值。考虑到这个目标,我做到了这一点:

sampler2D al_tex;
varying vec4 varying_pos;
varying vec2 varying_texcoord;

uniform float time;

float rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.2332, 58.5065))) * 43758.5453); }

float ease(float p) { return 3*p*p - 2*p*p*p; }

float cnoise(vec3 pos, int wavelength)
{
    ivec3 iPos1 = (ivec3(pos) / wavelength) * wavelength; //The first value that I'll sample to interpolate
    ivec3 iPos2 = iPos1 + wavelength; //The second value

    vec3 transPercent = (pos - iPos1) / wavelength; //Transition percent - A float in [0-1) indicating how much of each of the above values will contribute to final result
    transPercent.x = ease(transPercent.x);
    transPercent.y = ease(transPercent.y);
    transPercent.z = ease(transPercent.z);

    float t1 = rand(vec3(iPos1.x, iPos1.y, iPos1.z));
    float t2 = rand(vec3(iPos2.x, iPos1.y, iPos1.z));
    float t3 = rand(vec3(iPos2.x, iPos2.y, iPos1.z));
    float t4 = rand(vec3(iPos1.x, iPos2.y, iPos1.z));
    float t5 = rand(vec3(iPos1.x, iPos1.y, iPos2.z));
    float t6 = rand(vec3(iPos2.x, iPos1.y, iPos2.z));
    float t7 = rand(vec3(iPos2.x, iPos2.y, iPos2.z));
    float t8 = rand(vec3(iPos1.x, iPos2.y, iPos2.z));

    float tt1 = mix(t1, t2, transPercent.x);
    float tt2 = mix(t4, t3, transPercent.x);
    float tt3 = mix(t5, t6, transPercent.x);
    float tt4 = mix(t8, t7, transPercent.x);

    float tt5 = mix(tt1, tt2, transPercent.y);
    float tt6 = mix(tt3, tt4, transPercent.y);

    return mix(tt5, tt6, transPercent.z);
}

float fbm(vec3 p)
{
    float t = 0;

    int noIterations = 8;

    for (int i = 0; i < noIterations; i++)
        t += cnoise(p, int(pow(2, i))) / pow(2, noIterations - i);

    return t;
}

void main()
{
    vec3 p = vec3(varying_pos.xy, time);

    float t = fbm(p);

    gl_FragColor = vec4(vec3(t), 1);
}

然而,在这样做时,动画感觉......很奇怪。好像我正在观看perlin噪音幻灯片的幻灯片放映,个别幻灯片渐渐消失。我尝试过的所有其他perlin噪音示例(如https://github.com/ashima/webgl-noise)实际上都是动画的 - 你实际上可以看到它是动画的,并且不会觉得图像正在消失,而不是实际上是动画的。我知道我可以使用webgl-noise着色器,但是我想为自己制作一个,并且出于某种原因,我惨遭失败。任何人都可以告诉我我哪里出错了,或者建议我如何随着时间的推移实际制作动画?

2 个答案:

答案 0 :(得分:1)

你应该在sin函数中包含z:

float rand(vec3 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233)) + co.z) * 43758.5453); }

显然有些随机数是素数。这是为了避免噪音中的模式。我找到了另一个素数,94418953,并包含在sin / dot函数中。试试这个:

float rand(vec3 co) { return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 9441.8953))) * 43758.5453); }

编辑:您没有考虑z轴上的波长。这意味着所有迭代都将具有相同的插值距离。换句话说,您将获得您描述的淡入淡出效果。尝试以与计算x和y相同的方式计算z:

int iz1 = (int(p.z) / wavelength) * wavelength;
int iz2 = (int(p.z) / wavelength) * wavelength + wavelength;

float z1 = iz1 / 720.0f;
float z2 = iz2 / 720.0f;

float zOffset = (varying_pos.z - iz1) / wavelength;

然而,这意味着z值将变化与y相同的速率。因此,如果您希望它从0缩放到1,那么在将z传递到噪声函数之前,您应该将z与720相乘。

答案 1 :(得分:0)

检查此代码。它是3d噪声的简单版本:

// Here are some easy to understand noise gens... the D line in cubic interpolation (rounding) 


function rndng ( n: float ): float
{//random proportion -1, 1   ... many people use Sin to take 
 //linearity out of a pseudo random, exp n*n is faster on central processor.
    var e = ( n *321.9234)%1;
    return  (e*e*111.07546)%2-1;
}

function lerps(o:float, v:float, alpha:float):float
{
    o += ( v - o ) * alpha;
    return o;
}

 //3d ----------------
function lnz ( vtx: Vector3 ): float //3d perlin noise code fast
{  
    vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
    var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
    var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
    D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
    var W = I.x + I.y*71.0 + 125.0*I.z;

    return lerps(          
                lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
                ,
                lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
                ,
                D.z
                );
}

 //1d ----------------
function lnzo ( vtx: Vector3 ): float //perlin noise, same as unityfunction version
{
    var total = 0.0;  
    for (var i:int = 1; i < 5; i ++)
    {
        total+= lnz2(Vector3 (vtx.x*(i*i),0.0,vtx.z*(i*i)))/(i*i);
    }

    return total*5;

}

 //2d 3 axis  honeycombe noise ----------------
function lnzh ( vtx: Vector3 ): float // perlin noise, 2d, with 3 axes at 60'instead of 2 x y axes
{  
    vtx= Vector3 ( Mathf.Abs(vtx.z) , Mathf.Abs(vtx.z*.5-vtx.x*.866) , Mathf.Abs(vtx.z*.5+vtx.x*.866) ) ;
    var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
    var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
   //D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
    var W = I.x + I.y*71.0 + 125.0*I.z;

    return lerps(          
                lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
                ,
                lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
                ,
                D.z
                );
}

 //2d ----------------
function lnz2 ( vtx: Vector3 ): float // i think this is 2d perlin noise
{  
    vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
    var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
    var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
    D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
    var W = I.x + I.y*71.0 + 125.0*I.z;

    return lerps(      
                lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.z)
                ,
                lerps( rndng(W+125.0), rndng(W+126.0),D.x)
                ,
                D.z
                );                  
}