Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrote some perlin noise kind of code, it looks blocky

The previous answered question doesn't seem to answer my problem "Blocky" Perlin noise

I tried to simplify the most I could to make my code readable and understandable.

I don't use the permutation table, instead I use the mt19937 generator.

I use SFML

using namespace std;
using namespace sf;
typedef Vector2f Vec2;
Sprite spr;
Texture tx;
// dot product
float        prod(Vec2 a, Vec2 b)       { return a.x*b.x + a.y*b.y; }
// linear interpolation
float         interp(float start,float end,float coef){return coef*(end-start)+start;}
// get the noise of a certain pixel, giving its relative value vector in the square with [0.0 1.0] values
float getnoise(Vec2&A, Vec2&B, Vec2&C, Vec2&D, Vec2 rel){
    float
    dot_a=prod(A ,Vec2(rel.x   ,rel.y)),
    dot_b=prod(B ,Vec2(rel.x-1 ,rel.y)),
    dot_c=prod(C ,Vec2(rel.x   ,rel.y-1)),
    dot_d=prod(D ,Vec2(rel.x-1 ,rel.y-1));
    return interp
    (interp(dot_a,dot_b,rel.x),interp(dot_c,dot_d,rel.x),rel.y);
//    return interp
//    (interp(da,db,rel.x),interp(dc,dd,rel.x),rel.y);
}
// calculate the [0.0 1.0] relative value of a pixel
Vec2 getrel(int i, int j, float cellsize){
    return Vec2
    (float
     (i // which pixel
      -(i/int(cellsize))//which cell
      *cellsize)// floor() equivalent
      /cellsize,// [0,1] range
     float(j-(j/int(cellsize))*cellsize)/cellsize
     );
}
// generates an array of random float values
vector<float> seeded_rand_float(unsigned int seed, int many){
    vector<float> ret;
    std::mt19937 rr;
    std::uniform_real_distribution<float> dist(0, 1.0);

    rr.seed(seed);

    for(int j = 0 ; j < many; ++j)
        ret.push_back(dist(rr));
    return ret;
}
// use above function to generate an array of random vectors with [0.0 1.0] values
vector<Vec2>seeded_rand_vec2(unsigned int seed, int many){
    auto coeffs1 = seeded_rand_float(seed, many*2);
//    auto coeffs2 = seeded_rand_float(seed+1, many); //bad choice !
    vector<Vec2> pushere;
    for(int i = 0; i < many; ++i)
        pushere.push_back(Vec2(coeffs1[2*i],coeffs1[2*i+1]));
//    pushere.push_back(Vec2(coeffs1[i],coeffs2[i]));
    return pushere;
}
// here we make the perlin noise
void make_perlin()
{
    int seed = 43;
    int pixels = 400; // how many pixels
    int divisions = 10; // cell squares
    float cellsize = float(pixels)/divisions; // size of a cell

    auto randv = seeded_rand_vec2(seed,(divisions+1)*(divisions+1));
    // makes the vectors be in [-1.0 1.0] range
    for(auto&a:randv)
        a = a*2.0f-Vec2(1.f,1.f);
    Image img;
    img.create(pixels,pixels,Color(0,0,0));

    for(int j=0;j<=pixels;++j)
    {
        for(int i=0;i<=pixels;++i)
        {
            int ii = int(i/cellsize); // cell index
            int jj = int(j/cellsize);
            // those are the nearest gradient vectors for the current pixel
            Vec2
            A = randv[divisions*jj      +ii],
            B = randv[divisions*jj      +ii+1],
            C = randv[divisions*(jj+1)  +ii],
            D = randv[divisions*(jj+1)  +ii+1];

            float val = getnoise(A,B,C,D,getrel(i,j,cellsize));
            val = 255.f*(.5f * val + .7f);

            img.setPixel(i,j,Color(val,val,val));
        }
    }
    tx.loadFromImage(img);
    spr.setPosition(Vec2(10,10));
    spr.setTexture(tx);
};

Here are the results, I included the resulted gradients vector (I multiplied them by cellsize/2).

result1

result2

My question is why are there white artifacts, you can somehow see the squares...

PS: it has been solved, I posted the fixed source here http://pastebin.com/XHEpV2UP

Don't make the mistake of applying a smooth interp on the result instead of the coefficient. Normalizing vectors or adding an offset to avoid zeroes doesn't seem to improve anything. Here is the colorized result: result3

like image 717
jokoon Avatar asked Jun 26 '14 15:06

jokoon


People also ask

What is Lacunarity in Perlin noise?

Lacunarity. A multiplier that determines how quickly the frequency increases for each successive octave in a Perlin-noise function. The frequency of each successive octave is equal to the product of the previous octave's frequency and the lacunarity value.

Is Perlin noise random?

Perlin noise is a procedural texture primitive, a type of gradient noise used by visual effects artists to increase the appearance of realism in computer graphics. The function has a pseudo-random appearance, yet all of its visual details are the same size.

Is Perlin noise normally distributed?

But a perlin noise is normal distributed (ranges from -1 to 1, mean at 0) so it's not that easy.

What is noise in coding?

In computer science, syntactic noise is syntax within a programming language that makes the programming language more difficult to read and understand for humans. It fills the language with excessive clutter that makes it a hassle to write code.


1 Answers

The human eye is sensitive to discontinuities in the spatial derivative of luminance (brightness). The linear interpolation you're using here is sufficient to make brightness continuous, but it does not not make the derivative of the brightness continuous.

Perlin recommends using eased interpolation to get smoother results. You could use 3*t^2 - 2*t^3 (as suggested in the linked presentation) right in your interpolation function. That should solve the immediate issue.

That would look something like

// interpolation
float        linear(float start,float end,float coef){return coef*(end-start)+start;}
float        poly(float coef){return 3*coef*coef - 2*coef*coef*coef;}
float        interp(float start,float end,float coef){return linear(start, end, poly(coef));}

But note that evaluating a polynomial for every interpolation is needlessly expensive. Usually (including here) this noise is being evaluated over a grid of pixels, with squares being some integer (or rational) number of pixels large; this means that rel.x, rel.y, rel.x-1, and rel.y-1 are quantized to particular possible values. You can make a lookup table for values of the polynomial ahead of time at those values, replacing the "poly" function in the code snippet provided. This technique lets you use even smoother (e.g. degree 5) easing functions at very little additional cost.

like image 159
Jerry Federspiel Avatar answered Nov 15 '22 11:11

Jerry Federspiel