1. 程式人生 > >延遲渲染(Deferred Shading)技術詳解

延遲渲染(Deferred Shading)技術詳解

一、Deferred shading技術簡介

Deferred shading是這樣一種技術:將光照/渲染計算推遲到第二步進行計算。我們這樣做的目的是為了避免多次(超過1次)渲染同一個畫素。

基本思想如下:

1、在第一步中,我們渲染場景,但是與通常情況下應用反射模型計算片斷顏色不同的是,我們只是簡單的將幾何資訊(位置座標,法線向量,紋理座標,反射係數等等)儲存在中間緩衝區中,這樣的緩衝區我們稱之為g-buffer(g是幾何geometry的縮寫)。

2、在第二步,我們從g-buffer中讀取資訊,應用反射模型,計算出每個畫素的最終顏色。

Deferred shading技術的應用使得我們避免了應用反射模型於最終不可見的片斷上。例如,考慮這樣的畫素,它位於兩個多邊形重疊的區域。通常的片斷著色器會讀對每個多邊形分別計算那個畫素一次;然而,兩次執行的結果最終只有一個成為該畫素的最終顏色(這裡基於的一個假設是:混合已被禁用)。這樣,其中的一次計算就是無用的。有了Deferred shading技術,反射模型的計算會推遲到所有幾何體被處理之後,那時候每個畫素位置幾何體的可見性也是已知的。這樣,對於螢幕上的每個畫素,反射模型的計算只會發生一次。

Deferred shading容易懂而且便於使用。它能夠幫助實施很複雜的光照/反射模型。

二、結合例子來說明Deferred shading技術

下面的例子採用Deferred shading技術渲染了一個包含一個茶壺和一個圓環的場景。效果如下:


圖一 場景渲染效果圖

在這個例子中,我們將位置座標、法線以及漫反射因子儲存在g-buffer裡。在第二步的時候,我們使用g-buffer裡面的資料來進行漫反射光照模型的計算。

g-buffer包含3個紋理:分別用來儲存位置座標、法線以及漫反射因子。對應的採用了3個uniform變數:PositionTex、NormalTex、ColorTex。

他們均被關聯到一個FBO上。關於FBO使用見:FBO

下面是建立包含g-buffer的FBO的程式碼:

[cpp] view plaincopyprint?
  1. GLuint depthBuf, posTex, normTex, colorTex;  
  2. // Create and bind the FBO
  3.     glGenFramebuffers(1, &deferredFBO);  
  4.     glBindFramebuffer(GL_FRAMEBUFFER, deferredFBO);  
  5. // The depth buffer
  6.     glGenRenderbuffers(1, &depthBuf);  
  7.     glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);  
  8.     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);  
  9. // The position buffer
  10.     glActiveTexture(GL_TEXTURE0);   // Use texture unit 0
  11.     glGenTextures(1, &posTex);  
  12.     glBindTexture(GL_TEXTURE_2D, posTex);  
  13.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  14.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  15.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  16. // The normal buffer
  17.     glActiveTexture(GL_TEXTURE1);  
  18.     glGenTextures(1, &normTex);  
  19.     glBindTexture(GL_TEXTURE_2D, normTex);  
  20.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  21.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  22.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  23. // The color buffer
  24.     glActiveTexture(GL_TEXTURE2);  
  25.     glGenTextures(1, &colorTex);  
  26.     glBindTexture(GL_TEXTURE_2D, colorTex);  
  27.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  28.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  29.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  30. // Attach the images to the framebuffer
  31.     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuf);  
  32.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, posTex, 0);  
  33.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex, 0);  
  34.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex, 0);  
  35.     GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,  
  36.                         GL_COLOR_ATTACHMENT2};  
  37.     glDrawBuffers(4, drawBuffers);  
  38.     glBindFramebuffer(GL_FRAMEBUFFER, 0);  
[cpp] view plaincopyprint?
  1. GLuint depthBuf, posTex, normTex, colorTex;  
  2. // Create and bind the FBO
  3.     glGenFramebuffers(1, &deferredFBO);  
  4.     glBindFramebuffer(GL_FRAMEBUFFER, deferredFBO);  
  5. // The depth buffer
  6.     glGenRenderbuffers(1, &depthBuf);  
  7.     glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);  
  8.     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);  
  9. // The position buffer
  10.     glActiveTexture(GL_TEXTURE0);   // Use texture unit 0
  11.     glGenTextures(1, &posTex);  
  12.     glBindTexture(GL_TEXTURE_2D, posTex);  
  13.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  14.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  15.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  16. // The normal buffer
  17.     glActiveTexture(GL_TEXTURE1);  
  18.     glGenTextures(1, &normTex);  
  19.     glBindTexture(GL_TEXTURE_2D, normTex);  
  20.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  21.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  22.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  23. // The color buffer
  24.     glActiveTexture(GL_TEXTURE2);  
  25.     glGenTextures(1, &colorTex);  
  26.     glBindTexture(GL_TEXTURE_2D, colorTex);  
  27.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);  
  28.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  29.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  30. // Attach the images to the framebuffer
  31.     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuf);  
  32.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, posTex, 0);  
  33.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex, 0);  
  34.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex, 0);  
  35.     GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,  
  36.                         GL_COLOR_ATTACHMENT2};  
  37.     glDrawBuffers(4, drawBuffers);  
  38.     glBindFramebuffer(GL_FRAMEBUFFER, 0);  
