unity-shader-延迟渲染
unity-shader-延迟渲染
前篇
相关资料
- 延迟着色法 - https://learnopengl-cn.readthedocs.io/zh/latest/05%20Advanced%20Lighting/08%20Deferred%20Shading/
使用光体积更好的方法是渲染一个实际的球体,并根据光体积的半径缩放。这些球的中心放置在光源的位置,由于它是根据光体积半径缩放的,这个球体正好覆盖了光的可视体积。这就是我们的技巧:我们使用大体相同的延迟片段着色器来渲染球体。因为球体产生了完全匹配于受影响像素的着色器调用,我们只渲染了受影响的像素而跳过其它的像素。下面这幅图展示了这一技巧:
它被应用在场景中每个光源上,并且所得的片段相加混合在一起。这个结果和之前场景是一样的,但这一次只渲染对于光源相关的片段。它有效地减少了从nr_objects * nr_lights
到nr_objects + nr_lights
的计算量,这使得多光源场景的渲染变得无比高效。这正是为什么延迟渲染非常适合渲染很大数量光源。
管线流程
unity 中使用 延迟渲染
官方文档: Deferred shading rendering path - https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html
pc, 要求显卡支持 MRT, sm3.0 和 深度图. 大部分2006年后生成的显卡都支持 延迟渲染, 如: GeForce 8xxx, Radeon X2400, Intel G45 以后的显卡
移动设备, 所有 OpenGL ES 3.0 以上都支持延迟渲染
Implementation details
Objects with Shaders that do not support deferred shading are rendered after deferred shading is complete, using the forward rendering
path.
The default layout of the render targets (RT0 - RT4) in the geometry buffer (g-buffer) is listed below. Data types are placed in the various channels of each render target. The channels used are shown in parentheses.
- RT0, ARGB32 format: Diffuse color (RGB), occlusion (A).
- RT1, ARGB32 format: Specular color (RGB), roughness (A).
- RT2, ARGB2101010 format: World space normal (RGB), unused (A).
- RT3, ARGB2101010 (non-HDR) or ARGBHalf (HDR) format: Emission + lighting + lightmaps
+ reflection probes
buffer. - Depth+Stencil buffer.
So the default g-buffer layout is 160 bits/pixel (non-HDR) or 192 bits/pixel (HDR).
If using the Shadowmask or Distance Shadowmask modes for Mixed lighting, a fifth target is used:
- RT4, ARGB32 format: Light occlusion values (RGBA).
在 shader 中使用这样的变量定义去获取 gbuffer
1 | sampler2D _CameraGBufferTexture0; |
geometry pass, lighting pass
geometry pass : 渲染所有的几何体的 颜色,高光,法线,自发光 数据到 gbuffer 的 四个RT 中
lighting pass : 从 gbuffer (四个RT) 中取出数据进行光照模型着色.
官方文档 说了默认的 lighting pass 使用的是内置的 Internal-DeferredShading.shader 里面的着色. 如果想使用不同的光照模型, 可以参照这个 shader 去修改你自己的着色代码, 然后再设置 edit -> project settings -> graphics -> built-in shader settings, deferred : 设置为 custom shader, 然后把自定义的 shader 拖进去.
shader
参考: Unity 渲染教程(十三):延迟渲染 - http://gad.qq.com/program/translateview/7200862 ( 好文, 引起极度舒适 )
这里只有 geometry pass, 也就是往 gbuffer 中填充数据.
相机的 Rendering Path 设置为 Deferred. (此时 MSAA 无效)
shader 中指定
"LightMode" = "Deferred"
. frag的返回值为四个buffer. (具体参考: My Lighting.cginc)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22struct FragmentOutput {
float4 gBuffer0 : SV_Target0;
float4 gBuffer1 : SV_Target1;
float4 gBuffer2 : SV_Target2;
float4 gBuffer3 : SV_Target3;
};
// 然后往这四个buffer中填充数据
FragmentOutput MyFragmentProgram (Interpolators i) {
...
FragmentOutput output;
color.rgb = exp2(-color.rgb);
output.gBuffer0.rgb = albedo;
output.gBuffer0.a = GetOcclusion(i);
output.gBuffer1.rgb = specularTint;
output.gBuffer1.a = GetSmoothness(i);
output.gBuffer2 = float4(i.normal * 0.5 + 0.5, 1);
output.gBuffer3 = color;
return output;
}- 第一个G缓冲区 (SV_Target0) 用于存储漫反射率和表面遮挡。这是一个ARGB32纹理,就像一个常规的帧缓冲区。反射率存储在RGB通道中,遮挡存储在A通道中。
- 第二个G缓冲区 (SV_Target1) 用于存储RGB通道中的镜面高光颜色,以及A通道中的平滑度值。它也是一个ARGB32纹理。
- 第三个G缓冲区 (SV_Target2) 包含的是世界空间中的法向量。它们存储在ARGB2101010纹理的RGB通道中。这意味着每个坐标都是使用十位进行存储,而不是通常的八位。这使得它们更加精确。A通道只有两位-所以总共再次是32位 - 但是它没有被使用,所以我们只需将它设置为1。法线的编码就像是一个普通的法线贴图
- 第三个G缓冲区 (SV_Target3) 用于累积场景的光照。它的格式取决于相机是否设置为高动态光照渲染或是低动态光照渲染。在低动态光照渲染的情况下,它是一个ARGB2101010纹理,就像正常的缓冲区一样。当启用高动态光照渲染的时候,格式为ARGBHalf,它存储每个通道的16位浮点值,总共64位。 所以高动态光照渲染版本他缓冲区的大小是其他缓冲区的两倍。仅仅使用RGB通道,因此A通道可以再次设置为1。
可以在场景编辑器中查看各个缓冲区的纹理
lighting 阶段伪代码
原文链接: [https://learnopengl-cn.readthedocs.io/zh/latest/05%20Advanced%20Lighting/08%20Deferred%20Shading/](https://learnopengl-cn.readthedocs.io/zh/latest/05 Advanced Lighting/08 Deferred Shading/)
我们在渲染之前绑定了G缓冲中所有相关的纹理,并且发送光照相关的uniform变量到着色器中。
光照处理阶段的片段着色器和我们之前一直在用的光照教程着色器是非常相似的,除了我们添加了一个新的方法,从而使我们能够获取光照的输入变量,当然这些变量我们会从G缓冲中直接采样。
伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
struct Light {
vec3 Position;
vec3 Color;
};
const int NR_LIGHTS = 32;
uniform Light lights[NR_LIGHTS];
uniform vec3 viewPos;
void main()
{
// 从G缓冲中获取数据
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = texture(gAlbedoSpec, TexCoords).a;
// 然后和往常一样地计算光照
vec3 lighting = Albedo * 0.1; // 硬编码环境光照分量
vec3 viewDir = normalize(viewPos - FragPos);
for(int i = 0; i < NR_LIGHTS; ++i) // 每个光源进行进行光照着色
{
// 漫反射
vec3 lightDir = normalize(lights[i].Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Albedo * lights[i].Color;
lighting += diffuse;
}
FragColor = vec4(lighting, 1.0);
}
光照处理阶段着色器接受三个uniform纹理,代表G缓冲,它们包含了我们在几何处理阶段储存的所有数据。如果我们现在再使用当前片段的纹理坐标采样这些数据,我们将会获得和之前完全一样的片段值,这就像我们在直接渲染几何体。在片段着色器的一开始,我们通过一个简单的纹理查找从G缓冲纹理中获取了光照相关的变量。注意我们从gAlbedoSpec
纹理中同时获取了Albedo
颜色和Spqcular
强度。
因为我们现在已经有了必要的逐片段变量(和相关的uniform变量)来计算布林-冯氏光照(Blinn-Phong Lighting),我们不需要对光照代码做任何修改了。我们在延迟着色法中唯一需要改的就是获取光照输入变量的方法。
再执行 lighting 阶段着色光源时, 此时场景所有的里面所有物体都已经填充 gbuff 完毕, 只需要遍历每个光源, 取出 gbuff 数据进行着色叠加就行, 这个就是延迟光照的优点, 光源数量与场景复杂度无关, 批次的增加只会随着 光源 的增加而增加.
结合延迟渲染与正向渲染
原文链接: [https://learnopengl-cn.readthedocs.io/zh/latest/05%20Advanced%20Lighting/08%20Deferred%20Shading/
《Real-Time Rendering 3rd》 提炼总结 - 阅读笔记
来源于: https://github.com/QianMo/Real-Time-Rendering-3rd-CN-Summary-Ebook
7.1 延迟渲染 Deferred Rendering
我们知道,正向渲染(Forward Rendering),或称正向着色(Forward Shading),是渲染物体的一种
非常直接的方式,在场景中我们根据所有光源照亮一个物体,之后再渲染下一个物体,以此
类推。
传统的正向渲染思路是,先进行着色,再进行深度测试。 其的主要缺点就是光照计算跟场景
复杂度和光源个数有很大关系。假设有 n 个物体, m 个光源,且每个每个物体受所有光源的
影响,那么复杂度就是 O(m*n)。
正向渲染简单直接,也很容易实现,但是同时它对程序性能的影响也很大,因为对每一个需
要渲染的物体,程序都要对每个光源下每一个需要渲染的片段进行迭代,如果旧的片段完全
被一些新的片段覆盖,最终无需显示出来,那么其着色计算花费的时间就完全浪费掉了。
可以将延迟渲染( Deferred Rendering)理解为先将所有物体都先绘制到屏幕空间的缓冲(即 Gbuffer, Geometric Buffer,几何缓冲区)中,再逐光源对该缓冲进行着色的过程,从而避免了
因计算被深度测试丢弃的⽚元的着色而产⽣的不必要的开销。 也就是说 延迟渲染基本思想
是,先执行深度测试,再进行着色计算,将本来在物空 间(三维空间)进行光照计算放到了
像空间(二维空间)进行处理。
对应于正向渲染 O(m*n)的 复杂度,经典的延迟渲染复杂度为 O(n+m)。
7.2 几何缓冲区 G-buffer
G-Buffer,全称 Geometric Buffer ,译作几何缓冲区,它主要用于存储每个像素对应的位置
( Position),法线( Normal),漫反射颜色( Diffuse Color)以及其他有用材质参数。 根据这
些信息,就可以在像空间(二维空间)中对每个像素进行光照处理。
下图是一帧中 G-buffer 中存储的内容:
可以将延迟渲染理解为两个 Pass 的过程:
1、几何处理阶段(Geometry Pass)。这个阶段中,我们获取对象的各种几何信息,并将第二步
所需的各种数据储存(也就是渲染)到多个 G-buffer 中;
2、光照处理阶段(Lighting Pass)。 在这个 pass 中,我们只需渲染出一个屏幕大小的二维矩形,
使用第一步在 G-buffer 中存储的数据对此矩阵的每一个片段计算场景的光照;光照计算的过
程还是和正向渲染以前一样,只是现在我们需要从对应的 G-buffer 而不是顶点着色器(和一些
uniform 变量)那里获取输入变量了。
下面这幅图片很好地展示了延迟着色的整个过程:
7.5 延迟渲染 vs 正向渲染
正向渲染( Forward Rendering)管线流程
延迟渲染 (Deferred Rendering) 管线流程