Случайные / шумовые функции для GLSL
поскольку поставщики драйверов GPU обычно не утруждают себя реализацией noiseX
в GLSL, я ищу "графика рандомизации швейцарский армейский нож" набор служебных функций, предпочтительно оптимизированных для использования в шейдерах GPU. Я предпочитаю GLSL, но код любой язык будет делать для меня, я в порядке с переводом его на свой собственный GLSL.
в частности, я ожидал:
a)псевдослучайные функции - n-мерное, равномерное распределение по [-1,1] или более [0,1], рассчитанный из M-мерного семени (в идеале это любое значение, но я в порядке с тем, чтобы семя было ограничено, скажем, 0..1 для равномерного распределения результатов). Что-то вроде:
float random (T seed);
vec2 random2 (T seed);
vec3 random3 (T seed);
vec4 random4 (T seed);
// T being either float, vec2, vec3, vec4 - ideally.
б) непрерывный шум как шум Перлина-опять же, n-мерное, + - равномерное распределение, с ограниченным набором значений и, хорошо, хорошо выглядит (некоторые параметры для настройки внешнего вида, такие как уровни Перлина, тоже могут быть полезны). Я бы ожидал подписи например:
float noise (T coord, TT seed);
vec2 noise2 (T coord, TT seed);
// ...
Я не очень люблю теорию генерации случайных чисел, поэтому я бы с большим нетерпением пошел на готовые решения, но я также был бы признателен за такие ответы, как " вот очень хороший, эффективный 1D rand (), и позвольте мне объяснить вам, как сделать хороший N-мерный rand () поверх него..." .
9 ответов:
для очень простых псевдослучайных вещей я использую эту oneliner, которую я нашел в интернете где-то:
float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
вы также можете создать текстуру шума, используя любой PRNG, который вам нравится, а затем загрузить это обычным способом и попробовать значения в вашем шейдере; я могу выкопать образец кода позже, если вы хотите.
кроме того, проверьте этот файл для реализаций GLSL Перлина и Симплексного шума, Стефан Густавсон.
реализация Густавсона использует 1D текстуру
нет это не так, не с 2005 года. Просто люди настаивают на загрузке старой версии. Версия, которая находится на ссылке, которую вы предоставили, использует только 8-битные 2D текстуры.
новая версия Яна Макьюэна из Ashima и меня не использует текстуру, но работает примерно в два раза быстрее на типичных настольных платформах с большим количеством текстурной полосы пропускания. На мобильных платформах текстурная версия может быть быстрее, потому что текстурирование часто является значительным узким местом.
наш активно поддерживаемый исходный репозиторий:
https://github.com/ashima/webgl-noise
коллекция как текстурных, так и текстурных версий шума здесь (используя только 2D-текстуры):
http://www.itn.liu.se / ~stegu/simplexnoise/GLSL-noise-vs-noise.zip
Если у вас есть какие-либо конкретные вопросы, не стесняйтесь по электронной почте мне непосредственно (мой адрес электронной почты можно найти в
classicnoise*.glsl
источники.)
Мне кажется, что вы можете использовать простую целочисленную хэш-функцию и вставить результат в мантиссу поплавка. IIRC спецификация GLSL гарантирует 32-разрядные целые числа без знака и представление с плавающей точкой IEEE binary32, поэтому он должен быть идеально переносимым.
Я только что попробовал. Результаты очень хорошие: он выглядит точно так же, как статический с каждым входом, который я пробовал, никаких видимых шаблонов вообще. Напротив, популярный фрагмент sin/fract имеет довольно выраженные диагональные линии на моем ГПУ дали те же входные данные.
одним из недостатков является то, что он требует GLSL v3.30. И хотя это кажется достаточно быстрым, я не эмпирически оценил его производительность. Анализатор шейдеров AMD утверждает, что 13.33 пикселей на часы для версии vec2 на HD5870. Контраст с 16 пикселами в часы для фрагмента sin/fract. Так что это, конечно, немного медленнее.
вот моя реализация. Я оставил его в различных перестановках идеи, чтобы сделать его легче получить свой собственный функции от.
/* static.frag by Spatial 05 July 2013 */ #version 330 core uniform float time; out vec4 fragment; // A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm. uint hash( uint x ) { x += ( x << 10u ); x ^= ( x >> 6u ); x += ( x << 3u ); x ^= ( x >> 11u ); x += ( x << 15u ); return x; } // Compound versions of the hashing algorithm I whipped together. uint hash( uvec2 v ) { return hash( v.x ^ hash(v.y) ); } uint hash( uvec3 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ); } uint hash( uvec4 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w) ); } // Construct a float with half-open range [0:1] using low 23 bits. // All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0. float floatConstruct( uint m ) { const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask const uint ieeeOne = 0x3F800000u; // 1.0 in IEEE binary32 m &= ieeeMantissa; // Keep only mantissa bits (fractional part) m |= ieeeOne; // Add fractional part to 1.0 float f = uintBitsToFloat( m ); // Range [1:2] return f - 1.0; // Range [0:1] } // Pseudo-random value in half-open range [0:1]. float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); } float random( vec2 v ) { return floatConstruct(hash(floatBitsToUint(v))); } float random( vec3 v ) { return floatConstruct(hash(floatBitsToUint(v))); } float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); } void main() { vec3 inputs = vec3( gl_FragCoord.xy, time ); // Spatial and temporal inputs float rand = random( inputs ); // Random per-pixel value vec3 luma = vec3( rand ); // Expand to RGB fragment = vec4( luma, 1.0 ); }
скриншоты:
Я проверил скриншот в программе редактирования изображений. Есть 256 цветов и среднее значение 127, что означает, что распределение является равномерным и охватывает ожидаемый диапазон.
Золотые Шума
// Gold Noise ©2015 dcerisano@standard3d.com // - based on the Golden Ratio, PI and Square Root of Two // - superior distribution // - fastest noise generator function // - works with all chipsets (including low precision) precision lowp float; float PHI = 1.61803398874989484820459 * 00000.1; // Golden Ratio float PI = 3.14159265358979323846264 * 00000.1; // PI float SQ2 = 1.41421356237309504880169 * 10000.0; // Square Root of Two float gold_noise(in vec2 coordinate, in float seed){ return fract(tan(distance(coordinate*(seed+PHI), vec2(PHI, PI)))*SQ2); }
смотрите Золотой шум в вашем браузере прямо сейчас!
эта функция улучшила случайное распределение по текущей функции в ответе @appas по состоянию на 9 сентября 2017 года:
функция @appas также является неполной, учитывая, что семя не поставляется (uv не является семенем-то же самое для каждого кадра) и не работает с низким прецизионные чипсеты. Золотой шум работает с низкой точностью по умолчанию (гораздо быстрее).
также есть хорошая реализация, описанная здесь по McEwan и @StefanGustavson, который выглядит как Perlin noise, но "не требует никакой настройки, т. е. ни текстур, ни однородных массивов. Просто добавьте его в исходный код шейдера и называйте это как хотите".
Это очень удобно, особенно учитывая, что более ранняя реализация Gustavson, с которой связан @dep, использует 1D-текстуру, которая не поддерживается в GLSL ES (язык шейдеров WebGL).
только что нашел эту версию 3d шума для GPU, alledgedly это самый быстрый из доступных:
#ifndef __noise_hlsl_ #define __noise_hlsl_ // hash based 3d value noise // function taken from https://www.shadertoy.com/view/XslGRr // Created by inigo quilez - iq/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. // ported from GLSL to HLSL float hash( float n ) { return frac(sin(n)*43758.5453); } float noise( float3 x ) { // The noise function returns a value in the range -1.0f -> 1.0f float3 p = floor(x); float3 f = frac(x); f = f*f*(3.0-2.0*f); float n = p.x + p.y*57.0 + 113.0*p.z; return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x), lerp( hash(n+57.0), hash(n+58.0),f.x),f.y), lerp(lerp( hash(n+113.0), hash(n+114.0),f.x), lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z); } #endif
прямая, зубчатая версия 1D Perlin, по существу случайный зигзаг lfo.
half rn(float xx){ half x0=floor(xx); half x1=x0+1; half v0 = frac(sin (x0*.014686)*31718.927+x0); half v1 = frac(sin (x1*.014686)*31718.927+x1); return (v0*(1-frac(xx))+v1*(frac(xx)))*2-1*sin(xx); }
Я также нашел 1-2-3-4D perlin noise на веб-сайте shadertoy владельца inigo quilez perlin tutorial, и voronoi и т. д., У него есть полные быстрые реализации и коды для них.
ниже приведен пример добавления белого шума к отрисованной текстуре. Решение состоит в том, чтобы использовать две текстуры: оригинальный и чистый белый шум, как этот:белый шум Вики
private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "uniform mat4 uMVMatrix;\n" + "uniform mat4 uSTMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "varying vec4 vInCamPosition;\n" + "void main() {\n" + " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + " gl_Position = uMVPMatrix * aPosition;\n" + "}\n"; private static final String FRAGMENT_SHADER = "precision mediump float;\n" + "uniform sampler2D sTextureUnit;\n" + "uniform sampler2D sNoiseTextureUnit;\n" + "uniform float uNoseFactor;\n" + "varying vec2 vTextureCoord;\n" + "varying vec4 vInCamPosition;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTextureUnit, vTextureCoord);\n" + " vec4 vRandChosenColor = texture2D(sNoiseTextureUnit, fract(vTextureCoord + uNoseFactor));\n" + " gl_FragColor.r += (0.05 * vRandChosenColor.r);\n" + " gl_FragColor.g += (0.05 * vRandChosenColor.g);\n" + " gl_FragColor.b += (0.05 * vRandChosenColor.b);\n" + "}\n";
общий фрагмент содержит параметр uNoiseFactor, который обновляется при каждом рендеринге главным приложением:
float noiseValue = (float)(mRand.nextInt() % 1000)/1000; int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit"); GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
хэш: В настоящее время webGL2.0 существует так целые числа доступны в (w)GLSL. -> для качественного портативного хэша (по аналогичной стоимости, чем уродливые плавающие хэши) теперь мы можем использовать "серьезные" методы хэширования. IQ реализовал некоторые в https://www.shadertoy.com/view/XlXcW4 (и более)
например:
const uint k = 1103515245U; // GLIB C //const uint k = 134775813U; // Delphi and Turbo Pascal //const uint k = 20170906U; // Today's date (use three days ago's dateif you want a prime) //const uint k = 1664525U; // Numerical Recipes vec3 hash( uvec3 x ) { x = ((x>>8U)^x.yzx)*k; x = ((x>>8U)^x.yzx)*k; x = ((x>>8U)^x.yzx)*k; return vec3(x)*(1.0/float(0xffffffffU)); }