原文
unity 2020 怎么写 shader 使其接受光照?_Unity渲染路径
正文
(基本上是翻译)
着色器(shaders)定义了物体本身的样子(材质属性),也定义了物体是如何应对光照的。 (本文主要讲 Unity Surface Shaders 背后的光照和渲染管线)
渲染路径(Rendering Paths)
Rending path
决定了光照是如何完成的,哪些 Passes 被使用。Shader 的 pass 通过 Pass Tags
来说明光照类型。
Shader的一个 Pass 块会引起一次对应
GameObject
的几何结构(一般是mesh)的渲染。也就是一个 pass 对应一次渲染。
Forward Rendering path(前向渲染路径)
Forward Rendering path 可能用一个或多个 pass 渲染每个物体,这由影响该物体的光源决定。
在前向渲染中,在影响物体的光中,一些最亮的光(可以有至多 4
个点光源)使用 逐像素 的方式渲染。别的光使用以 逐顶点 的方式或 Spherical Harmonics lighting 的方式计算(SH Lighting 比较快,但只是一种近似)。
一个光(light)是否为逐像素光,取决于:
- 渲染模式(
Render Mode
)被设置为(Not Important)的光总是为 逐顶点 (per-vertex)光或者 SH 光。 - 最亮的平行光(或者方向光,Directional Light)总是逐像素光(per-pixel)。
- 渲染模式(
Render Mode
)被设置为(Important)的光总是为逐像素光。 - 若经过上面三步后,已确定的逐像素光数量仍小于
Quality Setting
中设置的 Pixel Light Count,则更多的光可以使用 per-pixel 方式渲染,根据光的 亮度递减顺序 决定。
每个物体按照如下方式渲染:
BasePass
Base Pass 中按 per-pixel 方式计算一个平行光(directional light),这个平行光可以有阴影,和所有逐顶点光和 SH 光。若 pass flag
中设置了 “OnlyDirectional”,则只计算一个平行光。这个 Pass 中可以使用 lightmaps、环境光、自发光(emissive lighting)等。
Additional Passes
除最亮的平行光外,别的每一个 per-pixel 光分别用一个 pass 计算,这些 passes 被称为 additional passes。这个 Pass 中的 per-pixel 光 默认是没有阴影 的,除非使用 multi_compile_fwdadd_fullshadows
。
举例: 如上图,有一个物体(用图中的圆表示),它受到光 A-H 影响。假设光 A-H 有相同的颜色和强度,渲染模式都为
Auto
,则最亮的光 A-D 使用 per-pixel 模式渲染,最多 4 个。光源 D-G 使用 per-vertex 渲染,最终剩余的光(G-H)使用 SH 渲染,如下图,注意这几个光组是有重叠的(怎么实现的?)。
Deferred shading rendering path(延迟渲染路径)
在三维计算机图形学领域,deferred shading, 延迟渲染,是一种屏幕空间 shading 技术,最早由 Michael Deering 于 1988 年提出。之所以称之为 “延迟”(deferred)是因为在它的第一个 pass 中并 没有渲染 (shading)发生,而是推迟到第二个 pass 才进行渲染(shading)。 deferred shader 的第一个pass中,只是计算了一些渲染需要的一些数据,如:位置、法线、材质。这些数据被渲染到几何缓存 G-buffer
(geometry buffer)中。然后,在 pixel shader(也即 fragment shader)中利用第一个 pass 中得到的 G-buffer 中的信息计算直接或间接光照信息。
优势
延迟渲染的 主要优势 是从光照(lighting)中解耦了几何(geometry)信息。只需要一个 pass 来计算几何信息。每一个光(light)只对它影响到的像素进行计算。因此可以在场景中添加大量光而不会造成显著的性能影响。
劣势
- 延迟渲染的一个 主要缺点 是不能处理透明(transparency)(开启
Z-buffer
的场景都不能)。 - 另一个 严重的问题 是不能使用多种材质(multiple materials),因为大量不同材质要求更多的数据存储在
G-buffer
中(G-buffer 中本来就有大量数据,占用大量存储空间了)。 - 还有一个 重要的问题 是硬件反走样(hardware anti-aliasing)不能产生正确的结果,因为光照计算阶段(lighting stage)和几何计算阶段(geometric stage)是分开的(在不同 Pass 中完成),采样插值会得到错误的位置、法线、切线等信息。
Overview
在 Unity 中,当使用延迟渲染时,影响一个 GameObject
的光(light)的数量没有限制。并且所有的光是按 per-pixel 方式计算的,可以使用法线贴图(normal map)。另外,所有的光可以有 cookies 和阴影。
优点
由上文已知,在 deferred shading 中,光照的处理开销和光所影响的像素的数量成正比,而和光点亮的 GameObjects
的数量无关。因此,可以通过尽量减小光照的区域来提升性能。Deferred shading 具有高度一致且可预测的行为。由于每一个光都是按照 per-pixel 方式计算的,在大的三角形面片上不会有光照计算瑕疵。
缺点
和上文提到的一样,deferred shading 不能真正地支持反走样,不能处理半透明物体(这些是在 forward rendering 中处理的)。另外,在 deferred shading 中 Mesh Renderer 也不可以使用 Receive Shadows
标志,且 culling masks
也会受限制,最多可以使用 4 个 culling masks。
使用要求
-
要求显卡支持多个渲染目标(Multiple Render Targets, MRT),Shader Model 3.0 以上,且支持深度渲染纹理(Depth render textures)。大部分在 2006 年以后生产的 PC 上的显卡(以 GeForce 8xxx, Radeon X2400, Intel G45 等开头)支持 deferred shading。
-
运行 OpenGL ES3.0 以上的所有移动平台支持 deferred shading。
注意: Deferred rendering 不支持 正交投影(Orthographic projection)。若相机投影模式设置为
Orthographic
,则相机会使用 Forward rendering 。
性能注意事项
由于在延迟渲染中光的渲染开销和它 “点亮的” 像素个数成正比,和场景复杂度无关。因些小的点光源或聚光灯(spot lights)是非常廉价的,如果它们被别的物体遮挡的话,性价比就更高了。
当然,支持阴影的光会比不支持阴影的光开销大很多。
实现细节
具有不支持 deferred shading 的 shaders 的物体在 deferred shading 完成后,按照 forward rendering 方式渲染。
G-buffer 中渲染目标(render targets, RT0-RT4)的默认布局如下。数据类型放在每个 render target 的各个通道中。通道放在括号中:
RT0
, ARGB32 格式: Diffuse color(RGB), occlusion(A).RT1
, ARGB32 格式: Specular color(RGB), roughness(A).RT2
, ARGB2101010 格式: World space normal(RGB), unused(A).RT3
, ARGB2101010 (non-HDR)或者 ARGBHalf(HDR) 格式: Emission+lighting+lightmaps+reflection probes buffer. Depth+Stencil buffer.
因此 g-buffer 默认布局: 160 bits/pixel (non-HDR) 或者 192 bits/pixel(HDR).
若使用 Shadowmask
或者 Distance Shadowmask
模式用于混合光照(Mixed lighting),将会用到第 5 个渲染目标:
RT4
, ARGB32 格式: Light occlusion values(RGBA).
因此,g-buffer 布局为: 192 bits/pixel (non-HDR) 或者 224 bits/pixel (HDR).
若硬件 不支持 五个并行渲染目标,则使用 shadowmasks
的对象将使用forward rendering path.
当相机 未使用 HDR 时,Emission+lighting buffer (RT3) 将被对数编码以提供更大的动态范围(相比于通常使用 ARGB32 纹理)。
注意 当相机使用 HDR rendering 时,不会有独立于 RT3 的渲染目标被创建;相反,使用的渲染目标是 RT3。
G-Buffer pass
G-buffer pass 只渲染每个对象一次。Diffuse colors, specular colors, surface smoothness, world space normal 和emission+ambient+reflections+lightmaps 被渲染进 g-buffer 纹理。g-buffer 纹理被设置为 全局 的 shader 属性,以便稍后由 shaders 来访问(CameraGBufferTexture0 .. CameraGBufferTexture3)。
Lighting pass
Lighting pass 根据 g-buffer 和 depth 计算光照。光照是在 屏幕空间 计算的,因此其处理时间 不依赖于场景复杂度。光照被添加到 emission buffer。
唯一可以使用的光照模型(lighting model)是 Standard
。
若想使用一个不同的模型,可以修改 lighting pass shader:把修改版的 Internal-DeferredShading.shader
(来自 Built-in shaders)放到项目的 "Assets/Resources
" 目录下,然后打开 Edit>Project Settings,点击 Graphics category,修改 "Deferred" 下拉列表为 "Custom Shader",最后把 Shader选 项设为自己定义的 shader。
(未完待续)
参考文献:
- Unity’s Rendering Pipeline
- ShaderLab: Pass
- en.wikipedia.org/wiki/Deferr…
- Spherical Harmonics lighting
- Deferred shading rendering path