之前在编写Phong与BlinnPhong的时候,我将光照计算的部分放在顶点程序vert当中

顶点程序是针对顶点的,比如一个模型有8个顶点,就会计算8次

而片段程序当中是针对像素的,有n个像素就会计算n次

所以在之前编写的着色器当中,由于在顶点当中进行,最后光照的效果,出现了斑块,效果不够平滑

基于顶点程序的光照效果

在以前由于图像处理器性能不强,所以很多的计算放在顶点程序当中进行

但是随着现代图像处理器的发展,GPU以及帮我们优化好了这一部分的问题

所以现在很多光照的计算部分可以放在片元程序当中进行了

下面就是改写为片元程序实现的光照效果

基于片元程序的Phong冯氏光照模型

在计算Phong的光照过程中需要利用到模型的法线normal以及模型坐标系下的顶点vertex

所以就需要在v2f中传递模型坐标系下的vertex与normal

在v2f使用的参数符合类型且不占用语义即可,即TEXCOORD0与POSITION1

为了区分变换过后的vertex,所以在传递变换过后的顶点坐标时采用的语义为SV_POSITION

关于SV_POSITION

SV_POSITION:SV_前缀的变量代表system value,

在DX10以后的语义绑定中被使用代表特殊的意义,和POSITION用法并无不同。

唯一区别是 SV_POSTION一旦被作为vertex shader的输出语义,

那么这个最终的顶点位置就被固定了(不能tensellate,不能再被后续改变它的空间位置?),

已经成为了转换裁剪世界的坐标,是可以直接用来进入光栅化处理的坐标,

如果作为fragment shader的输入语义那么和POSITION是一样的,

代表着每个像素点在屏幕上的位置(这个说法其实并不准确,

事实是fragment 在 view space空间中的位置,但直观的感受是如括号之前所述一般)

其次:在DX10版本之前没有引入SV_的预定义语义,POSITION被用作vertex shader的输入,输出,

fragment shader的输入参数。但DX10之后就推荐使用SV_POSITION作为vertex shader的输出和fragment shader的输入了,

注意vertex shader的输入还是使用POSITION!切记。

但是DX10以后的代码依旧兼容POSITION作为全程表达,估计编译器会自动判断并替换

总结:

两个的用法大致相同,都是存储坐标信息,但是一个是转换前的,一个是转换后的。

POSITION:用来存储,模型在本地坐标下,模型空间中(objcet space)的顶点坐标,转换为剪裁空间坐标前的坐标,unity告诉我们的模型顶点坐标,没经过转换的。可用作定点着色器(vertex shader)的输入、输出;片元着色器(frag)的输入。

SV_POSITION:用来存储,模型在剪裁空间,投影空间中的位置信息,即把模型空间的定点坐标,转化为剪裁空间的坐标,可用作定点着色器(vertex shader)的输出;片元着色器(frag)的输入。

Shader "ShaderLearning/FragmentDiffuseShader/FrgmentPhongShader"  
{  
    Properties  
    {  
        \_MainColor ("MainColor", Color) = (1,1,1,1)  
        \_SpecularColor ("SpecularColor", Color) = (0.5,0.5,0.5,1)  
        \_Shineness("Shineness",Range(1,32))=8  
    }  
    SubShader  
    {  
        Tags { "LightMode"="ForwardBase" }  
        Pass  
        {  
            CGPROGRAM  
            #pragma multi\_compile\_fwdbase  
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  
            #include "Lighting.cginc"  
            struct v2f  
            {  
                float4 pos : SV\_POSITION;  
                float3 normal : TEXCOORD0;  
                float4 vertex: POSITION1;  
            };  
            fixed4 \_MainColor;  
            fixed4 \_SpecularColor;  
            float \_Shineness;  
            v2f vert (appdata\_base v)  
            {  
                v2f o;  
                o.pos=UnityObjectToClipPos(v.vertex);  
                o.normal=v.normal;  
                o.vertex=v.vertex;  
                return o;  
            }  
            fixed4 frag (v2f i) : COLOR  
            {  
                //环境光颜色
                fixed4 col=UNITY\_LIGHTMODEL\_AMBIENT;  
                //模型坐标系下的法线转位世界坐标系下  
                float3 N=UnityObjectToWorldNormal(i.normal);  
                //获取世界坐标系下的光照方向  
                float3 L=normalize(WorldSpaceLightDir(i.vertex));  
                //计算漫反射  
                float diffuse=saturate(dot(L,N));
                //计算摄像机观察视角向量  
                float3 V=normalize(WorldSpaceViewDir(i.vertex));  
                //计算入射光反射向量,L取反就是入射光向量  
                float3 R=reflect(-L,N);  
                //计算镜面高光  
                float specular=pow(saturate(dot(V,R)),\_Shineness);  
                //叠加上漫反射与镜面高光的颜色乘上光照颜色  
                col+= \_LightColor0\*((\_MainColor\*diffuse)+(\_SpecularColor\*specular));  
                //计算世界坐标系下的顶点位置
                float3 wpos=mul(unity\_ObjectToWorld,i.vertex).xyz;  
                //计算4个点光源颜色  
                col.rgb+=Shade4PointLights(unity\_4LightPosX0,  
                unity\_4LightPosY0,  
                unity\_4LightPosZ0,  
                unity\_LightColor\[0\].rgb,unity\_LightColor\[1\].rgb,  
                unity\_LightColor\[2\].rgb,unity\_LightColor\[3\].rgb,  
                unity\_4LightAtten0,wpos,N);  
                return col;  
            }  
            ENDCG  
        }  
    }  
}

