车漆渲染做法Clear-Coat
什么是ClearCoat
clearcoat指的是透明图层模型。PBR等标准材质模型等适合描述单层的,各项同性的材质表面。但实际上现实中多层材质相当普遍,常见的表面上有一层薄薄的透明涂层的材质,比如透明车漆,家具上的透明薄膜。
我们可以在标准材质模型上添加第二个镜面反射来模拟透明图层(也就是添加第二个镜面反射BRDF项)。为了简化实现复杂度,我们假定透明涂层是各项同性的非金属。我们把原来的基础材质层称为base layer,再添加一层ClearCoat层反射。如图所示。
ClearCoat镜面反射的BRDF
原理上来说,ClearCoat的BRDF双向反射函数模型和标准PBR的结构没有什么区别。
但为了节省计算,DFG项会使用代价更小的实现公式。
D项会采用GGX
G项会采用Kelemen01提出的38式,Kelemen的可见项V的实现
F项会采用Schlick
假定透明涂层是由聚氨酯(常用于清漆)构成的,光线从空气入射聚氨酯的折射率(IOR)为1.5,由此我们可以计算出透明涂层的f0值
可以看出透明涂层的反射率和常见非金属材质的反射率一样都是4%。
最后,我们把透明图层的BRDF项一起整合添加
(这个fc的BRDF包不包括F项呢?据我分析:fd代表diffuse)
注:(38)(39)(40)等代表引用书中的公式,第38,第39,第40个公式,不作为公式的一部分。
透明涂层的参数设定
透明涂层模型支持我们之前定义的标准模型的所有参数。它本身额外定义了下表中的两个参数。
参数定义
ClearCoat透明涂层的强度。标量,取值范围0到1
ClearCoatRoughness透明涂层的感知粗糙度。标量,取值范围0到1
ClearCoatRoughness参数使用和Roughness参数一样的方式进行重映射。
透明图层的GLSL实现代码(伪代码)
void BRDF(){
//从标准模型计算Fd和Fr
//重映射和线性化Roughness
clearCoatPerceceptualRoughness=clamp(clearCoatPerceceptualRoughness,0.089,1.0);
clearCoatRoughness=clearCoatPerceceptualRoughness*clearCoatPerceceptualRoughness;
//clear coat BRDF
float Dc=D_GGX(clearCoatRoughness,NoH);
float Vc=V_Kelemen(clearCoatRoughness,LoH);
float Fc=F_Schlick(0.04,LoH)*clearCoat; //clear coat strength
float Frc=(Dc*Vc)*Fc;
//考虑Base层的能量损失,据我分析,Fr是基础的镜面反射BRDF,Fd是Diffuse
return color*((Fd+Fr*(1.0-Fc))*(1.0-Fc)+Frc);
}
ClearCoat对基础层的修改
加入透明图层后,基础层原来的光线从空气到材质的反射率f0需要换成从透明图层到基础层的反射率。
为此,我们可以先用基础层原来的f0计算出光线从空气入射基础层的折射率(IOR),然后结合透明图层的折射率(IOR)1.5计算出新的基础层f0值。
注:反射率f0是BRDF中F项的参数
UE4中ClearCoat实现的分析
PC端延时渲染下的实现
在UE4根据宏开关MATERIAL_SHADINGMODEL_CLEAR_COAT判断是否开启了ClearCoat光照模型,
在ShadingModelsMaterial.ush中通过GetMaterialCustomData0(MaterialParameters)获得蓝图中ClearCoat参数,
填充到GBuffer.CustomData结构体中,然后再填充进OutGBufferD中。
然后通过GBUFFER_HAS_PRECSHADOWFACTOR宏开关,传递给MRT[5/4]通道中,默认是4
传给G-Buffer以后,开始计算光照,其实就是对G缓存中的图像合成过程。
最终在ReflectionEnvironmentPixelShader.usf文件中把关于ClearCoat高光部分的GBuffer整合。具体代码如下:
主要就是这两个部分对ClearCoat进行了计算。
首先是重映射RemapClearCoatDiffuseAndSpecularColor,这部分应该是计算Base层的能量损失。代码如下:
然后是ReflectionEnvironment函数,首先Color为采样的CubeMap或者平面反射+SSR,根据ClearCoat强度插值,方便下一步使用。
具体代码如下:
简单分析下,首先采样了一张预计算的LUT图用来拟合环境光部分的DGF项,节省计算量,然后最终ClearCoat的输出结果为SSR+GatherRadiance+GatherRadiance(叠加了两次)
由于是GBuffer的合并,这一步最后的结果会叠加到SV_Target上。Blend One One模式
与PC端前向渲染的区别
切换到前向渲染后,效果差距很大,经过调试分析出以下几点
1,默认前向没有SSR,默认延时有SSR(影响不是特别大)
2,前向渲染的效果和标准PBR的SpecularIBL几乎一致,感觉并没有叠加第二遍运算。
最终工作:修改ForwardLighting支持ClearCoat
修改项一:SpecularIBL部分
在前向渲染BassPassPixelShader中,关于IBL的计算只计算了一次,并没有叠加第二次。所以我们需要叠加第二次IBL的计算。
forward修改后:
修改项二:基础层部分的干扰
通过分析ClearCoat的光照模型可以得知,加了一层透明薄膜层会对基础层的DiffuseColor和SpecularColor造成影响,但是在Forward里并没有发现对于这部分的影响,所以我们需要加上。
forward修改后:
修改项三:
全部移植过去后,仍发现效果有些对不上,经过一步步调试,发现关于Capture和cubemap的Blend混合结果这部分出现了问题,IBL叠加后效果不对。
REFLECTION_COMPOSITE_USE_BLENDED_REFLECTION_CAPTURES
为了工作效率,简单粗暴的直接开启了这部分计算。
至此,关于Forward前向渲染支持ClearCoat工作到此结束。
Mobile端延时渲染下的实现
经分析,Mobile端效果和deferred效果基本一致,就不做过多分析
低配Mobile端延时渲染下的实现
准备采用MatCap