Matcap Shader 详解【4】 - 多材质与混合材质

Matcap Shader 详解【4】 - 多材质与混合材质

https://zhuanlan.zhihu.com/p/38575505

 

Matcap Shader 详解【4】 - 多材质与混合材质

首发于图形程序/技术美术 Shading Artist 小屋

Matcap Shader 详解【4】 - 多材质与混合材质

Matcap Shader 详解【4】 - 多材质与混合材质

Matcap Shader 详解【4】 - 多材质与混合材质

天源

天是天光云影共徘徊的天,源是为有源头活水来的源

前言

上篇中讲了一下怎样用Matcap实现PBR光照模型,但实践中还会遇到一个新的问题:

  • BPR材质里可以实现多种材质混合的效果,比如一把斧头拥有金属和木质,而一件衣服可能同时具有布料和皮质。

这次就来讨论一下多材质实现的两种方案吧~

Unity官方PBR多材质效果

这次使用了一个中世纪骑士的模型,所有的质感都是通过高光贴图展现的——比如身上同时具备金属和皮革,在这张图上,白色代表了金属部分,黑色代表非金属,灰色则是介于两者之间:

Matcap Shader 详解【4】 - 多材质与混合材质

 

Unity官方PBR(Standard Specular Setup)渲染结果如下:

Matcap Shader 详解【4】 - 多材质与混合材质

 

方法一:通过高光贴图实现Matcap材质混合

高光贴图作为蒙版,将不该添加高光的部分遮掩掉,便可以实现混合的效果:

Matcap Shader 详解【4】 - 多材质与混合材质

 

源码如下:

Shader "TJia/Matcap_SpecTex" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex("Albedo Tex", 2D) = "white" {}
        _BumpMap ("Normal Tex", 2D) = "bump" {}
        _BumpValue ("Normal Value", Range(0,10)) = 1
        _MatCapDiffuse ("MatCap Diffuse (RGB)", 2D) = "white" {}
        _DiffuseValue ("Diffuse Value", Range(0,5)) = 1
        _MatCapSpec ("MatCap Spec (RGB)", 2D) = "white" {}
        _SpecValue ("Spec Value", Range(0,5)) = 0
        _SpecTex("Spec Tex", 2D) = "white" {}
        _SpecTexValue("Spec Tex Value", Range(0,2)) = 1 
    }

    Subshader {
        Tags { "RenderType"="Opaque" }

        Pass {
            Tags { "LightMode" = "Always" }

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f { 
                    float4 pos : SV_POSITION;
                    float4  uv : TEXCOORD0;
                    float3  TtoV0 : TEXCOORD1;
                    float3  TtoV1 : TEXCOORD2;
                };

                uniform float4 _BumpMap_ST;
                uniform float4 _MainTex_ST;

                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos (v.vertex);
                    o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                    o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);


                    TANGENT_SPACE_ROTATION;
                    o.TtoV0 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[0].xyz));
                    o.TtoV1 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[1].xyz));
                    return o;
                }

                uniform fixed4 _Color;
                uniform sampler2D _BumpMap;
                uniform sampler2D _MatCapDiffuse;
                uniform sampler2D _MainTex;
                uniform sampler2D _MatCapSpec;
                uniform sampler2D _SpecTex;
                uniform fixed _BumpValue;
                uniform fixed _DiffuseValue;
                uniform fixed _SpecValue;
                uniform fixed _SpecTexValue;

                fixed lum(fixed3 col)
                {
                    return col.r * 0.2 + col.g * 0.7 + col.b * 0.1;
                }

                float4 frag (v2f i) : COLOR
                {
                    fixed4 c = tex2D(_MainTex, i.uv.xy);
                    float3 normal = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
                    normal.xy *= _BumpValue;
                    normal.z = sqrt(1.0- saturate(dot(normal.xy ,normal.xy)));
                    normal = normalize(normal);

                    half2 vn;
                    vn.x = dot(i.TtoV0, normal);
                    vn.y = dot(i.TtoV1, normal);

                    vn = vn * 0.5 + 0.5;

                    fixed4 matcapDiffuse = tex2D(_MatCapDiffuse, vn) * _DiffuseValue;   
                    fixed4 specTex = tex2D(_SpecTex, i.uv.xy) * _SpecTexValue;
                    fixed4 matcapSpec = tex2D(_MatCapSpec, vn) * _SpecValue * specTex;
                    fixed4 diffuse = matcapDiffuse * c * _Color;
                    fixed4 finalColor = diffuse + lerp(0, matcapSpec, lum(specTex.rgb));
                    return finalColor;
                }

            ENDCG
        }
    }
}

