Why does switching from Mersenne twister to other PRNGs in the gradient noise generator give poor results?

I am trying to create a generalized gradient noise generator (which does not use the hash method to get gradients). Code below:

class GradientNoise {
    std::uint64_t m_seed;
    std::uniform_int_distribution<std::uint8_t> distribution;
    const std::array<glm::vec2, 4> vector_choice = {glm::vec2(1.0, 1.0), glm::vec2(-1.0, 1.0), glm::vec2(1.0, -1.0),
                                                    glm::vec2(-1.0, -1.0)};

public:
    GradientNoise(uint64_t seed) {
        m_seed = seed;
        distribution = std::uniform_int_distribution<std::uint8_t>(0, 3);
    }

    // 0 -> 1
    // just passes the value through, origionally was perlin noise activation
    double nonLinearActivationFunction(double value) {
        //return value * value * value * (value * (value * 6.0 - 15.0) + 10.0);
        return value;
    }

    // 0 -> 1
    //cosine interpolation
    double interpolate(double a, double b, double t) {
        double mu2 = (1 - cos(t * M_PI)) / 2;
        return (a * (1 - mu2) + b * mu2);
    }

    double noise(double x, double y) {
        std::mt19937_64 rng;
        //first get the bottom left corner associated
        // with these coordinates
        int corner_x = std::floor(x);
        int corner_y = std::floor(y);

        // then get the respective distance from that corner
        double dist_x = x - corner_x;
        double dist_y = y - corner_y;

        double corner_0_contrib; // bottom left
        double corner_1_contrib; // top left
        double corner_2_contrib; // top right
        double corner_3_contrib; // bottom right

        std::uint64_t s1 = ((std::uint64_t(corner_x) << 32) + std::uint64_t(corner_y) + m_seed);
        std::uint64_t s2 = ((std::uint64_t(corner_x) << 32) + std::uint64_t(corner_y + 1) + m_seed);
        std::uint64_t s3 = ((std::uint64_t(corner_x + 1) << 32) + std::uint64_t(corner_y + 1) + m_seed);
        std::uint64_t s4 = ((std::uint64_t(corner_x + 1) << 32) + std::uint64_t(corner_y) + m_seed);


        // each xy pair turns into distance vector from respective corner, corner zero is our starting corner (bottom
        // left)
        rng.seed(s1);
        corner_0_contrib = glm::dot(vector_choice[distribution(rng)], {dist_x, dist_y});

        rng.seed(s2);
        corner_1_contrib = glm::dot(vector_choice[distribution(rng)], {dist_x, dist_y - 1});


        rng.seed(s3);
        corner_2_contrib = glm::dot(vector_choice[distribution(rng)], {dist_x - 1, dist_y - 1});


        rng.seed(s4);
        corner_3_contrib = glm::dot(vector_choice[distribution(rng)], {dist_x - 1, dist_y});


        double u = nonLinearActivationFunction(dist_x);
        double v = nonLinearActivationFunction(dist_y);


        double x_bottom = interpolate(corner_0_contrib, corner_3_contrib, u);
        double x_top = interpolate(corner_1_contrib, corner_2_contrib, u);
        double total_xy = interpolate(x_bottom, x_top, v);
        return total_xy;
    }
};

Then I create an OpenGL texture to display as follows:

int width = 1024;
int height = 1024;
unsigned char *temp_texture = new unsigned char[width*height * 4];
double octaves[5] = {2,4,8,16,32};

for( int i = 0; i < height; i++){
    for(int j = 0; j < width; j++){
        double d_noise = 0;
        d_noise += temp_1.noise(j/octaves[0], i/octaves[0]);
        d_noise += temp_1.noise(j/octaves[1], i/octaves[1]);
        d_noise += temp_1.noise(j/octaves[2], i/octaves[2]);
        d_noise += temp_1.noise(j/octaves[3], i/octaves[3]);
        d_noise += temp_1.noise(j/octaves[4], i/octaves[4]);
        d_noise/=5;
        uint8_t noise = static_cast<uint8_t>(((d_noise * 128.0) + 128.0));
        temp_texture[j*4 + (i * width * 4) + 0] = (noise);
        temp_texture[j*4 + (i * width * 4) + 1] = (noise);
        temp_texture[j*4 + (i * width * 4) + 2] = (noise);
        temp_texture[j*4 + (i * width * 4) + 3] = (255);
    }
}

Which gives good results:

enter image description here

But gprof tells me that the Mersenne twister takes 62.4% of my time and grows with large textures. Nothing else takes anyone where almost the same amount of time. While Mersenne twister is fast after initialization, the fact that I initialize it every time I use it seems to make it pretty slow.

100% , , x y ( -, RNG ).

PRNG , Xorshiftplus, , , :

LCG ( , 5 ) enter image description here

enter image description here

Xorshiftplus

enter image description here

10 000 . enter image description here

:

, .

, PRNG . .

? , , , mersenne twister?

:

, , , - , , , :

1, x y ( , / )

2, , (, , )

3, (4), , 3, 3. , .

prime = 257 xorshiftplus! ( 2048 2048 , 256 256)

enter image description here

+4
5

LCG, , .

Xorshift128 + , . . .

. , Bob.

, , ( , , ): enter image description here

, :

#include <cmath>
#include <stdio.h>

unsigned int hash(unsigned int a) {
    a = (a ^ 61) ^ (a >> 16);
    a = a + (a << 3);
    a = a ^ (a >> 4);
    a = a * 0x27d4eb2d;
    a = a ^ (a >> 15);
    return a;
}

unsigned int ivalue(int x, int y) {
    return hash(y<<16|x)&0xff;
}

float smooth(float x) {
    return 6*x*x*x*x*x - 15*x*x*x*x + 10*x*x*x;
}

float value(float x, float y) {
    int ix = floor(x);
    int iy = floor(y);
    float fx = smooth(x-ix);
    float fy = smooth(y-iy);

    int v00 = ivalue(iy+0, ix+0);
    int v01 = ivalue(iy+0, ix+1);
    int v10 = ivalue(iy+1, ix+0);
    int v11 = ivalue(iy+1, ix+1);
    float v0 = v00*(1-fx) + v01*fx;
    float v1 = v10*(1-fx) + v11*fx;
    return v0*(1-fy) + v1*fy;
}

unsigned char pic[1024*1024];

int main() {
    for (int y=0; y<1024; y++) {
        for (int x=0; x<1024; x++) {
            float v = 0;

            for (int o=0; o<=9; o++) {
                v += value(x/64.0f*(1<<o), y/64.0f*(1<<o))/(1<<o);
            }

            int r = rint(v*0.5f);

            pic[y*1024+x] = r;
        }
    }

    FILE *f = fopen("x.pnm", "wb");
    fprintf(f, "P5\n1024 1024\n255\n");
    fwrite(pic, 1, 1024*1024, f);
    fclose(f);
}

, - ( , ), , .

+2

(?) PRNG. !

, PRNG . , PRNG.

+2

, LCG . , LCG m - , m ( ), .

, Xorshift PRNG.

, PRNG , . , proc gen PRNG. , ( ) . , , . , , proc gen.

- - Diehard. - , , , PRNG .

+1

, PRNG, PRNG. xorshift128+, , , xorshift128+ ( mod 2 64 ) ( ). PRNG -.

+1

, , - PRNG. Mersenne Twister - PRNG , DIEHARD. , - , . LCG, , PRNG, - , , . Xorshift . , Mersenne Twister, (xorshift128 +) , BigCrush TestU01.

, , Mersenne Twister, , , . xorshift128+. PRNG ( ).

0

All Articles