为什么换行坐标不能使我的单工噪声平铺无缝?

时间:2018-08-31 15:38:56

标签: random glsl textures procedural-generation simplex-noise

我一直在尝试创建一个在shadertoy(see here, use wasd to move, arrow keys to rotate)中重复的假3D纹理,但是正如您所看到的,它不会平铺。

我自己产生了噪声,并且在this minimal example中隔离了噪声的产生,但是无论我做什么,它似乎都不会产生无缝可平铺的噪声。

代码如下:

//Common, you probably won't have to look here. 
vec2 modv(vec2 value, float modvalue){
    return vec2(mod(value.x, modvalue), 
                mod(value.y, modvalue));
}
vec3 modv(vec3 value, float modvalue){
    return vec3(mod(value.x, modvalue), 
                mod(value.y, modvalue),
                mod(value.z, modvalue));
}
vec4 modv(vec4 value, float modvalue){
    return vec4(mod(value.x, modvalue), 
                mod(value.y, modvalue),
                mod(value.z, modvalue),
                mod(value.w, modvalue));
}

//MATH CONSTANTS
const float pi  = 3.1415926535897932384626433832795;
const float tau = 6.2831853071795864769252867665590;
const float eta = 1.5707963267948966192313216916397;
const float SQRT3 = 1.7320508075688772935274463415059;
const float SQRT2 = 1.4142135623730950488016887242096;
const float LTE1 =  0.9999999999999999999999999999999;
const float inf = uintBitsToFloat(0x7F800000u);

#define saturate(x) clamp(x,0.0,1.0)
#define norm01(x) ((x + 1.0) / 2.0)

vec2 pos3DTo2D(in vec3 pos, 
               const in int size_dim, 
               const in ivec2 z_size){
    float size_dimf = float(size_dim);
    pos = vec3(mod(pos.x, size_dimf), mod(pos.y, size_dimf),  mod(pos.z, size_dimf));
    int z_dim_x = int(pos.z) % z_size.x;
    int z_dim_y = int(pos.z) / z_size.x;
    float x = pos.x + float(z_dim_x * size_dim);
    float y = pos.y + float(z_dim_y * size_dim);
    return vec2(x,y);
}

vec4 textureAs3D(const in sampler2D iChannel, 
                 in vec3 pos, 
                 const in int size_dim, 
                 const in ivec2 z_size,
                 const in vec3 iResolution){
    //only need whole, will do another texture read to make sure interpolated?

    vec2 tex_pos = pos3DTo2D(pos, size_dim, z_size)/iResolution.xy;
    vec4 base_vec4 = texture(iChannel, tex_pos);

    vec2 tex_pos_z1 = pos3DTo2D(pos+vec3(0.0,0.0,1.0), size_dim, z_size.xy)/iResolution.xy;
    vec4 base_vec4_z1 = texture(iChannel, tex_pos_z1);
    //return base_vec4;
    return mix(base_vec4, base_vec4_z1, fract(pos.z));
}

vec4 textureZ3D(const in sampler2D iChannel, 
                 in int y,
                 in int z,
                 in int offsetX,
                 const in int size_dim, 
                 const in ivec2 z_size,
                const in vec3 iResolution){
    int tx = (z%z_size.x);
    int ty = z/z_size.x;
    int sx = offsetX + size_dim * tx;
    int sy = y  + (ty *size_dim);
    if(ty < z_size.y){
        return texelFetch(iChannel, ivec2(sx, sy),0);
    }else{
        return vec4(0.0);
    }
    //return texelFetch(iChannel, ivec2(x, y - (ty *32)),0);
}
//Buffer B this is what you are going to have to look at. 
//noise

//NOISE CONSTANTS
// captured from https://en.wikipedia.org/wiki/SHA-2#Pseudocode
const uint CONST_A = 0xcc9e2d51u;
const uint CONST_B = 0x1b873593u;
const uint CONST_C = 0x85ebca6bu;
const uint CONST_D = 0xc2b2ae35u;
const uint CONST_E = 0xe6546b64u;
const uint CONST_F = 0x510e527fu;
const uint CONST_G = 0x923f82a4u;
const uint CONST_H = 0x14292967u;

