
本来计划半小时从0到精通学一个shader的
但半小时真的够吗,
现在这个互联网时代,我结果用半小时做了:
1.学习向前渲染和延迟渲染
2.写这个文章
3.看一个游戏主城做的效果,并学习
Forward rendering

Forward rendering
这是最初始的渲染方式,原理是以mesh
为单位进行渲染,在光栅化后,对每个PS
进行计算时,根据光照
进行着色计算,所以这种方式称为前向着色
。Forward Rendering 是绝大数引擎都含有的一种渲染方式。要使用 Forward Rendering,一般在 Vertex Shader
或 Fragment Shader
阶段对每个顶点或每个像素进行光照计算,并且是对每个光源进行计算产生最终结果。下面是 Forward Rendering 的核心伪代码:
=====
For each light: For each object affected by the light: framebuffer += object * light
====
比如在 Unity3D 4.x 引擎中,对于下图中的圆圈(表示一个 Geometry
),进行 Forward Rendering 处理:

Unity3D 4.x 圆圈
将得到下面的处理结果:

Unity3D 4.x 圆圈 着色结果
也就是说,对于 ABCD 四个光源我们在 Fragment Shader 中我们对每个 pixel 处理光照, 对于 DEFG 光源我们在 Vertex Shader 中对每个 vertex 处理光照,而对于 GH 光源,我们采用球调和(SH)函数进行处理。
这种方式存在以下弊端:
- 如果像素被其他像素遮蔽了,就浪费了宝贵的处理结果
- 光源多起来后管理很麻烦,Shader也不好写。
因此,Deferred rendering就应运而生了。
(但其实乐乐姐说过,Deferred渲染反而是旧技术,移动互联网时代反而流行)
属于旧瓶装新酒
Deferred rendering


Deferred Rendering(延迟渲染)顾名思义,就是将光照处理这一步骤延迟一段时间再处理。
具体做法就是将光照放在已经将三维物体生成二维图片之后进行处理。也就是说将物空间的光照处理放到了像空间进行处理。要做到这一步,需要一个重要的辅助工具——G-Buffer。
G-Buffer 主要是用来存储每个像素对应的 Position,Normal,Diffuse Color 和其他 Material parameters。根据这些信息,我们就可以在像空间中对每个像素进行光照处理。
下面是 Deferred Rendering 的核心伪代码。
===================
For each object: Render to multiple targets For each light: Apply light as a 2D postprocess
========
这种渲染方式相比 Forward rendering 就是在渲染mesh时,并不进行光照计算,而是按照以下步骤进行:物空间
的光照处理放到了像空间
进行处理。要做到这一步,需要一个重要的辅助工具——G-Buffer
。
G-Buffer 主要是用来存储每个像素对应的 Position,Normal,Diffuse Color 和其他 Material parameters
。根据这些信息,我们就可以在像空间中对每个像素进行光照处理。
下面是 Deferred Rendering 的核心伪代码。
For each object: Render to multiple targets For each light: Apply light as a 2D postprocess
这种渲染方式相比 Forward rendering 就是在渲染mesh
时,并不进行光照计算,而是按照以下步骤进行:
- 将深度、法线、Diffuse、Specular等
材质属性
分别输出到GBuffer
里(其实就是几张RT
) - 然后GBuffer里的深度和法线信息,累加所有光照的强度到一张
光照强度RT
上 - 根据GBuffer里的Diffuse和Specular信息,以及光照强度RT,进行着色计算
名词解释
GBuffer:指Geometry Buffer,亦即“物体缓冲”。区别于普通的仅将颜色渲染到纹理中,G-Buffer指包含颜色、法线、世界空间坐标的缓冲区,亦即指包含颜色、法线、世界空间坐标的纹理。由于G-Buffer需要的向量长度超出通常纹理能包含的向量的长度,通常在游戏开发中,使用多渲染目标技术来生成G-Buffer,即在一次绘制中将颜色、法线、世界空间坐标分别渲染到三张浮点纹理中
下面简单举个例子:
首先我们用存储各种信息的纹理图。比如下面这张 Depth Buffer
,主要是用来确定该像 素距离视点的远近的。

Depth Buffer
根据反射光的密度/强度分度图来计算反射效果。

Specular Intensity/Power
下图表示法向数据,这个很关键。进行光照计算最重要的一组数据。

Normal Buffer
下图使用了 Diffuse Color Buffer。

Diffuse Color Buffer
这是使用 Deferred Rendering 最终的结果。

Deferred Lighting ResultsDeferred rendering 的最大的优势就是将光源的数目和场景中物体的数目在复杂度层面上完全分开,也就是说场景中不管是一个三角形还是一百万个三角形,最后的复杂度不会随光源数目变化而产生巨大变化。从上面的伪代码可以看出 Deferred rendering 的复杂度为 O(scree
- 由于硬件限制或者性能限制,GBuffer里保存的材质信息有限,对于特殊材质来说,例如人的皮肤、翡翠等,渲染结果很不好
- 延迟计算光照会大幅增加纹理带宽和帧缓冲区带宽的开销
- 当光源数量很多时,光源会不断对光照强度RT进行累加,也会大幅增加帧缓冲区带宽开销
- 由于硬件限制或者性能限制,不能使用硬件支持的MSAA,只能使用类似后期处理的FXAA或者Temporal AA
名词解释
MSAA、FXAA、Temporal AA都是抗锯齿(Anti-Aliasing)技术,锯齿的来源是因为场景的定义在三维空间中是连续的,而最终显示的像素则是一个离散的二维数组。所以判断一个点到底没有被某个像素覆盖的时候单纯是一个“有”或者“没有"问题,丢失了连续性的信息,导致锯齿。
具体区别可见FXAA、FSAA与MSAA有什么区别?
总结:
无论向前还是延迟渲染,这2者都有优点,和局限性,半个小时学不完,学了也做不到,后面优化的时候再详细说
最终的效果是这个:

需要知识点:
场景的深度图
模型+场面
水面反光
(基于webgl,一开始我也没想过,有点没学会走路就被逼搭飞机和建造飞机内味,但应该只关注每一个实现原理,unity shader都能用)