▷ 虚幻引擎4中的真实着色

⌹ 365体育app官方下载 ⏱️ 2025-11-02 07:39:23 👤 admin 👁️‍🗨️ 608 ❤️ 57
虚幻引擎4中的真实着色

翻译:王成林(麦克斯韦的麦斯威尔 ) 审校:黄秀美(厚德载物)

作者Brian Karis,Epic Games

图1:UE4:《渗入者》演示

导言

大约在一年以前,我决定投入一些时间来改进我们的着色模型,采取一个更加基于物理的材质制作流程。我这样做一部分原因是想要渲染更多逼真的图片,另外我们也想看看采用一个更加基于物理的方法制作材质和使用材质分层能够实现什么效果。美术人员认为这样会大大提升工作流程和质量,而我在另外一家工作室亲眼看到过这些成效,当时我们采取了离线合成材质层的方法。Epic这里的一位技术美工尝试在着色器中进行分层,得到了出色的结果,这甚至成为了一项额外要求。

为了坚持这个改进方向,我们知道材质分层需要简单高效。恰巧在这个时候,迪士尼展示[2]了他们在《无敌破坏王》中使用的基于物理的着色和材质模型。Brent Burley展示了离线特征电影级渲染即使只有少量几个参数也可能会足够复杂。他还指出一个相对实用的着色模型可以适用于大多数的采样材质。他们得出的结论成为我们工作的灵感和基础,另外仿照他们的“原则” ,我们决定为我们的系统确定目标:

实时性能

· 首先也是最重要的一点,它应高效地同时使用多个可见光。

降低复杂度

· 参数应尽可能的少。参数过多会导致决策瘫痪,反复试错,或者会产生一些互相关联的属性,从而为了实现一个效果需要改变很多数值。

· 我们应该可以交替使用基于图片的光照以及分析性光源,所以参数必须在所有光照类型中都表现一致。

接口要直观

· 我们更喜欢易于理解的数值,而不是诸如折射率之类的物理术语。

感觉上是线性的

· 我们希望支持通过蒙版(mask)进行分层,但是每个像素我们只能渲染一次。这意味着根据参数进行混合的着色必须尽量符合着色结果的混合。

易于掌握

· 我们希望人们无需对电介质和导体具备过多理论上的知识,另外要把制作那些基础的基于物理真实的材质所需的努力降到最低。

稳定

· 应该很难错误地制作出基于物理但不真实的材质

· 参数之间的所有组合得到的结果都应该尽量真实无误

富于表达性

· 延迟着色限制了我们可以拥有的着色模型数量,所以我们的基础着色模型需要具有足够强的描述性以涵盖真实世界中99%的材质。

· 所有可以分层的材质需要共用相同的参数集以便在它们之间进行混合。

灵活性

· 其它的项目以及项目制作人也许不想实现照片级真实效果,所以要能够灵活地开启非照片级真实渲染。

着色模型

漫反射BRDF

我们研究了Burley的漫反射模型,但是发现它和Lambertian漫反射(等式1)只有少数不同之处,所以我们无法解释额外的开销。另外,任何更加复杂的漫反射模型很难有效地用在基于图片的光照或者球谐光照(Spherical harmonic lighting)中。因此,我们没在其它选项上花费过多精力。

其中cdiff是材质的漫反射系数。

微平面(microfacet)高光BRDF

普通的Cook-Torrance[5,6]微平面高光着色模型是:

查看该课程中的[9]以了解更多细节。

我们从迪士尼的模型入手,通过对比更加高效的模型来衡量每一项的重要性。这比听上去的要更困难;发布的公式中每一项使用的输入参数不一定相同,而这对于比较结果正确与否至关重要。

高光D项

对于正态分布函数(NDF),我们发现迪士尼采用的GGX/Trowbridge-Reitz值得它的成本。使用Blinn-Phong所需的额外开销相对很小,且更长的“尾部”所产生的独特的、自然的外表也吸引了我们的美工。我们还采用了迪士尼的重新参数化。

高光G项

关于高光几何衰减项,我们进行了比其他项更多的评估。最终,我们选择使用Schlick模型[19],但是我们使用k=α/2以便更好地适合GGX[21]的Smith模型。通过这个改动,Schlick模型在α=1时完全对应Smith模型,而且在[0,1]范围内(见图2)也是相当接近的近似了。我们还选择使用迪士尼的调整,在平方之前先将粗糙度映射为以降低“热度”。重点注意一下,该调整仅用于分析性光源;如果应用于基于图片的光照,那么掠射角的结果会过于黑暗。

