shader基本构架
2022-01-28 本文已影响0人
Rayson
//首先,为Shader起一个名字;
Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
Properties {
//为了得到并且控制材料的漫反射颜色,我们在语义块中声明一个Color类型的属性,并把它的初始值设为白色。
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader {
//因为顶点/片元着色器的代码需要写在Pass语义块
Pass {
//指明该Pass的光照模式
Tags { "LightMode"="ForwardBase" }
//用来包围CG代码片,
CGPROGRAM
//使用#pragma指令告诉Unity,我们定义的顶点着色器和片元着色器叫什么。本例中名字为vert和frag/
#pragma vertex vert
#pragma fragment frag
//为了使用Unity内置的一些变量,如_LightColor0,还需要包含进Unity的内置文件Lighting.cginc
#include "Lighting.cginc"
//为了在Shader中使用Properties语义块中声明的属性,我们需要定义一个和该属性类型相匹配的变量
fixed4 _Diffuse;
//通过这样的方式,我们就可以得到漫反射公式中需要的参数之一——材质的漫反射属性。
//由于颜色属性的范围在0到1之间,因此我们可以使用fixed精度的变量来存储它。
//然后,我们定义了顶点着色器的输入和输出结构体(输出结构体同时也是片元着色器的输入结构体)
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
//为了访问顶点的法线,我们需要在a2v中定义一个normal变量,并通过使用NORMAL语义
//来告诉Unity要把模型顶点的法线信息存储到noraml变量中,为了把在顶点着色器中计算得到的
//光照颜色传递给片元着色器,我们需要在v2f中定义一个color变量,且并不是必须使用COLOR语义,
//一些资料中会使用TEXCOORD0语义。
//接下来是关键的顶点着色器。由于是实现一个逐顶点的漫反射光照,因此漫反射部分的计算都将在顶点着色器中进行:
v2f vert(a2v v) {
//定义返回值o。
//顶点着色器最基本的任务就是把顶点位置从模型空间转换到裁剪空间中,因此我们需要使用Unity内置的模型*世界*投影矩阵
//UNITY_MATRIX_MVP来完成这样的坐标变换。接下来,我们通过Unity的内置变量UNITY_LIGHTMODEL_AMBIENT得到了环境光部分。
v2f o;
// Transform the vertex from object space to projection space将顶点从对象空间转换为投影空间
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// Get ambient term得到周围的项
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Transform the normal from object space to world space将标准从对象空间转换为世界空间
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
// Get the light direction in world space在世界空间中获得光明的方向
//为了计算漫反射光照我们需要知道4个参数。在前面的步骤中,我们已经知道了
//材质的漫反射颜色_Diffuse以及顶点法线v.normal。我们还需要知道光源的颜色和轻度信息
//(注意:想要得到正确的值需要定定义合适的LightMode标签,_WorldSpaceLightPos0只适合场景中只有一个光源且该光源的类型是平行光)
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term计算扩散项
//在计算法线和光源方向之间的点积时,我们需要选择他们所在的坐标系,只有两者处于同一坐标空间下,
//他们的点积才有意义。这这里我们选择了世界坐标空间。而由a2v得到的顶点法线是位于模型空间下的,
//因此我们首先需要把法线转换到世界空间中。在4.7节中,我们已经知道可以使用顶点变换矩阵的逆转置
//矩阵对法线进行相同的变换,因此我们首先得到模型空间到世界的变换矩阵的逆矩阵——World2Object,
//然后通过调换他在mul函数中的位置,得到和转置矩阵相同的矩阵乘法。由于法线是一个三维矢量,因此我们
//只需要截取_World2Objct的前三行前三列即可。
//在得到了世界空间中的法线和光源方向后,我们需要对它们进行归一化操作。在得到
//他们的点积的结果后,我们需要防止这个结果为负值。为此,我们使用了saturate函数。
//saturate函数是CG提供的一种函数,它的作用是可以把参数截取到[0,1]的范围内。最后,
//再与光源的颜色和强度以及材质的漫反射颜色相乘即可得到最终的漫反射光照部分。
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
}
}
//最后,我们需要把这个Unity Shader的回调shader设置为内置的Diffuse;
FallBack "Diffuse"
}
//至此,我们已经详细解释了逐顶点的漫反射光照的实现。对于细分程度较高的模型,逐顶点光照已经可以得到比较好的光照效果了。
//但对于一些细分程度较低的模型,逐顶点光照就会出现一些视觉问题,例如我们可以在展示图中看到在胶囊体的
//背光面与光面交界处有一些锯齿。为了解决这些问题,我们可以使用逐像素的漫反射光照