【翻译】Real Shading in Unreal Engine 4
更新至4.20
原文为SIGGRAH2013上Epic Games工程师Brian Karis的分享,虽然现在已经是8102年了,但总体变动不大,依然有学习的价值。
图1.UE4渗透者Demo
介绍-Introduction
大约在一年前(2012年),我们决定投入一些时间去提升我们的着色模型并且包含一个更加基于物理的材质工作流。它部分驱动自渲染更真实的图像的需求,但是我们也对我们通过更基于物理的方法进行材质创建、使用分层材质可能达到的目标感兴趣。艺术家感觉这可能是对工作流和质量的一个巨大的提升,并且我已经在另一个工作室第一时间看到了这些成效,在那里我们已经过渡到离线混合的材质层。我们的其中一位技术美术在Epic在着色器中做分层的实验,实现了结果很有希望,这成为了一个额外的需求。
为了支持这个方向,我们知道材质的分层需要简单,并且高效。迪士尼的演讲来的时间十分完美,涉及到了他们的基于物理的着色和使用Wreck-It Ralph的材质模型。Brent Burley 展示了一个非常小的材质参数集合可以足够精致的表现离线特性的电影渲染。他童谣展示了一个相当实用的着色模型可以紧密的适用于大多数采样的材质。他们的工作成为了我们的一个灵感和基础,并且像他们的“规则”相似,我们也决定去定义一个我们自己的系统的目标。
实时性能-Real-Time Performance
·首要的是,它需要在每次许多可见灯光的条件下高效地使用。
简化复杂度-Reduced Complexity
·参数尽可能的少。大批的参数不仅会造成决定无力,反复试验和错误,或者相互关联的属性对于一个预期效果需要许多的值来改变。
·我们需要能够使用基于图像的光照和解析光源可切换,所以参数必须在多有的光照类型中表现一致。
直观的界面-Intuitive Interface
·我们更倾向易于理解的值,而不是像折射率这样的物理参数。
感知线性-Perceptually Linear
·我们希望通过蒙版支持分层,但是我们只能承受逐像素一次着色的负担。这意味着混合参数着色必须尽可能的匹配着色结果的混合。
简单掌握-Easy to Master
·我们想要避免需要电介质和导体的技术理解,同时最小化创建基本的貌似物理的材质所需要的努力。
健壮-Robust
·错误的创建物理上不可信的材质很困难。
·参数的所有合并应该尽可能的健壮和可信。
善于表现-Expressive
·延迟渲染限制了我们可以使用的着色模型的数量,所以我们的基本着色模型需要足够描述覆盖显示世界中99%的材质。
·所有可分层的材质需要共享相同的参数集在他们中混合。
灵活-Flexible
·其他的项目和授权可能不共享相同的真实感目标,所以也需要足够灵活来允许非真实感渲染。
着色模型-Shading Model
漫反射双向反射分布函数-Diffuse BRDF
我们评估了Burley的漫反射模型但是只观察到与Lambertain模型相比轻微的差别(等式1),所以我们不能证明额外的消耗的合理性。除此之外,任何更复杂的漫反射模型很难高效的使用基于图像或球面谐调的光照。因此,我们不在评估其他选择上投入精力。
这里Cdiff是材质的漫反射率。
微表面镜面反射双向反射分布函数-Microfacet Specular BRDF
常规的Cook-Torrance微表面镜面反射着色模型是:
我们开始使用迪士尼的模型,并且评估了比起更高效的替代,每一项的重要性。这远比听起来困难;每个项的公布的公式不一定使用相同的输入参数,但这对于正确的比较是至关重要的。
镜面反射D-Specular D
对于法线分布函数(NDF),我们发现迪士尼选择的GGX/Trowbridge-Reitz的成本是值得的。对比使用Blinn-Phong的额外消耗相当的小,并且提供了更长的“拖尾”的明显且自然的表现吸引了我们的艺术家。我们也采用了迪士尼的二次参数化α=Roughness^2。
镜面反射G-Specular G
比起其他项,我们对于镜面反射集合衰减项评估了更多的选择。最后,我们选择了Schlick模型,但是令k=α/2,为了更好地适应GGX的Smith模型。应用这个修改,在α=1时Schlick模型恰好匹配Smith,并且在[0,1]范围内相当的接近近似值(图2)。我们也选择去使用迪士尼的修改来降低“热度”通过在平方前使用(Roughness+1)/2重映射粗糙度。注意到这个调整仅用于解析的光源时很重要的;如果在基于图像的光照中应用,结果会导致在掠射角变得很暗。
镜面反射F-Specular F
对于菲涅尔,我们做出使用Schlick近似的经典选择,但是有一点修改,我们使用了球面高斯Spherical Gaussian近似来代替power。这稍微提高了计算效率,并且差异微不可察,公式为:
图2.使用k=α/2的Schlick非常接近Smith匹配
基于图像的光照-Image-Based Lighting
为了在基于图像的光照使用这个着色模型,需要解决辐射率积分,这通常使用重要性采样来完成。下面的等式描述了这个数值积分:
下面的HLSL代码展示了如何在我们的着色模型中实现(以下与原文略不一致,使用了4.20的代码替换):
1 float4 ImportanceSampleGGX( float2 E, float Roughness ) 2 { 3 float m = Roughness * Roughness; 4 float m2 = m * m; 5 6 float Phi = 2 * PI * E.x; 7 float CosTheta = sqrt( (1 - E.y) / ( 1 + (m2 - 1) * E.y ) ); 8 float SinTheta = sqrt( 1 - CosTheta * CosTheta ); 9 10 float3 H; 11 H.x = SinTheta * cos( Phi ); 12 H.y = SinTheta * sin( Phi ); 13 H.z = CosTheta; 14 15 float d = ( CosTheta * m2 - CosTheta ) * CosTheta + 1; 16 float D = m2 / ( PI*d*d ); 17 float PDF = D * CosTheta; 18 19 return float4( H, PDF ); 20 }
1 float3 SpecularIBL( uint2 Random, float3 SpecularColor, float Roughness, float3 N, float3 V ) 2 { 3 float3 SpecularLighting = 0; 4 5 const uint NumSamples = 32; 6 for( uint i = 0; i < NumSamples; i++ ) 7 { 8 float2 E = Hammersley( i, NumSamples, Random ); 9 float3 H = TangentToWorld( ImportanceSampleGGX( E, Roughness ).xyz, N ); 10 float3 L = 2 * dot( V, H ) * H - V; 11 12 float NoV = saturate( dot( N, V ) ); 13 float NoL = saturate( dot( N, L ) ); 14 float NoH = saturate( dot( N, H ) ); 15 float VoH = saturate( dot( V, H ) ); 16 17 if( NoL > 0 ) 18 { 19 float3 SampleColor = AmbientCubemap.SampleLevel( AmbientCubemapSampler, L, 0 ).rgb; 20 21 float Vis = Vis_SmithJointApprox( Roughness, NoV, NoL ); 22 float Fc = pow( 1 - VoH, 5 ); 23 float3 F = (1 - Fc) * SpecularColor + Fc; 24 25 // Incident light = SampleColor * NoL 26 // Microfacet specular = D*G*F / (4*NoL*NoV) = D*Vis*F 27 // pdf = D * NoH / (4 * VoH) 28 SpecularLighting += SampleColor * F * ( NoL * Vis * (4 * VoH / NoH) ); 29 } 30 } 31 32 return SpecularLighting / NumSamples; 33 }