US3S1L5-1——Phong光照模型的逐顶点光照

知识回顾

Phong光照模型公式:

物体表面光照颜色=环境光颜色+兰伯特光照模型所得颜色+Phong式高光反射光照模型所得颜色物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色

其中:

  • 环境光颜色 = UNITY_LIGHTMODEL_AMBIENT​(或者:unity_AmbientSky​、unity_AmbientEquator​、unity_AmbientGround​)
  • 漫反射光颜色 = 兰伯特光照模型 计算得到的颜色
  • 高光反射光颜色 = Phong式高光反射光照模型 计算得到的颜色

利用Phong式光照模型实现光照效果(逐顶点光照)

关键步骤:

  1. 计算兰伯特光照模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    fixed4 _MainColor;      //属性设置的漫反射颜色

    //计算兰伯特光照模型 颜色相关函数(逐顶点)
    //参数:
    // objNormal: 顶点的法线信息
    fixed3 getVertLambertColor(in float3 objNormal)
    {
    float3 normal = UnityObjectToWorldNormal(objNormal); //将模型空间下的法线转换到世界空间下
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //光源0在世界坐标系下的位置标准化,用于和法线计算夹角
    //兰伯特光照模型的实现,这里的颜色计算只取rgb,不考虑透明度的情况
    fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(normal, lightDir));
    return color;
    }
  2. 计算Phong式高光反射光照模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fixed4 _SpecularColor;  //属性设置的材质高光颜色
    float _SpecularNum; //属性设置的光泽度

    //计算Phong高光反射光照模型 颜色相关函数(逐顶点)
    //参数:
    // objVertex: 顶点坐标
    // objNormal: 顶点的法线信息
    fixed3 getVertSpecularColor(in float4 objVertex, in float3 objNormal)
    {
    //计算标准化的观察方向向量
    float3 worldPos = mul(UNITY_MATRIX_M, objVertex); //使用UNITY_MATRIX_M矩阵,将模型空间下的顶点转换到世界空间下
    float3 viewDir = _WorldSpaceCameraPos.xyz - worldPos; //将摄像机坐标和顶点坐标通过相减,得到观察方向向量
    viewDir = normalize(viewDir); //将观察空间向量标准化

    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //得到世界空间下的指向光源方向的标准化向量
    float3 normal = UnityObjectToWorldNormal(objNormal); //得到世界空间下的法线向量
    float3 reflectDir = reflect(-lightDir, normal); //计算反射光线向量,由于光源向量指向光源,而函数需要入射向量,因此需要对光源向量取反
    //Phong高光反射模型的实现,这里的颜色计算只取rgb,不考虑透明度的情况
    fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);
    return color;
    }

完整的Shader代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Shader "TeachShader/Lesson38"
{
Properties
{
_MainColor("MainColor", Color) = (1, 1, 1, 1) //材质的漫反射颜色
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1) //材质高光反射颜色
_SpecularNum("SpecularNum", Range(0, 20)) = 0.5 //光泽度
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

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

struct v2f
{
float4 svPos : SV_POSITION; //裁剪空间下的顶点坐标
fixed3 color : COLOR; //颜色信息
};

fixed4 _MainColor; //属性设置的漫反射颜色
fixed4 _SpecularColor; //属性设置的材质高光颜色
float _SpecularNum; //属性设置的光泽度

//计算兰伯特光照模型 颜色相关函数(逐顶点)
//参数:
// objNormal: 顶点的法线信息
fixed3 getVertLambertColor(in float3 objNormal)
{
float3 normal = UnityObjectToWorldNormal(objNormal); //将模型空间下的法线转换到世界空间下
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //光源0在世界坐标系下的位置标准化,用于和法线计算夹角
//兰伯特光照模型的实现,这里的颜色计算只取rgb,不考虑透明度的情况
fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(normal, lightDir));
return color;
}

//计算Phong高光反射光照模型 颜色相关函数(逐顶点)
//参数:
// objVertex: 顶点坐标
// objNormal: 顶点的法线信息
fixed3 getVertSpecularColor(in float4 objVertex, in float3 objNormal)
{
//计算标准化的观察方向向量
float3 worldPos = mul(UNITY_MATRIX_M, objVertex); //使用UNITY_MATRIX_M矩阵,将模型空间下的顶点转换到世界空间下
float3 viewDir = _WorldSpaceCameraPos.xyz - worldPos; //将摄像机坐标和顶点坐标通过相减,得到观察方向向量
viewDir = normalize(viewDir); //将观察空间向量标准化

float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //得到世界空间下的指向光源方向的标准化向量
float3 normal = UnityObjectToWorldNormal(objNormal); //得到世界空间下的法线向量
float3 reflectDir = reflect(-lightDir, normal); //计算反射光线向量,由于光源向量指向光源,而函数需要入射向量,因此需要对光源向量取反
//Phong高光反射模型的实现,这里的颜色计算只取rgb,不考虑透明度的情况
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);
return color;
}

v2f vert (appdata_base v)
{
v2f v2fData;
v2fData.svPos = UnityObjectToClipPos(v.vertex); //首先将模型空间下的顶点转换到裁剪空间下
//计算Phong式光照模型需要的各种颜色
fixed3 lambertColor = getVertLambertColor(v.normal); //计算漫反射
fixed3 specularColor = getVertSpecularColor(v.vertex, v.normal); //计算高光反射颜色
v2fData.color = UNITY_LIGHTMODEL_AMBIENT + lambertColor + specularColor; //计算Phong式光照模型颜色

return v2fData;
}

fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color.rgb, 1); //因为传递过来的颜色变量不包括透明度,因此这里需要手动指定透明度
}
ENDCG
}
}
}

显示效果(漫反射颜色为红色,高光反射颜色为白色,光泽度为5):

image