Unity Shader教程(一)Toon shader(卡通着色)

发表于2018-05-27
评论0 7.8k浏览
做unity引擎开发的同学都有学习过shader,可能这方面的教程也看了不少,对于原理什么的也都烂熟于心,但是真正要用shader实现某个功能,可能还是有很多开发者不会,下面这个系列的案例教程就是希望大家能用起shader,掌握shader后面的算法,加深对shader的认识。首先大家介绍的是shader中最常见的Toon shader(卡通着色)

游戏shader中最常见的Toon shader(卡通shader)

. Toon 效果图 
                                      四种不同的效果,分为带灯光和不带灯光的

本文shader源码所在目录:Standard Assets/Effects/ToonShading

ToonBasic 的原理与写法

Basic的原理是通过一个Cubemap类型的贴图和当前的shader进行叠加相乘得到的(两个像素相乘是改变对应像素的亮度或者色值,具体可以查阅下资料像素相乘和相加的意义)。这个时候你就会看到模型的脸,腿的部分很亮,其他部分就暗色些。

具体的写法如下:
Shader "Toon/Basic" {
    Properties {
        _Color ("Main Color", Color) = (.5,.5,.5,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
    }
    SubShader {
        Tags { "RenderType"="Opaque" } //渲染不透明物体
        Pass {
            Name "BASE" //pass的名字,这个后续的shader会用到
            Cull Off  //双面渲染
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog  //①编译多种雾效的类型
            #include "UnityCG.cginc"
            sampler2D _MainTex; 
            samplerCUBE _ToonShade;
            float4 _MainTex_ST;
            float4 _Color;
            struct appdata {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 pos : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float3 cubenormal : TEXCOORD1;
                UNITY_FOG_COORDS(2) //②获取fog的坐标
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);  //③获取2d纹理坐标
                o.cubenormal = mul (UNITY_MATRIX_MV, float4(v.normal,0));
                UNITY_TRANSFER_FOG(o,o.pos); //④输出雾效的数据
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color * tex2D(_MainTex, i.texcoord);
                fixed4 cube = texCUBE(_ToonShade, i.cubenormal);
                fixed4 c = fixed4(2.0f * cube.rgb * col.rgb, col.a);
                UNITY_APPLY_FOG(i.fogCoord, c);  //⑤i.fogcoord是从顶点数据取出来的一个2维的纹理坐标
                return c;
            }
            ENDCG           
        }
    } 
    Fallback "VertexLit"
}

补充说明下:这里有几条unitycg.cginc里面定义的指令
1. 是编译多种类型的雾的变体,你可以在fog设置的地方看到fog mode的设置情况,这句话就表明了在不同mode 的时候对对本shader的编译
2. fog的顶点数据,为后面作色做准备
3. 获取2d纹理的坐标数据
4. 获取无效数据
5. 对雾的颜色值和当前的像素进行插值,如果渲染的模式是renderpath的那么末日的作色是黑色

ToonBasicOutLine的原理与写法

outline的原理:沿着视角垂直的地方向外拉升像素,用于作色使用(这个应该是目前游戏中使用最多的算法了)

具体的写法如下:
Shader "Toon/Basic Outline" {
    Properties {
        _Color ("Main Color", Color) = (.5,.5,.5,1)
        _OutlineColor ("Outline Color", Color) = (0,0,0,1)
        _Outline ("Outline width", Range (.002, 0.03)) = .005
        _MainTex ("Base (RGB)", 2D) = "white" { }
        _ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
    }
    CGINCLUDE
    #include "UnityCG.cginc"
    struct appdata {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };
    struct v2f {
        float4 pos : SV_POSITION;
        UNITY_FOG_COORDS(0)
        fixed4 color : COLOR;
    };
    uniform float _Outline;
    uniform float4 _OutlineColor;
    v2f vert(appdata v) {
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        float3 norm   = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
        float2 offset = TransformViewToProjection(norm.xy);
        #ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
            o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
        #else
            o.pos.xy += offset * o.pos.z * _Outline; //核心地方:在处理顶点的时候沿着视线垂直的地方进行向外拉升
        #endif
        o.color = _OutlineColor;
        UNITY_TRANSFER_FOG(o,o.pos);
        return o;
    }
    ENDCG
    SubShader {
        Tags { "RenderType"="Opaque" }
         UsePass "Toon/Basic/BASE"  //引用之前的base pass也就是先作色人物
        Pass {
            Name "OUTLINE"  //命名为outline
            Tags { "LightMode" = "Always" }
            Cull front //剔除正面
            ZWrite On  //打开写缓存
            ColorMask rgb //对rgb颜色值进行蒙板
            Blend SrcAlpha OneMinusSrcAlpha //混合
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_APPLY_FOG(i.fogCoord, i.color);
                return i.color;
            }
            ENDCG
        }
    }
    Fallback "Toon/Basic"
}

文章很长,后面的两个带光的shader其实和这两个shader没啥区别,唯一不同的是使用了自己写的光照模型。希望通过本篇的介绍,大家都能理解Toon shader(卡通着色)。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引