OpenGL3.3-延迟着色法

前向渲染

前向渲染就是opengl默认的流水线 先计算顶点 然后光栅化 最后再逐片段计算颜色 最后深度测试
就算我们使用early-z 也发现在光源比较多的情况下 所有的光源信息都在着色器中 我们也会将所有的光源和该片段进行一次计算 但是其实很多光源并不起效果 例如太远的光源或内部的光源 这就导致了多余的计算 而且就算你增加在fs中对距离的计算 减少掉不必要的光照 其实根据GPU的特性 SIMD也会一样执行 所以就有了延迟渲染

什么延迟渲染

其实就是将在fs中的部分计算移到了顶点着色器中
进行两次渲染 第一次几何渲染 这一步中其他步骤完全和正常渲染一样 但是在fs中不计算光照 而是单纯将各个顶点,法线,纹理坐标,纹理像素写入g-buff中存储 这一步就将每个会在屏幕中显示的不会被遮挡的所有像素信息准备好了
第二次就是光照渲染了 会先按光源的影响范围绘制一个球体 球体绘制时使用模板测试 覆盖的像素 就是被影响的像素 从而知道这个光源影响具体哪些像素 然后直接就将这些信息传给fs在这里进行光照计算最终输出就行了

延迟渲染的过程

延迟渲染的过程:
几何阶段:
几何阶段的任务是创建一个帧缓冲 然后绑定多个颜色缓冲也就是gbuffer 存储一些需要的信息 比如说世界坐标 法线 还有些纹理 然后就顶点处理就按正常的处理吧 然后经过了一系列 什么坐标转化 光栅化 对数据也进行了插值什么的 就到了片元着色器 这里并不输出颜色值给flagColor 这个并不是一个真正的绘制 就是为了把这些要绘制的数据存起来 因为是从fs输出的嘛 我们也没有使用混合什么的 就已经经过了深度测试 绘制在纹理附件上的就是最终需要显示在屏幕上的片段 最后就使用 OpenGL 中的 Multiple Render Targets (MRT) 能力将不同的顶点属性一次性输出到不同的纹理中 这个阶段目的就是剔除掉没必要的片段 但是我觉得这个early-z也能做到 所以重点应该是下面这部分

光照阶段
遍历 G buffer 中的每一个像素,对不同的纹理进行采样以获得像素属性 然后我们就有了计算光照需要的数据了 但是这个并没有达到效果 我们要建个和光照能辐射到范围一样大的球体 模拟他们影响到的像素 然后通过模板测试就能知道哪些像素会受到影响 这样就可以防止像素和多个光源无效的计算

在这里插入图片描述

2.光照处理阶段 使用G缓冲的纹理数据
渲染一个屏幕大小的方形,并使用G缓冲中的几何数据对每一个片段计算场景的光照()
在每个像素中我们都会对G缓冲进行迭代(看不懂)

不再是从顶点着色器或者uniform那里获取数据了 之间从G缓存获取数据

对于渲染过程进行解耦(?),将它高级的片段处理挪到后期进行,而不是直接将每个对象从顶点着色器带到片段着色器

优点

可以使用更复杂的场景光照了 极大的优化了效率 而且也方便使用SSAO
材质和光照的shader完全分开

缺陷

不支持混色(因为我们只有最前面的片段信息)
他占用的带宽和内存都比较大

不能使用混色的原因

因为在几何阶段就将完成了深度测试的像素存储了 这就导致在真正渲染的时候就只有一层了 也就是后面没有坐标点了 例如透明物体就没办法获取他后面的颜色

不能使用MSAA?错误的!

我们想想 使用MSAA要什么条件
第一是他需要一个三角形或者一个图元吧 需要采样 但是这个时候获取的直接就是颜色缓冲 一张纹理了 完全没办法进行采样 感觉就是 缺失了顶点信息了 什么插值那些都做不了了 因为第二次绘制的时候顶点除了坐标信息 其他信息都没了

解决办法