GLuint depthBuf, posTex, normTex, colorTex;

    // Create and bind the FBO
    glGenFramebuffers(1, &deferredFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, deferredFBO);

    // The depth buffer
    glGenRenderbuffers(1, &depthBuf);
    glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);

    // The position buffer
    glActiveTexture(GL_TEXTURE0);   // Use texture unit 0
    glGenTextures(1, &posTex);
    glBindTexture(GL_TEXTURE_2D, posTex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // The normal buffer
    glActiveTexture(GL_TEXTURE1);
    glGenTextures(1, &normTex);
    glBindTexture(GL_TEXTURE_2D, normTex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // The color buffer
    glActiveTexture(GL_TEXTURE2);
    glGenTextures(1, &colorTex);
    glBindTexture(GL_TEXTURE_2D, colorTex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // Attach the images to the framebuffer
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuf);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, posTex, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex, 0);

    GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
                        GL_COLOR_ATTACHMENT2};
    glDrawBuffers(4, drawBuffers);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
注意:三個紋理分別使用函式glFramebufferTexture2D()關聯到FBO的顏色關聯點0、1、2上面。接著呼叫函式glDrawBuffers把它們和片斷著色器的輸出變數聯絡起來。

函式glDrawBuffer指示了FBO成員和片斷著色器輸出變數之間的聯絡。FBO中的第i個成員對應片斷著色器中的索引為i的輸出變數。這樣,片斷著色器(下面列出了完整程式碼)中相對應的輸出變數分別是PosiutionData,NormalData和ColorData。

頂點著色器實現了一個很簡單的功能:將位置座標和法線轉化到eye sapce中,然後傳遞到片斷著色器中。而紋理座標則沒有發生變化。

片斷著色器如下:

[cpp] view plaincopyprint?
  1. #version 400
  2. struct LightInfo {  
  3.   vec4 Position;  // Light position in eye coords.
  4.   vec3 Intensity; // A,D,S intensity
  5. };  
  6. uniform LightInfo Light;  
  7. struct MaterialInfo {  
  8.   vec3 Kd;            // Diffuse reflectivity
  9. };  
  10. uniform MaterialInfo Material;  
  11. subroutine void RenderPassType();  
  12. subroutine uniform RenderPassType RenderPass;  
  13. uniform sampler2D PositionTex, NormalTex, ColorTex;  
  14. in vec3 Position;  
  15. in vec3 Normal;  
  16. in vec2 TexCoord;  
  17. layout (location = 0) out vec4 FragColor;  
  18. layout (location = 1) out vec3 PositionData;  
  19. layout (location = 2) out vec3 NormalData;  
  20. layout (location = 3) out vec3 ColorData;  
  21. vec3 diffuseModel( vec3 pos, vec3 norm, vec3 diff )  
  22. {  
  23.     vec3 s = normalize(vec3(Light.Position) - pos);  
  24. float sDotN = max( dot(s,norm), 0.0 );  
  25.     vec3 diffuse = Light.Intensity * diff * sDotN;  
  26. return diffuse;  
  27. }  
  28. subroutine (RenderPassType)  
  29. void pass1()  
  30. {  
  31. // Store position, normal, and diffuse color in textures
  32.     PositionData = Position;  
  33.     NormalData = Normal;  
  34.     ColorData = Material.Kd;  
  35. }  
  36. subroutine(RenderPassType)  
  37. void pass2()  
  38. {  
  39. // Retrieve position and normal information from textures
  40.     vec3 pos = vec3( texture( PositionTex, TexCoord ) );  
  41.     vec3 norm = vec3( texture( NormalTex, TexCoord ) );  
  42.     vec3 diffColor = vec3( texture(ColorTex, TexCoord) );  
  43.     FragColor = vec4( diffuseModel(pos,norm,diffColor), 1.0 );  
  44. }  
  45. void main() {  
  46. // This will call either pass1 or pass2
  47.     RenderPass();  
  48. }  
