Physically Based Shading on Mobile (Translate)
Introduction
大约一年前,我们发现我们的美术对手机所展示的游戏美术作品并不满意,他们想打造远超之前标准的产品。因此,在过去一年中,我们使用Unity Shader转换工具链(Unity shader variants toolchain)研发了Uber Shader。
Uber Shader可以把我们在游戏里所有的表面制作成One-Stop-Shop(举个例子,就是能在一个上面里面买到所有东西的那种一站式购物商店?)
在不遵循PBR正规构造规则,使用方针的方面上来讲,这是一个从PBR获得灵感的Shader。
这个后面被采纳为工作室整体研发pipeline之后,美术团队提出了越来越多的功能性需求,然后shader就完成为了我们现在知道的这个样子。
目前支持以下多种功能。
Detail mapsTexture blendingReflectionsPseudo sub surface scatteringFresnelNormal mapsMetallic surfacesLinear & gamma space workflows
Issues
Shader转换太多,会导致编译时间过长。
所有的转换Shader,包含LOD以及Shader pass的话,shader编译时间可能会达到30分钟以上。
这样会妨害到快速的重复经验研发。
我们有定制的shader Inspector。
当属性设置为特定贴图,或者启动转换工具的话,Shader的功能会被启动或者禁用。
我们发现Inspector变得过于复杂,而最初的想法是将功能模块化的,以便美术同学们可以很容易地找到它。
但是事情变得这么复杂的事情是我们刚刚才发现的。
我们赋予美术团队变通之后,很遗憾的发现美术同学们几乎不能自己在游戏内达到数据的均衡了。
他们制作以及构建数据所用的时间太多了。
而且我们的Material在Tool-Chain (Maya, Substance Painter and Unity)里面看起来完全是另一个样子。
这个意味着美术团队要用非常多样化的设置,去搭建3次surface。
Substance Painter
我们美术团队非常喜欢Substance Painter。可以非常视觉、非常艺术的方式制作数据并且进行融合。
全世界几百个工作室也在使用。
一般来说,美术同学喜欢使用在Painter里面看视觉效果的工作方式。
经过一点调查,我们马上知道在Substance painter里面有PBR pipeline。
理想条件下,美术团队使用Substance painter制作的数据在手机设备里面应该能达到或者类似达到与Unity一致的效果。
Research
有很多基于物理的渲染和阴影相关信息。
过去的5年也在广泛应用。
我们读完论文,看过发表内容并看过slide后讲过。
这是尽量可以认知更多信息的水平上,可执行的组合。
第一个公开的是,几乎所有 PBR 信息和研究的设计都是用于desktop,console hardware。
但是我们想在手机实现相同的画面。
我在这篇例文下方的研究上添加了很多链接。
Decisions
Disney PBR Principles
Disney以 Physically-Based Shading这个题目发表论文的时候, PBR第一次被聚焦。
他们 2012 年制作 Wreck-It Ralph 的时候讲过转换到 PBR的方法。
不仅仅是定义光照模型,也定义了要用在 PBR 管线的制作和设定上的规则。
要使用直观的物理参数。参数的数量尽量要少。参数要在差不多的范围里的 0到 1。已经超过了参数有意义的地方,需要Push 。参数的组合要合适。
Workflow
开发PBR 管线的时候有两个需要遵守的基本工作流。
Specular
以7 个通道组成:
Albedo (RGB)
Specular (RGB)
Smoothness [0, 1]

Metallic
以5 个通道组成 :
Base Colour (RGB)
Roughness [0, 1]
Metallic [0, 1]

