固定管线着色器(已经淘汰)
顶点片段着色器
表面着色器(最终编译为顶点片段着色器,可以理解为unity封装过的顶点片段着色器)
顶点片段着色器 ——
(1)缺点是不能直接和光照交互
(2)用 CG或HLSL编写,嵌入在 Pass 块中
(3)CG代码必须编写在 CGPROGRAM 和 ENDCG 之间
编译指令 | 说明 |
---|---|
pragma vertex name | 将名称为name的函数编译为顶点着色器(通常命名 vert) |
pragma fragment name | 将名称为name的函数编译为片段着色器(通常命名 frag) |
pragma fragmentoption optime | 添加“optime”到已经编译的opengl片元程序 |
pragma target name | 指定着色器目标(常见值 3.0) |
CPU 阶段 ————> GPU阶段,顶点数据必须以一个结构体的形式提交给CG/HLSL顶点程序。
常用的顶点结构定义在 UnityCG.cginc 文件中,通常够用了,也可以自定义结构体。
常用顶点结构体 | 说明(这个是CPU阶段传过来的顶点数据,作为顶点着色器的输入) |
---|---|
appdata_base | 顶点位置、法线、一个纹理坐标 (vertex、normal、texcoord) |
appdata_tan | 顶点位置、切线、法线、一个纹理坐标 (vertex、tangent、normal、texcoord) |
appdata_full | 顶点位置、切线、法线、两个纹理坐标、颜色 (vertex、tangent、normal、texcoord、texcoord1、color) |
常见变换矩阵 UNITY_MATRIX_MVP
优点是可以处理光照,最终编译为顶点片段着色器,由unity处理光照、阴影、不同的渲染路径
CGPROGRAM—ENDCG 代码放在 SubShader 中,而不是 Pass 块中。将会被编译为多个 Pass
需要懂的 ——
(1)表面着色器编译指令、可选参数
(2)表面函数的输入输出结构体
(3)自定义光照模型
(4)顶点变换函数
(5)最终颜色修改函数
SubShader
{
Tags{"RenderType"="Opaque"} //标签
LOD 200 //渲染设置(这里没有Pass,直接写在外边)
CGPROGRAM //注意这里的包含的东西从哪里开始
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
fixed4 _Color;
struct Input{ float2 uv_MainTex; }; //输入结构体一般要自己定义,必须命名 Input,
//另外不需要指定语义它就能直接被解析,也是挺神奇的
//见过的一般是用上贴图变量名,前面加 uv
void surf(Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
#pragma surface [optionalparams]
//表面函数通常为 void Surf(Input IN, inout SurfaceOutputStandard o){}
//内置光照模型 Lambert、BlinnPhong (自定义光照模型在下边第3点)
//可选参数,很多,详细看吴亚峰296页
部分可选参数 | 说明 |
---|---|
alpha | Alpha混合模式,用于半透明材质 |
alphatest : VariableName | Alpha测试模式,用于透明镂空着色器,镂空值为浮点型变量 |
vertex : VertexFunction | 自定义名为 VertexFunction 的顶点函数 |
finalColor : ColorFunction | 自定义名为 ColorFunction 的最终颜色修改函数 |
decal : add | 附加印花着色器 |
decal : blend | 附加半透明印花着色器 |
Input —— 用于为表面着色器函数输入所需的纹理坐标和其他数据
SurfaceOutput —— 在表面着色器中写入相应的值,用于输出数据
Input 一般是自定义的,并且必须命名为 Input ,貌似没有内置的 Input 结构体
SurfaceOutput 一般是使用内置的,如果自定义,那么必须包含内置的那些参数
与顶点片段着色器中定义结构体不同,表面着色器定义结构体不需要指定语义。。。。
Input结构体 | 说明 |
---|---|
float2 uv_纹理名称 | 第一个纹理坐标 |
float2 uv2_纹理名称 | 第二个纹理坐标 |
float3 viewDir | 视线方向() |
float4 color | 每个顶点颜色的插值 |
float4 screenPos | 屏幕空间的位置。为了获取反射效果,需要包含屏幕坐标 |
float3 worldPos | 世界坐标空间位置 |
float3 worldRefl | 世界空间中的反射向量,但必须表面着色器不写入 o.Normal 参数 |
float3 worldNormal | 世界空间中的法线向量,但必须表面着色器不写入 o.Normal 参数 |
float3 worldRefl; INTERNAL_DATA | 世界坐标反射向量,但必须表面着色器 写入 o.Normal (要基于逐像素法线贴图获取反射向量,请使用 WorldReflectionVector( IN , o.Normal)) |
float3 worldNormal; INTERNAL_DATA | 世界坐标法线向量,但必须表面着色器 写入 o.Normal (要基于逐像素法线贴图获取法线向量,请使用 WorldNormalVector( IN , o.Normal )) |
表面着色器的输出结构体是内置定义好的,用户只需要在表面函数中,为需要的变量赋值就可以了。
struct SurfaceOutput
{
half3 Albedo; //漫反射颜色值 r g b
half3 Normal; //法线坐标
half3 Emission; //自发光颜色
half Specular; //镜面反射系数
half Gloss; //光泽系数
half Alpha; //透明度
};
编写表面着色器的过程, 就是描述一个表面的属性(颜色、法线、反射系数、光泽度、透明度等),然后由光照模型完成光照交互的计算。 |
---|
系统内置 Lambert、BlinnPhong 光照模型。
自定义光照模型,以 Lighting 开头的函数实现
//正向渲染路径、非视线方向相关
half4 Lighting(SurfaceOutput s, half3 lightDir, half atten){ }
//正向渲染路径、视线方向相关
half4 Lighting(SurfaceOutput s, half3 lightDir, half3 viewDir, hlaf atten){ }
//延迟光照路径
half4 Lighting_PrePass(SurfaceOutput s, half4 light){ }
//例如
float4 LightingPhong(SurfaceOutput s, float3 lightDir, half3 viewDir, half atten)
{
float4 c;
//一系列计算
c.rgb = s.Albedo *...; //用到表面属性(先执行了表面函数,再将表面属性传入光照模型函数)
c.a = s.Alpha;
return c; //返回最终光照颜色
}
顶点变换函数:修改顶点着色器中的输入顶点数据、为表面着色器函数传递顶点数据。
可用于程序性动画、沿法线挤压等
在表面着色器的编译指令中 指定 vertex : Name
#pragma surface surf Lambert vertex:vert
顶点函数可以有多种形式
void Name(inout appdata_full v){} //用于只修改顶点着色器中的输入顶点数据
half4 Name(inout appdata_full v , out Input o){} //用于修改顶点着色器中的输入数据,
//并且为表面着色器函数传递数据
// appdata_full 是顶点着色器的输入结构体
// Input 是表面着色器的输入结构体
例如
void vert(inout appdata_full v)
{
v.vertex.xyz += v.normal * _Amount; //通过法线挤压实现充气效果
}
最终颜色修改函数,用于修改表面着色器的最终颜色。可用于绘制物体表面的最终颜色。
(应该是先执行了表面函数,然后才执行最终颜色修改函数,至于光照模型函数的顺序,则不清楚。。。)
表面着色器的编译指令
#pragma surface surf Lambert finalcolor:myColorFunction
最终颜色修改函数的声明如下
void Name(Input IN, SurfaceOutput o, inout fixed4 color){}
// Input结构体,用于顶点变换函数为最终颜色修改函数传递数据(涉及顶点变换函数。。。)
// SurfaceOutput,用于为最终颜色修改函数传递数据(传递物体表面的参数数据)
// inout 类型的 color ,为最终颜色修改函数输出最终颜色
例如
fixe4 _ColorTint;
void myFinalColor(Input IN,SurfaceOutput o,inout fixed4 color)
{
color *= _ColorTint; //通过调色数值修改最终颜色
}