高光F项

对于菲涅尔,我们选择使用传统的Schlick近似[19],但是做了一个小改动:我们使用球面高斯近似[10]来代替指数。这样计算的效率稍微高一些,且几乎感觉不到它们之间的差别。公式为:

其中F0是沿法线入射时的高光反射值。

图2:k=α/2的Schlick和Smith非常接近

基于图片的光照

为了在基于图片的光照中使用这个着色模型,我们需要解出辐射率(radiance)积分,常用的方法是使用重要性采样。以下表达式描述了该数值积分:

以下HLSL代码展示了如何使用我们的着色模型进行采样:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687float3 ImportanceSampleGGX( float2 Xi, float Roughness, float3 N ) { float a = Roughness * Roughness; float Phi = 2 * PI * Xi.x; float CosTheta = sqrt( (1 - Xi.y) / ( 1 (a*a - 1) * Xi.y ) ); float SinTheta = sqrt( 1 - CosTheta * CosTheta ); float3 H; H.x = SinTheta * cos( Phi ); H.y = SinTheta * sin( Phi ); H.z = CosTheta; float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0); float3 TangentX = normalize( cross( UpVector, N ) ); float3 TangentY = cross( N, TangentX ); // Tangent to world space return TangentX * H.x TangentY * H.y N * H.z; } float3 SpecularIBL( float3 SpecularColor , float Roughness, float3 N, float3 V ) { float3 SpecularLighting = 0; const uint NumSamples = 1024; for( uint i = 0; i < NumSamples; i ) { float2 Xi = Hammersley( i, NumSamples ); float3 H = ImportanceSampleGGX( Xi, Roughness, N ); float3 L = 2 * dot( V, H ) * H - V; float NoV = saturate( dot( N, V ) ); float NoL = saturate( dot( N, L ) ); float NoH = saturate( dot( N, H ) ); float VoH = saturate( dot( V, H ) ); if( NoL > 0 ) { float3 SampleColor = EnvMap.SampleLevel( EnvMapSampler , L, 0 ).rgb; float G = G_Smith( Roughness, NoV, NoL ); float Fc = pow( 1 - VoH, 5 ); float3 F = (1 - Fc) * SpecularColor Fc; // Incident light = SampleColor * NoL // Microfacet specular = D*G*F / (4*NoL*NoV) // pdf = D * NoH / (4 * VoH) SpecularLighting = SampleColor * F * G * VoH / (NoH * NoV); } } return SpecularLighting / NumSamples; }即使使用重要性采样,我们仍然需要采集很多样本。使用mip map[3]可以极大地减少样本数量,但是总数仍需大于16以保证质量足够高。由于我们为了本地反射在每个像素上混合了许多环境贴图,我们实际上只能为每个像素提供一个单一样本。

分开求和近似

为了实现这一点,我们将上述求和分成两个求和项进行近似。然后我们可以分别预计算每个求和项。该近似对于一个恒定的Li(l)非常准确,对于普通环境相对准确。

预先过滤的环境贴图

我们使用了不同粗糙度预先计算了第一个求和项,并将结果存储在立方图的mip-map级别中。大多数的游戏公司采用了这种方法[1,9]。一个细微的区别是,我们使用着色模型的GGX分布对环境贴图进行卷积。由于它是一个微平面模型,分布的形状会根据到平面的视角变化而变化,所以我们假设该角度为0,即n=v=r。该各向同性的假设是第二次近似,不幸的是,它意味着我们在掠射角不会得到长反射。和分开求和的近似相比,该近似对于IBL结果造成的误差更大。如上面代码所展示的,我们发现使用cosθlk加权能实现更佳的结果[1]。

123456789101112131415161718192021222324252627282930313233343536373839float3 PrefilterEnvMap( float Roughness, float3 R ) { float3 N = R; float3 V = R; float3 PrefilteredColor = 0; const uint NumSamples = 1024; for( uint i = 0; i < NumSamples; i ) { float2 Xi = Hammersley( i, NumSamples ); float3 H = ImportanceSampleGGX( Xi, Roughness, N ); float3 L = 2 * dot( V, H ) * H - V; float NoL = saturate( dot( N, L ) ); if( NoL > 0 ) { PrefilteredColor = EnvMap.SampleLevel( EnvMapSampler , L, 0 ).rgb * NoL; TotalWeight = NoL; } } return PrefilteredColor / TotalWeight; }

环境BRDF

