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

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

Мой GPU немного старше (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);
}

0

Решение

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

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

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/dissertation.pdf

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

0

Другие решения

Других решений пока нет …