说明:

  • 高光值与高光贴图相乘,是对高光的第一次遮罩
  • 最终与漫反射颜色叠加时,高光又通过高光贴图的明度做了一次插值,这是第二次遮罩

方法二:通过混合贴图实现Matcap材质混合

上述方法中,高光贴图的作用是遮罩,而另一种常见的材质混合方法便是使用混合贴图——比如Unity地形系统中的贴图混合,就是通过这样的方式。

所以这一次,我们将高光贴图当做混合贴图来使用,从而混合两种不同的Matcap材质——白色用材质一,黑色用材质二,灰色介于两者之间:

Matcap Shader 详解【4】 - 多材质与混合材质

 

源码:

Shader "TJia/Matcap_PBR_Mask" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex("Albedo Tex", 2D) = "white" {}
        _BumpMap ("Normal Tex", 2D) = "bump" {}
        _BumpValue ("Normal Value", Range(0,10)) = 1
        _MatCapDiffuse ("MatCap Diffuse (RGB)", 2D) = "white" {}
        _DiffuseValue ("Diffuse Value", Range(0,5)) = 1
        _MatCapSpec ("MatCap Spec (RGB)", 2D) = "white" {}
        _SpecValue ("Spec Value", Range(0,5)) = 0
        _MatCapDiffuse2("MatCap Diffuse2 (RGB)", 2D) = "white" {}
        _DiffuseValue2("Diffuse Value2", Range(0,5)) = 1
        _MatCapSpec2("MatCap Spec2 (RGB)", 2D) = "white" {}
        _SpecValue2("Spec Value2", Range(0,5)) = 0
        _MatcapMask("Matcap Mask", 2D) = "white" {}
        _MatcapMaskValue("Matcap Mask Value", Range(0,1)) = 0
    }

    Subshader {
        Tags { "RenderType"="Opaque" }

        Pass {
            Tags { "LightMode" = "Always" }
            Cull Off

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f { 
                    float4 pos : SV_POSITION;
                    float4  uv : TEXCOORD0;
                    float3  TtoV0 : TEXCOORD1;
                    float3  TtoV1 : TEXCOORD2;
                };

                uniform float4 _BumpMap_ST;
                uniform float4 _MainTex_ST;

                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos (v.vertex);
                    o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                    o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);


                    TANGENT_SPACE_ROTATION;
                    o.TtoV0 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[0].xyz));
                    o.TtoV1 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[1].xyz));
                    return o;
                }

                uniform fixed4 _Color;
                uniform sampler2D _BumpMap;
                uniform sampler2D _MatCapDiffuse;
                uniform sampler2D _MatCapSpec;
                uniform sampler2D _MatCapDiffuse2;
                uniform sampler2D _MatCapSpec2;
                uniform sampler2D _MainTex;             
                uniform sampler2D _MatcapMask;
                uniform fixed _BumpValue;
                uniform fixed _DiffuseValue;
                uniform fixed _SpecValue;
                uniform fixed _DiffuseValue2;
                uniform fixed _SpecValue2;
                uniform fixed _MatcapMaskValue;

                fixed lum(fixed3 col)
                {
                    return col.r * 0.2 + col.g * 0.7 + col.b * 0.1;
                }

                float4 frag (v2f i) : COLOR
                {
                    fixed4 c = tex2D(_MainTex, i.uv.xy);
                    float3 normal = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
                    normal.xy *= _BumpValue;
                    normal.z = sqrt(1.0- saturate(dot(normal.xy ,normal.xy)));
                    normal = normalize(normal);

                    half2 vn;
                    vn.x = dot(i.TtoV0, normal);
                    vn.y = dot(i.TtoV1, normal);

                    vn = vn * 0.5 + 0.5;
                    fixed4 mask = tex2D(_MatcapMask, i.uv.xy) * _MatcapMaskValue;
                    fixed4 matcapDiffuse = lerp(tex2D(_MatCapDiffuse, vn) * _DiffuseValue, tex2D(_MatCapDiffuse2, vn) * _DiffuseValue2, mask);
                    fixed4 matcapSpec = lerp(tex2D(_MatCapSpec, vn) * _SpecValue, tex2D(_MatCapSpec2, vn) * _SpecValue2, mask * lum(mask));
                    fixed4 diffuse = matcapDiffuse * c * _Color;
                    fixed4 finalColor = diffuse + matcapSpec;
                    return finalColor;
                }

            ENDCG
        }
    }
}

