Unity Toon Shading

卡通渲染

Cel/Toon Shading

照片级真实感渲染相反 通过将明暗之间有平滑过渡的阴影转换成 明或者暗 单一的色彩

首先需要了解 [ 冯氏光照模型 Blinn-Phong Reflection Model ]  

https://en.wikipedia.org/wiki/Phong_shading

在 Phong Shading 中 对光照的计算实际上是环境光,漫反射光和镜面发射光的直接叠加。漫反射光 / Diffuse Light是形成物体表面明暗的主要原因

当光源发出的光线照射到物体表面时 会向各个方向均匀的发散出去 最终进入我们的眼睛

物体表面的光只和光源的位置有关

当光线垂直照射到物体表面上时 物体表面会反射出最亮的光线

当光源与物体表面的夹角越小 物体表面反射的光线就越暗

这时候 需要用到 线性代数里的 点乘

当物体表面朝向的法线向量(N) 与 光源方向(L)  夹角为0时( 与物体表面垂直 ) 得到的点乘结果为 1

当物体表面朝向的法线向量(N) 与 光源方向(L)  夹角为90度时( 与物体表面平行 ) 得到的点乘结果为 0

intensity = N · L

然后我们需要在Unity编辑器里创建 shader文件    Create - Shader

以及创建一个材质球  Create - Material

将新建的Shader脚本赋给材质球

如何将shader脚本赋给材质球

然后 我们来修改下shader脚本

首先我们需要理解 基础 shader脚本中的输入及输出

从而 获取 Fragment Shader中的法向量 计算漫反射强度

appdata结构 由系统传入vertex shader 系统会将对应参数关联 并传入

v2f 是由 vertex shader 返回后 传递给 fragment shader的

在 appdata结构体里 加入字段 normal

后面的 :  NORMAL 是Unity自己的语义 它会告诉Unity将三维模型中的法向量和当前定义的参数关联起来


 

在 v2f结构体中也 加入参数 normal

将 法向量传递给 片元着色器 Fragment Shader

_WorldSpaceLightPos0: Unity 定义好的世界坐标系下的光源坐标 它返回一个(x,y,z,0) 最后一个数值用来区分 平行光,点光源的枚举

在Unity中 三维向量和四维向量做点乘 Unity会自动帮你把两个向量当作三维向量来做 点乘

如果我们直接用If判断 会让物体的表面光暗变化显得很刺眼 同时if在shader语法中性能很低 因尽量避免 关于if性能的问题 我们需要了解一个 分支预测 的概念

在这里我们用到了一个 smoothstep的内建函数

最后 让我们来看下效果

对比下正常的shader效果

完整卡通shader源码

Shader "Unlit/ToonShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Threshold ("Threshold",float) = 0.05
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Threshold;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                
                o.normal = v.normal;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                
                float intensity = dot(_WorldSpaceLightPos0, i.normal);
                intensity = smoothstep(0, _Threshold, intensity);
                col *= intensity;
                return col;
            }
            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39162566/article/details/113839997