原文地址: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. ROW_NUMBER() OVER函数的基本用法,也可用于去除重复行

    语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN) 简单的说row_number()从1开始,为每一条分组记录返回一个数字,这里的ROW ...

  2. python版本坑:md5例子(python2与python3中md5区别)

    对于一些字符,python2和python3的md5加密出来是不一样的. Python2 和Python3MD5加密 # python2.7 pwd = "xxx" + chr(1 ...

  3. 【Ensemble methods】组合方法&集成方法

    机器学习的算法中,讨论的最多的是某种特定的算法,比如Decision Tree,KNN等,在实际工作以及kaggle竞赛中,Ensemble methods(组合方法)的效果往往是最好的,当然需要消耗 ...

  4. 把git上面的scala工程转为eclipse工程

    1,安装sbt工具,url:https://www.cnblogs.com/zeling/p/8494828.html 2,把插件添加到plugins.sbt文件中: addSbtPlugin(&qu ...

  5. iOS 检测网络状态 自动判断 认为提示网络改变

    检测网络状态 在网络应用中,需要对用户设备的网络状态进行实时监控,目的是让用户了解自己的网络状态,防止一些误会(比如怪应用无能)根据用户的网络状态进行智能处理,节省用户流量,提高用户体验WIFI\3G ...

  6. jquery开发的”天才笨笨碰“游戏

    前段时间湖南卫视的快乐大本营里有一款“天才笨笨碰”游戏非常火.这款游戏主要是考选手的声母联想词语的能力. 小篇在看完这个节目后用jquery制作了“天才笨笨碰”网页游戏.先上效果图: 游戏规则: 1. ...

  7. div模态显示内容

    业务需要,上传的图片,本地显示大图: 模态代码: <div onclick="hidebigimg()" class = "bg-model" style ...

  8. 命令行下mysql新建用户及分配权限

    创建用户: CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明:username – 你将创建的用户名, host – 指定该用户在哪 ...

  9. 从零写Java Web框架——请求的处理DispatcherServlet

    大概思路 继承 HttpServlet,实现 DispatcherServlet,拦截所有请求: DispatchServlet 重写 init()方法,负责初始化框架: 重写 service()方法 ...

  10. angula学习

    入门 http://www.angularjs.cn/A004 http://www.cnblogs.com/whitewolf/p/angularjs-start.html http://www.n ...