原文地址: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. iOS微信支付demo运行报错解决如下

    要接入微信支付的小伙伴,首先要下载一份官方demo(APP微信支付官方Demo下载),然后打开工程,准备大干一场. 1.编译报错 编译的时候居然直接报错了(orz) 错误提示: APP微信支付官方De ...

  2. 使用EGit插件将Eclipse现有项目分享到git@osc

    . . . . . 程序员一定要养成使用版本管理工具的好习惯,即使是自己一个人开发的项目也要加入到版本管理工具中.使用版本管理工具主要有两个好处:一个是更好的管理多个副本,这个优势不用说了:另一个就是 ...

  3. spark streaming checkpoint

    Checkpoint机制 通过前期对Spark Streaming的理解,我们知道,Spark Streaming应用程序如果不手动停止,则将一直运行下去,在实际中应用程序一般是24小时*7天不间断运 ...

  4. 解决MYSQL ERROR 1045 (28000)问题

    ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)   Red Hat Enterpr ...

  5. Flexbox的布局

    http://segmentfault.com/blog/gitcafe/1190000002490633 https://css-tricks.com/snippets/css/a-guide-to ...

  6. Java图形界面设计——substance皮肤

    http://jianweili007-163-com.iteye.com/blog/1141358 ————————————————————————————————————————————————— ...

  7. nvalid bound statement (not found)

    问题详情: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.coocaa.te ...

  8. JSONObject遍历获取键值方法合并两个JSONObject

    JSONObject obj1= new JSONObject(); try { obj1.put("obj1_data", obj1_data); if (null != obj ...

  9. R matrix 转换为 dataframe

    When I try converting a matrix to a data frame, it works for me: > x <- matrix(1:6,ncol=2,dimn ...

  10. R语言判断向量中是否存在一个元素

    判断ori_data[,1]中是否存在元素a: a %in% ori_data[,1] 如果存在返回 true,否则返回 false