在绘制三维图形的时候,不论是三维建模软件还是Three.js都提供了很多现成的材质,可供开发者选择,如Lambert材质、Phong材质、法向材质等。然鹅在做开发时,这些材质往往不满足设计需求,比如发光材质就是设计常用的材质,那么如何写出一个透明发光材质呢?
分析透明发光材质的特点,它是在三维图形结构的边缘处颜色不透明度越高,向内不透明度逐渐降低,其着色跟视角方向强相关。
敲黑板~ 这是一个经典的菲涅尔反射问题。菲尼尔反射描述了一种光学现象,即当光纤照射到物体表面上时,部分光发生反射,部分光发生折射或散射,反射的光和入射的光存在一定比例关系,这个关系可用菲涅尔等式计算。常见的例子如站在湖边,脚边的湖面的水是透明的,可以看到水底的石头,远处的湖面是看不到水下物体的,只能看到水面反射的结果。
常用的菲涅尔近似等式有:
F_{schlick}(v, n) = F_{0} + (1 - F_{0})(1 - v\cdot n)
其中 F_{0} 是一个反射系数,用来控制反射的强度, v 是视角方向, n 是法线方向。
我们用的是另一个广泛应用的等式:
F_{Empricial}(v, n) = max(0, min(1, bias + scale \times (1 - v\cdot n)^{power}))
其中 bias 、 scale 和 power 是控制项。
看图说话:
令归一化的法线方向为 n ,视角方向为 v ,那么有表面的透明度与 n\cdot v 成反比。当 | n\cdot v | 接近 0 时,越不透明,| n\cdot v |接近 1 时,越透明。
简化后的代码有:
void main()
{
float a = pow( bias + scale * abs(dot(vNormal, vPositionNormal)), power );
gl_FragColor = vec4( glowColor, a );
}
其中bias值决定了颜色最亮值的位置,power决定了透明度变化速度及方向。
下图为bias取1.0,power取2.0的效果,scale取-1.0。
同理,下图为bias取 0,power取2.0,scale取1.0。
此处注意,vNormal的normalMatrix为模型矩阵的逆矩阵的转置。
代码示例请戳:
https://codepen.io/mysisi/details/xYNWNZ/
参考:
[1] Unity Shader入门精要,冯乐乐.
[2] Everything has Fresnel
请大家持续关注我们的公众号
我们会不断地分享更多有趣的干货~
笔芯~
原文地址:https://zhuanlan.zhihu.com/p/38548428 |