Плиточный рендеринг, вычисление шейдера

0

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

Мой графический процессор несколько старше (AMD Radeon 6490m), и по странным причинам вычислительные шейдеры работают в бесконечном цикле, когда атомарные операции вызывают внутри них на общих переменных, поэтому я не мог вычислить минимальную и максимальную глубину с помощью вычислительных шейдеров. Во всяком случае, это не очень трудоемкая операция, поэтому я делаю это в флеш-шейдере.

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

Проблема в том, что я не могу найти какой-либо другой способ, как это сделать. Любая идея, как отбирать и создавать списки тайлов, используя неатомические?

Вот псевдокод моего вычислительного шейдера:

#version 430

#define MAX_LIGHTS  1024
#define TILE_SIZE   32
#define RX  1280
#define RY  720

struct Light {
    vec4 position;
    vec4 quad;
    vec3 color;
    float radius;
}

uint getTilesXCount(){
    return uint(( RX + TILE_SIZE - 1) / TILE_SIZE);
}

uint getTilesYCount(){
    return uint((RY + TILE_SIZE - 1) / TILE_SIZE);
}

layout (binding = 0, rgba16f) uniform readonly image2D minMaxTex;
layout (binding = 1, rgba16f) uniform readonly image2D diffTex;
layout (binding = 2, rgba16f) uniform readonly image2D specTex;

layout (std430, binding = 3) buffer pointLights {
    Light Lights[];
};


//tile light list & light count
shared uint lightIDs[MAX_LIGHTS];
shared uint lightCount = 0;

uniform uint totalLightCount;

layout (local_size_x = TILE_SIZE, local_size_y = TILE_SIZE) in;

void main(void){

        ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
        vec2 tile = vec2(gl_WorkGroupID.xy * gl_WorkGroupSize.xy) / vec2(1280, 720);

        //get minimum & maximum depth for tile
        vec2 minMax = imageLoad(minMax, tile).xy;

        uint threadCount = TILE_SIZE * TILE_SIZE;
        uint passCount = (totalLightCount + threadCount - 1) / threadCount; 

        for(uint i = 0; i < passCount; i++){

            uint lightIndex = passIt * threadCount + gl_LocalInvocationIndex;

            // prevent overrun by clamping to a last "null" light
            lightIndex = min(lightIndex, numActiveLights);

            Light l = pointLights[lightIndex];

            if(testLightBounds(pixel, l.quad)){

                if ((minMax.y < (l.position.z + l.radius))
                    && 
                    (minMax.x > (l.position.z - l.radius))){


                    uint index;
                    index = atomicAdd(lightCount, 1);
                    pointLightIndex[index] = lightIndex;
                }
            }
        }

    barrier();

    //do lighting for actual tile
    color = doLight();

    imageStore(out, pos, color);
}
Теги:
glsl
opengl-4
compute-shader
deferred-rendering

1 ответ

0

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

  • Попросите свой вычислительный шейдер построить кортеж, содержащий идентификатор света и ячейки, и сохраните его в буфере с использованием текущего потока в качестве индекса.
  • Сортируйте этот буфер по идентификатору ячейки, используя ваш любимый алгоритм GPU (сортировка по методу radix или bitonic sort).
  • После сортировки буфера создайте гистограмму и выполните сканирование суммы префикса, чтобы найти, где каждая из ячеек начинается в буфере.

Ex.

(Cell, Light)
1st pass: Cell Buffer -> [ 23, 0 ] [ 7, 1 ] [ 9, 2 ] ....
2nd pass: Cell Buffer -> [ 7, 1 ] [ 9, 2 ] [ 23, 0 ] ....

(Start, End)
3rd pass: Index Buffer -> [0 0] [0 0] [0 0] [0 0] [0 0] [0 0] [0 1] [1 1] [1 2] ...

Более подробно этот метод описан в Simon Green "Моделирование частиц с использованием CUDA": http://idav.ucdavis.edu/~dfalcant/downloads/dissallery.pdf

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню