Случайные / шумовые функции для 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 145

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 );
}

скриншоты:

Output of random(vec3) in static.frag

Я проверил скриншот в программе редактирования изображений. Есть 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);
}

смотрите Золотой шум в вашем браузере прямо сейчас!

enter image description here

эта функция улучшила случайное распределение по текущей функции в ответе @appas по состоянию на 9 сентября 2017 года:

enter image description here

функция @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));
}