|
在unity教程中对于布料这块的渲染好像并不多,网上能搜到的也是寥寥,因为目前大多数的基于物理的材质都是用BRDF进行渲染的,但是传统的BRDF是建立在微表面理论,但是对于微表面不能当成完美镜面的,传统的BRDF并不能很好的模拟,所以我们需要找到一个能“模拟”布料的BRDF公式。
在下面实现的代码都是基于以下的公式:
diffuse项是DisneyDiffuse 项:
其中,
在UnityStandardBRDF中已经为我们实现好了,我们直接拿来用:
half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness);如果我们要为其添加次表面散射,则需要用到如下公式:
其中w是一个0-1之间的值,用来控制散射的柔和程度,实际上就是WrapLighting,
代码:
inline float Wrap(float nl, float w) {
return saturate((nl + w) / (1.0 + w)*(1.0 + w));
}
diffuseTerm *= Wrap(nl,_FabricScatterSale);
half3 Fd = diffColor * diffuseTerm * saturate(_FabricScatterColor + nl);这样Fd就算好了,接着就是specular项:
这里的公式和Cook-Torrance公式有点不一样,首先是D项:
在Physically Based Rendering in Filament 种他提到了两种模型,一种是Ashikhmin模型,他们认为在衣物的渲染中distribution占了很大的比重并且建立在这样的假设上:遮罩带来的影响为0。模型本身是一个逆高斯函数,并且当添加偏移后能很好的灯光对毛绒的高光的模拟,这里偏移为1:
第二个模型为Charlie模型,这也是建立在遮罩影响为0的情况下,并且相对于上面的cot运算,Charlie模型的运算量更小。
两种代码:
inline float D_Ashikhmin(float roughness,float nh){
float a2 = roughness * roughness;
float cos2h = nh * nh ;
float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
float sin4h = sin2h * sin2h;
float cot2 = -cos2h / (a2 * sin2h);
return 1.0 / (PI * (4.0 * a2 + 1.0) * sin4h) * (4.0 * exp(cot2) + sin4h);
}
inline float D_Charlie(float roughness, float nh) {
float invAlpha = 1.0 / roughness;
float cos2h = nh * nh;
float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}对于specular中的G项(Unity中的Visibility)
inline float VisibilityCloth(float nl, float nv, float roughness){
return 1/(4 * ( nl + nv - nl*nv) + 1e-5f);
}最后整合得到Fr。
注意我们没有菲尼尔项,取而代之的是用Sheen来控制高光。在Physically Based Rendering in Filament 一文中,Sheen的缺省值为0.04 以实现天鹅绒的效果,如果考虑实现其他布料材质,我们可以考虑使用luminance来作为sheen值。
高光项的效果如下:(注意黑丝)
Charlie模型下的高光结果
加上漫反射项:
没有使用sss
加上sss:
加上sss后感觉更透了,像超能陆战队的大白一样。
不同参数下的结果:
注意:我们在使用sss时,不应该乘
最后我们的代码应该看起来像这样的:
half4 Cuntom_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
float perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);
#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0
#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
// The amount we shift the normal toward the view vector is defined by the dot product.
half shiftAmount = dot(normal, viewDir);
normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
// A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
//normal = normalize(normal);
half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
#else
half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact
#endif
half nl = saturate(dot(normal, light.dir));
float nh = saturate(dot(normal, halfDir));
half lv = saturate(dot(light.dir, viewDir));
half lh = saturate(dot(light.dir, halfDir));
//diffuse term
half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness);
//specular term
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
roughness = max(roughness, 0.002);
half V = VisibilityCloth (nl, nv, roughness);
half D = DistributionCloth(roughness, nh);
#ifdef USE_LUMINANCE
half luminance = dot(diffColor,half3(0.299,0.587,0.114));
half3 F = luminance;
#else
half3 F = specColor;//0.04 should be default
#endif
half specularTerm = V * D * _SpecularScale;
specularTerm = max(0, specularTerm * nl);
half surfaceReduction;
surfaceReduction = 1.0 / (roughness*roughness + 1.0);
specularTerm *= any(specColor) ? 1.0 : 0.0;
//half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
#ifdef SUBSURFACE_COLOR_ON
diffuseTerm *= Wrap(nl,_FabricScatterSale);
half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
* saturate(_FabricScatterColor + nl));
half3 Fr = specularTerm * light.color;
half3 color = Fd + Fr;
#else
half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
* nl);
half3 Fr = specularTerm * light.color;
half3 color = Fd + Fr;
#endif
return half4(color, 1);
}另外网上还有一种Custom Fabric的文章,并且知乎上有人实现了效果,传送门。但我觉得并不是很真实,他使用了比较强的菲尼尔系数,导致材质看起来蒙蒙的。
最后还有一篇关于快速模拟sss的文章,与本篇内容无关,但挺有意思的,传送门。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|