[cpp] view plaincopyprint?
  1. #version 400
  2. struct LightInfo {  
  3.   vec4 Position;  // Light position in eye coords.
  4.   vec3 Intensity; // A,D,S intensity
  5. };  
  6. uniform LightInfo Light;  
  7. struct MaterialInfo {  
  8.   vec3 Kd;            // Diffuse reflectivity
  9. };  
  10. uniform MaterialInfo Material;  
  11. subroutine void RenderPassType();  
  12. subroutine uniform RenderPassType RenderPass;  
  13. uniform sampler2D PositionTex, NormalTex, ColorTex;  
  14. in vec3 Position;  
  15. in vec3 Normal;  
  16. in vec2 TexCoord;  
  17. layout (location = 0) out vec4 FragColor;  
  18. layout (location = 1) out vec3 PositionData;  
  19. layout (location = 2) out vec3 NormalData;  
  20. layout (location = 3) out vec3 ColorData;  
  21. vec3 diffuseModel( vec3 pos, vec3 norm, vec3 diff )  
  22. {  
  23.     vec3 s = normalize(vec3(Light.Position) - pos);  
  24. float sDotN = max( dot(s,norm), 0.0 );  
  25.     vec3 diffuse = Light.Intensity * diff * sDotN;  
  26. return diffuse;  
  27. }  
  28. subroutine (RenderPassType)  
  29. void pass1()  
  30. {  
  31. // Store position, normal, and diffuse color in textures
  32.     PositionData = Position;  
  33.     NormalData = Normal;  
  34.     ColorData = Material.Kd;  
  35. }  
  36. subroutine(RenderPassType)  
  37. void pass2()  
  38. {  
  39. // Retrieve position and normal information from textures
  40.     vec3 pos = vec3( texture( PositionTex, TexCoord ) );  
  41.     vec3 norm = vec3( texture( NormalTex, TexCoord ) );  
  42.     vec3 diffColor = vec3( texture(ColorTex, TexCoord) );  
  43.     FragColor = vec4( diffuseModel(pos,norm,diffColor), 1.0 );  
  44. }  
  45. void main() {  
  46. // This will call either pass1 or pass2
  47.     RenderPass();  
  48. }  
#version 400

struct LightInfo {
  vec4 Position;  // Light position in eye coords.
  vec3 Intensity; // A,D,S intensity
};
uniform LightInfo Light;

struct MaterialInfo {
  vec3 Kd;            // Diffuse reflectivity
};
uniform MaterialInfo Material;

subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;

uniform sampler2D PositionTex, NormalTex, ColorTex;

in vec3 Position;
in vec3 Normal;
in vec2 TexCoord;

layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec3 PositionData;
layout (location = 2) out vec3 NormalData;
layout (location = 3) out vec3 ColorData;

vec3 diffuseModel( vec3 pos, vec3 norm, vec3 diff )
{
    vec3 s = normalize(vec3(Light.Position) - pos);
    float sDotN = max( dot(s,norm), 0.0 );
    vec3 diffuse = Light.Intensity * diff * sDotN;

    return diffuse;
}

subroutine (RenderPassType)
void pass1()
{
    // Store position, normal, and diffuse color in textures
    PositionData = Position;
    NormalData = Normal;
    ColorData = Material.Kd;
}

subroutine(RenderPassType)
void pass2()
{
    // Retrieve position and normal information from textures
    vec3 pos = vec3( texture( PositionTex, TexCoord ) );
    vec3 norm = vec3( texture( NormalTex, TexCoord ) );
    vec3 diffColor = vec3( texture(ColorTex, TexCoord) );

    FragColor = vec4( diffuseModel(pos,norm,diffColor), 1.0 );
}

void main() {
    // This will call either pass1 or pass2
    RenderPass();
}

片斷著色器則包含了關於光源、材料的一些資訊,都是uniform變數,以用於光照計算。

片斷著色器裡面使用了subroutine技術,實現了兩個函式pass1和pass2,分別包含了第一步和第二步的操作。我們在OpenGL應用程式中通過設定uniform變數的值可以選擇使用相應的功能。

在OpenGL應用程式裡面,

實施第一步的步驟如下:

1、繫結FBO;

2、情況顏色以及深度緩衝區,選擇pass1 subroutine函式,啟用深度測試;

3、渲染場景。

實施第二步的步驟是:

1、去除FBO繫結(將其繫結到0),目的是能夠渲染場景到預設緩衝區,而不是FBO裡面,它就能顯示到螢幕上;

2、清除顏色緩衝去物件。禁用深度測試;

3、選擇pass2 subroutine函式,渲染一個充滿螢幕的四邊形,帶有紋理座標,每個方向的紋理座標的範圍都是從0到1.計算光照模型,得出最後的片斷顏色。

三、如何選擇使用Deferred shading技術

在圖形學領域,關於Deferred shading技術的優點和缺陷備受爭議。這種技術並不適用所有的場合,它取決於你的應用程式的需求。因此在覺得是否採用這個技術之前一定要權衡它帶來的優點和缺陷。

Deferred shading技術帶來一個很重要的缺點就是不能使用基於硬體實現的多重取樣抗鋸齒功能。因為渲染過程發生在第二步,所以我們在第二步需要多個樣本。但是,在第二步我們只有每一個畫素的一個樣本。

另外一個缺點就是不能使用混合技術。

參考資料:

《GPU Gems 2》的第9章

《GPU Gems 3》的第19章