基于物理的渲染(PBR, Physically Based Rendering)采用了物理真實的光照模型,符合自然世界的直觀認知規(guī)律,近年來比較流行换况。
PBR可理解為是一套渲染標準醉者,標準化有利于簡化美術(shù)流程玛追,但是只使用標準PBR達不到具體項目的渲染需求,因為游戲風格差異化大多來自對渲染的自定義俱萍,比如卡通化PBR渲染:
上圖為NS主機游戲異度之刃2的游戲截圖端壳,角色臉部和頭發(fā)都是比較卡通的,其它部分的金屬質(zhì)感盔甲和場景也都是寫實風格的枪蘑。
異度之刃2在非真實建模的前提下损谦,使用真實渲染和非真實渲染結(jié)合岖免,配合后期調(diào)色,形成了游戲特有的卡渲風格照捡。游戲中角色臉部和頭發(fā)渲染這種具有PBR屬性颅湘,但又風格化的渲染就屬于擴展PBR。
一栗精、標準PBS
PBR的核心是PBS(Physically Based Shading)著色模型闯参,具體實現(xiàn)由各大引擎自己負責,Unity的PBS實現(xiàn)封裝為Standard悲立,Unreal 4(下稱UE4)中實現(xiàn)封裝為Default Lit知市。本文的擴展是在Unity的標準PBS的基礎(chǔ)上去修改和擴展渠抹,這里先剖析Unity的標準PBS的代碼實現(xiàn)。
1.1 BRDF
BRDF(雙向反射分布函數(shù))光照模型是PBS的重要組成部分系瓢,用于描述光在物體表面的反射情況竿奏。該模型基于微表面理論寺酪,認為光在物體表面反射的光量是物體表面的所有微小表面漫反射和鏡面反射光量的總和输涕,符合能量守恒:
1) 反射的光總量不大于入射的光總量盐数,且漫反射和鏡面反射是互斥關(guān)系;
2) 粗糙的表面反射的光線分散且暗姑隅,光滑的表面反射集中且亮写隶。
Unity的BRDF的內(nèi)部實現(xiàn)文件為UnityStandardBRDF.cginc,主要實現(xiàn)函數(shù)為BRDF?_Unity_PBS讲仰。Unity的BRDF實現(xiàn)按平臺分為3個檔次樟澜,這里討論的是針對Console/PC平臺,光照模型更加精確的第1檔實現(xiàn)BRDF1_Unity_PBS叮盘。
BRDF的漫反射部分為Disney漫反射模型,該計算模型基于表面粗糙度霹俺,主要實現(xiàn)代碼為:
half nlPow5 = Pow5 (1-nl);
half nvPow5 = Pow5 (1-nv);
half Fd90 = 0.5 + 2 * lh * lh * roughness;
half disneyDiffuse = (1 + (Fd90-1) * nlPow5) * (1 + (Fd90-1) * nvPow5);
half diffuseTerm = disneyDiffuse * nl;
代碼中diffuseTerm 為計算得到的漫反射部分柔吼。
Disney漫反射模型與不考慮表面粗糙度的Lambert漫反射模型實際效果區(qū)別不大,所以在Unity的第2,3檔中diffuse計算用的是更簡單的Lambert模型丙唧。
BRDF的鏡面反射部分基于Torrance-Sparrow微表面模型愈魏,公式類似為:
主要實現(xiàn)代碼為:
#if UNITY_BRDF_GGX
half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
half D = GGXTerm (nh, roughness);
#else
half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
half D = NDFBlinnPhongNormalizedTerm (nh, RoughnessToSpecPower (roughness));
#endif
F = FresnelTerm (specColor, lh);
half specularTerm = (V * D * F) * (UNITY_PI/4);
代碼中SpecularTerm 為計算得到的鏡面反射部分,實現(xiàn)上基本遵守了Torrance-Sparrow的公式想际。
其中培漏,法線分布D和幾何衰減G按是否采用GGX計算模型會有些不同。這里附一張不同粗糙度的法線分布函數(shù)(NDF)曲線示意圖:
上圖中胡本,X軸為Half半角向量和表面Normal的夾角弧度牌柄,Y軸為NDF返回值,可看出Smoothness越高的函數(shù)曲線越陡峭侧甫,可解釋“粗糙的表面反射的光線分散且暗珊佣,光滑的表面反射集中且亮”能量守恒蹋宦。
公式中的Frensnel部分的代碼實現(xiàn)為:
inline half3 FresnelTerm (half3 F0, half cosA)
{
half t = Pow5 (1 - cosA); // ala Schlick interpoliation
return F0 + (1-F0) * t;
}
F = FresnelTerm (specColor, lh)
FresnelTerm 的函數(shù)曲線符合之前《理論基礎(chǔ)》文章所示的Fresnel曲線:
其中,F(xiàn)resnelTerm 函數(shù)的第1個參數(shù)specColor對應(yīng)著示意圖中的Base Reflectivities咒锻。
接下來分析“Diffuse和Specular互斥”能量守恒冷冗。UnityStandardUtils.cginc文件包含了主要內(nèi)部實現(xiàn)代碼:
inline half OneMinusReflectivityFromMetallic(half metallic)
{
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
return albedo * oneMinusReflectivity;
}
代碼根據(jù)金屬度計算漫反射和鏡面反射比例,當Metallic為1時惑艇,反射率接近1蒿辙,函數(shù)返回的diffColor接近0,表示幾乎不反射漫反射滨巴。
Unity的內(nèi)置變量Unity_ColorSpaceDielectricSpec定義了絕緣體的高光顏色和反射率思灌,不完全為0,是一個經(jīng)驗值兢卵。
Unity還提供了Specular Setup工作流程來控制漫反射和鏡面反射比例习瑰。內(nèi)部實現(xiàn)代碼為:
// Diffuse/Spec Energy conservation
inline half3 EnergyConservationBetweenDiffuseAndSpecular (half3 albedo, half3 specColor, out half oneMinusReflectivity)
{
oneMinusReflectivity = 1 - SpecularStrength(specColor);
#if !UNITY_CONSERVE_ENERGY
return albedo;
#elif UNITY_CONSERVE_ENERGY_MONOCHROME
return albedo * oneMinusReflectivity;
#else
return albedo * (half3(1,1,1) - specColor);
#endif
}
代碼中用1減去鏡面反射比例,得到漫反射比例秽荤。當傳入的specColor為白色時甜奄,SpecularStrength返回1,結(jié)果漫反射比例為0窃款,發(fā)生完美鏡面反射课兄。
計算得到的diffColor和specColor作為比例系數(shù)用于最終漫反射和鏡面反射計算:
// BRDF = kD / pi + kS * (D * V * F) / 4
代碼中的 kD和kS對應(yīng)著diffColor和specColor。
1.2 IBL
在材質(zhì)上反應(yīng)出周圍的環(huán)境也是PBS的重要組成部分晨继。在光照模型中一般把周圍的環(huán)境當作一個大的光源來對待烟阐,不過環(huán)境光不同于實時光,而是作為間接光(Indirect Light)通過IBL(Image Based Lighting)來實現(xiàn)紊扬。間接光計算也包含漫反射部分和鏡面反射部分蜒茄。
UnityGlobalIllumination.cginc文件包含了主要內(nèi)部實現(xiàn)代碼:
inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld)
{
UnityGI o_gi;
ResetUnityGI(o_gi);
.....
#if UNITY_SHOULD_SAMPLE_SH
o_gi.indirect.diffuse = ShadeSHPerPixel (normalWorld, data.ambient, data.worldPos);
#endif
#if defined(LIGHTMAP_ON)
// Baked lightmaps
fixed4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
half3 bakedColor = DecodeLightmap(bakedColorTex);
#ifdef DIRLIGHTMAP_COMBINED
......
#elif DIRLIGHTMAP_SEPARATE
.....
#else // not directional lightmap
o_gi.indirect.diffuse = bakedColor;
......
#endif
#endif
#ifdef DYNAMICLIGHTMAP_ON
......
#endif
o_gi.indirect.diffuse *= occlusion;
return o_gi;
}
Unity內(nèi)置了Unity_Lightmap、Unity_SHAr等全局變量餐屎,來從預(yù)先烘焙好的Lightmap貼圖或Light Probe中讀取顏色檀葛,其中UNITY_SHOULD_SAMPLE_SH代碼段處理的是從light probe中讀取顏色值。一般渲染時靜態(tài)物體讀取Lightmap腹缩,非靜態(tài)物體讀取Light Probe屿聋。
UnityGI_Base函數(shù)返回的顏色值為間接光的漫反射部分。
inline half3 UnityGI_IndirectSpecular(UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn)
{
half3 specular;
#if UNITY_SPECCUBE_BOX_PROJECTION
// we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice), so keep original to pass into BoxProjectedCubemapDirection
half3 originalReflUVW = glossIn.reflUVW;
#endif
#if UNITY_SPECCUBE_BOX_PROJECTION
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
#endif
#ifdef _GLOSSYREFLECTIONS_OFF
specular = unity_IndirectSpecColor.rgb;
#else
half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
#if UNITY_SPECCUBE_BLENDING
......
#else
specular = env0;
#endif
#endif
return specular * occlusion;
}
Unity用Reflection Probe來保存預(yù)先烘焙好的環(huán)境光反射貼圖藏鹊,通過內(nèi)置變量Unity_SpecCube0润讥,Unity_SpecCube1訪問。
UnityGI_IndirectSpecular返回的顏色值為間接光的鏡面反射部分盘寡。
另外楚殿,“粗糙的表面反射的光線分散且暗,光滑的表面反射集中且亮”能量守恒在這里同樣被遵守宴抚,函數(shù)輸入?yún)?shù)包含粗糙度信息勒魔,用于環(huán)境光貼圖的LOD取值:
half mip = roughness * UNITY_SPECCUBE_LOD_STEPS;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, glossIn.reflUVW, mip);
表面越粗糙甫煞,用于采樣Mipmap貼圖的LOD值越高,UNITY_SAMPLE_TEXCUBE_LOD采樣的結(jié)果越模糊冠绢,反之亦然抚吠。
1.3 BRDF+IBL
正如光照計算公式中多個光源的強度是疊加關(guān)系,PBS模型光照計算的結(jié)果是實時光BRDF與間接光IBL之和弟胀。BRDF1_Unity_PBS函數(shù)最后的顏色返回值代碼:
half grazingTerm = saturate(oneMinusRoughness + (1-oneMinusReflectivity));
half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm)
+ specularTerm * light.color * FresnelTerm (specColor, lh)
+ surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv);
附自定義Standard Shader得到的結(jié)果分解圖:
二楷力、擴展
擴展PBR的具體內(nèi)容是對標準PBS代碼進行修改和擴展。除了核心的光照計算之外還需要對標準PBS的其他代碼進行修改和擴展孵户,這里附一張Unity的Standard實現(xiàn)示意圖:
在此基礎(chǔ)上去擴展萧朝,對內(nèi)主要包括修改和擴展上圖中的數(shù)據(jù)結(jié)構(gòu)、光照模型和繪制過程夏哭。對外給用戶提供可選擇的Shading Model:
上圖左為UE4引擎的Shading Model检柬,右為本文在Unity中按UE4方式擴展實現(xiàn)的代碼文件列表,其中TT_Unity???.cginc文件為Unity內(nèi)部的Unity???.cginc文件的修改擴展版本竖配。
其中何址,TT_UnityStandardBRDF.cginc擴展了各個Shadering Model的實現(xiàn):
half4 SKIN_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, half4 sssTex)
{
...
}
half4 HAIR_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, float3 tangentWorld = float3(0, 0, 1), half2 anisoCtrl = half2(1, 1))
{
...
}
half4 CLEARCOAT_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, float3 normal_clearcoat, UnityIndirect gi_clearcoat)
{
...
}
half4 FABRIC_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
...
}
TT_UnityStandardCore.cginc根據(jù)Shadering Model的類型選擇實現(xiàn):
half4 fragForwardBaseInternal (VertexOutputForwardBase i)
{
...
#if _SKIN
half4 c = SKIN_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect, sssTex);
#elif _HAIR
half4 c = HAIR_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect, s.tangentWorld, anisoMap.rg);
#elif _CLEARCOAT
half4 c = CLEARCOAT_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect, s.normalWorld_clearcoat, gi_clearcoat.indirect);
#elif _FABRIC
half4 c = FABRIC_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
#else
half4 c = UNITY_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
#endif
...
}
...
half4 fragForwardAddInternal (VertexOutputForwardAdd i)
{
...
#if _SKIN
half4 c = SKIN_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect, sssTex);
#elif _HAIR
half4 c = HAIR_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect);
#elif _CLEARCOAT
half4 c = CLEARCOAT_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect, s.normalWorld_clearcoat, noIndirect);
#elif _FABRIC
half4 c = FABRIC_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect);
#else
half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect);
#endif
...
}
具體的Shader文件去定義Shadering Model的類型,比如TT_Character_Skin.shader定義了_SKIN類型:
SubShader
{
Tags{ "RenderType" = "Opaque" "PerformanceChecks" = "False" }
LOD 300
CGINCLUDE
...
#define _SKIN 1
...
}
Unity的原生內(nèi)置著色器cginc文件在類似目錄:C:\Program Files\Unity 2018.1.0b13\Editor\Data\CGIncludes\进胯,也可到官網(wǎng)下載Unity安裝版本對應(yīng)的內(nèi)置著色器代碼用爪。
下面對各個Shading Model的理論模型和具體實現(xiàn)依次大致介紹。
2.1 Subsurface
The Subsurface Shading Model simulates the effect of Subsurface Scattering. This is a real-world phenomenon in which light penetrates a surface and then diffuses throughout it. It can be most readily seen on such objects as ice, wax candles, and skin.
the final colour of our pixels depend is the sum of two components. The first one is the “traditional” lighting. The second one is the light contribution from a virtual light source illuminating the back of our model. This gives the impression that light from the original source actually passed through the material.
Subsurface模型實現(xiàn)核心代碼:
half4 SKIN_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, half4 sssTex)
{
...
// Translucency
float3 H = normalize(light.dir + normal * _Distortion);
float transDot = pow(saturate(dot(viewDir, -H)), _Power) * thickness * _ThicknessScale;
half3 lightScattering = transDot * _SubColor;
...
}
2.2 Skin
The Preintegrated Skin Shading Model is very similar in nature to the Subsurface model, but geared toward low performance cost skin rendering on human characters.
the higher curvature on the nose creates stronger incident light gradient, Which will result in a lot more visible scattering.
Skin模型實現(xiàn)核心代碼:
inline fixed4 SKIN_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi,
half4 sssTex)
{
...
// Skin Lighting
float2 brdfUV;
// Half-Lambert lighting value based on blurred normals.
brdfUV.x = dotNL * 0.5 + 0.5;
brdfUV.y = curvature;
// Curvature amount. Multiplied by light's luminosity so brighter light = more scattering.
half3 diffuseTerm = diffColor * light.color * tex2D( _BRDFTex, brdfUV ).rgb;
...
2.3 ClearCoat
The Clear Coat Shading Model can be used to better simulate multilayer materials that have a thin translucent layer of film over the surface of a standard material. In addition to this, the Clear Coat Shading Model can also be used with either a metal or nonmetal surfaces. In fact, it was specifically designed to model this second class of smooth colored films over a non-colored metal. Some examples include acrylic or lacquer clear coats, and colored films over metals such as soda cans and car paint.
ClearCoat模型實現(xiàn)核心代碼:
half4 fragForwardBaseInternal (VertexOutputForwardBase i)
{
...
#if _CLEARCOAT
FragmentCommonData s_clearcoat = (FragmentCommonData) 0;
s_clearcoat.specColor = _ReflectionSpecular.rgb;
s_clearcoat.smoothness = _ReflectionGlossiness;
s_clearcoat.normalWorld = s.normalWorld_clearcoat;
s_clearcoat.eyeVec = s.eyeVec;
s_clearcoat.posWorld = s.posWorld;
UnityGI gi_clearcoat = FragmentGI(s_clearcoat, occlusion, i.ambientOrLightmapUV, atten, mainLight);
half4 c = CLEARCOAT_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect, s.normalWorld_clearcoat, gi_clearcoat.indirect);
#endif
...
}
half4 CLEARCOAT_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, float3 normal_clearcoat, UnityIndirect gi_clearcoat)
{
half4 c = BRDF1_Unity_PBS(diffColor, specColor, oneMinusReflectivity, smoothness, normal, viewDir, light, gi);
// SPECULAR & SMOOTHNES
diffColor = 0;
specColor = _ReflectionSpecular.rgb;
smoothness = _ReflectionGlossiness;
oneMinusReflectivity = 1 - SpecularStrength(specColor);
c += BRDF1_Unity_PBS(diffColor, specColor, oneMinusReflectivity, smoothness, normal_clearcoat, viewDir, light, gi_clearcoat);
return c;
}
2.4 Cloth
For fabrics, like black velvet, the most distinguishing features are due to rim lighting (both forward and backward scattering). If the light is in the same direction as the viewer then specular contributes most towards the edge of the object due to backscattering and how the fabric is constructed. Tiny fibers are attached to the surface so that they try to stand up straight. When the light and view direction are aligned the light will backscatter when the surface normal is 90 degrees from the light or view direction. Additionally, if the light is behind the objects the fibers will forward scatter light through giving a nice rim light effect.
![理想反射與實際織物的反射對比(https://upload-images.jianshu.io/upload_images/15536448-7dc7ccd743e96ef1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Cloth模型實現(xiàn)核心代碼:
inline float FabricD (float NdotH, float roughness)
{
return 0.96 * pow(1 - NdotH, 2) + 0.057;
}
inline half FabricScatterFresnelLerp(half nv, half scale)
{
half t0 = Pow4 (1 - nv);
half t1 = 0.4 * (1 - nv);
return (t1 - t0) * scale + t0;
}
half4 FABRIC_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
...
float D = FabricD (nh, roughness);
...
half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm)
+ specularTerm * light.color * FresnelTerm (specColor, lh)
+ _FabricScatterColor * (nl*0.5 + 0.5) * FabricScatterFresnelLerp(nv, _FabricScatterScale);
...
2.5 Hair
我們?nèi)粘I钪杏泻芏辔矬w呈現(xiàn)各向異性反射效果胁镐。比如:拉絲金屬偎血,毛發(fā),光碟等盯漂。一般這種反射效果是由物體表面的微表面特性導(dǎo)致的:物體表面主要由大量的方向一致的細長劃痕或纖維微表面組成颇玷。 比如,拉絲金屬物件表面由大量平行的絲狀劃痕組成就缆;光碟的表面由一圈一圈的環(huán)形細小軌道(用于存放數(shù)據(jù))組成亚隙;頭發(fā)的表面由大量的頭發(fā)絲組成等。沿著這些劃痕或纖維的法線分布不同于通常的垂直于表面的法線分布违崇, 使得物體整體的光照反射表現(xiàn)呈現(xiàn)各向異性。
Hair模型實現(xiàn)核心代碼:
inline half AnisoDCore(half smoothness, half3 normalWorld, half3 tangentWorld, half3 halfDir, half nh, half D, half gloss, half spec, half mask)
{
half3 Y = cross(normalWorld, tangentWorld);
half RoughnessX = SmoothnessToRoughness(saturate(smoothness * gloss));
RoughnessX += !RoughnessX * 1e-4f;
half mx = RoughnessX * RoughnessX;
half XdotH = dot(tangentWorld, halfDir);
half YdotH = dot(Y, halfDir);
half d = XdotH * XdotH / (mx * mx) + YdotH * YdotH + nh * nh;
d += !d * 1e-4f;
half Da = 1 / (UNITY_PI * mx * d * d);
D = lerp(Da, D, mask);
D *= lerp(spec, 1, mask);
return D;
}
inline half3 JitterTangent(half3 T, half3 N, float shift)
{
half3 shiftedT = T + shift * N;
return normalize(shiftedT);
}
inline half AnisoD(half smoothness, half3 normalWorld, half3 tangentWorld, half3 halfDir, half nh, half D, half2 anisoCtrl)
{
half jitter = anisoCtrl.r;
half mask = anisoCtrl.g;
half3 tangentWorld1 = JitterTangent(tangentWorld, normalWorld, 0 + _TangentShift1);
half AnisoDLow = AnisoDCore(smoothness, normalWorld, tangentWorld1, halfDir, nh, D, _AnisoGloss1, _AnisoSpec1, mask);
half3 tangentWorld2 = JitterTangent(tangentWorld, normalWorld, jitter + _TangentShift2);
half AnisoDHigh = AnisoDCore(smoothness, normalWorld, tangentWorld2, halfDir, nh, D, _AnisoGloss2, _AnisoSpec2, mask);
return AnisoDLow + AnisoDHigh;
}
half4 HAIR_BRDF_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi, float3 tangentWorld = float3(0, 0, 1), half2 anisoCtrl = half2(1, 1))
{
...
float D = GGXTerm(nh, roughness);
D = AnisoD(smoothness, normal, tangentWorld, halfDir, nh, D, anisoCtrl);
...
}
完整代碼見:https://lab.uwa4d.com/lab/5b69a13ed7f10a201f02360ad
三诊霹、Further
目前的代碼框架擴展實現(xiàn)了上面列舉的幾種常見的Shading Model羞延,未來的工作就是在這些Shading Model的基礎(chǔ)上去風格化,或者擴展新的Shading Model脾还。
另外伴箩,說明一下從源碼級別自定義PBR的意義:目前的材質(zhì)節(jié)點編輯器,比如Amplify Shader Editor鄙漏,生成的是Surface中間代碼嗤谚,內(nèi)部走的是Standard流程棺蛛;Shader Forge(目前已停更),生成的代碼調(diào)用的也是內(nèi)置的Standard接口巩步;Unity官方的SRP配套使用的Shader Graph只支持Standard材質(zhì)旁赊。
參考文獻:
https://marmoset.co/posts/physically-based-rendering-and-you-can-too
https://www.alanzucconi.com/2017/08/30/fast-subsurface-scattering-1
https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/MaterialProperties/LightingModels
http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf
這是侑虎科技第497篇文章,感謝作者Young供稿椅野。歡迎轉(zhuǎn)發(fā)分享终畅,未經(jīng)作者授權(quán)請勿轉(zhuǎn)載。如果您有任何獨到的見解或者發(fā)現(xiàn)也歡迎聯(lián)系我們竟闪,一起探討离福。(QQ群:793972859)
作者主頁:https://www.zhihu.com/people/chen-yong-59-86,作者也是U Sparkle活動參與者炼蛤,UWA歡迎更多開發(fā)朋友加入U Sparkle開發(fā)者計劃妖爷,這個舞臺有你更精彩!