自学内容网 自学内容网

Unity Shader学习日记 part6 基本光照模型

从这篇之后呢,就主要是具体的实现了,今天呢,先来看看基本的光照模型。

至于光照什么的就不赘述了,主要是学习以及实现一些基本的光照模型,如果有感兴趣的,可以自行寻找相关文档学习。

标准光照模型

光照模型的种类很多,不过在早期游戏开发者使用的基本上只有一个--标准光照模型,具体的概念我就不写了,实际上这个模型主要有4个部分,他们分别是自发光(emissve),高光反射(specular),漫反射(diffuse),环境光(ambient)。

下面我会给出几种光的计算公式,原理不会去说。

环境光:

自发光:

漫反射:

        n是表面法线,l是光源的单位矢量,m(diffuse)是材质的漫反射颜色,c(light)光源颜色

        注意:我们需要对n和l的点乘限制大于0,可以防止物体被后面的光源照亮 

高光反射:

        高光反射需要的信息比较多,需要知道表面法线,视角方向,光源方向,反射方向等等,下面的公式中,字母都表示向量

实际上,我们只需要知道这三个即可,第四个反射方向可以通过计算的到。

m(gloss)光泽度或者反光度(shinness),它可以控制反光区域的大小,这个值与高光区域大小成反比, m(specular) 高光反射颜色,c(light)光源颜色。

除了这个公式之外呢,还有另外一个公式(Blinn),可以避免计算反射方向r,通过引入h这个矢量,h可以通过v和l取平均后归一化得到。

 

所以他的公式是这样的。

那么在学习了公式之后,我们在哪里去使用他们,在开发中,代码主要在顶点函数以及片元函数中,因此我们也同样是在这两个中来计算。

不过,在不同的函数中去计算得到的效果是不一样的,这就分成了逐像素以及逐顶点

逐像素:在片元着色器中计算

逐顶点:在顶点着色器中计算

两个的具体区别我们会在实践中看到。

由于环境光以及自发光不需要编写,所以下面主要是对于漫反射和高光反射的实现。

注意,下面的实现只有代码部分,至于材质自己新建即可,然后将天空盒取消

漫反射的实现

回忆一下公式,我们需要知道4个参数,怎么得到这些参数,代码中会有具体的注释。我们先来看看逐顶点的方式实现

逐顶点

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "StanderLightMode/Diffuse Vertex-Level"
{
    Properties
    {
        _Diffuse ("Diffuse",Color)=(1,1,1,1)//漫反射颜色
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}//光照模式

            CGPROGRAM

            #pragma vertex vertex
            #pragma fragment frag

            #include "Lighting.cginc"//引入内置函数

            fixed4  _Diffuse; //同名变量

            struct a2v
            {
                float4 vertex:POSITION;//模型空间的顶点数据
                float3 normal:NORMAL;//法线
            };
            struct v2f
            {
                float4 pos:SV_POSITION;//裁剪空间中的顶点坐标
                fixed3 color:COLOR ;
            };

            v2f vertex(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);//顶点转到裁剪空间中

                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//内置变量的环境光

                fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//法线方向
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                //漫反射计算
                //saturate可以将值截取在【0,1】,你也可以使用max的方式实现
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));

                o.color=ambient+diffuse;

                return o;

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

        }
    }
}
逐顶点的漫反射

 

逐像素

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "StanderLightMode/Diffuse Pixe-Level"
{
    Properties
    {
        _Diffuse ("Diffuse",Color)=(1,1,1,1)//漫反射颜色
    }
    SubShader
    {

        Pass
        {
            Tags {"LightMode"="ForwardBase"}//光照模式

            CGPROGRAM

            #pragma vertex vertex
            #pragma fragment frag

            #include "Lighting.cginc"//引入内置函数

            fixed4  _Diffuse; //同名变量

            struct a2v
            {
                float4 vertex:POSITION;//模型空间的顶点数据
                float3 normal:NORMAL;//法线
            };
            struct v2f
            {
                float4 pos:SV_POSITION;//裁剪空间中的顶点坐标
                fixed3 worldNormal:TEXCOORD0 ;
            };

            v2f vertex(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);//顶点转到裁剪空间中

                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 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                //漫反射计算
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));

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


            ENDCG

        }
    }
}
逐像素的漫反射