效果图⬇️

基于片元程序的Phong光照效果

基于片元程序的BlinnPhong布林冯光照模型

更改为BlinnPhong只需要将计算镜面高光部分改为使用半角向量进行计算即可

              //计算镜面高光,使用半角向量
              float specular=pow(saturate(dot(N,normalize(L+V))),_Shineness);

完整代码

Shader "ShaderLearning/FragmentDiffuseShader/FrgmentBlinnPhongShader"  
{  
    Properties  
    {  
        \_MainColor ("MainColor", Color) = (1,1,1,1)  
        \_SpecularColor ("SpecularColor", Color) = (0.5,0.5,0.5,1)  
        \_Shineness("Shineness",Range(1,32))=8  
    }  
    SubShader  
    {  
        Tags { "LightMode"="ForwardBase" }  
        Pass  
        {  
            CGPROGRAM  
            #pragma multi\_compile\_fwdbase  
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  
            #include "Lighting.cginc"  
            struct v2f  
            {  
                float4 pos : SV\_POSITION;  
                float3 normal : TEXCOORD0;  
                float4 vertex: POSITION1;  
            };  
            fixed4 \_MainColor;  
            fixed4 \_SpecularColor;  
            float \_Shineness;  
            v2f vert (appdata\_base v)  
            {  
                v2f o;  
                o.pos=UnityObjectToClipPos(v.vertex);  
                o.normal=v.normal;  
                o.vertex=v.vertex;  
                return o;  
            }  
            fixed4 frag (v2f i) : COLOR  
            {  
                //环境光颜色
                fixed4 col=UNITY\_LIGHTMODEL\_AMBIENT;  
                //模型坐标系下的法线转位世界坐标系下  
                float3 N=UnityObjectToWorldNormal(i.normal);  
                //获取世界坐标系下的光照方向  
                float3 L=normalize(WorldSpaceLightDir(i.vertex));  
                //计算漫反射  
                float diffuse=saturate(dot(L,N));
                //计算摄像机观察视角向量  
                float3 V=normalize(WorldSpaceViewDir(i.vertex));  
                //计算镜面高光,使用半角向量  
                float specular=pow(saturate(dot(N,normalize(L+V))),\_Shineness);  
                //叠加上漫反射与镜面高光的颜色乘上光照颜色  
                col+= \_LightColor0\*((\_MainColor\*diffuse)+(\_SpecularColor\*specular));  
                //计算世界坐标系下的顶点位置
                float3 wpos=mul(unity\_ObjectToWorld,i.vertex).xyz;  
                //计算4个点光源颜色  
                col.rgb+=Shade4PointLights(unity\_4LightPosX0,  
                unity\_4LightPosY0,  
                unity\_4LightPosZ0,  
                unity\_LightColor\[0\].rgb,unity\_LightColor\[1\].rgb,  
                unity\_LightColor\[2\].rgb,unity\_LightColor\[3\].rgb,  
                unity\_4LightAtten0,wpos,N);  
                return col;  
            }  
            ENDCG  
        }  
    }  
}

效果图(Phong与BlinnPhong对比)

左侧Phong,右侧BlinnPhong

Phong与BlinnPhong对比