const uint CONST_0 = 4294967291u;
const uint CONST_1 = 604807628u;
const uint CONST_2 = 2146583651u;
const uint CONST_3 = 1072842857u;
const uint CONST_4 = 1396182291u;
const uint CONST_5 = 2227730452u;
const uint CONST_6 = 3329325298u;
const uint CONST_7 = 3624381080u;




uvec3 singleHash(uvec3 uval){
    uval ^= uval >> 16;
    uval.x *= CONST_A;
    uval.y *= CONST_B;
    uval.z *= CONST_C;
    return uval;
}

uint combineHash(uint seed, uvec3 uval){
    // can move this out to compile time if need be. 
    // with out multiplying by one of the randomizing constants
    // will result in not very different results from seed to seed. 
    uint un = seed * CONST_5;
    un ^= (uval.x^uval.y)* CONST_0;
    un ^= (un >> 16);
    un = (un^uval.z)*CONST_1;
    un ^= (un >> 16);
    return un;
}

/* 
//what the above hashes are based upon, seperate 
//out this mumurhash based coherent noise hash
uint fullHash(uint seed, uvec3 uval){
    uval ^= uval >> 16;
    uval.x *= CONST_A;
    uval.y *= CONST_B;
    uval.z *= CONST_D;
    uint un = seed * CONST_6;
    un ^= (uval.x ^ uval.y) * CONST_0;
    un ^= un >> 16;
    un = (un^uval.z) * CONST_2;
    un ^= un >> 16;
    return un;
}
*/

const vec3 gradArray3d[8] = vec3[8](
    vec3(1, 1, 1), vec3(1,-1, 1), vec3(-1, 1, 1), vec3(-1,-1, 1),
    vec3(1, 1,-1), vec3(1,-1,-1), vec3(-1, 1,-1), vec3(-1,-1,-1)
);


vec3 getGradient3Old(uint uval){
    vec3 grad = gradArray3d[uval & 7u];
    return grad;
}

//source of some constants
//https://github.com/Auburns/FastNoise/blob/master/FastNoise.cpp
const float SKEW3D = 1.0 / 3.0;
const float UNSKEW3D = 1.0 / 6.0;
const float FAR_CORNER_UNSKEW3D = -1.0 + 3.0*UNSKEW3D;
const float NORMALIZE_SCALE3D = 30.0;// * SQRT3;
const float DISTCONST_3D = 0.6;

