OpenGL 4.0 GLSL 延迟渲染 Deferred shading

Deferred shading 是把应用光照或shading推迟到第二帧来做。这样可以避免了不必要的像 素计算。

如下图只用了漫反射光照


基本算法思想如下:

1. 在第一帧渲染时,我们只渲染场景,而不计算反射模型来确定片元的最终颜色,我们只简单的存贮几何信息(位置position、法线normal,纹理t坐标xture coordinate 等等)在一个临时缓冲区中,这些统称为g-buffer (g 代表 Geometry)

2. 在第二帧时,我们只读区g-buffer,计算反射模型,位每个像素生成最终的颜色值。


利用deferred shading ,我们可以避免计算对最终渲染效果无用的片元的光照模型。例如,两个多边形重叠的部分将会各执行一次shading 计算,但最终的渲染效果只有一个多边形对最终渲染效果起作用(前提 没有透明)。而应用deferred shading ,所有几何模型的最终像素都确定了才计算光照模型,因而,光照模型只为屏幕上的每个像素执行了一次。

void SceneDeferred::createGBufTex( GLenum texUnit, GLenum format, GLuint &texid ) {
    glActiveTexture(texUnit); 
    glGenTextures(1, &texid);
    glBindTexture(GL_TEXTURE_2D, texid);
    glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}

void SceneDeferred::setupFBO()
{
    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);

    // Create the textures for position, normal and color
    createGBufTex(GL_TEXTURE0, GL_RGB32F, posTex);  // Position
    createGBufTex(GL_TEXTURE1, GL_RGB32F, normTex); // Normal
    createGBufTex(GL_TEXTURE2, GL_RGB8, colorTex);  // Color

    // Attach the textures 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);
}


顶点shader

#version 440

layout( location = 0 ) in vec3 VertexPosition;
layout( location = 1 ) in vec3 VertexNormal;
layout( location = 2 ) in vec2 VertexTexCoord;

out vec3 Position;
out vec3 Normal;
out vec2 TexCoord;

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
    Normal = normalize( NormalMatrix * VertexNormal);
    Position = vec3( ModelViewMatrix * vec4(VertexPosition,1.0) );
    TexCoord = VertexTexCoord;
    gl_Position = MVP * vec4(VertexPosition,1.0);
}

片元shader

#version 440

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;

layout(binding=0) uniform sampler2D PositionTex;
layout(binding=1) uniform sampler2D NormalTex;
layout(binding=2) uniform sampler2D 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();
}


在为g-buffer设置FBO时,我们为位置position和法线normal设置纹理的内部格式GL_RGB32F。

然后用glFrameBufferTexture2D 把三个纹理附加到FBO上,分别为color Attachment 0,1,and 2。它们分别对应片元着色器的输出变量(用glDrawBuffers函数设置)。

如:GLenum drawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2};    glDrawBuffers(4, drawBuffers);

说明片元shader中的out layout 0对应GL_NONE,layout 1 对应GL_COLOR_ATTACHMENT0 ,等等。


注意:在 渲染第二帧的时候(pass 2),实际上没有必要向fragment传递和转换normal和position。可以用在vertex shader中使用 subroutine来关闭转换。当然在片元着色器中gl_Position 是必须要赋值的!


Deferred shading 并不是对所有解决方案都是有效的,它很多程度上取决于软件的需求,在使用它前,我们需要权衡deferred shading 的缺点和优点。


缺点(Drawback):一个很大的缺点就是是用deferred shading 将不能用硬件实现多重采样抗锯齿(multisample  anti-aliasing )。在deferred shading 要实现多重采样本来应该在第二帧(pass 2),但是,在第二帧每个像素只有单一采样的几何信息。为了实现这个,我们需要多重纹理和为每次采样选择正确的纹理的能力,但是目前OpenGL做不到这一点。(不过,我们可以用边缘探测edge detection 和 模糊过滤 blur filter 来提供一个粗糙的 抗锯齿anti-aliasing 功能)

另一个缺点是:deferred shading 对融合/透明处理不是太好。事实上融合blending 根本不可能。


Deferred shading优点:在deferred shading 中可以保存整个深度缓存,这个深度缓存作为一个纹理可以实现好多基于深度的算法,例如 深度模糊depth blur,屏幕空间环境光遮蔽 screen space ambient occlusionscreen space ambient occlusion、volumetric particles等其他类似技术。 







  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值