Пиксельный шейдер OpenGL: как создать случайную матрицу из 0 и 1 (для каждого пикселя)? - PullRequest
4 голосов
/ 07 июня 2011

Итак, что мне нужно, это просто: каждый раз, когда мы выполняем наш шейдер (т.е. для каждого пикселя), мне нужно вычислить случайную матрицу из 1 с и 0 с с помощью resolution == originalImageResolution. Как это сделать?

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

#ifdef GL_ES
precision highp float;
#endif

uniform vec2 resolution;
uniform float time;
uniform sampler2D tex0;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (43758.5453+ time));
}
vec3 getOne(){
    vec2 p =  gl_FragCoord.xy / resolution.xy;
    vec3 one;
    for(int i=0;i<15;i++){
        for(int j=0;j<15;j++){
            if(rand(p)<=0.5)
                one = (one.xyz + texture2D(tex0,vec2(j,i)).xyz)/2.0;
        }
    }
    return one;
}

void main(void)
{
    gl_FragColor = vec4(getOne(),1.0);
}

И один для Adobe Pixel Bender:

<languageVersion: 1.0;> 

kernel random
<   namespace : "Random";
    vendor : "Kabumbus";
    version : 3;
    description : "not as random as needed, not as fast as needed"; >
{

    input image4 src;
    output float4 outputColor;

float rand(float2 co, float2 co2){
    return fract(sin(dot(co.xy ,float2(12.9898,78.233))) * (43758.5453 + (co2.x + co2.y )));
}

float4 getOne(){
    float4 one;
    float2 r = outCoord();
    for(int i=0;i<200;i++){
        for(int j=0;j<200;j++){
            if(rand(r, float2(i,j))>=1.0)
                one = (one + sampleLinear(src,float2(j,i)))/2.0;
        }
    }
    return one;
}
void
evaluatePixel()
{
    float4 oc = getOne();
    outputColor = oc;
}
}

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

Обновление: То, что я хочу создать, называется Single-Pixel Camera (Google Compressive Imaging или Compressive Sensing), я хочу создать программную реализацию на основе GPU.

Идея проста:

  • у нас есть изображение - NxM.
  • для каждого пикселя изображения мы хотим, чтобы графический процессор выполнял следующие операции:
    • для генерации NxM матрицы случайных значений - 0 с и 1 с.
    • вычисляет среднее арифметическое всех пикселей исходного изображения, координаты которых соответствуют координатам 1 с в нашей случайной NxM матрице
    • результат вывода среднего арифметического в виде цвета пикселя.

То, что я пытался внедрить в мои шейдеры, было имитировать этот осторожный процесс.

Что действительно глупо пытаться сделать это на GPU:

  • Compressive Sensing не позволяет нам вычислять матрицу NxM таких средних арифметических значений, а просто соответствует ей (например, 1/3). Поэтому я оказываю некоторое давление на графический процессор. Однако тестирование на большем количестве данных не всегда плохая идея.

1 Ответ

2 голосов
/ 08 июня 2011

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

Извините, что медлительный, но я пытаюсь понять проблему и цель.В вашем примере GLSL я не вижу генерируемой матрицы.Я вижу, что один vec3 генерируется путем суммирования случайного (изменяющегося во времени) ячеек из текстуры 15х15 (матрицы).И это vec3 пересчитывается для каждого пикселя.Тогда vec3 используется в качестве цвета пикселя.

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

Я пытаюсь понять, зачем вам матрица - чтобы сохранить согласованную случайную основу для всех пикселей?Если это так, вы можете либо предварительно вычислить случайную текстуру, либо использовать непротиворечивую псевдослучайную функцию, как в rand (), но не использовать время.Вы четко знаете об этом, так что, думаю, я до сих пор не понимаю цели.Почему вы суммируете случайный выбор ячеек из текстуры для каждого пикселя?

Я полагаю, что причина сбоя вашего шейдера в том, что ваша функция main() превышает ограничение по времени - либо для одного пикселя,или для всего набора пикселей.Вызов rand() 40000 раз на пиксель (во вложенном цикле 200 * 200), безусловно, может объяснить это!Если у вас было 200 x 200 пикселей, и вы вызываете sin () по 40 000 раз для каждого, это 160 000 000 вызовов на кадр.Плохой графический процессор!

Я надеюсь, что если мы лучше поймем цель, мы сможем порекомендовать более эффективный способ получить желаемый эффект.

Обновление.

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

Новое обновление на основе обновленного вопроса.

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

Это означает, что мы не можем избежать генерации (NxM) ^ 2 случайных значений на кадр, то есть NxM случайных значений на пиксель и NxMпиксели.Таким образом, для N = M = 200 это 160 миллионов случайных значений на кадр.

Однако , мы все же можем оптимизировать некоторые вещи.

  • Во-первых, так как вашслучайные значения должны иметь только один бит (вам нужен только логический ответ, чтобы решить, включать ли каждую ячейку из исходной текстуры в микс), вы, вероятно, можете использовать более дешевый генератор псевдослучайных чисел.Тот, который вы используете, выдает гораздо больше случайных данных за вызов, чем один бит.Например, вы можете вызвать ту же функцию PRNG, что и сейчас, но сохранить значение и извлечь из него 32 случайных бита.Или, по крайней мере, несколько, в зависимости от того, сколько достаточно случайно.Кроме того, вместо использования функции sin (), если у вас есть расширение GL_EXT_gpu_shader4 (для побитовых операторов), вы можете использовать что-то вроде этого :

.

int LFSR_Rand_Gen(in int n)
{
  // <<, ^ and & require GL_EXT_gpu_shader4.
  n = (n << 13) ^ n;
  return (n * (n*n*15731+789221) + 1376312589) & 0x7fffffff;
}
  • Во-вторых, вы в настоящее время выполняете одну операцию деления для каждой включенной ячейки (/2.0), что, вероятно, относительно дорого, если только компилятор и графический процессор не могут оптимизировать его до сдвига битов (возможно ли это для плавающей запятой?) , Это также не даст арифметического среднего значения входных значений, как обсуждалось выше ... это придаст гораздо больший вес более поздним значениям и очень мало - более ранним. В качестве решения следует вести подсчет количества включаемых значений и разделить его на один раз после завершения цикла.

Достаточно ли этих оптимизаций для того, чтобы драйвер GPU мог обрабатывать 200x200 * 200x200 пикселей на кадр, я не знаю. Они определенно должны позволить вам существенно увеличить разрешение.

Это идеи, которые приходят мне в голову. Я далеко не эксперт по GPU. Было бы замечательно, если бы кто-то более квалифицированный мог присоединиться к предложениям.

P.S. В своем комментарии вы в шутку (?) Упомянули возможность предварительного вычисления N * M NxM случайных матриц. Может быть, это не плохая идея? 40,000x40,000 - это большая текстура (не менее 40 МБ), но если вы сохраняете 32 бита случайных данных на ячейку, то получается 1250 x 40000 ячеек. Жаль, что ванильный GLSL не поможет вам с побитовыми операторами извлекать данные, но даже если у вас нет расширения GL_EXT_gpu_shader4, вы все равно можете его подделать. (Может быть, вам также понадобится специальное расширение для неквадратных текстур?)

...