电介质材料,反射高光的颜色是灰色阴影值。所以使用 specular 模型就会浪费 specular 贴图的两个通道。如果是金属表面,扩散色几乎是黑色,所以会浪费反射模型里的3种通道。
考虑这些后,考虑到适合手机环境的结构跟Disney的第二个原则 "参数尽可能少点.",我们决定只使用金属工作流。
Bungee (Destiny 2), Unreal Engine, Frostbite Engine 和 Unity都支持金属工作流(Metallic Work-Flow) (Unity也支持 Specular Work-Flow)。
ALU 对 LUT
我们得 Uber Shader 工具链会把 specular 和照明效果输出成搜索贴图。
之后为了扩散使用 N.L,为了反射高光使用 [N.H, smoothness]采样这个贴图。
这可以使用消耗很小,或者多数消耗大的光照模型。
但是了解到高光有时候不够明显,而且通过贴图和数据的压缩损失了部分数据。
我们知道手机硬件支持多种光是不可能实现的。 (最近一定程度上是可以了。)
单个surface上有4个光的影响,在照明上会导进8个贴图。
Lighting Model
数十年之间最有名的反射光模型应该是Blinn模型。
转到 PBR的人初期采用的是手机Shader里标准化的 Blinn Phong 光照模型。
但我们想要更好的品质,所以大部分主机和 desktop title
是跟现在的 GGX specular 模型一起使用的。
跟实际反射高光比较时,会更写实。
但是很不幸,这种写实随着很多追加演算!
SIGGRAPH 2015上 Unity发表了题目叫 PBR 优化的论文。 是很棒的读物,也会提示我们采用的修饰。
是非常接近实际手机设备上执行的近似值。
Implementation
之后是为了 ForwardBase Lighting Pass 开发的fragment shader的(简化)概要。
所有导入的色彩和基于色彩的贴图都修正Gamma在线性空间制作着。
结果是,会重新在Gamma空间的frame buffer上写最终颜色。
表面粗糙度和金属性,周边封锁的参数储存到线性贴图上。
我们又把 emissive color mask和 texture blend mask储存到第二个贴图上。
牺牲 ALU 压缩 Full over head ,可以在一个贴图放所有东西。
sampleMaterialTexture () 和 initSurface () 函数呼叫内,
对尽可能多的贴图数据做prefetch。
我们尽可能预先计算更多的可重复使用的表面信息,储存到 SurfaceData 结构里。
Roughness
Normals
Diffuse & Specular terms
Reflection probe samples
BRDF分为几个 Helper function 。需要分享的部分很多,这里有重点部分。
我们这样结合环境照明。
为了阻止 Stall ,开始Shader的时候 (在 initSurface () 函数呼叫中) Cube map数据做 Pre-fetch 来防止 stall 。
Profiling
有必要对Shader 做 profiling
最好的道具是 XCode的 GPU Debugger。
显示 Shader 的 Line 费用。
采样环境 Cube map后,立即 decoding 其值并开始了。
在 Shader 开始的部分采样 Cube map,之后在Shader里,更晚点decoding 的话只是整个Shader时间的3 %。
Conclusion
可以开发工具链整体上非常类似,对象平台 (Android 和 iOS)上执行顺畅的原型。
从左到右 : Substance Painter, Unity Windows, Unity iOS. iOS和其他平台间不一致最严重的是从 LDR 反射 Pbobe 开始的。
Performance: ALU vs Texture Fetch
我们想证明 ALU 照明的方法对我们更有意义。
在场景添加更多照明的时候更是如此。
下面的表格是对 1, 2 和 4个照明相关我们的 3 种实现上,显示相关 ALU 指令总数和贴图Fetch总数。
我们可以知道基于 ALU 的方法会使用更多的 ALU 指令和更少的贴图Fetch。
这更适合对象平台。
手机上带宽很费的!
数年间手机 GPU越来越强大,虽然可以处理复杂的数学,但在shader 加贴图的读取就会更经常造成 GPU 的瓶颈现象。
Performance: ALU vs sRGB Gamma to Linear Conversion
支持 Open GL ES 3.0 以上的手机设备支持读取 sRGB 。
GPU 贴图 sampler可以把贴图数据在Gamma里变换成线性空间。
以前的设备需要手动变换Gamma和线性空间。

我们可以知道基于 ALU 的方法会使用更多的 ALU 指令和更少的贴图Fetch。
这更适合对象平台。
手机上带宽很费的!
数年间手机 GPU越来越强大,虽然可以处理复杂的数学,但在shader 加贴图的读取就会更经常造成 GPU 的瓶颈现象。
Performance: ALU vs sRGB Gamma to Linear Conversion
支持 Open GL ES 3.0 以上的手机设备支持读取 sRGB 。
GPU 贴图 sampler可以把贴图数据在Gamma里变换成线性空间。
以前的设备需要手动变换Gamma和线性空间。
从两个里选一个 commit 之前,要找出各自的费用。
使用 XCode GPU Debugger 渲染整体球状的时候 ALU 的变换是 3.05ms ,但 sRGB method 是 2.79ms。中断 OpenGL ES 2.0 的支持,或者可以在这种设备的Gamma空间工作的话, 😢, 请选择 sRGB method。
Next Steps
到这里为止的所有工作都是从个人测试和研究开始的。
我们还在原型阶段,发展到最终生成管线的功能Set是收尾的状态。
Shader代码我们让它变通,尽量都写成了模块形式。
可以 scripting 的 Render pipeline ,最终转换到延迟阴影是我们的目标。