《unityshader 入门精要》漫反射光照与高光反射光照模型学习笔记

时间:2022-02-03 10:23:42

 

unity 版本5.4.1,场景中只有一个方向光,没有天空盒子,光照模式为forward。

光照模式设置为Forward(菜单Edit->Project Settings->Player->OtherSettings->Rending Path->Forward)

 

顶点漫反射光照模型

虽然优化方面来说比较好,但是效果不好。基于顶点的光照计算导致,光照效果不平滑。

 

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'   
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
//unity shader5.0更新。。详见:
https://docs.unity3d.com/Manual/UpgradeGuide5-Shaders.html
Shader "LT/VertexDiffuse"
{
    Properties
    {
        _MainTex ("Texture"2D) = "white" {}
        _Color("Overall Diffuse Color Filter",Color) = (1,1,1,1)
    }
    SubShader
    {

        Pass
        {
            Tags { "LightMode"="ForwardBase" }  //光照模式注意要设置为Forward
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform float4 _LightColor0; // color of light source (from "Lighting.cginc")

            sampler2D _MainTex;
            float4 _Color;
            float4 _specColor;
            float _Shininess;

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

            struct v2f
            {
                float4 col : COLOR;
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            v2f vert (appdata v)
            {
                v2f o;
                //ModelMatrix和ModelMatrixInverse,UNITY内置uniform参数
                float4x4 ModelMatrix = unity_ObjectToWorld;    //模型矩阵  法向量N变化至对象坐标系
                float4x4 ModelMatrixInverse = unity_WorldToObject;
                 //计算对象坐标系中的顶点法向量的单位向量
                 //将mesh传递过来的顶点法向量 乘以 模型-->世界坐标系矩阵 得到世界坐标系中的法向量  然后单位化。
                 float3 normalDirection = normalize(mul(ModelMatrix,float4(v.normal,0.0)).xyz);
                 //float4(v.normal,0.0),ModelMatrixInverse 上面那句乘法部分换成这个结果是一样的,意思也是一样的
                 //计算入射线量的单位向量
                 float3 LightDirection= normalize(_WorldSpaceLightPos0.xyz);//==normalize(float3  (_WorldSpaceLightPos0));

                 //计算入射后的颜色
                 //先将光源颜色与材料颜色相乘, 载乘以上max(0,cos(N,L)),入射向量与法线夹角超过90度为模型的另一面,避免出现>90度,cos为负数的情况
                 float3 diffuseReflection = _LightColor0.xyz * _Color.xyz * max(0.0,dot(normalDirection,LightDirection));
                 o.col = float4(diffuseReflection,1.0) + UNITY_LIGHTMODEL_AMBIENT; //float4(diffuseReflection,1.0)
                 o.vertex =mul(UNITY_MATRIX_MVP,v.vertex) ;
                 o.uv = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col =  tex2D(_MainTex, i.uv) * i.col;
                return col;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}
 

 

半兰伯特光照模型

半兰伯特光照模型,解了决光照无法到达的区域, 背光面更亮一些

shader"LT/HalfLambert"
{
    Properties
    {
        _Diffuse("Diffuse Color",Color) = (1,1,1,1)
        _MainTex("Texture",2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
         Tags{"LightMode"="ForwardBase"   

        //Tags{"LightMod"="ForwardBass"}  这句之前单词拼错了,结果导致移动视角的时候模型乎暗乎亮的。unity没报错找半天原因才发现额
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        #include "Lighting.cginc"              //==与下面那句uniform float4 _LightColor0; 2选一写出即可
        
        fixed4 _Diffuse;
        sampler2D _MainTex;
//        uniform float4 _LightColor0; 
        
        struct a2v
        {
            float4 pos :POSITION;
            float2 uv:TEXCOORD0;
            float3 normal:NORMAL;
        };
        
        struct v2f
        {
            float4 pos: SV_POSITION;
            float3 worldNormal : TEXCOORD1;
            float2 uv:TEXCOORD0;
        };
        
        v2f vert(a2v v)
        {
            v2f o;
//            float4x4 modelMetrix =  unity_ObjectToWorld;                   //可声明也可不声明直接写在下句
            o.worldNormal = normalizemul((float3x3)unity_ObjectToWorld,v.normal));   //法线模型坐标转化为世界坐标
            o.uv = v.uv;
            o.pos = mul(UNITY_MATRIX_MVP,v.pos);
            return o;
        }
        
        fixed4 frag(v2f i): SV_Target
        {
            fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT;   //环境光
            fixed4 worldLight = normalize(_WorldSpaceLightPos0);
            fixed halfLambert = dot(i.worldNormal,worldLight) * 0.5+0.5;   //半兰伯特公式算法
            fixed4 diffuse = _LightColor0 * _Diffuse * halfLambert; //半兰伯特漫反射
        
            fixed4 col = tex2D(_MainTex , i.uv) * (diffuse + ambient); 
            return  col;
        }
        ENDCG
        }
    }
    Fallback "Diffuse"
}
 

高光反射光照

逐像素光照,光照效果过度更均匀。

Shader "LT/PixelSpecular"
{
    Properties
    {
        _MainTex ("Texture"2D) = "white" {}
        _Diffuse("Diffuse",Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)   //高光反射颜色
        _Gloss("gloss"Range(5,30)) = 10     //高光区域大小
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }  //光照模式注意要设置为Forward
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            
            #include "UnityCG.cginc"
            
            sampler2D _MainTex;
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;
            float4 _MainTex_ST;

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldNormal:TEXCOORD1;
                float3 worldPos:TEXCOORD2;
            };

            
            v2f vert (appdata v)
            {
                v2f o;
                o.worldNormal = normalize(mul((float3x3)unity_ObjectToWorld,v.normal));   //法向量从对象坐标系转换为世界坐标
                o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;       //顶点从对象坐标系转换为世界坐标

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射光照计算
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal,worldLightDir));

                //反射方向 = 单位化反射光照计算函数
                fixed3  reflectDir = normalize(reflect(-worldLightDir,i.worldNormal));

                //视角方向 = 单位化(相机方向- 顶点位置)世界坐标系下
                fixed3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);

                //反射光 = 光照颜色*反射光颜色*视角与反射光方向的点积的光滑洗漱次方
                fixed3 specular = _LightColor0.rgb* _Specular.rgb * pow(saturate(dot(ViewDir,reflectDir)),_Gloss); 

                fixed4 col = tex2D(_MainTex, i.uv) * fixed4(diffuse + specular + ambient,1.0);
                return col;
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

 

Blinn-Phong光照模型

大部分内容与前面逐像素高光反射模型差不多,效果会更亮一些

Shader "LT/Blinn-Phong"
{
    Properties
    {
        _MainTex ("Texture"2D) = "white" {}
        _Diffuse("Diffuse",Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)   //高光反射颜色
        _Gloss("gloss"Range(5,30)) = 10     //高光区域大小
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }    //光照模式注意要设置为Forward
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            
            #include "UnityCG.cginc"
            
            sampler2D _MainTex;
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;
            float4 _MainTex_ST;

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldNormal:TEXCOORD1;
                float3 worldPos:TEXCOORD2;
            };

            
            v2f vert (appdata v)
            {
                v2f o;
                o.worldNormal = normalize(mul((float3x3)unity_ObjectToWorld,v.normal));   //法向量从对象坐标系转换为世界坐标
                o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;       //顶点从对象坐标系转换为世界坐标

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射光照计算
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal,worldLightDir));

                //反射方向 = 单位化反射光照计算函数
//                fixed3  reflectDir = normalize(reflect(-worldLightDir,i.worldNormal));

                //视角方向 = 单位化(相机方向- 顶点位置)世界坐标系下
                fixed3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir+ViewDir);

                //反射光 = 光照颜色*反射光颜色*视角与反射光方向的点积的光滑洗漱次方
                fixed3 specular = _LightColor0.rgb* _Specular.rgb * pow(saturate(dot(ViewDir,halfDir)),_Gloss); 

                fixed4 col = tex2D(_MainTex, i.uv) * fixed4(diffuse + specular + ambient,1.0);
                return col;
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

 

 最后上张效果图,用到的C#脚本后面会说到

《unityshader 入门精要》漫反射光照与高光反射光照模型学习笔记