Unity Shader 布料渲染(二)具体实现
在上一篇文章中,简单的介绍了布料的几种编织结构,并由这些结构明白了丝绸类布料在游戏渲染中为什么要在高光使用各向异性。这篇文章讲一下布料渲染的具体实现,这些也都是我自己学习的总结,肯定不会太系统而且会有很多不足的地方,希望各位大佬多多指点。
从布料的编织结构和实物表现,在渲染中可以将布料分为两类:
常规PBR渲染,包括平纹组织、斜纹组织、沙罗组织,因为浮长线都比较短,不会形成缎纹组织表现出来的线性高光丝绸类PBR渲染,包括缎纹组织和浮长线比较长的斜纹组织,高光额外使用一个各向异性的法线贴图,表现线性高光
四种编织结构
常规PBR渲染
因为沙罗、平纹、浮长线短的斜纹都类似,所以就用游戏里最常用的平纹组织来介绍。
这张贴图的结构比较明显
从Albedo和Normal贴图可以看出来,其实这类型的布料和其他物体没有什么区别,所以只用常规的PBR代码就可以渲染出想要的结果,如果想有更细节的表现,再加上遮蔽和高度就可以了。
因为布料的原料问题,其实布类也是有一定的次表面散射的,如果是用动物毛发编织的,那次表面的效果可能会更强一点。但毕竟是薄薄的一层布料,所以次表面的表现在正面观看时会相对没有那么的明显,在布料背面看向光源的时候会强一点,不过游戏里好像没有这么做,毕竟也没这个必要。
这种材料表面粗糙不需要表现明显的高光,如果是斜纹组织或者是比较光滑的丝质平纹组织也是有一定可见高光的
丝绸类PBR渲染
终于到了重点的丝绸类渲染,面料上有绣花或者像锦类的织物暂且不管,只说最朴素的光面缎纹组织。不需要Albedo贴图来表现表面结构,只需要给高光一个表现各项异性的法线贴图和一个主颜色就可以了。
手里没有专门的贴图,所以用这个贴图在x上乘了20倍使用
拉近距离后已经可以看出有缎纹组织的感觉了
除了线性的法线贴图,还需要一个各项异性的算法,我找到了两种算法:
Award Aniso
float AnisoD_Ward(float aniso, float NdotL, float NdotV, float NdotH, float HdotX, float HdotY){
float aspect = sqrt(1.0f - aniso * 0.9f);
float X = max(.001, sqr(1.0-_Glossiness)/aspect) * 5;
float Y = max(.001, sqr(1.0-_Glossiness)*aspect) * 5;
float exponent = -(sqr(HdotX/X) + sqr(HdotY/Y)) / sqr(NdotH);
float Distribution = 1.0 / (4.0 * 3.14159265 * X * Y * sqrt(NdotL * NdotV));
Distribution *= exp(exponent);
return Distribution;
}
D *= AnisoD_Ward(_Anisotropic, nl, nv, nh, hx, hy);还有一个不知道叫什么,从别人代码里扒出来的,原来是用来渲染头发的各项异性的:
//Aniso
float _TangentShift1, _AnisoGloss1, _AnisoSpec1;
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-f;
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, half anisoCtrl)
{
half mask = anisoCtrl;
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;
}
D = AnisoD(smoothness, normal, tangentWorld, halfDir, nh, D, _Anisotropic);两种方法渲染出来的结果差不多,调整参数也没有发现有明显的区别,估计是第二种方法用的不太对吧。
法线方面,漫反射用的是顶点数据里的法线,高光是采样法线图,根据需要看漫反射需不需要使用和高光一样的法线。
另外稍微加了一点Metallic,当Metallic=0的时候看起来有点像是塑料。
其实到这里就算是完了,代码也没什么复杂的,想要更好的效果就调整参数就可以了。毕竟是入门级别,还有很多地方需要学习,之后会继续慢慢研究。
参考: 请问大佬是如何处理切线的? 我这布块比较简单,就没加切线处理,可以用flowmap扰动tangent的方向
https://zhuanlan.zhihu.com/p/84313625
https://zhuanlan.zhihu.com/p/65945680
可以参考这两篇文章,我抽空把这部分更新到文章中
页:
[1]