Случайные / шумовые функции для 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.
b)непрерывный шум как шум Перлина-опять же, n-мерное, + - равномерное распределение, с ограниченным набором значений и, Ну, хорошо выглядит (некоторые варианты настройки внешнего вида, такие как уровни Перлина, также могут быть полезны). Я ожидаю подписи например:
float noise (T coord, TT seed);
vec2 noise2 (T coord, TT seed);
// ...
Я не очень разбираюсь в теории генерации случайных чисел, поэтому я бы с радостью пошел на готовые решения, но я также был бы признателен за ответы, такие как .
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 и меня не использует текстуру, но работает примерно на половине скорости на типичных настольных платформах с большой пропускной способностью текстуры. На мобильных платформах версия textureless может быть быстрее, потому что текстурирование часто является значительным узким местом.
наш активно поддерживаемый исходный репозиторий:
https://github.com/ashima/webgl-noise
коллекция как текстурных, так и текстурных версий шума находится здесь (используя только 2D-текстуры):
http://www.itn.liu.se / ~stegu/simplexnoise/GLSL-noise-vs-noise.zip
Если у вас есть какие-либо конкретные вопросы, не стесняйтесь по электронной почте мне непосредственно (мой адрес электронной почты можно найти в classicnoise*.glsl
источники.)
Мне кажется, что вы можете использовать простую целочисленную хэш-функцию и вставить результат в мантиссу float. IIRC спецификация GLSL гарантирует 32-разрядные целые числа без знака и представление поплавка IEEE binary32, поэтому оно должно быть идеально переносимым.
Я только что попробовал. Результаты очень хорошие: он выглядит точно так же, как статический с каждым входом, который я пробовал, никаких видимых шаблонов вообще. Напротив, популярный фрагмент sin/fract имеет довольно выраженные диагональные линии на моем GPU с теми же входами.
одним недостатком является то, что он требует на GLSL В3.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, который выглядит как шум Перлина, но "не требует никакой настройки, т. е. не текстур и не однородных массивов. Просто добавьте его в исходный код шейдера и называйте это как хотите".
Это очень удобно, особенно учитывая, что более ранняя реализация 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 на веб-сайте ShaderToy владельца inigo quilez perlin tutorial, а вороной и так далее, у него есть полные быстрые реализации и коды для них.
ниже приведен пример добавления белого шума к отображаемой текстуре. Решение состоит в том, чтобы использовать две текстуры: оригинальный и чистый белый шум, как этот:wiki белый шум
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 так целые доступны в (Ш)на GLSL. -> для качественного портативного хэша (по аналогичной цене, чем уродливые хеши float) теперь мы можем использовать "серьезные" методы хэширования. 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));
}