说明:

  • 漫反射颜色通过混合贴图差值获得(使用了rgba通道)
  • 高光颜色通过混合贴图的明度 差值获得

一些趣味玩法

一般情况下,两个Matcap材质混合足够表达一个物体了,如果不够,可以使用通道混合的方法,用RGBA的0和1代表8种不同的材质,实现更多的混合。(需要注意的是,OpenGLES2最多只支持八张贴图,不过聪明的你们一定可以找到解决方法的~比如将16张Matcap采样图放入同一张贴图等等)

 

除此之外呢,Matcap实现的效果依旧是充满了不同的玩法,比如快速更换不同感觉的金属:

Matcap Shader 详解【4】 - 多材质与混合材质

 

或者变身成壕,穿上玻璃种翡翠之衣,戴上粉水晶之甲:

Matcap Shader 详解【4】 - 多材质与混合材质

 

附上一点Matcap渲染的细节~:

Matcap Shader 详解【4】 - 多材质与混合材质


系列链接(全):

Matcap Shader 详解【1】 - 基础思想与Unity中实现

Matcap Shader 详解【2】 - Matcap的固有色贴图与法线贴图

Matcap Shader 详解【3】 - 利用Matcap实现基本PBR光照模型

Matcap Shader 详解【4】 - 多材质与混合材质

Matcap Shader 详解【5】-动态光照、机位与反射

Matcap Shader 详解【6】-平面渲染与更佳的反射

编辑于 2018-07-19

shader

游戏开发

Unity(游戏引擎)

文章被以下专栏收录

Matcap Shader 详解【4】 - 多材质与混合材质

 

图形程序/技术美术 Shading Artist 小屋

Shading Artist / TA 交流地

进入专栏

推荐阅读

Matcap Shader 详解【4】 - 多材质与混合材质

在Unity中使用Amplify制作雨天特效Shader(第二部分)

Faith...发表于3D Ar...

Matcap Shader 详解【4】 - 多材质与混合材质

写一个水材质与冰材质的shader

雪落桑田

Matcap Shader 详解【4】 - 多材质与混合材质

【翻译】通过Unity学习PBR算法 ⑥

董宸发表于游戏开发我...

Matcap Shader 详解【4】 - 多材质与混合材质

Mask的艺术 – 近似PBR的Dota2角色渲染

拳四郎发表于游戏开发启...

5 条评论

写下你的评论...

 

  • Matcap Shader 详解【4】 - 多材质与混合材质知乎用户7 个月前

    用CUBE就能转了,IBL基本思想,最后还是看美术

Matcap Shader 详解【4】 - 多材质与混合材质

天源 (作者) 回复知乎用户6 个月前

可以看一下最新一篇Matcap Shader 详解【5】-动态光照、机位与反射,讲了讲Cubemap的优缺点和另外一种思路

Matcap Shader 详解【4】 - 多材质与混合材质

天源 (作者) 回复星临6 个月前

嗯嗯没错~可以看一下最新一篇

  • Matcap Shader 详解【4】 - 多材质与混合材质知乎用户7 个月前

    收藏了