Shader实现漫反射、高光反射、纹理映射

发表于2017-06-12
评论0 1.9k浏览
Shader在项目开发中的重要性就不多说了,下面介绍的是其中用的比较多的漫反射、高光反射这类光照模型,还有纹理映射这种纹理贴图,为了方便更好的去学习shader,下面就给大家详细介绍下。

漫反射效果

漫反射几何计算公式:
Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角) )    Tip:cosθ = 光方向· 法线方向

代码如下:
Shader "LJL/02_DiffuseVert"  
{  
    Properties   
    {  
        _Color("_Color",Color)=(1,1,1,1)  
    }  
    SubShader   
    {  
        Pass  
        {  
            CGPROGRAM  
            #include "UnityCG.cginc"  
            //_LightColor0 从Unity获取第一个直射光颜色  _WorldSpaceLightPos0  从Unity获取第一个直射光世界位置  
            #include "Lighting.cginc"    
            #pragma vertex vert  
            #pragma fragment frag  
            struct a2v  
            {  
                //将Unity模型空间下的顶点位置填充给position  
                float4 position:POSITION ;  
                //将Unity模型空间下的法线填充给normal  
                float3 normal:NORMAL;  
            };  
            struct v2f  
            {  
                float4 position:SV_POSITION;  
                fixed3 color:COLOR;  
            };   
            fixed4 _Color;  
            v2f vert(a2v v)  
            {  
                v2f f;  
                f.position = mul(UNITY_MATRIX_MVP,v.position);  
                //获取单位法线向量,并将向量在模型坐标转化为世界位置  
                fixed3  normalDir=normalize(mul(v.normal,(float3x3)_World2Object));  
                //获取世界位置下直射光单位向量(由于是平行光,在世界坐标下和在物体坐标方向一致)  
                fixed3  lightDir=normalize(_WorldSpaceLightPos0.xyz);  
                //漫反射计算公式:直射光颜色*max(cos0,0)  cos0是发现与直射光方向  
                fixed3 diffuse= _LightColor0.rgb * max(dot(normalDir,lightDir),0) * _Color;  
                //获取环境光  
                fixed3 ambient= UNITY_LIGHTMODEL_AMBIENT.rgb;  
                //加上环境光颜色  
                f.color=ambient + diffuse;  
                return f;  
            }  
            fixed4 frag(v2f f):SV_Target  
            {  
                return fixed4 (f.color,1);  
            }  
            ENDCG  
        }  
    }  
    Fallback "Diffuse" 
}

其效果如下图所示:

但是发现阴影部分和高亮部分过渡的不够平滑,这是如果需要的话可以在片元函数里实现漫反射效果

Shader如下:
Shader "LJL/03_DiffuseFrag"  
{  
    Properties   
    {  
        _Color("_Color",Color)=(1,1,1,1)  
    }  
    SubShader   
    {  
        Pass   
        {  
            CGPROGRAM  
        #include "UnityCG.cginc"  
        #include "Lighting.cginc"    
        #pragma vertex vert  
        #pragma fragment frag   
        fixed4 _Color;  
        struct a2v  
        {  
            float4 position:POSITION ;  
            float3 normal:NORMAL;  
        };   
        struct v2f  
        {  
            float4 position:SV_POSITION;  
            float3 color:COLOR;  
        };  
        v2f vert(a2v v)  
        {  
            v2f f;  
            f.position=mul(UNITY_MATRIX_MVP,v.position);  
            f.color=v.normal;  
            return f;  
        }  
        float4 frag(v2f f):SV_Target  
        {  
            float3 normalDir=normalize(mul(f.color,(float3x3)_World2Object));  
            float3 lightDir=normalize(_WorldSpaceLightPos0.xyz);  
            //计算法线  
            float3 diffuse = _LightColor0.rgb*max(dot(normalDir,lightDir),0)*_Color;  
            //获取环境光  
            float3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;  
            return float4(diffuse+ambient,1);  
        }  
        ENDCG  
        }  
    }  
    Fallback "Diffuse" 
}  

效果就更加平滑

高光反射效果

漫反射几何计算公式:
Specular=直射光  * pow( max(cosθ,0),10)  θ:是反射光方向和视野方向的夹角

