Среда программирования:
Linux QT Creator
Статья по теме:
Пример для шума перлина, кода довольно много, но он хорошо раскомментирован.
Весь шум перлина отделен в отдельный класс - можете использовать по своему усмотрению.
Запуск: выкидываете ехе-фаил в любое удобное место и кладете рядом SDL.dll, должно работать.
Интерфейс: Окно с сами шумом. Вводите 3 коэффициента через пробел и получаете отрисовку шума.
Управление: консоль
Код программы:
Конструктор класса для шума
//PN.H BEGIN #ifndef PN_H #define PN_H #include <vector> class PN { // Вектор значений std::vector<int> p; public: // Инициализируем значения вектора PN(); // Генерирует новый вектор из зерна PN(unsigned int PSeed); // Получаем 2D шум, z может принимать любое значение double PNoise(double x, double y, double z); private: double FadeOut(double t); double LinearInterpolation(double t, double a, double b); double Gradient(int hash, double x, double y, double z); }; #endif // PN.H END
сам шум:
//PN.CPP BEGIN #include "PN.h" #include <algorithm> #include <numeric> #include <random> PN::PN() { // Инициализируем вектор псевдослучайных чисел p = { 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142, 8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117, 35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71, 134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41, 55,46,245,40,244,102,143,54, 65,25,63,161,1,216,80,73,209,76,132,187,208, 89, 18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226, 250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182, 189,28,42,223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246, 97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239, 107,49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; p.insert(p.end(), p.begin(), p.end()); } // Генерируем новый вектор из зерна (рандомное число = рандомный вектор) PN::PN(unsigned int PSeed) { p.resize(256); //сжимаем вектор до 256 значений // Заполним р значениями от 0 до 255 std::iota(p.begin(), p.end(), 0); // Инициализируем рандомные значения зерном std::default_random_engine engine(PSeed); // Перемешиваем значения вектора std::shuffle(p.begin(), p.end(), engine); // И вставим p.insert(p.end(), p.begin(), p.end()); } double PN::PNoise(double x, double y, double z) { // Находим куб содержащий данную точку int X = (int) floor(x) & 255; int Y = (int) floor(y) & 255; int Z = (int) floor(z) & 255; // Находим значение x, y, z после запятой x -= floor(x); y -= floor(y); z -= floor(z); // Вычисляем кривую по значения переменных double u = FadeOut(x); double v = FadeOut(y); double w = FadeOut(z); // Получаем 8 векторов из углов куба int A = p[X] + Y; int AA = p[A] + Z; int AB = p[A + 1] + Z; int B = p[X + 1] + Y; int BA = p[B] + Z; int BB = p[B + 1] + Z; double res = LinearInterpolation(w, LinearInterpolation(v, LinearInterpolation(u, Gradient(p[AA], x, y, z), Gradient(p[BA], x-1, y, z)), LinearInterpolation(u, Gradient(p[AB], x, y-1, z), Gradient(p[BB], x-1, y-1, z))), LinearInterpolation(v, LinearInterpolation(u, Gradient(p[AA+1], x, y, z-1), Gradient(p[BA+1], x-1, y, z-1)), LinearInterpolation(u, Gradient(p[AB+1], x, y-1, z-1), Gradient(p[BB+1], x-1, y-1, z-1)))); return (res + 1.0)/2.0; } double PN::FadeOut(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } double PN::LinearInterpolation(double t, double a, double b) { return a + t * (b - a); } double PN::Gradient(int hash, double x, double y, double z) { int h = hash & 15; // Конвертирует 4 нижних значения векторов в 12 градиентных double u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } // PN.CPP END
Графическая обертка (SDL2):
//main.cpp BEGIN #include "PN.h" #include <cmath> #include <cstdlib> #include <ctime> #include <iostream> #include <random> #include <SDL.h> #undef main #define TEST 400 using namespace std; void SetPixel(SDL_Surface* surface, int x, int y, Uint32 pixel) //функция установки пикселя на поверхность, принимает название поверхности, координаты х и у, и цвет в формате uint32 { Uint32 *p = (Uint32 *) surface->pixels + y*surface->w+x; *p = pixel; } int main() { unsigned int width=TEST, height=TEST; //размеры нашего окна SDL_Window *window= NULL; //пустое окно SDL_Renderer *renderer = NULL; //пустой рендерер SDL_Surface *image = NULL; //пустая поверхность SDL_Texture *imageTexture = NULL; //пустая текстура if(SDL_Init(SDL_INIT_VIDEO)<0) { //инициализируем видеодрайвер или выкидываем ошибку cout<<"Ошибка инициализации видео"<<endl; return 1; } window = SDL_CreateWindow("Perlin", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0); //создаем окно с ранее заданными размерами по центру или кидаем ошибку if(!window) { cout<<"SDL не создало окно"<<endl; return 2; } renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE); //создаем рендерер для нашего окна или кидаем ошибку if(!renderer) { cout<<"SDL не получила доступа к рендереру"<<endl; return 3; } Uint32 rmask, gmask, bmask, amask; //создаем пиксель с цветами в формате RGBA #if SDL_BYTEORDER == SDL_BIG_ENDIAN //здесь описывается маска пикселей для разных архитектур rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, rmask, gmask, bmask,amask); //создаем поверхность под наши пиксели if(!image) { cout << "Все плохо с поверхностью :(" <<endl; return 4; } unsigned int PSeed=0; //зернышко для рандома Uint8 colourByte; //упрощаем пиксель до uint (у нас же ЧБ изображение) bool quit = false; //бул для выхода SDL_Event event; //событие для обработки нажатий PN pn(PSeed); //создаем объект шума Перлина по зерну int ax=0, ay=0, e=0; //начальные значения для формулы шума while (!quit) { //пока не ESC или не закрыли окно for (unsigned int i = 0; i < height; ++i) //циклом высчитываем шум и заполняем поверхность for (unsigned int j = 0; j < width; ++j) { double x = (double)j / ((double)width); double y = (double)i / ((double)height); double n = pn.PNoise(ax*x, ay*y, e); colourByte = Uint8(n * 0xff); SetPixel(image, j, i, SDL_MapRGB(image->format, colourByte, colourByte, colourByte)); } SDL_RenderClear(renderer); //очищаем буфер рендерера и пихаем в него новую текстуру imageTexture = SDL_CreateTextureFromSurface(renderer, image); //создаем изображение (которое пойдет на экран) SDL_RenderCopy(renderer, imageTexture, NULL, NULL); SDL_RenderPresent(renderer); //проверяем все ли хорошо if (!imageTexture) { cout << "Не создалась поверхность из текстуры" << endl; return 5; } cout << " ax: " << ax << " ay: " << ay << " e: " << e << endl; //выводим в консоль текущие коэффициенты cout << "New ax, ay, e (spaced): "; cin >> ax >> ay >> e; //этот участок кода скорее всего не понадобится, //поскольку выход можно выполнить просто закрыв консоль //да и окно отвечать не будет, но пусть while (SDL_PollEvent(&event)) { //ждем дальнейших указаний: switch (event.type) { case SDL_QUIT: //закрыли окно -> выход quit = true; break; } } } //выход: SDL_FreeSurface(image); //очищаем поверхность SDL_DestroyTexture(imageTexture); //уничтожаем текстуру SDL_DestroyRenderer(renderer); //и рендерер SDL_DestroyWindow(window); // и окно SDL_Quit(); //выключаем библиотеку SDL return 0; //выход из программы } //main.cpp END
Прикрепленный файл | Размер |
---|---|
Perlin_noise.zip | 692.48 кб |