openGL 概念学习(三)
一、 shadowMap
二、法线贴图
我们可以使用一张图来记录每个Fragment的法线信息,法线是xyz,而每个像素颜色是rgb,刚好互相对应,这张图用来记录每个Fragment的图片即为法线贴图
。
但这样的问题是,当前的法线是按照这个墙面指向z轴正方向来实现的,如果墙面发生旋转,法线需要跟着一起做相应的旋转。
因此为了美术制作的方便,我们希望输出的法线贴图不论对应哪个方向的哪个片段,默认都是对当前这个片段的法线进行摇摆,也就是说我们只希望法线贴图单纯的表达相对于片段主方向的偏移程度。
2.2 切线空间
在一个不同的坐标空间中进行光照,这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;所有的光照向量都相对与这个正z方向进行变换。这样我们就能始终使用同样的法线贴图,不管朝向问题。这个坐标空间叫做切线空间(tangent space)。
如果我们可以求出TBN三个向量,构成笛卡尔坐标系(切线空间),那么我们就可以将每一个切线空间当中的RGB法线向量通过类似视图矩阵的方式转化到世界坐标系空间内,从而当做正常的法线进行计算。
2.2.1 TBN矩阵
tangent:切线方向
bitangent:副切线方向
normal:法线
2.2.1 TBN矩阵施密特正交化
四、 延迟渲染
对渲染过程进行解耦,将它高级的片段处理挪到后期进行,而不是直接将每个对象从顶点着色器带到片段着色器。
包含两个处理阶段(Pass):
- 几何处理阶段(Geometry Pass):我们先渲染场景一次,之后获取对象的各种几何信息,并储存到G-buffer的纹理中;
- 光照处理阶段(Lighting Pass): 在光照处理阶段中,我们渲染一个屏幕大小的方形,并使用G缓冲中的几何数据对每一个片段计算场景的光照;
优点:
- 能保证在G缓冲中的片段和在屏幕上呈现的像素所包含的片段信息是一样的,因为片段都已经过深度测试。保证了在光照处理阶段中处理的每一个像素都只处理一次。
缺点:
- 由于G缓冲要求我们在纹理颜色缓冲中存储相对比较大的场景数据,这会消耗比较多的显存,尤其是类似位置向量之类的需要高精度的场景数据。
- 不支持混色和MSAA(因为我们只有最前面的片段信息)
4.1 GBuffer
G缓冲(G-buffer)是对所有用来储存光照相关的数据,并在最后的光照处理阶段中使用的所有纹理的总称。它本质上就是一个帧缓存对象,包含了多个颜色缓冲和一个单独的深度渲染缓冲对象(Depth Renderbuffer Object)
。
对于位置
和法向量
的纹理,我们希望使用高精度的纹理(每分量16或32位的浮点数),而对于反照率
和镜面值
,使用默认的纹理(每分量8位浮点数)就够了。
在正向渲染中照亮一个片段所需要的所有数据:
- 一个3D位置向量来计算(插值)片段位置变量供lightDir和viewDir使用
- 一个RGB漫反射颜色向量,也就是反照率(Albedo)
- 一个3D法向量来判断平面的斜率
- 一个镜面强度(Specular Intensity)浮点值
- 所有光源的位置和颜色向量
- 玩家或者观察者的位置向量
4.2 MRT技术
OpenGL ES 3.0 新特性,它允许应用程序一次渲染到多个缓冲区。
应用场景,我们现在希望一个PASS完成两个任务:
- 将场景渲染到colorBufferA
- 将灯光渲染到colorBufferB
并且希望使用同一个FBO,应该如何处理?
解决方案:在FBO中存在多个ColorAttachment选项,我们可以启动两个Color附件,在FS当中对这两个目标进行渲染。
第一步:将两个ColorBuffer放入两个attachment当中
第二步:将两个绘制ColorBuffer目标,绑定到FS的两个锚定位置
4.3 延迟渲染技术
思考:我们有没有可能将复杂度降低为N+M这类线性复杂度?我们是否能把所有物体都“拍平”,找到屏幕上每个像素到底显示的哪个物体的哪个Fragment,然后只针对这个Fragment进行计算?
延迟渲染(DeferredRendering)思想:
第一Pass:第一Pass渲染的时候,我们在FrameBuffer上记录计算光照需要的信息称为G-buffer(每个fragment的世界坐标信息,已经过深度测试剪裁过了):
- Position
- Normal
- DIffuse RGB
- Specular强度
第二Pass:使用G-buffer当中的内容与所有光照进行对比计算。
问题一:新加入的物体无法再做深度测试或blend
可以单独将深度信息拷贝出来
4.4 光体积渲染
但是对于GPU这种高并行架构来讲IF语句实际上还是都会执行的。
解决思路:我们可以为每一个光照做一个球,然后使用G-Buffer的信息作为绑定纹理,只渲染每一个光照球,这个球体Fragment范围内的G-buffer才会被渲染。
但有可能有如下两个问题:
-
如果光源之间存在重合,重合区域如何绘制?
答:使用Blend功能,设置混合模式,比如我们可以设置对物体影响最强的光源为主:
-
球体存在正反两面,开启CullFace会导致我们进入球体内部就不渲染了?
- 点赞
- 收藏
- 关注作者
评论(0)