我在前一篇写了对于衣服和硬表面材质的区别。这一篇来实现一下正常的棉麻类的渲染实现。
首先,说一下棉麻类的衣服的特点。
棉麻类的布料是由线组成的,也就是一根根的线织出来的,所以它是相对来说比较平整的结构。但是呢,它还有一层在表面绒毛的存在,这是一层的绒纤维,属于一层树立在表面的短绒毛,但是这层绒毛的方向和长短都是不确定的,在渲染时,我们需要考虑一下这层绒毛的效果。
所以,为了实现棉麻类的效果,我们需要实现这两层的渲染效果。
那么,在直接光渲染时,漫反射我们还是使用默认的漫反射,但是在实现直接光的镜面高光时,我们需要使用两层高光去实现。之前也说过,正常的表面使用反向的GGX高光,然后再使用一层高光来表现表层上面的绒毛的镜面高光效果。
在实现绒毛效果上,它是在边缘的比较发亮的,所以,我们需要一张lut图取获取第二层高光的强度。
代码如下:
float3 ClothBRDF(float3 DiffuseColor, float3 SheenColor, float Roughness, float SheenRoughness, float SheenDFG, float3 N, float3 V, float3 L, float3 LightColor, float Shadow)
{
float a2 = Pow4(Roughness);
float3 H = normalize(L + V);
float NoH = saturate(dot(N, H));
float NoV = saturate(abs(dot(N, V)) + 1e-5);
float NoL = saturate(dot(N, L));
float VoH = saturate(dot(V, H));
float3 Radiance = NoL * LightColor * Shadow * PI;
//默认的漫反射
float3 DiffuseLighting = Diffuse_Lambert(DiffuseColor) * Radiance;
//默认的brdf
float D = D_Charlie_Filament(Roughness, NoH);
float Vis = Vis_Cloth(NoV, NoL);
float3 F = F_Schlick_UE4(0.04, VoH);
float3 SpecularLighting = ((D * Vis) * F) * Radiance;
//第二层绒毛类的brdf
float D2 = D_Charlie_Filament(SheenRoughness, NoH);
float Vis2 = Vis_Cloth(NoV, NoL);
float3 F2 = SheenColor;
float3 SheenLighting = ((D2 * Vis2) * F2) * Radiance;
//能量守恒,求出第二层的强度,减少其它强度
float sheenScaling = 1.0 - max(max(SheenColor.r, SheenColor.g), SheenColor.b) * SheenDFG;
DiffuseLighting *= sheenScaling;
SpecularLighting *= sheenScaling;
float3 DirectLighting = DiffuseLighting + SpecularLighting + SheenLighting;
return DirectLighting;
}
可以使用类似于上面的图,根据视角和法向作为u的值,然后以粗糙度作为v的值去获取当前第二层高光的强度。
//求出绒毛层的dfg (反射强度)
half2 dfgUV = half2(saturate(dot(normalWS, viewDirWS)), sfd.sheenRoughness);
half dfg = SAMPLE_TEXTURE2D(_DiffuseMap, sampler_DiffuseMap, dfgUV).r;
调的低一些,效果更明显
绒毛的效果是杂乱无章的,所以,我们需要一张贴图来表现出那种效果,还需要一张绒毛效果的图
//求出绒毛层的dfg (反射强度)
half2 dfgUV = half2(saturate(dot(normalWS, viewDirWS)), sfd.sheenRoughness);
half dfg = SAMPLE_TEXTURE2D(_ClothDGF_LUT, sampler_ClothDGF_LUT, dfgUV).r;
half2 fuzzUV = TRANSFORM_TEX(UV, _FuzzMap);
half fuzz = SAMPLE_TEXTURE2D(_FuzzMap, sampler_FuzzMap, fuzzUV).r;
dfg *= fuzz;
使用图片以后,会发现有那种杂乱无章的绒毛的效果了。
然后加一个颜色用于控制第二层的颜色效果。
能量守恒,高光的强度全体为1,所以我们需要在生产第二次高光时,做一下处理。
使用的dvf三项的代码,我也列出来
//Filament引擎的渲染衣服使用的D项
float D_Charlie_Filament(float Roughness, float NoH)
{
// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
float invAlpha = 1.0 / Pow2(Roughness);
float cos2h = NoH * NoH;
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);
}
//衣服类brdf的v项
float Vis_Cloth(float NoV, float NoL)
{
return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));
}
//ue的pbr使用的f项
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
float3 F_Schlick_UE4(float3 SpecularColor, float VoH)
{
float Fc = Pow5(1 - VoH); // 1 sub, 3 mul
//return Fc + (1 - Fc) * SpecularColor; // 1 add, 3 mad
// Anything less than 2% is physically impossible and is instead considered to be shadowing
return saturate(50.0 * SpecularColor.g) * Fc + (1 - Fc) * SpecularColor;
}
接下来是间接光部分的处理
间接光漫反射还是使用球谐光照的方式去处理
//SH
float3 DiffuseAO = AOMultiBounce(DiffuseColor, Occlusion);
float3 RadianceSH = SampleSH(N);
float3 IndirectDiffuse = RadianceSH * DiffuseColor * DiffuseAO;
float3 AOMultiBounce(float3 BaseColor, float AO)
{
float3 a = 2.0404 * BaseColor - 0.3324;
float3 b = -4.7951 * BaseColor + 0.6417;
float3 c = 2.7552 * BaseColor + 0.6903;
return max(AO, ((AO * a + b) * AO + c) * AO);
}
在间接光镜面反射部分,使用到了双层处理的方案
float3 IndirectLighting(float3 DiffuseColor, float3 SheenColor, float Roughness, float SheenRoughness, float ClothDFG, float SheenDFG,
float3 WorldPos, float3 N, float3 V, float Occlusion, float EnvRotation)
{
float NoV = saturate(abs(dot(N, V)) + 1e-5);
//SH
float3 DiffuseAO = AOMultiBounce(DiffuseColor, Occlusion);
float3 RadianceSH = SampleSH(N);
float3 IndirectDiffuse = RadianceSH * DiffuseColor * DiffuseAO;
//IBL
half3 R = reflect(-V, N);
R = RotateDirection(R, EnvRotation);
//获取第一层的镜面反射
half3 SpeucularLD = GlossyEnvironmentReflection(R, WorldPos, Roughness, 1.0f); //在反射球上获取镜面反射颜色
half3 SpecularDFG = ClothDFG * 0.04; //获取到衣服的反射强度 0.04是默认的非金属反射强度
float SpecularOcclusion = GetSpecularOcclusion(NoV, Pow2(Roughness), Occlusion); //计算镜面反射的AO
float3 SpecularAO = AOMultiBounce(float3(0.04, 0.04, 0.04), SpecularOcclusion); //AO和基础色进行混合
float3 IndirectSpecular = SpeucularLD * SpecularDFG * SpecularAO;
//获取绒毛层的镜面反射
half3 SheenSpeucularLD = GlossyEnvironmentReflection(R, WorldPos, SheenRoughness, 1.0f);
half3 SheenSpecularDFG = SheenColor * SheenDFG; //绒毛的反射强度
float SheenOcclusion = GetSpecularOcclusion(NoV, Pow2(SheenRoughness), Occlusion); //计算镜面反射的AO
float3 SheenAO = AOMultiBounce(SheenColor, SheenOcclusion); //AO和基础色进行混合
float3 SheenSpecular = SheenSpeucularLD * SheenSpecularDFG * SheenAO;
//能量守恒
float sheenScaling = 1.0 - max(max(SheenColor.r, SheenColor.g), SheenColor.b) * SheenDFG;
IndirectDiffuse *= sheenScaling;
IndirectSpecular *= sheenScaling;
return IndirectDiffuse + IndirectSpecular + SheenSpecular;
}
里面新增加的ClothDFG 是使用DFG_LUT贴图使用默认的roughness求得。
最终,就有了带有绒毛效果的棉麻织物的效果了。