第二个求和包含了其余所有内容。这和在纯白环境下对高光BRDF做积分是一样的,即Li(lk)=1。通过在Schlick的Fresnel中进行代换:F (v, h) =F0 (1-F0)(1-v.h)5,我们发现可以将F0 提到积分外面。

这样我们有了两个输入参数(粗糙度和cos)和两个输出参数(缩放大小和到F0的偏移值),所有这些参数的范围都在[0,1]中,方便我们计算。我们预先计算该函数的结果,将它存在一张2D查找纹理[2](LUT)中。

图3:2D查找纹理

在完成这项工作后,我们发现所有现存的研究都得到了和我们几乎一样的结果。Gotanda使用了一个3D LUT [8] ,而Drobot将其优化为一个2D LUT [7] ,和我们的做法几乎相同。另外,作为这门课程的一部分,Lazarov更进一步,展示了一个相似的积分[3]的一些分析性近似。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455float2 IntegrateBRDF( float Roughness, float NoV ) { float3 V; V.x = sqrt( 1.0f - NoV * NoV ); // sin V.y = 0; V.z = NoV; // cos float A = 0; float B = 0; const uint NumSamples = 1024; for( uint i = 0; i < NumSamples; i ) { float2 Xi = Hammersley( i, NumSamples ); float3 H = ImportanceSampleGGX( Xi, Roughness, N ); float3 L = 2 * dot( V, H ) * H - V; float NoL = saturate( L.z ); float NoH = saturate( H.z ); float VoH = saturate( dot( V, H ) ); if( NoL > 0 ) { float G = G_Smith( Roughness, NoV, NoL ); float G_Vis = G * VoH / (NoH * NoV); float Fc = pow( 1 - VoH, 5 ); A = (1 - Fc) * G_Vis; B = Fc * G_Vis; } } return float2( A, B ) / NumSamples; }最后,为了对使用重要性进行采样的参考作近似,我们将两个预先计算的和相乘:

123456789101112131415float3 ApproximateSpecularIBL( float3 SpecularColor , float Roughness, float3 N, float3 V ) { float NoV = saturate( dot( N, V ) ); float3 R = 2 * dot( V, N ) * N - V; float3 PrefilteredColor = PrefilterEnvMap( Roughness, R ); float2 EnvBRDF = IntegrateBRDF( Roughness, NoV ); return PrefilteredColor * ( SpecularColor * EnvBRDF.x EnvBRDF.y ); }

图4:顶部为参考,中间为分开求和近似,底部为包含n=v假设的完全近似。径向对称假设引入了最大的误差,但是综合近似仍和参考很像。

图5:和图4相同的对照,但使用的是绝缘体

材质模型

我们的材质模型是迪士尼模型的一个简化,侧重于实时渲染的效率。限制参数数量对于优化G-缓冲器空间,减少纹理的存储和使用,以及最小化在像素着色器中混合材质层的开销极为重要。

以下是我们的基础材质模型:

BaseColor(基础颜色) 单一颜色。很容易理解的概念。

Metallic(金属性) 无需理解绝缘体和导体反射率,减少出错的空间

Roughness(粗糙度) 它的意思很清楚,但是如果使用光滑度就需要一番解释了

Cavity(腔洞) 用于小规模的遮挡

基础颜色,金属性与粗糙度和迪士尼模型中的一样,但是腔洞参数在迪士尼模型中没有出现,所以有必要解释一下。腔洞被用来指定来自几何体的遮蔽,该几何体小于我们的运行时遮挡系统能处理的几何体,通常由于该几何体只出现在法线贴图中。例如地板之间的缝隙,以及衣服上的缝合处。

最值得注意的一点是我们省略了高光参数。事实上我们一直使用该参数,直到完成《渗入者》演示才停止使用,因为我们不喜欢它了。首先,我们认为“高光”是一个糟糕的参数名称,它造成了诸多误解,且在某种程度上不利于美工从控制高光强度过渡到控制粗糙度。美工和图像程序员都容易忘记它的范围,认为它的默认值为1,而它的实际默认值为Burley的0.5(对应4%的反射率)。高光被有效使用的情况几乎都是为了小规模的遮蔽。我们发现折射指数变量(IOR)对于非金属相对不那么重要,所以我们最近使用更加易于理解的腔洞参数代替高光。非金属的F0现在为常数0.04。

我们决定在我们的基础材质模型中不使用以下迪士尼模型中的参数,而是将它们作为特例:

Subsurface(亚表面) 使用不同的方法对阴影贴图采样

Anisotropy(各向异性) 需要很多IBL样本

Clearcoat(透明涂层) 需要双倍IBL样本

Sheen(光泽度) Burley的论文中没有很好的定义

我们还没有在产品中使用过这些特殊情况,除了《元素》演示中的冰块用到了亚表面。此外,我们有一个专门用于皮肤的着色模型。未来我们打算采用一个混合了延迟和前向着色器的方法,以便更好地支持更多有专门用途的着色器。目前我们采取纯延迟着色方法,使用一个id存储在G缓冲器中的着色模型的一个动态分支来处理不同的着色模型。

经历

到目前为止有一种情况我已经遇到多次了。我会告诉开始过渡的美工要使用不同的粗糙度,“要像你过去使用高光颜色那样使用粗糙度”,不久后我听到了他们激动的声音“起作用了!”但是随后一条有趣的评论说道:“感觉粗糙度弄反了。”结果发现美工希望看到他们制作的纹理中更亮的纹素对应更亮的高光。如果图片存储了粗糙度,那么亮就对应更粗糙,会导致高光强度降低。

有一个问题人们问了我无数遍:“金属性是二进制的吗?”,对此我一开始耐心地解释混合/分层材质的诸多细节。后来我学到了最好就是说一句“是的!”原因是美工一开始不愿意将参数设置到最大或最小,通常我发现他们将金属的金属性设置为0.8。接下来要讨论的材质层,在99%的情况下,金属性不仅仅是0或者是1.

从控制高光到控制粗糙度的过渡中我们遇到了一些问题,其中包括不能复制材质。最重要的几个问题来自于《堡垒之夜》,这是一款Epic目前正在制作的游戏。《堡垒之夜》的美术方向被定位为非照片级,它明确地使用了互补色表现漫反射和高光反射,从物理上看不是那么真实,且不能用我们的新材质模型表示。在一番讨论后,我们决定继续支持旧的漫射色/高光色,将其作为引擎中的一个选项以保持《堡垒之夜》的游戏质量,因为这个游戏已经开发很久了。但是,我们不认为新模型排除了非照片级渲染,迪士尼在《无敌破坏王》中的使用证明了这一点,所以我们打算未来所有的项目都使用新模型。

材质分层

在之前的方法中我们使用对应不同模型的纹理的数值单独指定材质参数。混合来源于同一个库的材质层相较我们之前的方法提供了更多的好处:

· 重新使用了多个资源的成果

· 减少了单一资源的复杂度

· 统一并集中了定义游戏外观的材质,使美术和技术方向更加简单。

为了彻底接受这个新工作流程,我们需要重新思考我们的工具。虚幻引擎自从第三代早期开始就加入了基于节点图表的材质编辑器这一特色功能。该节点图表明确了输入(如纹理,常量),运算和输出,这些都被编译到了着色器代码中。

尽管该功能的主要目的是为了实现材质分层,令人惊讶的是我们几乎不用加入任何工具就可以支持材质层的制作和混合。UE4的材质编辑器中的节点图表部分可以整合函数并使用多个材质。该功能可用于实现一个材质层。将材质层保留在基于节点的编辑器中而不是作为一个高高在上的固定函数系统,使材质层可以以一种可编程的方式被映射和合并。

为了简化工作流程,我们加入了一个新的数据类型:材质属性(Material Attribute),它存储了材质所有的输出数据。和其它类型一样,这个新的类型可以作为单独的针脚被传入或传出材质函数,沿着连线传递,或者被直接输出。有了这些变化,材质层可以作为输入被直接拖拽进来,可以采用和处理纹理时同样的方法合并,调整和输出。实际上,大多数的材质图表更加易于使用了,因为将采用分层作为一个特定材质的主要变动映射并混合了不同材质层。这比过去使用参数进行调整要简单多了。

由于有几个感觉上为线性的材质参数,事实上完全在着色器中混合不同材质层会实用一些。我们认为和一个完全离线合成的系统相比,这会使画质有一个实质性的提升。由于能够在不同频率下映射数据,纹理数据的可视分辨率(Apparent Resolution)可能会非常高:每个顶点或者低频纹理数据都是不同的,材质层混合蒙版,法线贴图和腔洞贴图在每个网格物体上都有被指定,材质层会沿着网格物体的表面进行覆盖。更高级的例子甚至会用到更多频率。虽然

图6:UE4材质编辑器中的简单材质层

由于着色器的开销我们只能使用有限数量的材质层,但是我们的美工还没发现该限制会引起任何问题。

有一点值得注意的是在一些情况中美工为了应付着色器中的分层限制,他们将网格物体分为多个部分,导致需要更多的绘图调用(draw call)。虽然由于UE4在CPU方面作了代码优化,我们期望绘图调用的数量会降低,但是这好像在未来会成为一个问题。还有一点我们没调查的,即在那些完全被一个材质层覆盖的区域中使用动态分支以减少着色器损耗。

到目前为止,我们对于材质层的使用经历非常愉快。我们发现我们的工作效率得到提升,且画面质量也有进步。我们希望改进美工用来处理材质层库文件的接口,使美工更容易找到这些接口并能够预览各材质层。我们还打算在当前运行时系统之外研究一个可以离线合成/烘焙的系统,以支持更多数量的材质层和提供更佳的可调控性。

图7:不同材质层和锈迹相混合

图8:使用不同层次细节的材质分层结果

光照模型

对于着色,我们希望使其更加基于物理从而改进我们的光照模型。我们着重考虑的两点是光线的衰减以及不确定的自发光源——即通常人们所说的区域光。

改进光线衰减相对直接:我们采用在物理上非常精确的平方反比衰减,并使用光度测量单位流明。不过,我们必须要处理的一个小麻烦是这种类型的衰减函数在接近零点时距离为无穷。但是为了效率——在实时计算和离线计算中——我们仍然需要人为地限制光源的影响范围。有很多方法可以实现这点[4],但是我们选择平方反比函数,使得光源大部分的影响范围相对不受影响,同时仍然柔和地过渡到零。它的优势在于修改光照的半径不会改变它的有效亮度,当光源被美工固定时这一点很重要,但是由于性能的原因我们仍需调整光照范围。

分母中的1是为了防止函数在靠近光源的位置激增。在不要求物理正确性的情况下,可以将其暴露为一个可供美工调控的参数。

这个简单的变化带来的画质区别,尤其在那些有很多本地光源的场景中,可能会节省巨大的开支。

图9:使用平方反比衰减得到更自然的结果

区域光

区域光源不仅仅会生成更真实的图片。当我们使用基于物理的材质时,区域光也非常重要。我们发现,没有它们美工会凭直觉不使用低粗糙值,这样会产生无限小的高光,看上去不自然。他们其实在尝试着重现从确定光源射出的区域光的效果[4]。

不幸的是,这会导致着色和光照之间具有相关性,违反了基于物理渲染的一条核心原则:当材质被用在和其制作环境不同的光照环境中时,不应该需要修改它的材质。

区域光是一块活跃的研究领域。在离线渲染中,通常的方法是使用均匀采样法(Uniform Sampling)或者重要性采样法获取光源表面多个发光点[12][20]。这对于实时渲染根本不现实。在探讨可行的方案前,先给出我们的要求:

· 连贯的材质外表

- 使用漫反射BRDF和高光BRDF计算得到的能量总数不能相差太多。

· 当立体角(SolidAngle)趋近于0时该模型会近似于点光源模型

- 我们不希望牺牲我们着色模型的任何特征以实现这一点

· 足够快,可以用在任何地方

- 否则,我们不能解决之前提到的“粗糙度偏移”问题。

公告牌反射(Billboard Reflection)

公告牌反射[13]是一种可被用作离散光源的IBL。一张存储了自发光光源的2D图片被映射到3D空间中的一块矩形区域。和环境贴图预过滤相似,图片也使用不同大小的高光分布圆锥进行预过滤。从图片计算高光着色可以被认为是一种圆锥跟踪,这里的圆锥接近高光NDF。圆锥中心的射线和公告牌所在平面相交。然后图片空间中的相交点会被用作纹理坐标,相交处的圆锥半径被用来推导出一个预先过滤的mip等级。糟糕的是,虽然图片可以直接表示复杂的区域光源,但是公告板反射由于多个原因不能满足我们的第二个要求:

· 在平面上图片被预先过滤,因此图片空间能表示的立体角有限。

· 当光线和平面不相交时没有数据

· 光照向量l是未知的,或者使用反射向量代替

◈ 相关文章

《朗读者》告诉我们:朗诵的四大好处,你知道吗?
⌹ 365bet国际

▷ 《朗读者》告诉我们:朗诵的四大好处,你知道吗?

⏱️ 08-09 👁️‍🗨️ 2970
蓝月传奇开服多少天合区?蓝月传奇最新版本
⌹ 365bet国际

▷ 蓝月传奇开服多少天合区?蓝月传奇最新版本

⏱️ 08-12 👁️‍🗨️ 3290