Коротко об mt19937, uniform_real_distribution и генерации одномерных шумных текстур

Столкнулся с интересной темой — надо загрузить в видеопамять большое (относительно) количество шума. Под шумом понимается некоторое количество равномерно распределённых случайных чисел, иначе говоря — случайных чисел из одного интервала.

Решение известно давно, но мало где описывается, поэтому продублирую сюда с некоторыми пояснениями, на поиск которых я потратил некоторое время.

  1. Первым делом объявляем класс, который с помощью внешнего источника генерирует начальные данные для последующего их использования в генераторе:

std::random_device rd;

2. Затем создаём сам генератор псевдослучайных чисел, который будет принимать вышеописанный объект класса random_device. Таким образом мы инициализируем наш генератор, который, к слову, имеет красивое название Вихрь Мерсенна:

std::mt19937 gen(rd())

На текущем этапе мы инициализировали генератор, что, кстати, делается во всей программе 1 раз.

3. Далее нам нужно задать интервал, в котором мы будем набирать случайные числа:

std::uniform_real_distribution<float> dis(0.0f, 1.0f);

Для шумов текстуры интервал от 0.0 до 1.0 — более, чем достаточно.

4. Генерируем саму последовательность с заданным количеством случайных чисел:

const GLuint randTexSize = 256;
GLfloat randTexData[randTexSize];
std::generate(randTexData, randTexData + randTexSize, [&](){return dis(gen);});

Первый аргумент — начало массива (оно же указатель на первый элемент) чисел с плавающей точкой, куда будет записана равномерная случайная последовательность.

Второй аргумент — конец массива (сдвиг на 256 элементов по массиву, т.к. память у нас располагается последовательно).

Третий аргумент — лямбда-выражение, возвращающее ссылку на сгенерированную последовательность, которая будет записана в массив.

Далее код, генерирующий текстуру в спецификации OpenGL:

GLuint randTexture;
//Генерируем текстурное имя. Первый аргумент - количество создаваемых текстурных имён, второй - массив, куда имена будут записаны.
glGenTextures(1, &randTexture);
//Активируем текстуру с номером 0
glActiveTexture(GL_TEXTURE0);
//Связываем тип 1D-текстуры с ID randTexture
glBindTexture(GL_TEXTURE_1D, randTexture);
//Задаём настройки для текстуры
//GL_TEXTURE_MIN_FILTER - если размер оригинальной текстуры меньше, чем размер области, куда её будем натягивать - используем
//GL_NEAREST - фильтр ближайшего соседа. Для отрисовки текстуры будут браться ближайшие текстели, таким образом у текстуры будут очень грубые тексельные края, что в нашем случае нам совсем не помешает.
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//аналогично, если область больше
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//если область, куда натягиваем текстуру - оказалась больше самой текстуры (в данном случае по оси x) - повторяем её
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
//Мы не использовали mipmaps, поэтому уровень загружаемой текстуры у нас = 0, потому что текстура одна и номер её уровня = 0:)
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, 0);
//Задаём изображение для текстуры
//1 - текстура одномерная
//2 - уровень детализации (опять же касательно mipmap) - 0
//3 - количество цветовых компонент в текстуре. В нашем случае однокомпонентный формат с плавающей запятой, использующий 16 бит на компонент
//4 - randTexSize - ширина изображения текстуры (в нашем случае 256)
//5 - формат данных пикселя. Формат стандартный, поэтому не указываем
//6 - тип данных пикселя
//7 - указатель на данные, которые записываем
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16F, randTexSize, 0, GL_RED, GL_FLOAT, randTexData);
//загружаем текстуру шейдер посредством uniform-переменной
glUniform1i(glGetUniformLocation(shaderName.programId(), "uni_var_name"), 0);

В последствии в шейдере можно использовать текстуру как неплохой источник рандомных чисел в высокой энтропией.

 

Полезные ссылки:

А теперь прочитаю свой код и запишу его на диктофон, чтобы не забыть. Ну и оставлю тут эту заметку, чтобы вернуться к ней, если сломается диктофон.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *