2014-02-28 124 views
0

我想在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

我還沒有真正落實式延期,但我認爲你可以在類似於建立一個粒子周邊列表爲模擬的方式處理這個。

  • 讓您的計算着色器構建一個包含指示燈和單元ID的元組,並使用當前線程作爲索引將其存儲在緩衝區中。
  • 使用您最喜愛的GPU算法(基數排序或雙向排序)按單元格對緩衝區進行排序。
  • 緩衝區排序後,建立一個直方圖並進行前綴和掃描,以便找到每個單元在緩衝區內的起始位置。

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] ... 

詳情,該方法在西蒙綠色的「使用CUDA粒子模擬」被描述:http://idav.ucdavis.edu/~dfalcant/downloads/dissertation.pdf

原始方法假定顆粒只能被放置在單個細胞內,但你應該能夠通過使用更大的工作量輕鬆解決這個問題。