可以发现,逐顶点和逐像素的区别注意在于代码的计算位置,除此之外,两者的效果也不一样。

在逐顶点中,可以明显的看到阴影部分的锯齿,而逐像素中这种现象减少很多。 

但是呢这两个方式,都会导致光照不到的地方全黑,这样会导致,无法看清模型细节,所以引入另外一种模型,半兰伯特光照模型。

半兰伯特光照模型

通常来说,a和b是0.5,所以

 

来看看他的实现

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "StanderLightMode/HalfLambert"
{
    Properties
    {
        _Diffuse ("Diffuse",Color)=(1,1,1,1)//漫反射颜色
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}//光照模式

            CGPROGRAM

            #pragma vertex vertex
            #pragma fragment frag

            #include "Lighting.cginc"//引入内置函数

            fixed4  _Diffuse; //同名变量

            struct a2v
            {
                float4 vertex:POSITION;//模型空间的顶点数据
                float3 normal:NORMAL;//法线
            };
            struct v2f
            {
                float4 pos:SV_POSITION;//裁剪空间中的顶点坐标
                fixed3 color:COLOR ;
            };

            v2f vertex(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);//顶点转到裁剪空间中

                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//内置变量的环境光

                fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                //漫反射计算
                fixed3 halfLambert=dot(worldNormal,worldLight)*0.5+0.5;
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halfLambert;

                o.color=ambient+diffuse;

                return o;

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


            ENDCG

        }
    }
}
半兰伯特漫反射

放一起看看

 

高光反射的实现

回忆一下公式

再结合代码来看

逐顶点

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "StanderLightMode/SpecularVertexLevelShader"
{
    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
            {
                fixed4 vertex:POSITION;
                fixed3 normal:NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                fixed3 color:COLOR;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//法线方向
                fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                fixed3 diffuse=_LightColor0.rgb*ambient*saturate(dot(worldNormal,worldLightDir));

                fixed3 relfectDir=normalize(reflect(-worldLightDir,worldNormal));//反射方向

                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);//视角方向

                fixed3  specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(relfectDir,viewDir)),_Gloss);

                o.color=ambient+diffuse+specular;

                return o;
            }

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

 

高光反射逐顶点实现

逐像素

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "StanderLightMode/SpecularPixeLevelShader"
{
    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
            {
                fixed4 vertex:POSITION;
                fixed3 normal:NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
                float3 worldPos:TEXCOOD1;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);

                o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                o.worldPos=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 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                fixed3 diffuse=_LightColor0.rgb*ambient*saturate(dot(worldNormal,worldLightDir));

                fixed3 relfectDir=normalize(reflect(-worldLightDir,worldNormal));//反设方向

                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);//视角方向

                fixed3  specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(relfectDir,viewDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }
            ENDCG
        }
        
    }
}
高光反射逐像素实现

Blinn-Phong模型 

回忆公式

具体实现

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "StanderLightMode/SpecularBlinnPhongShader"
{
    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
            {
                fixed4 vertex:POSITION;
                fixed3 normal:NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
                float3 worldPos:TEXCOOD1;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);

                //将法线法线从模型空间转到了世界空间
                o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                o.worldPos=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 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);//光源方向

                fixed3 diffuse=_LightColor0.rgb*ambient*saturate(dot(worldNormal,worldLightDir));

                fixed3 relfectDir=normalize(reflect(-worldLightDir,worldNormal));//反设方向

                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);//视角方向

                fixed3 halfDir=normalize(worldLightDir+viewDir);

                fixed3  specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }
            ENDCG
        }
        
    }
}
Blinn-Phong高光反射实现

放一起看看

 

实际上,代码方面有很多可以优化的地方,对于很多东西,我们采用的是自己计算的方式,不过呢,内置库中给我们提供了很多的方法可以避免这些计算,比如这些

不过,具体的替换之后的代码我就不放了。

目前学习参考《UnityShader入门精要》 ,只作为个人学习使用。

基本的光照就到这里来,接下来是材质相关的Shader学习。


原文地址:https://blog.csdn.net/weixin_70808146/article/details/145280851

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!