原文地址:http://www.verydemo.com/demo_c284_i6147.html

一、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的代码:

GLuint depthBuf, posTex, normTex, colorTex;  

    // Create and bind the FBO
glGenFramebuffers(, &deferredFBO);
glBindFramebuffer(GL_FRAMEBUFFER, deferredFBO); // The depth buffer
glGenRenderbuffers(, &depthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); // The position buffer
glActiveTexture(GL_TEXTURE0); // Use texture unit 0
glGenTextures(, &posTex);
glBindTexture(GL_TEXTURE_2D, posTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB32F, width, height, , 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(, &normTex);
glBindTexture(GL_TEXTURE_2D, normTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB32F, width, height, , 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(, &colorTex);
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB, width, height, , 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, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex, ); GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2};
glDrawBuffers(, drawBuffers); glBindFramebuffer(GL_FRAMEBUFFER, );
GLuint depthBuf, posTex, normTex, colorTex;  

    // Create and bind the FBO
glGenFramebuffers(, &deferredFBO);
glBindFramebuffer(GL_FRAMEBUFFER, deferredFBO); // The depth buffer
glGenRenderbuffers(, &depthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); // The position buffer
glActiveTexture(GL_TEXTURE0); // Use texture unit 0
glGenTextures(, &posTex);
glBindTexture(GL_TEXTURE_2D, posTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB32F, width, height, , 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(, &normTex);
glBindTexture(GL_TEXTURE_2D, normTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB32F, width, height, , 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(, &colorTex);
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, , GL_RGB, width, height, , 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, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex, );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex, ); GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2};
glDrawBuffers(, drawBuffers); glBindFramebuffer(GL_FRAMEBUFFER, );

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

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

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

片断着色器如下:

#version   

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 = ) out vec4 FragColor;
layout (location = ) out vec3 PositionData;
layout (location = ) out vec3 NormalData;
layout (location = ) 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();
}
#version   

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 = ) out vec4 FragColor;
layout (location = ) out vec3 PositionData;
layout (location = ) out vec3 NormalData;
layout (location = ) 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://blog.csdn.net/zhuyingqingfen/article/details/19406163

opengl deferred shading的更多相关文章

  1. Deferred shading rendering path翻译

    Overview 概述 When using deferred shading, there is no limit on the number of lights that can affect a ...

  2. D3D Deferred Shading

    在3D图形计算中,deferred shading是一个基于屏幕空间的着色技术.之所以被称为deferred shading,是因为我们将场景的光照计算与渲染"deferred"到 ...

  3. 引擎设计跟踪(九.14.3.2) Deferred shading的后续实现和优化

    最近完成了deferred shading和spot light的支持, 并作了一部分优化. 之前forward shading也只支持方向光, 现在也支持了点光源和探照光. 对于forward sh ...

  4. 引擎设计跟踪(九.14.3.1) deferred shading: Depthstencil as GBuffer depth

    问题汇总 1.Light support for Editor编辑器加入了灯光工具, 可以添加和修改灯光. 问题1. light object的用户互交.point light可以把对应的volume ...

  5. 引擎设计跟踪(九.14.3) deferred shading 准备

    目前做的一些准备工作 1.depth prepass for forward shading. 做depth prepass的原因是为了完善渲染流程, 虽然架构上支持多个pass, 但实际上从来没有测 ...

  6. Deferred Shading 延迟着色(翻译)

    原文地址:https://en.wikipedia.org/wiki/Deferred_shading 在3D计算机图形学领域,deferred shading 是一种屏幕空间着色技术.它被称为Def ...

  7. Deferred Shading,延迟渲染(提高渲染效率,减少多余光照计算)【转】

    Deferred Shading,看过<Gems2> 的应该都了解了.最近很火的星际2就是使用了Deferred Shading. 原帖位置:   http://blog.csdn.net ...

  8. Deferred Shading延迟渲染

    Deferred Shading 传统的渲染过程通常为:1)绘制Mesh:2)指定材质:3)处理光照效果:4)输出.传统的过程Mesh越多,光照处理越费时,多光源时就更慢了. 延迟渲染的步骤:1)Pa ...

  9. Unity的Deferred Shading

    什么是Deferred Shading Unity自身除了支持前向渲染之外,还支持延迟渲染.Unity的rendering path可以通过Edit/Project Settings中的Graphic ...

随机推荐

  1. 10个超赞的jQuery图片滑块动画

    在网站开发过程中,特别是前端开发这块,经常会使用到很多图片滑块动画,也就是一些基于jQuery和HTML5的焦点图插件.本文将为大家收集10个超赞的jQuery图片滑块动画,这些现成的jQuery插件 ...

  2. C语言禁术——goto语句

    goto语句是一种无条件转移语句,goto 语句的使用格式为:     goto  语句标号;其中标号是一个有效的标识符,这个标识符加上一个“:”(冒号)一起出现在函数内某处,执行goto语句后,程序 ...

  3. nginx动态配置及服务发现那些事

    Reference: http://xiaorui.cc/2016/10/16/nginx%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AE%E5%8F%8A%E6%9C%8D% ...

  4. wcf消息契约

    1.最多一个参数和一个返回值,返回值和参数的类型都是消息类型. 下面的代码为定义一个消息契约的实例 [MessageContract]    public class MyMessage    {   ...

  5. 【WPF】在MenuItem中下划线“_”显示不正常

    MenuItem的Header显示文字时发现一个问题,字符串中的第一个下划线“_”不能显示.如果该下划线前面是一个英文字母,则下划线会跑到该字母下方.(是中文汉字则不会) 查到原因是第一个下划线“_” ...

  6. html绝对路径与相对路径

    文件,图片和html文档同一个目录的直接写文件名即可(相对路径). 工作中一般使用相对路径,项目放到服务器上部署不能保证绝对路径不出错.

  7. spidev 驱动 probe 获取 dts 节点参数

    一. 尝试在 spi 驱动里边读取 设备树里面 节点的信息 // dts 里面的参数配置 503 &spi0 { 504 status = "okay"; 505 pinc ...

  8. JAVA是否可以作脚本语言呢

    答:是可以的. 只是叫另一个名字-beanshell.印象里她好像还是第一款jvm语言.应该挺多年没更新了. 我之所以知道它,是当年折腾jEdit这款编辑器较多,它的内置脚本引擎使用的就是beansh ...

  9. Linux定时任务Crontab命令详解_转

    转自:Linux定时任务Crontab命令详解 (部分修改) linux 定时系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服 ...

  10. Anaconda下载(改变了镜像路径,下载速度很快!!!)

    Anaconda下载(改变了镜像路径,下载速度很快!!!) 使用conda install 包名 安装需要的Python非常方便,但是官方的服务器在国外,因此下载速度很慢,国内清华大学提供了Anaco ...