C++ Perlin Noise

柏林噪声,这个弄了好久才弄对,明白了之后突然发现这东西很简单。

百度上的东西真的不靠谱,没有几个说到重点的。最后是靠google解决的。

当然,这里我也不会说重点。

上代码:

 

源码位置: https://files-cdn.cnblogs.com/files/rkexy/PerlinNoiseTest.zip

 

point.h

#pragma once

#include <stdint.h>
#include <type_traits>
#include <string>

template<uint8_t _D, typename _T_ele = double>
class __point 
{
public:
    using type = _T_ele;
    using index_type = decltype(_D);

protected:
    _T_ele _eles[_D];

public:
    template<typename ... TArgs>
    __point(TArgs... args)
    {
        static_assert(sizeof...(TArgs) == _D, "[ERROR] Wrong parameter count for construct of class point.");
        __point_init(args...);
    }

    __point() { clear(); }
    ~__point() { clear(); }

public:
    void clear() { std::memset(_eles, 0, sizeof _eles); }

public:
    template<index_type i> inline _T_ele& get() { return _eles[i]; }
    template<index_type i> inline _T_ele get() const { return _eles[i]; }
    inline _T_ele& get(index_type i) { return _eles[i]; }
    inline _T_ele get(index_type i) const { return _eles[i]; }

private:
    template<
        typename TArg,
        typename ... TArgs,
        typename std::enable_if<std::is_arithmetic<TArg>::value, TArg>::type* = nullptr
    > void __point_init(TArg arg0, TArgs... args)
    {
        constexpr index_type _index = _D - sizeof...(TArgs) - 1;
        _eles[_index] = arg0;

        __point_init(args...);
    }

    template<
        typename TArg,
        typename std::enable_if<std::is_arithmetic<TArg>::value, TArg>::type* = nullptr
    > void __point_init(TArg arg0)
    {
        _eles[_D - 1] = arg0;
    }
};


class point2d 
    : public __point<2, double>
{
public:
    point2d() : point2d(0, 0) {}

    point2d(double x, double y)
    {
        get<0>() = x;
        get<1>() = y;
    }

    ~point2d() { clear(); }

public:
    inline double x() const { return get<0>(); }
    inline double& x() { return get<0>(); }
    inline double y() const { return get<1>(); }
    inline double& y() { return get<1>(); }
};

#define vector2d point2d

class point2di
    : public __point<2, int>
{
public:
    point2di() : point2di(0, 0) {}

    point2di(int x, int y)
    {
        get<0>() = x;
        get<1>() = y;
    }

    ~point2di() { clear(); }

public:
    inline int x() const { return get<0>(); }
    inline int& x() { return get<0>(); }
    inline int y() const { return get<1>(); }
    inline int& y() { return get<1>(); }
};

 

grid.h

#pragma once

#include "point.h"

struct Grid
{
public:
    Grid() : Grid(0, 0, 0) {}
    Grid(int x, int y, int w) : _ul({ x, y }), _w(w) {}

public:
    point2di _ul;
    int _w;

public:
    const point2di& ul() const { return _ul; }
    point2di ur() const { return point2di(_ul.x() + _w, _ul.y()); }
    point2di bl() const { return point2di(_ul.x(), _ul.y() + _w); }
    point2di br() const { return point2di(_ul.x() + _w, _ul.y() + _w); }
};

 

perlin.h

#pragma once

#include <array>

#include "point.h"
#include "grid.h"

namespace perlin 
{
    class perlin2d
    {
    public:    //random
        static int rand_i(int x);

        //@return [0.0, 1.0]
        static double rand_d(int x);

    public:
        static vector2d get_gv(int x, int y);    // random completely
        static double fade(double x);
        static double fade2(double x);
        static double lerp(double a, double b, double x);
        static double grad(int x, int y, const vector2d& v);
        static double dot(const vector2d& v1, const vector2d& v2);
        static double gen(int x, int y, const Grid& grid);
        static bool is_valid(int x, int y, const Grid& grid);


    public:
        enum { PSIZE = 10086 };
        static void init(int seed = 123);
        static int _p[PSIZE];
    };
}

 

#include "grid.h"

#include "perlin.h"

#include <random>

namespace perlin
{
    int perlin2d::_p[PSIZE] = { 0 };
    
