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; }
效果图