代码如下:
Shader "LJL/04_SpecularVert"  
{  
    Properties   
    {  
        _Color("_Color",Color)=(1,1,1,1)  
        _Range ("_Range", Range(0.0, 10.0)) = 2.0  
    }  
    SubShader   
    {  
        Pass  
        {  
            CGPROGRAM  
            #pragma vertex vert  
            #pragma fragment frag   
            #include "Lighting.cginc"   
            float4 _Color;  
            float _Range;  
            struct a2v  
            {  
                float4 position:POSITION ;//将模型空间下的位置坐标填充给position  
                float3 normal:NORMAL ;//将模型空间下的顶点法线填充normal  
            };  
            struct v2f  
            {  
                float4 position:SV_POSITION ;  
                float3 color:COLOR;  
            };  
            v2f vert(a2v v)  
            {  
                v2f f;  
                f.position=mul(UNITY_MATRIX_MVP,v.position);  
                float4 vertexPos=mul(v.position,(float4x4)_World2Object);  
                //获取视野方向  
                float3 cameraDir=normalize(_WorldSpaceCameraPos.xyz - vertexPos);  
                //获取灯光方向  
                //float3 lightDir=_WorldSpaceLightPos0.xyz;  
                float3 lightDir = WorldSpaceLightDir(vertexPos);  
                float3 normalDir=normalize(mul(v.normal,(float3x3)_World2Object));  
                //获取反射光位置  
                float3 reflectDir = normalize(reflect(-lightDir,normalDir));  
                float3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,cameraDir),0),_Range) * _Color;  
                float3 diffuse = _LightColor0.rgb * max(dot(lightDir,normalDir),0)*_Color;  
                f.color=specular+UNITY_LIGHTMODEL_AMBIENT.rgb+diffuse;  
                return  f;  
            }  
            fixed4 frag(v2f f): SV_Target  
            {  
                return fixed4(f.color,1);  
            }  
            ENDCG  
        }  
    }  
    Fallback "Diffuse" 
}  

效果图如下:

纹理映射效果

映射原理:
在顶点函数中取得在顶点对应的纹理坐标,在片元函数中获取该纹理左边的像素颜色值并与漫反射融合,赋给SV_Target

代码如下:
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'  
Shader "LJL/Texture" {  
    Properties   
    {  
        _Color("_Color",Color)=(1,1,1,1)  
        _MainTex("_MainTex",2D)="white"{}  
    }  
    SubShader   
    {  
        Pass  
        {  
            CGPROGRAM  
            #include "UnityCG.cginc"   
            #include "Lighting.cginc"   
            #pragma vertex vert  
            #pragma fragment frag   
            sampler2D _MainTex;  
            float4 _MainTex_ST;//固定写法,用于获取纹理的缩放大小和偏移大小  
            fixed4 _Color;  
            struct a2v  
            {  
                //从语义里获取的值都是在模型坐标  
                float4 position:POSITION ;//将顶点位置填充给position  
                float3 normal:NORMAL ;//将模型的法线填充给normal  
                float4 texcoord:TEXCOORD0 ; //将该顶点的纹理坐标位置填充给texcoord  
            };   
            struct v2f  
            {  
                float4 position:SV_POSITION ;//屏幕位置  
                float3 wordNormal:COLOR0;//存储世界坐标下的法线方向  
                float4 wordPosition:TEXCOORD0 ; //存储在世界坐标下的顶点位置  
                float2 uv:TEXCOORD1 ;   
            };   
            v2f vert(a2v v)  
            {  
                v2f f;  
                f.position=mul(UNITY_MATRIX_MVP,v.position);  
                f.wordPosition=mul(v.position,(float4x4)unity_WorldToObject);//转化为世界坐标  
                f.wordNormal=mul(v.normal,(float3x3)unity_WorldToObject);//同上  
                //获取纹理坐标  
                f.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;  
                return f;  
            }  
            float4 frag(v2f f):SV_Target  
            {  
                //获取该顶点所对应的纹理uv像素值  
                fixed3 color = tex2D(_MainTex,f.uv)*_Color.rgb;  
                //获取灯光单位方向  
                float3 lightDir=normalize(WorldSpaceLightDir(f.wordPosition));  
                //获取法线单位方向  
                float3 normalDir=normalize(f.wordNormal);  
                //将像素颜色值与漫反射颜色融合  
                float3 diffuse=_LightColor0.rgb*color*max(dot(lightDir,normalDir),0);  
                //加上环境光渲染  
                float3 tempColor=diffuse+color*UNITY_LIGHTMODEL_AMBIENT.rgb;  
                return fixed4(tempColor,1);  
            }  
            ENDCG  
        }  
    }  
    Fallback "Diffuse" 
}

最终成功映射纹理同时支持纹理的缩放和偏移,效果图如下:

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论