目录

一、本节介绍

1 上集回顾

2 本节介绍

二、什么是漫反射材质球

三、 漫反射进化史

1 三种算法结果的区别

2 具体算法

2.1 兰伯特逐顶点算法

a.本小节使用的unity自带结构体。

b.兰伯特逐顶点算法公式

c.代码实现——兰伯特逐顶点算法

2.2 代码实现——兰伯特逐像素算法

a.像素和顶点算法的区别

b.实现代码

 2.3 代码实现——半兰伯特算法

a.为什么会出现半兰伯特

b.半兰伯特公式

c.代码实现

四、下集介绍


一、本节介绍

1 上集回顾

本集讲了如何让图片和外部颜色叠加显示。

2 本节介绍

如何做一个漫反射材质球。

二、什么是漫反射材质球

1 之前的颜色材质球

我们目前只学过直接上色的材质球(如图1所示),还有上节课的颜色和图片叠加的材质球。

2 现实的光照下的球

现实光照下的大部分材质球并不是纯色且全亮的,而是(如图2所示)。

 这种模拟大部分现实世界物体发光的状态,就是漫反射材质球。

三、 漫反射进化史

我们算到最后,对屏幕来说,仅仅想知道,我这个点应该用什么颜色。

所以,对这个颜色的计算出现了三种解法。

  • 兰伯特逐顶点算法
  • 兰伯特逐像素算法
  • 半兰伯特算法

备注:兰伯特是个人,他和别人一起研究出来了以上三个定律。

1 三种算法结果的区别

兰伯特逐顶点算法(白色和黑色交界处有些方块块的感觉、照不到的地方全黑)

兰伯特逐像素算法(白色和黑色交界处平滑过渡、照不到的地方全黑)

半兰伯特算法(白色和黑色交界处平滑过渡、照不到的地方不是全黑)

内容参考(侵权立删):

Unity Shader 漫反射(Lambert、Half Lambert) - 知乎

2 具体算法

2.1 兰伯特逐顶点算法
a.本小节使用的unity自带结构体。
struct appdata_full {
    float4 vertex : POSITION;    //顶点坐标
    float4 tangent : TANGENT;    //切线
    float3 normal : NORMAL;      //法线
    float4 texcoord : TEXCOORD0;    //第一纹理坐标
    float4 texcoord1 : TEXCOORD1;//第二纹理坐标
    float4 texcoord2 : TEXCOORD2;//第三纹理坐标
    float4 texcoord3 : TEXCOORD3;//第四纹理坐标
    fixed4 color : COLOR;        //顶点颜色
    UNITY_VERTEX_INPUT_INSTANCE_ID    //ID信息
};
b.兰伯特逐顶点算法公式

Unity | Shader基础知识(第八集:案例<漫反射材质球>)-LMLPHP


公式解释:

屏幕上对应点的颜色 = (光的颜色*物体的颜色)*max(0,该点的法向量*该点的光照方向)



得出结论:我们想计算漫反射的时候屏幕显示什么颜色,我们需要光的颜色物体的颜色该点的法向量(单位向量)该点的光照方向(单位向量)

c.代码实现——兰伯特逐顶点算法

会用到的方法:

UnityObjectToWorldNormal()     //把物体的法线坐标,换算到世界坐标下
normalize()                    //把任何一个向量变成单位向量
dot()                          //点乘
max()                          //上文讲过

_WorldSpaceLightPos0           //世界坐标下的光线坐标
                               //但是要引用#include "Lighting.cginc"才能找到

 实现的代码:

 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            
            //新的引用
            #include "Lighting.cginc"

            //返回结构体        //引用结构体
            appdata_full vert (appdata_full v)
            {    
                //模型顶点坐标转屏幕坐标
                v.vertex = UnityObjectToClipPos(v.vertex);
                
                //获取法线坐标并转换成世界坐标下的法线坐标
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);

                 //世界坐标下的光线坐标  //单位化坐标   //获取世界坐标下的光线坐标
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                
                 //上面的公式
                float3 diffuse =_LightColor0.rgb * v.color.rgb * max(0,dot(worldNormal,worldLight));

                //算出的值给颜色
                v.color = float4(diffuse,1);
                
                return v;
            }


            float4 frag (appdata_full v) : SV_Target
            {    
                //输出颜色    
                return float4(v.color,1) ;
            }
            ENDCG
        }
    }
2.2 代码实现——兰伯特逐像素算法
a.像素和顶点算法的区别
  • 从写法角度来看,顶点算法是在顶点着色器中写的,像素算法是在片元着色器中写的。
  • 从原理角度来说,因为顶点是初始值,经过一系列计算后,数据就会和我们想要的有些偏差。
b.实现代码
Shader "Unlit/005_1"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            appdata_full vert (appdata_full v)
            {   
                v.vertex = UnityObjectToClipPos(v.vertex);

                //把法线转换成世界坐标,传进去
                v.normal = UnityObjectToWorldNormal(v.normal);
                return v;
            }

            float4 frag (appdata_full v) : SV_Target
            {
                //法线世界坐标
                float3 worldNormal = v.normal;
                //光线世界坐标
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算颜色
                float3 diffuse =_LightColor0.rgb * v.color.rgb * max(0,dot(worldNormal,worldLight));
                
                //把颜色传进去
                return float4(diffuse,1) ;
            }
            ENDCG
        }
    }
}
 2.3 代码实现——半兰伯特算法
a.为什么会出现半兰伯特

兰伯特的两个算法,得到的球,在没有光线照射的时候都是黑色的,但玩游戏的时候往往希望,虽然光线无法照到,但我们可以看见。

因为我们实际上并不是需要它看不见,只是需要它要明暗变化,所以我们在环境光的基础上加上兰伯特公式计算出的值,就有了明暗变化。

于是就出现了第三种,半兰伯特。

b.半兰伯特公式
c.代码实现

这里其他代码都没有变,只更改了上图0.5的部分。最后输出前,再加入环境光。

Shader "Unlit/005_2"
{
     SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            appdata_full vert (appdata_full v)
            {
                v.vertex = UnityObjectToClipPos(v.vertex);
                v.normal = UnityObjectToWorldNormal(v.normal);
                return v;
            }

            float4 frag (appdata_full v) : SV_Target
            {
                float3 worldNormal = v.normal;
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                //本节变动
                //获取环境光
                float3 anbient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //计算范围
                float halfLamient = dot(worldNormal,worldLight)*0.5+0.5;

                //计算反射强度
                float3 diffuse =_LightColor0.rgb * v.color.rgb *halfLamient;

                //反射光加光照强度
                float3 c = anbient + diffuse;

                return float4(c,1) ;
            }
            ENDCG
        }
    }
}

四、下集介绍

本集讲了3种计算反射光的方法。

下集讲光照计算,高光反射。(最晚更新日期,1月7日)

12-21 07:54