float simplexNoiseV(uint seed, in vec3 pos, in uint wrap){
    pos = modv(pos, float(wrap));
    float skew_factor = (pos.x + pos.y + pos.z)*SKEW3D;
    vec3 fsimplex_corner0 = floor(pos + skew_factor);
    ivec3 simplex_corner0 = ivec3(fsimplex_corner0);

    float unskew_factor = (fsimplex_corner0.x + fsimplex_corner0.y + fsimplex_corner0.z) * UNSKEW3D;
    vec3 pos0 = fsimplex_corner0 - unskew_factor;

    //subpos's are positions with in grid cell. 
    vec3 subpos0 = pos - pos0;
    //precomputed values used in determining hash, reduces redundant hash computation
    //shows 10% -> 20% speed boost. 
    uvec3 wrapped_corner0 = uvec3(simplex_corner0);
    uvec3 wrapped_corner1 = uvec3(simplex_corner0+1);
    wrapped_corner0 = wrapped_corner0 % wrap;
    wrapped_corner1 = wrapped_corner1 % wrap;

    //uvec3 hashes_offset0 = singleHash(uvec3(simplex_corner0));
    //uvec3 hashes_offset1 = singleHash(uvec3(simplex_corner0+1));
    uvec3 hashes_offset0 = singleHash(wrapped_corner0);
    uvec3 hashes_offset1 = singleHash(wrapped_corner1);
    //near corner hash value
    uint hashval0 = combineHash(seed, hashes_offset0);
    //mid corner hash value
    uint hashval1;

    uint hashval2;
    //far corner hash value
    uint hashval3 = combineHash(seed, hashes_offset1);

    ivec3 simplex_corner1;
    ivec3 simplex_corner2;
    if (subpos0.x >= subpos0.y)
    {
        if (subpos0.y >= subpos0.z)
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset1.x, hashes_offset0.yz));
            hashval2 = combineHash(seed, uvec3(hashes_offset1.xy, hashes_offset0.z));
            simplex_corner1 = ivec3(1,0,0);
            simplex_corner2 = ivec3(1,1,0);
        }
        else if (subpos0.x >= subpos0.z)
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset1.x, hashes_offset0.yz));
            hashval2 = combineHash(seed, uvec3(hashes_offset1.x, hashes_offset0.y, hashes_offset1.z));
            simplex_corner1 = ivec3(1,0,0);
            simplex_corner2 = ivec3(1,0,1);
        }
        else // subpos0.x < subpos0.z
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset0.xy, hashes_offset1.z));
            hashval2 = combineHash(seed, uvec3(hashes_offset1.x, hashes_offset0.y, hashes_offset1.z));
            simplex_corner1 = ivec3(0,0,1);
            simplex_corner2 = ivec3(1,0,1);
        }
    }
    else // subpos0.x < subpos0.y
    {
        if (subpos0.y < subpos0.z)
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset0.xy, hashes_offset1.z));
            hashval2 = combineHash(seed, uvec3(hashes_offset0.x, hashes_offset1.yz));
            simplex_corner1 = ivec3(0,0,1);
            simplex_corner2 = ivec3(0,1,1);
        }
        else if (subpos0.x < subpos0.z)
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset0.x, hashes_offset1.y, hashes_offset0.z));
            hashval2 = combineHash(seed, uvec3(hashes_offset0.x, hashes_offset1.yz));
            simplex_corner1 = ivec3(0,1,0);
            simplex_corner2 = ivec3(0,1,1);
        }
        else // subpos0.x >= subpos0.z
        {
            hashval1 = combineHash(seed, uvec3(hashes_offset0.x, hashes_offset1.y, hashes_offset0.z));
            hashval2 = combineHash(seed, uvec3(hashes_offset1.xy, hashes_offset0.z));
            simplex_corner1 = ivec3(0,1,0);
            simplex_corner2 = ivec3(1,1,0);
        }
    }

    //we would do this if we didn't want to seperate the hash values. 
    //hashval0 = fullHash(seed, uvec3(simplex_corner0));
    //hashval1 = fullHash(seed, uvec3(simplex_corner0+simplex_corner1));
    //hashval2 = fullHash(seed, uvec3(simplex_corner0+simplex_corner2));
    //hashval3 = fullHash(seed, uvec3(simplex_corner0+1));

    vec3 subpos1 = subpos0 - vec3(simplex_corner1) + UNSKEW3D;
    vec3 subpos2 = subpos0 - vec3(simplex_corner2) + 2.0*UNSKEW3D;
    vec3 subpos3 = subpos0 + FAR_CORNER_UNSKEW3D;
    float n0, n1, n2, n3;

    //http://catlikecoding.com/unity/tutorials/simplex-noise/
    //circle distance factor to make sure second derivative is continuous
    // t variables represent (1 - x^2 + y^2 + ...)^3, a distance function with 
    // continous first and second derivatives that are zero when x is one. 
    float t0 = DISTCONST_3D - subpos0.x*subpos0.x - subpos0.y*subpos0.y - subpos0.z*subpos0.z;
    //if t < 0, we get odd dips in continuity at the ends, so we just force it to zero
    // to prevent it
    if(t0 < 0.0){
        n0 = 0.0;
    }else{
        float t0_pow2 = t0 * t0;
        float t0_pow4 = t0_pow2 * t0_pow2;
        vec3 grad = getGradient3Old(hashval0);
        float product = dot(subpos0, grad);
        n0 = t0_pow4 * product;
    }
    float t1 = DISTCONST_3D - subpos1.x*subpos1.x - subpos1.y*subpos1.y - subpos1.z*subpos1.z;
    if(t1 < 0.0){
        n1 = 0.0;
    }else{
        float t1_pow2 = t1 * t1;
        float t1_pow4 = t1_pow2 * t1_pow2;
        vec3 grad = getGradient3Old(hashval1);
        float product = dot(subpos1, grad);
        n1 = t1_pow4 * product;
    }
    float t2 = DISTCONST_3D - subpos2.x*subpos2.x - subpos2.y*subpos2.y - subpos2.z*subpos2.z;
    if(t2 < 0.0){
        n2 = 0.0;
    }else{
        float t2_pow2 = t2 * t2;
        float t2_pow4 = t2_pow2*t2_pow2;
        vec3 grad = getGradient3Old(hashval2);
        float product = dot(subpos2, grad);
        n2 = t2_pow4 * product;
    }

    float t3 = DISTCONST_3D - subpos3.x*subpos3.x - subpos3.y*subpos3.y - subpos3.z*subpos3.z;
    if(t3 < 0.0){
        n3 = 0.0;
    }else{
        float t3_pow2 = t3 * t3;
        float t3_pow4 = t3_pow2*t3_pow2;
        vec3 grad = getGradient3Old(hashval3);
        float product = dot(subpos3, grad);
        n3 = t3_pow4 * product;
    }
    return (n0 + n1 + n2 + n3);
}

