程序员人生 网站导航

Deferred shading技术简介

栏目:php教程时间:2016-02-28 10:07:27

Deferred shading是这样1种技术:将光照/渲染计算推延到第2步进行计算。我们这样做的目的是为了不屡次(超过1次)渲染同1个像素。

基本思想以下:

1、在第1步中,我们渲染场景,但是与通常情况下利用反射模型计算片断色彩不同的是,我们只是简单的将几何信息(位置坐标,法线向量,纹理坐标,反射系数等等)存储在中间缓冲区中,这样的缓冲区我们称之为g-buffer(g是几何geometry的缩写)。

2、在第2步,我们从g-buffer中读取信息,利用反射模型,计算出每一个像素的终究色彩。


Deferred shading技术的利用使得我们避免了利用反射模型于终究不可见的片断上。例如,斟酌这样的像素,它位于两个多边形堆叠的区域。通常的片断着色器会读对每一个多边形分别计算那个像素1次;但是,两次履行的结果终究只有1个成为该像素的终究色彩(这里基于的1个假定是:混合已被禁用)。这样,其中的1次计算就是无用的。有了Deferred shading技术,反射模型的计算会推延到所有几何体被处理以后,那时候每一个像素位置几何体的可见性也是已知的。这样,对屏幕上的每一个像素,反射模型的计算只会产生1次。


Deferred shading容易懂而且便于使用。它能够帮助实行很复杂的光照/反射模型。


2、结合例子来讲明Deferred shading技术

下面的例子采取Deferred shading技术渲染了1个包括1个茶壶和1个圆环的场景。效果以下:

图1 场景渲染效果图

在这个例子中,我们将位置坐标、法线和漫反射因子存储在g-buffer里。在第2步的时候,我们使用g-buffer里面的数据来进行漫反射光照模型的计算。

g-buffer包括3个纹理:分别用来存储位置坐标、法线和漫反射因子。对应的采取了3个uniform变量:PositionTex、NormalTex、ColorTex。

他们均被关联到1个FBO上。关于FBO使用见:FBO。


下面是创建包括g-buffer的FBO的代码:

[cpp] view plaincopy
  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 plaincopy
  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);

注意:3个纹理分别使用函数glFramebufferTexture2D()关联到FBO的色彩关联点0、1、2上面。接着调用函数glDrawBuffers把它们和片断着色器的输出变量联系起来。

函数glDrawBuffer唆使了FBO成员和片断着色器输出变量之间的联系。FBO中的第i个成员对应片断着色器中的索引为i的输出变量。这样,片断着色器(下面列出了完全代码)中相对应的输出变量分别是PosiutionData,NormalData和ColorData。


顶点着色器实现了1个很简单的功能:将位置坐标和法线转化到eye sapce中,然后传递到片断着色器中。而纹理坐标则没有产生变化。


片断着色器以下:

[cpp] view plaincopy
  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 plaincopy
  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. }

片断着色器则包括了关于光源、材料的1些信息,都是uniform变量,以用于光照计算。

片断着色器里面使用了subroutine技术,实现了两个函数pass1和pass2,分别包括了第1步和第2步的操作。我们在OpenGL利用程序中通过设置uniform变量的值可以选择使用相应的功能。


在OpenGL利用程序里面,

实行第1步的步骤以下:

1、绑定FBO;

2、情况色彩和深度缓冲区,选择pass1 subroutine函数,启用深度测试;

3、渲染场景。


实行第2步的步骤是:

1、去除FBO绑定(将其绑定到0),目的是能够渲染场景到默许缓冲区,而不是FBO里面,它就可以显示到屏幕上;

2、清除色彩缓冲去对象。禁用深度测试;

3、选择pass2 subroutine函数,渲染1个充满屏幕的4边形,带有纹理坐标,每一个方向的纹理坐标的范围都是从0到1.计算光照模型,得出最后的片断色彩。


3、如何选择使用Deferred shading技术

在图形学领域,关于Deferred shading技术的优点和缺点备受争议。这类技术其实不适用所有的场合,它取决于你的利用程序的需求。因此在觉得是不是采取这个技术之前1定要权衡它带来的优点和缺点。

Deferred shading技术带来1个很重要的缺点就是不能使用基于硬件实现的多重采样抗锯齿功能。由于渲染进程产生在第2步,所以我们在第2步需要多个样本。但是,在第2步我们只有每个像素的1个样本。

另外1个缺点就是不能使用混合技术。


参考资料:

《GPU Gems 2》的第9章

《GPU Gems 3》的第19章

版权声明:本文为【借你1秒】原创文章,转载请标明出处。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