1.前言
本文主要讲解以下Unity shader的一些基础内容,以无光照shader为例进行说明,后续进行详细分析与说明。此Shader可以直接复制到Unity中使用(unity2018/2018亲测可用),建议边使用边看如下分析。Shader如下所示:
Shader "LL/Unlit/UnlitShader_basicDismiss"
{
Properties
{
_MainTex("Main Texture",2D)="white"{}
_DissolveTex("Dissolve Texture",2D)="white"{}
_DissolveCutoff("Dissolve Cutoff",Range(0,1))=1
_Dismiss("Model Dismiss",Range(-1,1))=0
}
SubShader
{
pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _DissolveCutoff;
float _Dismiss;
sampler2D _MainTex;
sampler2D _DissolveTex;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
v.vertex.xyz=v.vertex.xyz+v.normal*_Dismiss;
f.vertex=mul(UNITY_MATRIX_MVP,v.vertex);
//f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
float4 dissolveTexColor=tex2D(_DissolveTex,f.uv);
clip(dissolveTexColor.rgb-_DissolveCutoff);
return mainTexColor;
}
ENDCG
}
}
}
名称如下所示,此shader名称为UnlitShader_basicDismiss,通过路径LL/Unlit/即可找到。
Shader "LL/Unlit/UnlitShader_basicDismiss"
{
}
属性为暴露在Inspector面板中的参数,可以人为更改的,基本格式为:属性名称(“显示名称”,变量类型)= 默认值,如下所示。
属性基本格式以_MainTex("Main Texture",2D)="white"{}
为例进行解释:_MainTex为变量名称,括号内双引号内第一个值"Main Texture"为面板上显示内容,第二个值2D为此变量所表示的内容,即为2D纹理。“white”{}则为默认值为白色纹理,写法使用双引号加{},但是对于其他变量则不是,比如_DissolveTex为float类型,则值为1,每一个变量后无需加分号。
Properties
{
_MainTex("Main Texture",2D)="white"{}
_DissolveTex("Dissolve Texture",2D)="white"{}
_DissolveCutoff("Dissolve Cutoff",Range(0,1))=1
_Dismiss("Model Dismiss",Range(-1,1))=0
}
变量类型以及使用类型如下表所示:
属性类型 | 例子 | 说明 |
---|---|---|
Int | _Int(“Int”,Int)=2 | Int类型,默认值为2 |
Float | _Float(“Float”,Float)=2.0 | Float类型,默认值为2 |
Range | _Range(“Range”,Range(0,1))=0.5 | Float类型,默认值为0.5,并锁定范围 |
Color | _Color(“Color”,Color)=(1,1,1,1) | Color类型,默认值为白色 |
Vector | _Vector(“Vector”,Vector)=(1,1,1,1) | Vector类型(四维向量),默认值为白色(1,1,1,1) |
2D | _MainTex(“Main Texture”,2D)=“white”{} | 2D纹理,默认值为白色纹理 |
Cube | _Cube(“SkyBox”,Cube)=“white”{} | Cube纹理,默认值为白色纹理 |
3D | _MainTex(“Main Texture”,3D)=“white”{} | 3D纹理,默认值为白色纹理 |
其中:Range类型是一个slider样式,支持滑动修改和手动直接输入,但是在2018以后无slider样式;Vector类型看名称虽然是向量,其实代表的为四维数据,也可以当作位置输入;Cube则表示此纹理为六面体纹理。
一个unity Shader中可以包含多个SubShader代码块,最少一个,加载时unity选择与目标平台匹配的第一个SubShader运行,如果都不支持,则会使用Fallback语义指定的Shader。一个SubShader中可以包含多个pass。pass中语句定义在 CGPROGRAM ENDCG之间,表示使用CG/HLSL编写的,当然也可以用其他的语言去写。
SubShader
{
pass
{
CGPROGRAM
。。。
ENDCG
}
}
通过#pragma vertex定义顶点着色方法vert(此为方法名称,可以随意修改),同时添加实现。同理通过#pragma fragment定义片元着色器frag,并实现改方法。其中#include "UnityCG.cginc"表示引入此文件中定义的方法、变量以及语义块。此文件获取章节会详细讲解。
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _DissolveCutoff;
float _Dismiss;
sampler2D _MainTex;
sampler2D _DissolveTex;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
v.vertex.xyz=v.vertex.xyz+v.normal*_Dismiss;
f.vertex=mul(UNITY_MATRIX_MVP,v.vertex);
//f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
float4 dissolveTexColor=tex2D(_DissolveTex,f.uv);
clip(dissolveTexColor.rgb-_DissolveCutoff);
return mainTexColor;
}
在上述代码中,定义了两个float以及sampler2D格式变量,这两个变量与属性模块中变量同名,这样变可获取到属性面板变量的值。
在上述代码中定义了两个struct类型a2v与v2f。其中struct中定义变量时格式为:变量类型+变量名称+变量语义,如下所示:
float4 vertex:POSITION;
float4表示变量vertex是一个四维向量或者数组。但是它代表的含义是由变量语义POSITION定义的,表示此变量为顶点坐标。所以变量normal则为此顶点处的法线,uv则为此处的纹理坐标。同理v2f中SV_POSITION则为裁剪空间下的坐标。在vert方法中返回值为v2f类型,则此时vertex表示的裁剪空间下的顶点坐标;而在frag中作为入口参数,vertex则表示片元的坐标位置,同理其uv也为片元的uv坐标。具体可参考上一篇中顶点/片元着色器部分。
变量类型如下表所示
类型 | 释义 |
---|---|
float | 最高位数数据变量 |
half | 中间位数数据变量 |
fixed | 最低位数精度数据变量(由于颜色值为0-1,所以颜色可用fixed4类型) |
变量类型+数字n表示一个n维变量,每一维度的数值为相应的类型,如fixed4,表示4维变量,每一维度类型为fixed。
vert方法最基本的任务是将顶点坐标转换到裁剪空间下(即GPU调用此vert方法对每一个顶点进行处理),所以其入口参数至少包含一个语义为POSITION的顶点坐标,输出值包含语义为SV_POSITION变量,最基本的vert方法如下所示,可根据需要传入其他变量,比如本例还传入了uv和法线。以如下为例通过UnityCG.cginc中的UnityObjectToClipPos方法将顶点坐标转化到裁剪空间中。也可以通过mul(UNITY_MATRIX_MVP,v.vertex)方法实现,即通过MVP矩阵直接成以坐标变量。其实UnityObjectToClipPos就是用的mvp矩阵直接计算。
float4 vert(float4 vertex:POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
本例中在计算裁剪空间下顶点坐标之前对顶点坐标做了处理,即在其法线方向移动_Dismiss距离,如果其值为0,则不移动。
片元着色器最根本的所用是产生一个像素的颜色,所以可以不用入口参数,直接返回一个颜色值,如下所示:
float4 frag():SV_TARGET
{
return fixed4(0.5,1,0,1);
}
本例中相对复杂一些,即根据uv值,通过tex2D(_MainTex,f.uv)方法,对主纹理进行采样,获取相应的颜色值进行返回,并根据_DissolveTex纹理,做了像素的舍弃clip(dissolveTexColor.rgb-_DissolveCutoff);即当clip的值小于0时discard掉此值,即frag在此位置不会返回颜色值,此时对应的位置就不会显示任何颜色,因为颜色rgb分量值范围为0-1,所以对应的_DissolveCutoff值也为0-1。
通过一个基本的Shader将shader的基本语法解释清楚了,后续会不断拓展。推荐学习的地方:添加几个连接:[完整代码版](https://blog.csdn.net/l773575310/article/month/2017/11)和[原文版](https://blog.csdn.net/e295166319/category_9271086.html)