shader篇-高光反射模型

shader篇-高光反射模型


    • shader篇-高光反射模型
    • 背景
    • 逐顶点光照
    • 逐像素光照
    • Blinn-Phong光照模型

背景

这里的高光反射模型,并不是现实意义上的高光反射,而是只是用来计算物理上那些沿着完全镜面反射方向被反射的光线,让物体看起来有光泽,例如金属材质
高光反射计算公式如下

Cdiffuse=(ClightMdiffuse)max(0,v⃗ r⃗ )Mgloss

v是视角方向,r是反射方向
Mgloss
是高光反射系数
反射方向可用表面法线n和光照方向l计算
r⃗ =l⃗ 2(n⃗ l⃗ )n⃗ 

逐顶点光照

Shader "Test Shader/SpecularVertexLevel{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射颜色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光区域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        #include "Lighting.cginc"

        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;

        struct a2v
        {
            //模型空间的顶点坐标
            float4 vertex:POSITION;
            //模型空间的法线方向
            float3 normal:NORMAL;

        };

        struct v2f
        {
            //输出的是裁剪空间的顶点坐标
            float4 pos:SV_POSITION;

            float3 color:COLOR;

        };

        v2f vert(a2v v){
            v2f o;

            //利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
            o.pos=UnityObjectToClipPos(v.vertex);
            //通过内置变量获取环境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            //法线转换到世界坐标
            //unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
            fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            //获取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式计算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //利用Cg内置反射光线方向计算函数计算反射光线
            fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
            //mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
            //视角方向=摄像头位置-顶点位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
            //根据公式计算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
            o.color=ambient+diffuse+specular;

            return o;
        }

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

        ENDCG
    }
}
FallBack "Specular"}

这里高光部分明显不平滑,这是因为高光反射的计算是非线性的,而在顶点着色器中计算光照再进行差值的过程是线性的,破坏了原有的视觉问题

逐像素光照

 Shader "Test Shader/SpecularPixelLevel"{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射颜色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光区域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "Lighting.cginc"
        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;
        struct a2v
        {
            //模型空间的顶点坐标
            float4 vertex:POSITION;
            //模型空间的法线方向
            float3 normal:NORMAL;
        };
        struct v2f
        {
            //输出的是裁剪空间的顶点坐标
            float4 pos:SV_POSITION;
            float3 worldNormal:TEXCOORD0;
            float3 worldPos:TEXCOORD1; 
        };
        v2f vert(a2v v){
            v2f o;
            //利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
            o.pos=UnityObjectToClipPos(v.vertex);
            //法线转换到世界坐标
            //unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
            o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
            return o;
        }
        fixed4 frag(v2f i):SV_Target
        {
            //通过内置变量获取环境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldNormal=normalize(i.worldNormal);
            //获取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式计算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //利用Cg内置反射光线方向计算函数计算反射光线
            fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
            //mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
            //视角方向=摄像头位置-顶点位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
            //根据公式计算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
            return fixed4(ambient+diffuse+specular,1.0);
        }
        ENDCG
    }
}
FallBack "Specular"}

这样的光照反射模型更平滑,而这一光照模型就是著名的phong光照模型

Blinn-Phong光照模型

blinn模型没有使用反射方向。而是引入了矢量h

h⃗ =(v⃗ +l⃗ )|v⃗ +l⃗ |

blinn模型光照公式如下
Cdiffuse=(ClightMdiffuse)max(0,n⃗ h⃗ )Mgloss

Shader "Test Shader/BlinnPhone"{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射颜色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光区域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        #include "Lighting.cginc"

        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;

        struct a2v
        {
            //模型空间的顶点坐标
            float4 vertex:POSITION;
            //模型空间的法线方向
            float3 normal:NORMAL;

        };

        struct v2f
        {
            //输出的是裁剪空间的顶点坐标
            float4 pos:SV_POSITION;

            float3 worldNormal:TEXCOORD0;
            float3 worldPos:TEXCOORD1; 

        };

        v2f vert(a2v v){
            v2f o;

            //利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
            o.pos=UnityObjectToClipPos(v.vertex);

            //法线转换到世界坐标
            //unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
            o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));

            o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;


            return o;
        }

        fixed4 frag(v2f i):SV_Target
        {
            //通过内置变量获取环境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldNormal=normalize(i.worldNormal);
            //获取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式计算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //mul(unity_ObjectToWorld, v.vertex)将顶点坐标转换为世界坐标
            //视角方向=摄像头位置-顶点位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
            //计算矢量h
            fixed3 h=normalize(worldLight+viewDir);
            //根据公式计算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, h)), _Gloss);
            return fixed4(ambient+diffuse+specular,1.0);
        }

        ENDCG
    }
}
FallBack "Specular"}

这样的高光反射模型反射部分更大更亮些,大多数情况下,我们都会选择这一高光反射模型
最后结果如下
shader篇-高光反射模型_第1张图片
从右到左分别是逐顶点光照,逐像素光照phong模型,blinn模型

你可能感兴趣的:(Shader)