1. 程式人生 > 其它 >【Unity Shader學習筆記】Unity光照基礎-漫反射光照

【Unity Shader學習筆記】Unity光照基礎-漫反射光照

本程式碼只適用於平行光。

1、逐頂點漫反射光照

1.1漫反射光照原理

1.2程式碼實現

在Properties語義塊中宣告一個漫反射顏色屬性

Properties
    {
        //漫反射引數,用於調整漫反射效果
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }

在SubShader語義塊中定義一個Pass語義塊。
在Pass的第一行指明光照模式。

	Tags{"LightMode" = "ForwardBase"}

在Pass中,使用CGPROGRAM與ENDCG包圍Cg程式碼片,並告訴Unity頂點著色器與片元著色器的名字。
使用#include包含Unity的內建檔案。

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag
	//包含內建檔案
	#include "Lighting.cginc"
	ENGCG

接著在CGPROGRAM與ENDCG中定義此前在Properties中定義的屬性。
fixed4 _Diffuse;
定義輸入與輸出結構體

            struct a2v
            {
                //模型變換
                float4 vertex_position : POSITION;
                //法線方向(計算)
                float3 normal : NORMAL;
            };

            struct v2f
            {
                //輸出頂點位置
                float4 pos : SV_POSITION;
                //儲存頂點著色器輸出的顏色
                fixed3 color_in : COLOR;
            };

主要計算都集中在頂點著色器。在頂點著色器中,我們有兩個目標:把座標從模型空間轉換至裁剪空間中,以及輸出漫反射計算之後的color。
座標變換很簡單。程式碼如下:

                //pos變換
                o.pos = UnityObjectToClipPos(v.vertex_position);

計算顏色要複雜一些。根據最上方的公式,我們一共需要四個變數。_Diffuse變數我們已經定義好,光源顏色不需要運算(_LightColor0.rgb可直接得到),接下來需要獲取世界座標下的光源方向世界座標下的法線方向

fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
//法線原本是模型空間下的,需要變換至世界座標,因此需要mul()
//光源方向本來就是世界座標下的,因此不需要mul()
//使用normalize()進行歸一化

隨後可以進行最重要的漫反射運算
saturate函式是Cg提供的函式,可以將引數擷取在[0,1]的範圍內。
dot()函式點乘兩個向量。

//漫反射部分計算
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

加上環境光輸出

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
return o;

別忘了在shader最後Fallback。FallBack "Diffuse"
最終寫成如下程式碼:

Shader "Practise/DiffuseVertexShader"
{
    Properties
    {
        //漫反射引數,用於調整漫反射效果
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            //包含內建檔案
            #include "Lighting.cginc"
            #pragma vertex vert
            #pragma fragment frag

            fixed4 _Diffuse;

            struct a2v
            {
                //模型變換
                float4 vertex_position : POSITION;
                //法線方向(計算)
                float3 normal : NORMAL;
            };

            struct v2f
            {
                //輸出頂點位置
                float4 pos : SV_POSITION;
                //儲存頂點著色器輸出的顏色
                fixed3 color_in : COLOR;
            };

            v2f vert(a2v v)
            {
                //兩個目標:輸出變換後的pos座標、輸出計算後的color
                v2f o;
                //pos變換
                o.pos = UnityObjectToClipPos(v.vertex_position);

                //color計算
                //變數準備
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //法線原本是模型空間下的,需要變換至世界座標
                //環境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                //漫反射部分計算
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
                //整理輸出
                o.color_in = ambient + diffuse;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return fixed4(i.color_in, 1.0);
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

2、逐畫素漫反射光照

逐畫素光照中,主要的計算過程需要放到片元著色器中。頂點著色器只需要將變換後的位置座標、變換後的世界空間中的發現座標傳到片元著色器中即可。
在逐頂點漫反射的基礎上修改:

//輸入頂點著色器
struct a2v{
	float3 pos : POSITION;
	float3 normal : NORMAL;
};
//頂點著色器輸出
struct v2f{
	float4 worldpos : SV_POSITION;
	fixed3 worldnormal : TEXCOORD0;
};

TEXCOORD0、TEXCOORD1 等語義用於指示任意高精度資料,如紋理座標和位置。

把計算挪到片元著色器:

            v2f vert(a2v v)
            {
                // 返回
                v2f o;
                //變換pos
                o.worldpos = UnityObjectToClipPos(v.pos);
                //變換法線
                o.worldnormal = mul(v.normal, (float3x3)unity_WorldToObject);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldnormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //計算漫反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                //環境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 color = ambient + diffuse;
                return fixed4(color, 1.0);
            }

得到程式碼:

Shader "Prcatice/DiffusePixelShader"
{
    Properties
    {
       _Diffuse("Diffuse", Color) = (1, 1, 1, 1)

    }
    SubShader
    {
        Pass{
            Tags {"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            fixed4 _Diffuse;
            //輸入頂點著色器
            struct a2v{
                float3 pos : POSITION;
                float3 normal : NORMAL;
            };
            //頂點著色器輸出
            struct v2f{
                float4 worldpos : SV_POSITION;
                fixed3 worldnormal : TEXCOORD0;
            };

            v2f vert(a2v v)
            {
                // 返回
                v2f o;
                //變換pos
                o.worldpos = UnityObjectToClipPos(v.pos);
                //變換法線
                o.worldnormal = mul(v.normal, (float3x3)unity_WorldToObject);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldnormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //計算漫反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                //環境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 color = ambient + diffuse;
                return fixed4(color, 1.0);
            }
            ENDCG
            }
    }
    FallBack "Diffuse"
}

最終效果:

3、對比

逐頂點漫反射可能會在向光面與背光面的之間形成一些鋸齒。
(如下圖,左側為逐畫素漫反射,右側為逐頂點漫反射,右側陰影明顯有菱形鋸齒)