延迟渲染(Deferred Shading)技术详解

http://blog.csdn.net/xiajun07061225/article/details/7680895

一、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);
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. }
#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章

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

原文地址:https://www.cnblogs.com/skiwnchiwns/p/10143008.html

时间: 2024-10-02 16:39:24

延迟渲染(Deferred Shading)技术详解的相关文章

延迟渲染 deferred Shading

流程: 1.先渲染一遍物体的位置,法线  和颜色  到三张纹理 2.在根据这三张纹理渲染一遍灯光 3.合成颜色图和灯光图  (ssao图) 看上去好像灯光不多,其实我在这里加了200个灯,明天继续...

腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

本文来自腾讯前端开发工程师" wendygogogo"的技术分享,作者自评:"在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦." 1.GIF格式的历史 GIF ( Graphics Interchange Format )原义是"图像互换格式",是 CompuServe 公司在1987年开发出的图像文件格式,可以说是互联网界的老古董了. GIF 格式可以存储多幅彩色图像,如果将这些图像((https://www.q

CDN技术详解及实现原理

CDN技术详解 一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精华放上网.公诸同好. 第一章    引言    “第一公里”是指万维网流量向用户传送的第一个出口,是网站服务器接入互联网的链路所能提供的带宽.这个带宽决定了一个 网站能为用户提供的访问速度和并发访问量.如果业务繁忙,用户的访问数越多,拥塞越严重,网站会在最需要向用户提供服务时失去用户.(还有“中间一公里” 和

基于柯西矩阵的Erasure Code技术详解

一.概述 Erasure Code可以应用于分布式存储系统中,替代多份数据拷贝的数据冗余方式,从而可以提高存储空间利用率.此外,Erasure code还可以应用于传统RAID系统中,增加数据冗余度,支持多块盘同时发生故障,从而可以提高数据可靠性. 采用范德蒙矩阵可以构建Erasure code(关于范德蒙矩阵的编解码方法,可以参考文章<基于范德蒙矩阵的Erasure code技术详解>),其生成矩阵表示如下: 采用范德蒙矩阵作为编码矩阵的问题在于算法复杂度太高,其解码算法复杂度为O(n^3)

「视频直播技术详解」系列之七:直播云 SDK 性能测试模型

?关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: (一)采集 (二)处理 (三)编码和封装 (四)推流和传输 (五)延迟优化 (六)现代播放器原理 (七)SDK 性能测试模型 本篇是<视频直播技术详解>系列的最后一篇直播云 SDK 性能测试模型,SDK 的性能对最终 App 的影响非常大.SDK 版本迭代快速,每次发布前都要进行系统的测试,测试要

unity3d开发实战《啪啪三国》技术详解!

去年11月,上海火溶网络CEO王伟峰以其第一款3d手游产品<啪啪三国>为例,着重讲解了unity3D手机网游开发的经验,其中涉及了团队组成.人员要求.常见的unity3d开发遇到的坑及解决办法.在演讲中,王伟峰也贡献了<啪啪三国>开发过程中总结的各种经验,从优化.插件库.服务器架构.SDK等很多细节进行了讲解.值得一说的是,王伟峰现场演讲十分幽默,冷笑话段子不断爆出,让在场观众在连续的笑声中听完这个特别的技术演讲. <ignore_js_op> 以下是王伟峰现场演讲实录

SSE技术详解:一种全新的HTML5服务器推送事件技术

前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Server-sent Events).关于这4种技术方式的优缺点,请参考<Web端即时通讯技术盘点:短轮询.Comet.Websocket.SSE>.本文将专门讲解SSE技术. 服务器推送事件(Server-sent Events),简称SSE,是 HTML 5 规范中的一个组成部分,可以用来从服务端

实现高性能纠删码引擎 | 纠删码技术详解(下)

作者介绍: 徐祥曦,七牛云工程师,独立开发了多套高性能纠删码/再生码编码引擎.柳青,华中科技大学博士,研究方向为基于纠删码的分布式存储系统. 前言: 在上篇<如何选择纠删码编码引擎>中,我们简单了解了 Reed-Solomon Codes(RS 码)的编/解码过程,以及编码引擎的评判标准.但并没有就具体实现进行展开,本篇作为<纠删码技术详解>的下篇,我们将主要探讨工程实现的问题. 这里先简单提炼一下实现高性能纠删码引擎的要点:首先,根据编码理论将矩阵以及有限域的运算工程化,接下来主

Linux磁盘阵列技术详解(二)--raid 1创建

我在Linux磁盘阵列技术详解(一)里已经详细介绍了几种RAID磁盘阵列方式,原理以及创建raid 0 的详细步骤.那么这篇文档就着重讲解如何创建raid 1的技术: 步骤如下: ① 分区 同样我们还是以一块硬盘的不同分区为例,实际工作中应该是不同的硬盘才对. 具体分区步骤不再赘述! 分区后结果如下图所示: ② 创建raid 1 mdadm -C -v /dev/md1 -l 1 -n 2 -x 1 /dev/sdc1 /dev/sdc2 /dev/sdc3 或者 mdadm -C -v /de