Можно ли сгенерировать случайное число внутри пиксельного шейдера? - PullRequest
9 голосов
/ 01 марта 2011

Я пытаюсь написать очень простой шейдер, который добавляет случайный блеск к соответствующим объектам. Я бы хотел сделать это, добавив случайный оттенок белого (R = G = B) к значению пикселя в пиксельном шейдере.

Кажется, что noise() не работает так, как я надеюсь, что это работает:

float multiplier = noise(float3(Input.Position[0], Input.Position[1], time));

Это дает мне «ошибку X4532: невозможно сопоставить выражение с набором команд пиксельных шейдеров», ссылаясь на вызов noise().

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

Есть ли способ создать случайное число из пиксельного шейдера? Если есть способ, то как?

Ответы [ 4 ]

24 голосов
/ 16 мая 2012

ОБНОВЛЕНИЕ Июль 2017: Я сделал "псевдослучайность" более стабильной

// Version 3
float random( vec2 p )
{
    vec2 K1 = vec2(
        23.14069263277926, // e^pi (Gelfond's constant)
         2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant)
    );
    return fract( cos( dot(p,K1) ) * 12345.6789 );
}

Это версии:

float random( vec2 p )
{
   // e^pi (Gelfond's constant)
   // 2^sqrt(2) (Gelfond–Schneider constant)
     vec2 K1 = vec2( 23.14069263277926, 2.665144142690225 );

   //return fract( cos( mod( 12345678., 256. * dot(p,K1) ) ) ); // ver1
   //return fract(cos(dot(p,K1)) * 123456.); // ver2
     return fract(cos(dot(p,K1)) * 12345.6789); // ver3
}

// Minified version 3:
float random(vec2 p){return fract(cos(dot(p,vec2(23.14069263277926,2.665144142690225)))*12345.6789);}

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

Поскольку переменные шейдера независимы для каждого фрагмента, они не могут повторно использовать существующие переменные между ними.Тогда возникает проблема использования «хорошего» семени случайных чисел.Иррациональные числа, кажется, отвечают всем требованиям с самого начала.Тогда это просто «простой» вопрос выбора хорошей «перестановочной» функции.

Вот некоторый бесплатный код, который делает свое дело:

// Input: It uses texture coords as the random number seed.
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive.
// Author: Michael Pohoreski
// Copyright: Copyleft 2012 :-)
// NOTE: This has been upgraded to version 3 !!
float random( vec2 p )
{
  // We need irrationals for pseudo randomness.
  // Most (all?) known transcendental numbers will (generally) work.
  const vec2 r = vec2(
    23.1406926327792690,  // e^pi (Gelfond's constant)
     2.6651441426902251); // 2^sqrt(2) (Gelfond–Schneider constant)
  return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) );  
}

Чтобы понять, как это работает, если мыразбив формулу на составные части, становится легче визуализировать происходящее:

const vec2 k = vec2(23.1406926327792690,2.6651441426902251);
float rnd0( vec2 uv ) {return dot(uv,k); }
float rnd1( vec2 uv ) { return 1e-7 + 256. + dot(uv,k); }
float rnd2( vec2 uv ) { return mod( 123456789., 256. * dot(uv,k) ); }
float rnd3( vec2 uv ) { return cos( mod( 123456789., 256. * dot(uv,k) ) ); }

// We can even tweak the formula
float rnd4( vec2 uv ) { return fract( cos( mod( 1234., 1024. * dot(uv,k) ) ) ); }
float rnd5( vec2 uv ) { return fract( cos( mod( 12345., 1024. * dot(uv,k) ) ) ); }
float rnd6( vec2 uv ) { return fract( cos( mod( 123456., 1024. * dot(uv,k) ) ) ); }
float rnd7( vec2 uv ) { return fract( cos( mod( 1234567., 1024. * dot(uv,k) ) ) ); }
float rnd8( vec2 uv ) { return fract( cos( mod( 12345678., 1024. * dot(uv,k) ) ) ); }
float rnd9( vec2 uv ) { return fract( cos( mod( 123456780., 1024. * dot(uv,k) ) ) ); }

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    mediump vec2 uv = fragCoord.xy / iResolution.xy;
    float i = rnd9(uv);
    fragColor = vec4(i,i,i,1.);
}

Вставьте вышеприведенное в:

Я также создал пример "сравнения" ShaderToy с 2 функциями шума и 2 случайными функциями:

Демонстрация использования шума "[2TC 15] Speckle Cross Fade "

" Классическая "случайная функция, иногда называемая snoise3, это плохая :

return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);

Если вы хотите сравнить «псевдослучайные» функции, проверьте хэш Дейва без шейдера .

8 голосов
/ 01 марта 2011

Что вы обычно делаете, когда хотите получить случайное значение в пиксельном шейдере, это передать текстуру, содержащую шум.Хотя он на самом деле не «случайный» - он выглядит случайным.

Например, вот код из пиксельного шейдера, который у меня лежит:

float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset));

Текстура, которую я использую, является текстурой RGB-шума, которая может пригодиться несколько раз.Но та же самая техника будет работать для градаций серого.

Масштабируя ее, я гарантирую, что пиксели в текстуре шума выровнены до экранных пикселей (вы также можете установить текстурный сэмплер на «точку»режим, чтобы вы не размыли текстуру шума).

Используя смещение, вы можете прокручивать текстуру - что-то вроде заполнения генератора случайных чисел.Используйте случайное смещение, если хотите избежать такого «прокручивания».

6 голосов
/ 01 марта 2011

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

Если вы используете, скажем, координату пикселя, то в итоге вы получите детерминированный результат (т. Е. Пиксель x, y всегда будет иметь одинаковую случайную вспышку), но в целом по всей грани он будет распределен случайным образом.

Теперь, если у вас есть в качестве входных данных что-то, что меняется в зависимости от среды (я ничего не знаю о пиксельных шейдерах), например общее расположение пикселя в глобальном пространстве комбинации сцены / камеры, чем относительное к самому многоугольнику, тогда, особенно в быстро меняющейся среде, ваши результаты будут фактически случайными.

Если все в глобальной среде оказывается одинаковым, то да, у вас будет точно такое же «случайное» распределение. Но если любой из этих факторов изменится (в частности, в зависимости от пользовательского ввода, который, скорее всего, является наиболее динамичным «источником шума»), то в целом эффект будет, скорее всего, «достаточно случайным».

Итак, главное, что ваш старт не обязательно должен быть результатом предыдущего запуска генератора случайных чисел. Это может быть что угодно. Таким образом, добавление начального числа для каждого пикселя на основе входных данных для шейдера в собственный RNG может дать вам необходимый эффект.

0 голосов
/ 01 августа 2018

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

...