Могу ли я генерировать случайное число внутри пиксельного шейдера?

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

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

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

это дает мне "ошибка X4532: не удается отобразить выражение в набор инструкций пиксельного шейдера", ссылаясь на вызов noise().

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

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

4 ответов


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

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

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

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

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


обновление июль 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);

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


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

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

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

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

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

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


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