原文地址: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. form的验证包括手机号邮箱等等

    $(function(){    var checkedByVerifyCode = false;    var checkMobieCode = false;    var checkedMobil ...

  2. sql server 与oracle 中字段类型的对应

    SqlServer 2k转换为Oracle 10g 列名 SqlServer数据类型 SqlServer长度 Oracle数据类型 column1 ) column2 ) column3 ) colu ...

  3. Python(八)之函数

    Python函数 函数作用: (1)代码重用 (2)一种设计工具,分解复杂问题 (3)将相关功能打包并参数化 函数种类: 全局函数:定义在模块中 局部函数:嵌套在其他函数中 lambda函数:表达式 ...

  4. idea 在tomcat中部署的时候 莫名其妙的错误

    针对web项目需要 打成 war exploded 包,这样就不会每次重新构建打包,也不会出现莫名其妙的错误

  5. java 多线程 26 : 线程池

    使用线程池与不使用线程池的差别 先来看一下使用线程池与不适应线程池的差别,第一段代码是使用线程池的: public static void main(String[] args) { long sta ...

  6. 手记:配置IIS服务器,支持sis、SISX、3GP、ADP、AMR、JAD、JAR、MMF、MFM、PMD、UMD等文件下载

    发此博文原因是遇到一个 手机端读取服务器端.amr格式文件失败的例子.   反复测试发现从服务端无法播放,或下载.amr格式的文件.就想到可能是服务器站点托管服务 IIS不支持对.amr格式的解析,意 ...

  7. egret list不显示问题

    1.循环滚动 2.单格滚动 不太清楚是把哪个约束勾上了,结果一直不显示,折腾了许久. 另外也不要把下面的这个约束勾上,否则不能滑动.

  8. java---简单的ATM存取系统,

    新手练手必备~ 密码账户为: 先创建账户类: package cn.Atm; /** * @author 偶my耶 */ import java.io.*; import com.project.pr ...

  9. EF中的1:0或1:1关系以及1:n关系

    先给出1:0关系 User表包括用户名和密码 public class User { public int ID { get; set; } public string UserName { get; ...

  10. 【WPF】ListBox嵌套与事件冒泡

    问题:两个ListBox嵌套后,当鼠标位于内部ListBox上,鼠标滚轮事件会被内部ListBox接收,导致外层ListBox不能用鼠标滚轮滑动!现在的需求是该事件要能给外部ListBox处理,即嵌套 ...