//settings for fractal brownian motion noise
struct BrownianFractalSettings{
    uint seed;
    int octave_count;
    float frequency;
    float lacunarity;
    float persistence;
    float amplitude;
};

float accumulateSimplexNoiseV(in BrownianFractalSettings settings, vec3 pos, float wrap){
    float accumulated_noise = 0.0;
    wrap *= settings.frequency;
    vec3 octave_pos = pos * settings.frequency;
    for (int octave = 0; octave < settings.octave_count; octave++) {
        octave_pos = modv(octave_pos, wrap);
        float noise = simplexNoiseV(settings.seed, octave_pos, uint(wrap));
        noise *= pow(settings.persistence, float(octave));
        accumulated_noise += noise;
        octave_pos *= settings.lacunarity;
        wrap *= settings.lacunarity;
    }
    float scale = 2.0 - pow(settings.persistence, float(settings.octave_count - 1));
    return (accumulated_noise/scale) * NORMALIZE_SCALE3D * settings.amplitude;
}

const float FREQUENCY = 1.0/8.0;
const float WRAP = 32.0;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    //set to zero in order to stop scrolling, scrolling shows the lack of tilability between
    //wrapping. 
    const float use_sin_debug = 1.0;
    vec3 origin = vec3(norm01(sin(iTime))*64.0*use_sin_debug,0.0,0.0);
    vec3 color = vec3(0.0,0.0,0.0);
    BrownianFractalSettings brn_settings = 
        BrownianFractalSettings(203u, 1, FREQUENCY, 2.0, 0.4, 1.0);
    const int size_dim = 32;
    ivec2 z_size = ivec2(8, 4);
    ivec2 iFragCoord = ivec2(fragCoord.x, fragCoord.y);
    int z_dim_x = iFragCoord.x / size_dim;
    int z_dim_y = iFragCoord.y / size_dim;

    if(z_dim_x < z_size.x && z_dim_y < z_size.y){
        int ix = iFragCoord.x % size_dim;
        int iy = iFragCoord.y % size_dim;
        int iz = (z_dim_x) + ((z_dim_y)*z_size.x);
        vec3 pos = vec3(ix,iy,iz) + origin; 
        float value = accumulateSimplexNoiseV(brn_settings, pos, WRAP);
        color = vec3(norm01(value));
    }else{
        color = vec3(1.0,0.0,0.0);
    }
    fragColor = vec4(color,1.0);
}
//Image, used to finally display
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    const float fcm = 4.0;
    //grabs a single 32x32 tile in order to test tileability, currently generates
    //a whole array of images however. 
    vec2 fragCoordMod = vec2(mod(fragCoord.x, 32.0 * fcm), mod(fragCoord.y, 32.0 * fcm));
    vec3 color = texture(iChannel2, fragCoordMod/(fcm*iResolution.xy)).xyz;
    fragColor = vec4(color, 1.0);
}

我尝试定位%包装值,通过隐秘修改包装值以及在当前使用的扭曲%包装值之后进行的操作(在simplexNoiseV中查找核心算法,在accumulateSimplexNoiseV中查找八度总和)。

根据these answers,应该就是这么简单(用于散列的mod位置),但是显然这是行不通的。我不确定这是否部分是因为我的哈希函数不是Ken Perlin的哈希函数,但似乎并没有什么不同。似乎坐标的倾斜应该使该方法根本不起作用,但是显然其他人已经成功了。

以下是一个不进行平铺的示例:enter image description here

为什么换行坐标不能使我的单面噪声平铺无缝?

更新:

我仍然没有解决该问题,但似乎在简化程序中[]拼贴工作正常,而不是此处看到的网格

enter image description here

我必须修改我的模数以解决偏斜吗?

0 个答案:

没有答案