shader篇-漫反射

shader篇-漫反射


    • shader篇-漫反射
    • 背景
    • 逐顶点光照
    • 逐像素光照
    • 半兰伯特光照模型

之前只是草草看了书,隔了一两个月回头看,很多都忘了,学习方法不对啊,希望利用写博客加深对知识的理解吧。

背景

渲染包含两大部分:像素可见性和光照计算。
今天就是介绍如何利用shader仿真现实世界中的漫反射。
漫反射计算公式如下

Cdiffuse=(ClightMdiffuse)max(0,n⃗ l⃗ )

计算漫反射需要四个参数,入射光颜色,强度
Clight
,材质反射系数
Mdiffuse
,表面法线n和光源方向l

逐顶点光照

 Shader "Test Shader/VertexLevel"{
Properties
{
    _Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
    Pass
    {
        Tags{"LightMode"="ForwardBase"}
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        fixed4 _Diffuse;
        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));
            //漫反射和环境光相加得到最终的光照结构
            o.color=ambient+diffuse;
            return o;
        }
        fixed4 frag(v2f i):SV_Target
        {
            return fixed4(i.color,1.0);
        }
        ENDCG
    }
}
FallBack "DiffuseFallBack"

把这个shader文件添加给某个材质,再把该材质添加到角色中,就能得到简单的漫反射效果
不过这样的漫反射会出现一些视觉问题,背光面和光面交界处有锯齿

逐像素光照

Shader "Test Shader/DiffusePixelLevel"{
Properties
{
    _Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
    Pass
    {
        Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"
        #include "Lighting.cginc"

        fixed4 _Diffuse;

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

        };

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

            float3 worldNormal:TEXCOORD0 ;

        };

        v2f vert(a2v v){
            v2f o;

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

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


            return o;
        }

        fixed4 frag(v2f i):SV_Target
        {
            //通过内置变量获取环境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

            fixed3 worldNormal=normalize(i.worldNormal);

            //获取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式计算漫反射
            //saturate(x)和max(0,x)能达到同样的效果,他的作用是把x截取到[0,1]范围内
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));

            fixed3 color=ambient+diffuse;
            return fixed4(color,1.0);
        }

        ENDCG
    }
}
FallBack "DiffuseFallBack"}

把计算挪到片元着色器中完成了逐像素光照,逐像素光照更平滑
不过这2种着色效果造成模型外观呈黑,
我们用半兰伯特光照模型解决这一问题

半兰伯特光照模型

Cdiffuse=(ClightMdiffuse)α(n⃗ l⃗ +β)

这里并没有使用max操作防止n,l点积为负值,而是对结果进行了一个a倍的缩放加上一个B大小的偏移,大多数情况下,a和b值为0.5
Cdiffuse=(ClightMdiffuse)0.5(n⃗ l⃗ +0.5)

Shader "Test Shader/HalfLambert"{
Properties
{
    _Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
    Pass
    {
        Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"
        #include "Lighting.cginc"

        fixed4 _Diffuse;

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

        };

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

            float3 worldNormal:TEXCOORD0 ;

        };

        v2f vert(a2v v){
            v2f o;

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

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


            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 halflambert=dot(worldNormal,worldLight)*0.5+0.5;
            //利用漫反射光照公式计算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halflambert;

            fixed3 color=ambient+diffuse;
            return fixed4(color,1.0);
        }

        ENDCG
    }
}
FallBack "DiffuseFallBack"}

我在下面的代码里使用半兰特伯模型取代了原来的半兰特伯模型

    fixed3 halflambert=dot(worldNormal,worldLight)*0.5+0.5;
            //利用漫反射光照公式计算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halflambert;

            fixed3 color=ambient+diffuse;
            return fixed4(color,1.0);

这是最终的效果,从左到右是逐顶点光照,逐像素光照,半兰特伯模型
shader篇-漫反射_第1张图片

你可能感兴趣的:(Shader)