6
\$\begingroup\$

I am trying to write a perlin noise class with c++ and sfml, but I am experiencing unwanted visual artifacts in the output. In the image, there are obvious lines being drawn between the points of the lattice:

My incorrect perlin noise


The class template representing the noise is a specialization of lattice noise for vectors. The important function, GetValue() expects values between 0.0-1.0, representing the coordinates at which to get the value of the noise, which should also return a value between 0.0-1.0. Calling the function at (0, 0) should return the value at the top left corner. NormX and normY are the distance between lattice points x0-x1 and y0-y1, again in the range 0.0-1.0.

Before calculating the dot product, the distance vector from each corner lattice point is calculated instead of only using the distance from the top left corner. Because the final value at any point may be negative (-1.0 - 1.0), it is mapped to the range 0.0-1.0 by adding 1 and dividing by two before being returned.

template<>
inline float LatticeNoise2D<sf::Vector2f>::GetValue(float u, float v)
{
    float normX = u * resX_;
    int x0 = normX;
    int x1 = x0 + 1;
    normX -= x0;

    float normY = v * resY_;
    int y0 = normY;
    int y1 = y0 + 1;
    normY -= y0;

    float fadeX = Fade(normX);
    float fadeY = Fade(normY);

    sf::Vector2f dist(normX, normY);
    float dot0 = Dot(At(x0, y0), dist);
    dist = sf::Vector2f(1 - normX, normY);
    float dot1 = Dot(At(x1, y0), dist);
    float lerp0 = Lerp(dot0, dot1, fadeX);

    dist = sf::Vector2f(normX, 1 - normY);
    dot0 = Dot(At(x0, y1), dist);
    dist = sf::Vector2f(1 - normX, 1 - normY);
    dot1 = Dot(At(x1, y1), dist);
    float lerp1 = Lerp(dot0, dot1, fadeX);

    float value = Lerp(lerp0, lerp1, fadeY);
    return (value + 1) / 2;
}

The At(x, y) function returns the pre-generated vector on the lattice (a 2d std::vector) at the given index. The dimensions of the lattice in each dimension are resX_ and resY_, respectivley.

template<typename value_type>
inline value_type LatticeNoise2D<value_type>::At(int x, int y)
{
    return lattice_[x % resX_][y % resY_];
}

The vectors at each point on the lattice are generated on the unit circle by randomly choosing an angle in radians (0-2pi) and then calculating the x & Y components given that the length of their hypotenuse is 1.0.

template<>
inline sf::Vector2f LatticeNoise2D<sf::Vector2f>::Random()
{
    float pi = 4 * atan(1);
    float theta = static_cast<float>(rand()) / RAND_MAX * 2 * pi;
    return sf::Vector2f(cos(theta), sin(theta));
}

The Lerp() function performs linear interpolation, and the Dot() function calculates the dot product of two vectors. The Fade(t) function maps a value from 0.0-0.1 to an S Curve to be used in the Lerp function instead of just the linear distance between lattice points.


Why am I getting these lines between the points of the lattice, and how can I fix them? My goal is to make noise like this example:

Ideal perlin noise

\$\endgroup\$
  • \$\begingroup\$ Does it improve if you switch from using linear interpolation to using a smoothstep function? \$\endgroup\$ – user1118321 Apr 4 '17 at 5:15
  • 1
    \$\begingroup\$ I would start by replacing rand() with c++ RNG, rand() is decades old and de facto obsolete in modern c++, not even mentioning RAND_MAX is typically very small integer, as small as 32k. \$\endgroup\$ – wondra Apr 4 '17 at 6:35
  • \$\begingroup\$ @user1118321 I'm not really just doing linear interpolation, because the value gets passed through the Fade() function before being passed to Lerp(). Fade() smooths it according to the curve 6t^5-15t^4+10t^t, similarly to the curve used by smoothstep (according to Wikipedia) 3t^2-2t^3. Using smoothstep did not significantly change the image. \$\endgroup\$ – Paraxon Apr 4 '17 at 23:06
  • \$\begingroup\$ OK, I should have read that part a little more closely. I know that I had similar artifacts that went away when I switched from linear to smoothstep, but must be something else this time. \$\endgroup\$ – user1118321 Apr 5 '17 at 1:17
  • \$\begingroup\$ Aren't normX and normY always 0 when going into Fade()? You set x0 to normX and then later subtract x0 from normX, leaving you with 0. \$\endgroup\$ – user1118321 Apr 5 '17 at 1:21
6
\$\begingroup\$

I don't entirely understand why, but I shouldn't have been doing 1-normX/Y. Instead, I looked at the question posted here and our approaches were similar enough that I could make It work. Changing from 1-x to x-1 seemed to do the trick.

\$\endgroup\$
  • 3
    \$\begingroup\$ Thank you for coming back and answering your own question. If only 98% of new users did the same. Good luck further in your endeavours. \$\endgroup\$ – Engineer Jun 30 '17 at 7:06
  • \$\begingroup\$ Thanks! I wanted anyone with a similar problem to reach the answer with less searching than I did. \$\endgroup\$ – Paraxon Aug 23 '17 at 3:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.