[webGL] 小米炫彩后盖材质分析

1,263 阅读4分钟

未标题-2.gif

红米k40 webGL链接

背景

在小米出mi 11的时候,就注意到小米商城app下有360全景体验手机的功能,第一次打开的时候给我的感觉是很震撼,震撼的不是模型,而是看到它的炫彩后盖能够渲染的那么真,感觉和真机是一模一样的,然后我就自己下载了手机模型,自己去做材质,发现怎么都实现不了这种炫彩材质,兴趣一下就没了,心想,我一个程序员,关心材质干什么,直接让建模师给到不就得了。。。就这样经过了几个月

发现

在最近时间,我在琢磨模型软件中的程序化着色器,在学习节点的过程中,看到了有个属性是菲涅尔,之前也有了解菲涅尔是水等透射物体上的物理现象,当视线和法向量的夹角越小时,越不容易形成反射,而在角度接近90°时,反射越来越明显。如下图:

00e93901213fb80e7becc013e798382eb9389a503e97.jpg

本来是对菲涅尔反射有点了解的,但基本都是透射物体上才能想到的物理知识点,完全没有往炫彩材质上想,后来在单独对菲涅尔属性进行颜色输出时,突然发现这个感觉和炫彩后盖材质有点联系,效果如下:

未标题-4.gif

但是又不太一样,我试着调整菲涅尔的方式,改为面朝向(其实也是菲涅尔,只是实现方式略有不同),效果如下:

未标题-5.gif

在模型软件中,材质我做到这个效果的时候,我发现这个就是我想要的,感觉和炫彩效果非常吻合,剩下的就是需要根据这个灰度改变颜色即可,于是我开始我的实践。

实现

下图是我做炫彩的全部流程,图片很大,可以放大查看:

未标题-1.jpg

  1. 得出菲涅尔值

首先是最主要的,也是前半截文章说的菲涅尔值,这里就用到了向量的点乘概念,点乘的值简单来说就是一个向量的长度乘以另一个向量投影在第一个向量的长度,如果夹角为90°是,投影的长度为0,结果为0,如果夹角远离90°时,点乘的结果会越来越大。

有了上述知识,再来看下菲涅尔,菲涅尔的值就是根据视线和物体的表面向量去进行点乘计算得来的,而面向量不好求,相反,各个面的法向量我们是知道的,于是就有了视线和法向量求点乘,得到的值,然后被1减去得到菲涅尔值(但是这里不需要被1减去,在混合颜色时,传入该值,颜色顺序对调即可)。

于是有了上图中左边的三个节点:

未标题-2.jpg

相关glsl代码如下(基于three):

    //片元着色器
    float dot_6 = dot(normalize(vViewPosition), normalize(vNormal));
  1. 根据菲涅尔值进行颜色混合

我在观察 mi11 蓝色款时,发现他其实又三个色调变化,从直视后盖到视线和后盖相切,依次变化是:

白/浅蓝交替变化 -> 深蓝 -> 白

如下图:

未标题-7.gif

其中第一个阶段到第二个阶段,以及第二个阶段到最后一个阶段很好理解,根据得到的菲涅尔值进行混合颜色处理,在深蓝到白的阶段时我对菲涅尔值微调,原因是因为我不想让白色影响的范围太大,后盖和视线快相切的时候才会起作用,并做了一次平滑处理:

未标题-8.jpg

相关glsl代码如下(基于three):

    //片元着色器
    ...
    vec4 mix_11 = mix( 
        vec4(150./256., 152./256., 161./256., 1.0), //深蓝
        mix_10, //白/浅蓝交替变化的混合颜色
        dot_6
    );
    vec4 mix_15 = mix(
        vec4(210./256., 214./256., 223./256., 1.0), //白
        mix_11, //前两个阶段的混合颜色
        smoothstep(0.0, 1.0, (dot_6 * 8.0))
    );

接下来说下第一个阶段(白/浅蓝交替变化),和之后的阶段有所不同的是,这个阶段交替变化,而不是出现一次,我的做法同样是根据菲涅尔值去进行mix混合颜色的,但是会乘以一个倍数,使值大于PI,然后sin曲线和规格化(使值变为0~1), 这样就可以得到交替变化的灰度值,再根据这个值进行颜色混合,就完成所有步骤了,最后输出这个混合颜色即可:

未标题-9.jpg

相关glsl代码如下(基于three):

    //片元着色器
    ...
    vec4 mix_10 = mix(
        vec4(194./256., 198./256., 212./256., 1.0), 
        vec4(220./256., 234./256., 243./256., 1.0), 
        ((sin((dot_6 * 20.0)) + 1.0) / 2.0)
    );

示例:

我尝试吸取k40pro 白色款的三种颜色用上述着色器做demo,感觉效果和预期一致。

999edc37231fdb2b55abbadd1f35608b.gif aa21e16fc5fceab595fa7fdb0b505934.gif