GLSL — Как получить доступ к пиксельным данным текстуры в шейдере GLSL?

Что я хочу сделать, так это получить доступ к пиксельным данным текстуры в шейдере OpenGL. После этого сравните их красный компонент, чтобы я мог получить координату пикселя с максимальным красным компонентом. Я могу сделать это с целью C, с вычислительной мощностью процессора. Код показан ниже.

- (void)processNewPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
    short maxR = 0;
    NSInteger x = -1, y = -1;

    CVPixelBufferLockBaseAddress(pixelBuffer, 0);    
    height = CVPixelBufferGetHeight(pixelBuffer);
    width = CVPixelBufferGetWidth(pixelBuffer);

    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
    uint8_t *src_buff = CVPixelBufferGetBaseAddress(pixelBuffer);

    short** rawData = malloc(sizeof(short*) * height);
    for (int i = 0; i < height; i++){
        rawData[i] = malloc((sizeof(short) * width));
        for (int j = 0; j < width; j++)
            rawData[i][j] = (short)src_buff[(i + width * j) * 4];
    }

    for (int j = 0; j < height; j++)
    for (int i = 0; i < width; i++)
        if (rawData[i][j] >= maxR) {
            maxR = rawData[i][j];
            x = i;
            y = j;
        }

    free(rawData);
}

Итак, мой вопрос: как мне использовать GPU для выполнения этого процесса? Я могу сделать пиксельный буфер как текстуру в шейдере OpenGL.

Вершинный шейдер

attribute vec4 position;
attribute vec4 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main()
{
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
}

Фрагментный шейдер

varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture; //The input sample, in RGBA format

void main()
{
    // Code here
}

Как изменить шейдер, чтобы определить пиксель с максимальным красным компонентом? Затем я хочу превратить этот пиксель в красный цвет, а другой пиксель в белый. Это возможно?


person Chiu    schedule 19.09.2012    source источник
comment
Добро пожаловать в Stack Overflow! Если ваш вопрос не получил ответа, правильный ответ нет на задайте тот же вопрос еще раз. Дополнительный материал должен был быть добавлен к вашему исходному вопросу; затем отметьте его для модератора, чтобы рассмотреть возможность повторного открытия.   -  person Nicol Bolas    schedule 19.09.2012
comment
Это действительно не очень хорошее приложение для фрагментного шейдера. Фрагментные шейдеры лучше всего работают при выполнении простых операций с очень ограниченным набором входных данных. Вы не можете прочитать каждый тексель текстуры каждый раз, когда запускается фрагментный шейдер.   -  person Tim    schedule 19.09.2012
comment
@Tim Вы можете использовать простой шейдер сокращения, который для каждого пикселя запрашивает четыре соседних тексела, вычисляет их максимальный красный цвет и координату этого максимального текселя и выводит эти значения в качестве цвета. Затем просто повторите это с этим выходом в качестве входной текстуры, пока у вас не будет координат maxred и texel в фреймбуфере 1x1 (или пока вы не сделаете это на маленьком изображении на процессоре). Но все же я не знаю, купит ли это ему что-нибудь. Тем не менее, конечно, вы не читаете каждый тексель при каждом вызове фрагментного шейдера.   -  person Christian Rau    schedule 19.09.2012


Ответы (1)


Что вы можете сделать, так это использовать классический шейдер сокращения. Вы визуализируете четырехугольник размером с экран в текстуру/экран с половиной размеров вашей входной текстуры (лучше всего делать это с помощью FBO), а во фрагментном шейдере вы вычисляете максимум блока текселей 2x2 для каждого пикселя, выводя максимальное значение и его соответствующие координаты текстуры:

//no need for any textrue coords, we render screen-aligned anyway
uniform sampler2D inputImageTexture; //The input sample, in RGBA format
uniform vec2 invImageSize; // (1/width, 1/height)

void main()
{
    vec2 coord = (2.0 * floor(gl_FragCoord.xy) + 0.5) * invImageSize;
    float ll = texture2D(inputImageTexture, coord).r;
    float lr = texture2D(inputImageTexture, coord+vec2(invImageSize, 0.0)).r;
    float ul = texture2D(inputImageTexture, coord+vec2(0.0, invImageSize)).r;
    float ur = texture2D(inputImageTexture, coord+vec2(invImageSize, invImageSize)).r;

    vec4 color = vec4(ll, coord, 1.0);
    if(lr > color.r)
        color.xyz = vec3(lr, coord+vec2(invImageSize, 0.0));
    if(ul > color.r)
        color.xyz = vec3(ul, coord+vec2(0.0, invImageSize));
    if(ur > color.r)
        color.xyz = vec3(ur, coord+vec2(invImageSize, invImageSize));
    gl_FragColor = color;
}

Затем вы используете эту выходную текстуру на следующем шаге в качестве входной текстуры и визуализируете в текстуру вдвое меньшего размера, пока не получите текстуру 1x1 (или, скорее, небольшую текстуру, которую вы можете обрабатывать на ЦП). Но, конечно, во втором и во всех последующих проходах вы должны выводить сохраненную координату текстуры вместо вычисленной.

vec2 coord = (2.0 * floor(gl_FragCoord.xy) + 0.5) * invImageSize;
vec4 ll = texture2D(inputImageTexture, coord);
vec4 lr = texture2D(inputImageTexture, coord+vec2(invImageSize, 0.0));
vec4 ul = texture2D(inputImageTexture, coord+vec2(0.0, invImageSize));
vec4 ur = texture2D(inputImageTexture, coord+vec2(invImageSize, invImageSize));

ll = (lr.r > ll.r) ? lr : ll;
ll = (ul.r > ll.r) ? ul : ll;
ll = (ur.r > ll.r) ? ur : ll;
gl_FragColor = ll;

Когда у вас, наконец, есть координата текселя с максимальной красной частью (как нормализованная координата текстуры в [0,1]), вам просто нужно нарисовать полностью белый квадрат размера экрана/текстуры и одну красную точку в этой позиции. Но я не могу вам обещать, что этот многопроходный алгоритм (довольно громоздкий по сравнению с простотой задачи) действительно купит вам что-нибудь по сравнению с чисто CPU-решением. Вы не можете сделать это всего за один проход, волшебным образом считывая все пиксели изображения для каждого выходного пикселя и определяя его цвет, просто фрагментные шейдеры не используются (или не должны использоваться).

EDIT: И кстати. Вероятно, нет необходимости копировать все ваши данные в дополнительный временный буфер в вашем процессорном решении, по крайней мере, в том, который даже дико разбрасывает их в памяти, используя странный массив массивов для того, что должно быть одним блоком памяти (или ни одним). при работе непосредственно с вводом), не говоря уже о массовом распределении памяти. Поэтому сначала исправьте свое решение для процессора, прежде чем даже думать о переносе чего-либо на графический процессор.

person Christian Rau    schedule 19.09.2012