欢迎前往个人博客 驽马点滴 和视频空间 哔哩哔哩-《挨踢日志》
5.1 本书使用的软件和环境
介绍 Unity Shader 的学习环境,开发平台、Unity 版本等信息。
5.2 一个最简单的顶点/片元着色器
5.2.1 顶点/片元着色器的基本结构
Shader "Unity Shaders Book/Chapter 5/ Simple Shader" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(v: POSITION): SV_POSITION {
// return mul(UNITY_MATRIX_MVP, v);
// Unity Upgrade NOTE: replace 'mul(UNITY_MATRIX_MVP, *)' with 'UnityObjectToClipPos(*)'
return UnityObjectToClipPos(v);
}
fixed4 frag(): SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}
ENDCG
}
}
}
几个点:
- Properties 属性不是必须的(方便材质面板中友好的用户显示);
- 顶点/片元着色器的 CGPROGRAM 和 ENDCG 写在 Pass 中;
- '#pragma vertex' 和 '#pragma fragment frag' 属于编译指令,告诉 Unity 顶点着色器和片元着色器的代码包含在哪个函数中;
- POSITION 是 Cg/HLSL 中的语义,用来指明顶点坐标;
- SV_POSITION 也是 Cg/HLSL 中的语义,用来指明顶点着色器的输出是裁剪空间中顶点坐标;
- mul(UNITY_MATRIX_MVP, v) 将顶点坐标变换到裁剪空间中(在第四章数学基础中已知),该使用方法,在新版本的 Unity 中,已替换成 UnityObjectToClipPos(v);
- SV_Target 是 HLSL 中的语义,它告诉渲染器把用户的输出颜色存储到一个渲染目标(Render Target)中;
- 片元着色器返回 fixed4 类型的变量,每个分量在 [0, 1] 中取值,[0, 0, 0] 表示黑色,[1, 1, 1] 表示白色;
上述 Shader 中,传入顶点着色器的只有顶点的位置 POSITION,而我们知道,顶点还记录了其他的信息,比如 法线、纹理坐标、颜色等等。为了获得这些的数据,该如何做呢?因此引出下一节的内容,定义一个结构体,给顶点着色器传入更多的数据。
5.2.2 模型数据从哪里来
我们想获取顶点的法线和纹理坐标信息,因此将 Shader 改写如下:
Shader "Unity Shaders Book/Chapter 5/ Simple Shader" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
float4 vert(a2v v): SV_POSITION {
return UnityObjectToClipPos(v.vertex);
}
fixed4 frag(): SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}
ENDCG
}
}
}
其中:
- struct 书写格式需要注意,结构体的定义在末尾需要加分号;
- a2v 的意思是应用程序数据转为顶点数据(application data to vertex data);
- 结构体中,声明 float4 vertex : POSITION ,那么 Unity 会将顶点数据自动填充到 vertex 变量中;声明 float3 normal: NORMAL,那么 Unity 会将法线数据自动填充到 normal 变量中;声明 float4 texcoord: TEXCOORD0, Unity 会将第一个纹理坐标填充到 texcoord 变量中;
5.2.3 顶点着色器和片元着色器之间如何通信
- 定义从顶点着色器输出的数据结构 v2f;
- 片元着色器中,将 v2f 对象作为参数传入;
Shader "Unity Shaders Book/Chapter 5/ Simple Shader" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos: SV_POSITION;
fixed3 color: COLOR0;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
return o;
}
fixed4 frag(v2f i): SV_Target {
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
注意点:
- 顶点着色器输出结构中,必须包含一个变量,其语义为 SV_POSITION ;
- COLOR0 语义中的数据可以由用户自己定义,一般存储颜色(如漫反射颜色或者高光反射颜色);
- 片元着色器中的输入,实际是将顶点着色器中的顶点数据进行插值后得到的结果;
5.2.4 如何使用属性
- 在 Shader 的 Properties 中添加变量 _Color("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0);
- 在 CGPROGRAM 中定义同名变量 fixed4 _Color;
3.在 frag 中使用该变量;
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 5/ Simple Shader" {
Properties {
_Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
return o;
}
fixed4 frag(v2f i): SV_Target {
fixed3 c = i.color;
c *= _Color.rgb;
return fixed4(c, 1.0);
}
ENDCG
}
}
}
5.3 强大的援手:Unity 提供的内置文件和变量
官方网站(http://unity3d.com/cn/get-unity/download/archive)上下载 Unity 的包含文件。
Unity 中常用的包含文件:
UnityCG.cginc ——包含了常用的帮助函数、宏和结构体;
HLSLSupport.cginc ——声明了很多用于跨平台编译的宏和定义,编译 Unity Shader 时,会自动包含;
UnityShaderVariables.cginc——包含了许多内置的全局变量,如 UNITY_MATRIX_MVP 等,编译 Unity Shader 时,会自动包含;
Lighting.cginc ——包含各种内置光照,如果是 Surface Shader ,会自动包含进来。
UnityCG.cginc 中包含的常用结构体:
appdata_base:可用于顶点着色器的输入,包含顶点位置、顶点法线、第一组纹理坐标;
appdata_tan: 可用于顶点着色器的输入,包含顶点位置、顶点法线、顶点切线、第一组纹理坐标;
appdata_full: 可用于顶点着色器的输入,包含顶点位置、顶点法线、顶点切线、四组(或更多组)纹理坐标;
appdata_img: 可用于顶点着色器的输入,包含顶点位置、第一组纹理坐标;
v2f_img: 可用于顶点着色器的输出,包含裁剪空间中的位置、纹理坐标;
一些帮助函数:(略)
5.5 程序员的烦恼:Debug
(略)主要通过输出颜色来调试,比较原始。
欢迎前往个人博客 驽马点滴 和视频空间 哔哩哔哩-《挨踢日志》
网友评论