    int perlin2d::rand_i(int x)
    {
        x = (x << 13) ^ x;
        return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7FFFFFFF);
    }

    double perlin2d::rand_d(int x)
    {
        return (1.0 - rand_i(x) * 1.0 / 1073741824.0);
    }

    vector2d perlin2d::get_gv(int x, int y)
    {
        return vector2d(rand_d(x), rand_d(y));
    }

    double perlin2d::fade(double x)
    {
        return 6 * x * x * x * x * x - 15 * x * x * x * x + 10 * x * x * x;
    }

    double perlin2d::fade2(double x)
    {
        return (3 * x * x - 2 * x * x * x);
    }

    double perlin2d::lerp(double a, double b, double x)
    {
        return a + x * (b - a);
    }  

    double perlin2d::grad(int x, int y, const vector2d& v)
    {
        vector2d gv = get_gv(_p[_p[x] + y], _p[_p[y] - x]);
        return gv.x() * v.x() + gv.y() * v.y();    //dot
    }

    double perlin2d::dot(const vector2d& v1, const vector2d& v2)
    {
        return v1.x() * v2.x() + v1.y() * v2.y();
    }

    bool perlin2d::is_valid(int x, int y, const Grid& grid)
    {
        return x >= grid.ul().x() && x < grid.ur().x()
            && y >= grid.ul().y() && y < grid.bl().y()
            ;
    }

    void perlin2d::init(int seed)
    {
        srand(seed);
        for (int i = 0; i < PSIZE; ++i)
        {
            int r = rand();
            _p[i] = r % PSIZE;
        }
    }

    double perlin2d::gen(int x, int y, const Grid& grid)
    {
        if (!is_valid(x, y, grid))
        {
            return 0.0;
        }
        
        int dx = x % grid._w;
        int dy = y % grid._w;

        //point P
        double fx = dx * 1.0 / grid._w;
        double fy = dy * 1.0 / grid._w;

        double u = fade(fx);
        double v = fade(fy);

        int ix = grid.ul().x() / grid._w;
        int iy = grid.ul().y() / grid._w;

        /*
            g0------g1
             ┆      ┆
             ┆      ┆
            g3------g2 
        */
        double g0 = grad(ix,     iy,     { fx,       fy      });    //ul: (0, 0); vector ul->P (fx - 0, fy - 0)
        double g1 = grad(ix + 1, iy,     { fx - 1, fy      });    //ur: (1, 0); vector ur->P (fx - 1, fy - 0)
        double g2 = grad(ix + 1, iy + 1, { fx - 1, fy - 1 });   //br: (1, 1); vector br->P (fx - 1, fy - 1)
        double g3 = grad(ix,     iy + 1, { fx,       fy - 1 });   //bl: (0, 1); vector bl->P (fx - 0, fy - 1)
        
        double g01 = lerp(g0, g1, u);
        double g12 = lerp(g3, g2, u);
        double gv = lerp(g01, g12, v);
        gv = (gv + 1) / 2;return gv;
    }
}

 

map.h

#pragma once

#include <array>
#include <functional>

#include "point.h"
#include "perlin.h"

namespace perlin
{
    template<int WIDTH_, int HEIGHT_, int GRID_>
    class map
    {
    public:
        map();
        ~map();

    public:
        enum 
        {
            WIDTH = WIDTH_ ,
            HEIGHT = HEIGHT_,
            GRID_SIZE = GRID_,
            GRID_CNT_W = WIDTH_ / GRID_SIZE,
            GRID_CNT_H = HEIGHT_ / GRID_SIZE,
        };

        using TVal = double;
        using TCoords = std::array<std::array<TVal, HEIGHT>, WIDTH>;

    public:
        inline TCoords& Coords() { return _coords; }
        inline const TCoords& Coords() const { return _coords; }

        inline bool IsCoordValid(int x, int y) const 
        {
            return
                x < WIDTH
                && y < HEIGHT
                && x >= 0
                && y >= 0
                ;
        }

        inline TVal& Val(int x, int y) { return _coords[x][y]; }
        inline const TVal& Val(int x, int y) const { return _coords[x][y]; }

        inline point2di Relative(int x, int y) const
        {
            return point2di(x % GRID_SIZE, y % GRID_SIZE);
        }

        //up-left at line.
        inline Grid GetGrid(int x, int y) const
        {
            return Grid(x - x % GRID_SIZE, y - y % GRID_SIZE, GRID_SIZE);
        }

        void foreach(std::function<bool(int x, int y, const TVal& v)>&& fc) const
        {
            for (int x = 0; x < WIDTH; ++x)
            {
                for (int y = 0; y < HEIGHT; ++y)
                {
                    if (fc(x, y, Val(x, y)))
                        continue;
                    else break;
                }
            }
        }
        
    private:
        TCoords _coords;

    public:
        double _min;
        double _max;
    };

    template<int W, int H, int G>
    map<W, H, G>::map()
    {
        static_assert(W % G == 0 && H % G == 0, "[ERROR] The Wrong Value Of Grid.");

        _min = 1;
        _max = 0;

        perlin::perlin2d::init();

        for (int x = 0; x < W; ++x)
        {
            for (int y = 0; y < H; ++y)
            {
                double v = perlin::perlin2d::gen(x, y, GetGrid(x, y)) 
                ;
                Val(x, y) = v;

                if (v > _max)
                    _max = v;

                if (v < _min)
                    _min = v;
            }
                
        }
    }

    template<int W, int H, int G>
    map<W, H, G>::~map()
    {
    }

}

 

win32程序,全局变量

 perlin::map<1800, 1000, 50> _mm; 

 

win32程序,WndProc函数

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE: 
    {
        int edge = 59;
        MoveWindow(hWnd, edge, edge, _mm.WIDTH, _mm.HEIGHT + edge, false);

        break;
    }

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            _mm.foreach(
                [&](int x, int y, const double& v) -> bool
                {
                    SetPixel(hdc, x, y, RGB(0xff * v, 0xff * v, 0xff * v));
                    return true;
                }
            );

            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

 

效果图

 

posted on 2022-04-19 19:18  __Even  阅读(254)  评论(0编辑  收藏  举报

导航