前言

最近再阅读《GPU 编程与 CG 语言之阳春白雪下里巴人》 就想着用Unity Shader实现其中的代码,文章夹杂了自己的一些理解,我自己也刚入门shader,难免会有很多错误,希望多多指出,一起交流进步。
阅读本文需要有一定的基础 0基础可以先去阅读 零基础入门Unity Shader 系列文章

10.1 Cook-Torrance 光照模型

公式如下:

其中 是漫反射光强, 是镜面反射光强的计算方法

其中 是菲涅尔反射系数 即:

其中 是反射系数, 是指向视点的向量,半角向量

值用于度量表面的粗糙程度,较大的m 值对应于粗糙平面,较小的值对应与较光滑的表面; 是顶点法向量N 和半角向量H 的夹角
化简之后得

最后公式如下

Unity shader 代码如下

Shader "Unlit/Cook-Torrance"
{
    Properties
    {
        _Ka ("Ambient Color", Color) = (1, 1, 1, 1)
        _Kd ("Diffuse Color", Color) = (1, 1, 1, 1)
        _Ks ("Specular Color", Color) = (1, 1, 1, 1)
        _F ("Fresnel", Range(0, 1)) = 0.5
        _M ("roughness", Range(0, 1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGINCLUDE
        // 重复使用
        float pow2(float x){
            return x * x;
        }
        ENDCG

        Pass
        {   
            Tags{
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;
                float3 worldPos : TEXCOORD1; 
            };

            float4 _Ka;
            float4 _Kd;
            float4 _Ks;

            float _F;
            float _M;

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 N = normalize(i.normal);
                float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
                float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));
                float3 H = normalize(L + V);

                float3 NL = max(dot(N, L), 0);
                float3 NV = max(dot(N, V), 0);
                float3 NH = dot(N, H);
                float3 VH = dot(V, H);


                // 环境光
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Ka;
                //漫反射
                float3 diffuse = _Kd.rgb * _LightColor0.rgb * NL;
                // 高光
                float3 specular = (0, 0, 0);  

                float temp = (pow2(NH) - 1)/(pow2(_M) * pow2(NH));

                // 粗糙程度
                float roughness = (exp(temp))/pow2(_M) * pow(NH, 4.0);

                float a = (2 * NH * NV)/VH;
                float b = (2 * NH * NL)/VH;

                // 几何衰减系数
                float geometric = min(a, b);
                geometric = min (1, geometric);
                // 菲涅尔反射系数
                float fresnelCoe = _F + (1 - _F) * pow (1 - VH, 5.0);

                float rs = (fresnelCoe * geometric * roughness)/(NV * NL);

                specular = rs * _LightColor0.rgb * NL * _Ks;

                return float4 (ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
}

效果如下:
图片说明

10.3 Bank BRDF经验模型

Bank BRDF的镜面反射部分公式为

分别表示镜面反射系数和高光系数; 表示入射光线方向、 表示实现观察方向、 表示该点的切向量。尤其要注意切向量的计算方法,因为一个三维空间点可能存在无数个切向量,通常我采用“顶点的法向量和视线方向做叉积,其结果作为

Unity shader 代码如下

Shader "Unlit/Bank-BRDF"
{
    Properties
    {
        _Ka ("Ambient Color", Color) = (1, 1, 1, 1)
        _Kd ("Diffuse Color", Color) = (1, 1, 1, 1)
        _Ks ("Specular Color", Color) = (1, 1, 1, 1)
        //ns 系数
        _Shininess ("Shininess", Range(8, 256)) = 8
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGINCLUDE
        // 重复使用
        float pow2(float x){
            return x * x;
        }
        ENDCG

        Pass
        {   
            Tags{
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;
                float3 worldPos : TEXCOORD1; 
            };

            float4 _Ka;
            float4 _Kd;
            float4 _Ks;

            float _Shininess;

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 N = normalize(i.normal);
                float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
                float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));
                float3 H = normalize(L + V);
                // 切线方向
                float3 T = normalize(cross(N, V));
                float3 NL = max(dot(N, L), 0);
                float3 NV = max(dot(N, V), 0);
                float3 NH = dot(N, H);
                float3 VH = dot(V, H);

                // 环境光
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Ka;
                //漫反射
                float3 diffuse = _Kd.rgb * _LightColor0.rgb * NL;
                // 高光
                float3 specular = (0, 0, 0);  


                float a = dot(L, T);
                float b = dot(V, T);
                // 计算bank BRDF 系数
                float c = sqrt(1 - pow2(a)) * sqrt(1 - pow2(b)) - a * b;

                float brdf = _Ks * pow(c, _Shininess);

                specular = brdf * _LightColor0.rgb * NL;

                return float4 (ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
}

效果如下:
图片说明
本章节完