在渲染BaseColor纹理的过程中执行MSAA,即可达到抗锯齿的效果。
MRT要求对每个RT使用相同的Sample,所以我们是没法对BaseColor开小灶的,要么多花一个Pass先画BaseColor再画其他,要么对其他RT也进行多倍采样。对于前者,新增Pass又再一次增大了性能消耗,而后者会对深度和法线进行插值,显然也不可行,除非自定义最后插值的过程。这样一通操作下来,还是消耗了数倍的带宽(不过GBuffer没有变大哦),并且除了BaseColor以外其他RT做的计算全部木大。这样权衡下来,还不如直接上SSAA算了,至少后者效果更好。
事实上现在大家确实是这么做的,TAA等后处理抗锯齿加SSAA的组合成为主流,MSAA的身影反而几乎看不到了。

G缓冲

G缓冲(G-buffer)是对所有用来储存光照相关的数据,并在最后的光照处理阶段中使用的所有纹理的总称
正向渲染中照亮一个片段所需要的所有数据:
一个3D位置向量来计算(插值)片段位置变量供lightDir和viewDir使用
一个RGB漫反射颜色向量,也就是反照率(Albedo)
一个3D法向量来判断平面的斜率
一个镜面强度(Specular Intensity)浮点值
所有光源的位置和颜色向量(可用uniform传递)
玩家或者观察者的位置向量(可用uniform传递)

glBlitframebuffer()

前四个参数定义了源矩形——右下角顶点的 X、Y 坐标,以及左上角的 X,Y 坐标。
接下来的四个参数以同样的方式定义了拷贝目标的矩形范围。

第九个参数定义了我们读取的缓存类型,如颜色缓存、深度缓存或者模板缓存,同样它的取值可以对应为 GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT 或者 GL_STENCIL_BUFFER_BIT。
最后一个参数定义了在拷贝过程中 OpenGL 对可能出现的缩放的处理方式(当拷贝源和拷贝目标的参数处于不同的维度时),其值可以为 GL_NEAREST 或者 GL_LINEAR(它的效果比 GL_NEAREST 好,但是需要更多的计算资源)。在拷贝颜色缓存时只能选择 GL_LINEAR
例如

    GLsizei HalfWidth = (GLsizei)(WINDOW_WIDTH / 2.0f);
    GLsizei HalfHeight = (GLsizei)(WINDOW_HEIGHT / 2.0f);
    glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,
        0, 0, HalfWidth, HalfHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);

void glDrawBuffers( GLsizei n,const GLenum *bufs);

定义了一个缓冲区数组,片段着色器数据的输出将写入其中。如果片段着色器将一个值写入一个或多个用户定义的输出变量,则每个变量的值将被写入缓冲区中bufs中指定的位置,该位置与分配给该用户定义的输出的位置相对应。用于分配给大于或等于n的位置的用户定义输出的绘图缓冲区被隐式设置为GL_NONE,并且写入该输出的任何数据都将被丢弃。
bufs中包含的符号常量必须为下列之一,这取决于GL是否绑定到默认帧缓冲区:
GL_NONE
片段着色器的输出值未写入任何颜色缓冲区。
GL_BACK
片段着色器的输出值被写入背景色缓冲区。
GL_COLOR_ATTACHMENTn例如GL_COLOR_ATTACHMENT1
片段着色器的输出值将写入当前帧缓冲区的第n个颜色附件中。 n的范围可以从零到GL_MAX_COLOR_ATTACHMENTS的值。
例子

GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
    glDrawBuffers(4/*获取数组的数量*/, DrawBuffers);

问题

如果g-buffer的纹理分辨率大于窗口分辨率

纹理分辨率 1600 1200
窗口分辨率800 600
粉色的灯是没有放到gbuffer里面
请添加图片描述

如果g-buffer的纹理分辨率大于窗口分辨率

纹理分辨率 400 300
窗口分辨率800 600
请添加图片描述

延迟渲染存储的位置信息精度问题 为什么不直接用深度来还原世界空间的位置

法线在哪个空间

应该是在世界空间里面 应该在计算光照的时候也是以世界坐标系来进行计算

怎么用AO

延迟渲染和前向渲染采样深度图的性能对比

延迟渲染需要更多的空间开销 也是属于用空间换时间的情况 延迟渲染减少了多余光照的计算

深度图和颜色图采样器的区别

阴影贴图的问题

需要先进行深度贴图的计算 先获取每个点光源视角下的深度值 然后再进行延迟渲染 因为如果先进行延迟渲染就会导致有些本应该会被遮挡 无法接受光照的片段接受了光照 但是好像msaa也拿了视空间的坐标 好像这两个正好可以一起实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值