赞
踩
对于不透明物体,开发者可以关闭混合(Blend)操作。这样片元着色器计算得到的颜色值就会之间覆盖掉颜色缓冲区中的像素值。但对于半透明物体,就需要混合操作来让这个物体看起来是透明的。
无光照->顶点片元
图像效果->特效
变体集->库文件
R:x轴
G:y轴
B:z轴
NORMAL
UnityObjectToWorldNormal(v.normal)
// 计算视图法线
// normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
// UNITY_MATRIX_IT_MV 模型逆转置 * 视图矩阵。
// 从模型空间 -> 逆转置到非模型空间 -> 视图空间
COMPUTE_VIEW_NORMAL
该坐标系中,坐标系方向的(x,y)为摄像机的(x,y),而z轴朝向摄像机,摄像机的远处为z轴无穷小处。
TransformViewToProjection(COMPUTE_VIEW_NORMAL)
该坐标系中,坐标系方向的x向摄像机右,y向摄像机下,而z轴只是简单的深度,因此没有意义。
Shader "Liluo/01 myShader"{ // 这里是shader名字 Properties{ // 公有属性 // 属性名,显示名,数据类型 float4 _Color("tintColor", Color) = (1, 1, 1, 1) } // 子着色器,可以有很多。如果第一个支持就运行第一个,不支持就往下来 SubShader{ // 标签可选项 Tags { "RenderType" = "Opaque" } // 可选的 // 裁剪 Cull off // 使用其他shader的pass通道 UsePass "Unlit/011-卡通着色/Outline" // 至少有一个Pass // 在这里编写shader代码 Pass{ // 使用CG语言编写shader代码 CGPROGRAM // 四个值的float类型 float4 _color; ENDCG } } Fallback "VertexLit" // 后备方案,所有的都不支持时候,会选择该方案 }
Properties 块:属性块
SubShader 块:子着色器,可以有很多。如果第一个支持就运行第一个,不支持就往下来。
Fallback 块:所有的都不支持时,会选择该方案
// 说是int,其实是float类型 _Int("Int", Int) = 2 // float _Float("float", Float) = 1.5 // 范围数据 (float) float _Range("Range", Range(0.0, 2.0)) = 0.5 // 颜色 float4 _Color("Color", Color) = (1, 1, 1, 1) // float4 _Vector("Vector", Vector) = (1, 2, 3, 4) // 图片类型 sampler2D类型,也可写作sampler2D_float类型提高精度,以防止在移动端贴图模糊 // "white" 普通图像 // "bump" 法线纹理 _2D("Texture", 2D) = "white"{} // 立方体类型(立方体贴图文件,常见于天空盒) samplerCube类型 _Cube("Cube", Cube) = "white"{} // 3D图片类型 sampler3D类型 _3D("3D", 3D) = "black"{}
// 访问四个值的
float4.xyzw = float4.rgba
// 访问任意三个值
float4.xyz = float4.rgb
float4.xz = float4.rb
float4.x = float4.r
fixed3 f3 = fixed3(1, 1, 1);
fixed4 f4 = fixed4(1, f3);
项 | 描述 | 可选值 |
---|---|---|
Queue | 渲染顺序(渲染器队列) | Transparent |
RenderType | 着色器替换功能 | Opaque |
DisableBatching | 是否进行合批 | Bool |
ForceNoShadowCasting | 是否投射阴影 | Bool |
IgnoreProjector | 是否受投影影响 | Bool |
CanUseSpriteAtlas | 是否用于图片的Shader | Bool |
PreviewType | 用于Shader面板预览的类型 | Plane |
可在 Pass 通道内编写,前提是去掉外部的 Render,这时每个 Pass 将使用各自的 Render。否则将使用外部的 Render(只要外部的 Render 存在)。
Cull:裁剪(默认:back)
参数 | 描述 |
---|---|
off | 两面都渲染 |
back | 切掉背面,只渲染正面 |
front | 切掉正面,只渲染背面 |
ZTest:深度测试(默认:LEqual)
参数 | 描述 |
---|---|
Always | 总是 |
Less | 小于 |
Greater | 大于 |
LEqual | 小于等于 |
GEqual | 大于等于 |
Equal | 等于 |
NotEqual | 不等于 |
ZWrite:深度写入(默认:On)
On 开启
Off 关闭
Blend:混合模式(默认:Off)
SrcFactor DstFactor :配置并启动混合。产生的颜色被乘以SrcFactor. 已存在于屏幕的颜色乘以DstFactor,并且两者将被叠加在一起。
LOD:着色器细节级别(默认:无穷大)
Name :声明名称
名称用于允许其他通道去use使用该通道
例:Name “Default”
额外的Tags可选项
项 | 描述 | 可选值 |
---|---|---|
LightMode | 定义该Pass通道在Unity渲染流水中的角色 | ForwardBase |
RequireOptions | 声明它只在一些额外的条件被满足的情况下被渲染 | SoftVegetation |
ColorMask | 是否写入颜色通道 | 0 |
使用其他shader的Pass通道
UsePass "Unlit/011-卡通着色/Outline"
CGPROGRAM // 声明顶点着色器函数名称 vert #pragma vertex vert // 声明片元着色器函数名称 frag #pragma fragment frag float4 vert (float4 v:POSITION):SV_POSITION { return UnityObjectToClipPos(v); } fixed4 frag () : SV_TARGET { return fixed4(1, 1, 1, 1); } ENDCG
编译注释。用于声明某种着色器使用的函数。
着色器 | 描述 |
---|---|
vertex | 顶点着色器(模型空间->齐次裁剪空间) |
fragment | 片元着色器(输出是一个或者多个颜色值) |
target | 目标Shader Target |
注:
类型 | 描述 |
---|---|
POSITION | 顶点信息 [ f4 ] |
NORMAL | 顶点法线 [ f3 ] |
TANGENT | 顶点切线 [ f4 ] |
TEXCOORD | 纹理坐标(包含颜色值的二维数组【位图】)[n套] [ f2 | f4 ] |
COLOR | 顶点颜色 [ fx4 ] [n套] |
SV_POSITION | 裁剪空间的顶点坐标 [ f4 ] |
SV_TARGET | 目标返回值(输出将会储存到渲染目标(RenderTarget)缓存中) [ f4 ] |
注:
函数 | 功能描述 |
---|---|
abs(x) | 返回输入参数的绝对值 |
acos(x) | 反余切函数,输入参数范围为[-1,1], 返回[0,π]区间的角度值 |
all(x) | 如果输入参数均不为0,则返回ture; 否则返回flase。&&运算 |
any(x) | 输入参数只要有其中一个不为0,则返回true。 |
asin(x) | 反正弦函数,输入参数取值区间为,返回角度值范围为, |
atan(x) | 反正切函数,返回角度值范围为 |
atan2(y,x) | 计算y/x的反正切值。实际上和atan(x)函数功能完全一样,至少输入参数不同。atan(x) = atan2(x, float(1))。 |
ceil(x) | 对输入参数向上取整。例如: ceil(float(1.3)) ,其返回值为2.0 |
clamp(x,a,b) | 如果x值小于a,则返回a;如果x值大于b,返回b;否则,返回x。 |
cos(x) | 返回弧度x的余弦值。返回值范围为 |
cosh(x) | 双曲余弦(hyperbolic cosine)函数,计算x的双曲余弦值。 |
cross(A,B) | 返回两个三元向量的叉积(cross product)。注意,输入参数必须是三元向量! |
degrees(x) | 输入参数为弧度值(radians),函数将其转换为角度值(degrees) |
determinant(m) | 计算矩阵的行列式因子。 |
dot(A,B) | 返回A和B的点积(dot product)。参数A和B可以是标量,也可以是向量(输入参数方面,点积和叉积函数有很大不同)。 |
exp(x) | 计算的值,e=2.71828182845904523536 |
exp2(x) | 计算的值 |
floor(x) | 对输入参数向下取整。例如floor(float(1.3))返回的值为1.0;但是floor(float(-1.3))返回的值为-2.0。该函数与ceil(x)函数相对应。 |
fmod(x,y) | 返回x/y的余数。如果y为0,结果不可预料。 |
frac(x) | 返回标量或矢量的小数 |
frexp(x, out i) | 将浮点数x分解为尾数和指数,即, 返回m,并将指数存入i中;如果x为0,则尾数和指数都返回0 |
isfinite(x) | 判断标量或者向量中的每个数据是否是有限数,如果是返回true;否则返回false; |
isinf(x) | 判断标量或者向量中的每个数据是否是无限,如果是返回true;否则返回false; |
isnan(x) | 判断标量或者向量中的每个数据是否是非数据(not-a-number NaN),如果是返回true;否则返回false; |
ldexp(x, n) | 计算的值 |
lerp(a, b, f) | 计算或者的值。即在下限a和上限b之间进行插值,f表示权值。注意,如果a和b是向量,则权值f必须是标量或者等长的向量。 |
lit(NdotL, NdotH, m) | N表示法向量;L表示入射光向量;H表示半角向量;m表示高光系数。 函数计算环境光、散射光、镜面光的贡献,返回的4元向量。 X位表示环境光的贡献,总是1.0; Y位代表散射光的贡献,如果 ,则为0;否则为 Z位代表镜面光的贡献,如果 或者,则位0;否则为;W位始终位1.0 |
log(x) | 计算的值,x必须大于0 |
log2(x) | 计算的值,x必须大于0 |
log10(x) | 计算的值,x必须大于0 |
max(a, b) | 比较两个标量或等长向量元素,返回最大值。 |
min(a,b) | 比较两个标量或等长向量元素,返回最小值。 |
modf(x, out ip) | 把x分解成整数和分数两部分,每部分都和x有着相同的符号,整数部分被保存在ip中,分数部分由函数返回 |
mul(M, N) | 矩阵M和矩阵N的积 |
mul(M, v) | 矩阵M和列向量v的积 |
mul(v, M) | 行向量v和矩阵M的积 |
noise(x) | 根据它的参数类型,这个函数可以是一元、二元或三元噪音函数。返回的值在0和1之间,并且通常与给定的输入值一样 |
radians(x) | 函数将角度值转换为弧度值 |
round(x) | 返回四舍五入值。 |
rsqrt(x) | x的平方根的倒数,x必须大于0 |
saturate(x) | 把x限制到[0,1]之间 |
sign(x) | 如果则返回1;否则返回0 |
sin(x) | 输入参数为弧度,计算正弦值,返回值范围 为[-1,1] |
sincos(float x, out s, out c) | 该函数是同时计算x的sin值和cos值,其中s=sin(x),c=cos(x)。该函数用于“同时需要计算sin值和cos值的情况”,比分别运算要快很多! |
sinh(x) | 计算x的双曲正弦 |
smoothstep(min, max, x) | 值x位于min、max区间中。如果x=min,返回0;如果x=max,返回1;如果x在两者之间,按照下列公式返回数据: |
step(a, x) | 如果x<a返回0;如果x>=a返回1 |
sqrt(x) | 求x的平方根,,x必须大于0 |
tan(x) | 计算x正切值 |
tanh(x) | 计算x的双曲线切线 |
transpose(M) | 矩阵M的转置矩阵 |
几何函数(只对三元向量有效)
函数 | 功能描述 |
---|---|
distance(pt1, pt2) | 两点之间的欧几里德距离(Euclidean distance) |
faceforward(N,I,Ng) | 如果,返回N;否则返回-N。 |
length(v) | 返回一个向量的模(长度),即sqrt(dot(v,v)) |
normalize(v) | 返回v向量的单位向量 |
reflect(I, N) | 根据入射光纤方向I和表面法向量N计算反射向量,仅对三元向量有效(入射方向为从目标朝向该对象) |
refract(I,N,eta) | 根据入射光线方向I,表面法向量N和折射相对系数eta,计算折射向量。如果对给定的eta,I和N之间的角度太大,返回(0,0,0)。 |
纹理映射函数函数
函数 | 功能描述 |
---|---|
tex1D(sampler1D tex, float s) | 一维纹理查询 |
tex1D(sampler1D tex, float s, float dsdx, float dsdy) | 使用导数值(derivatives)查询一维纹理 |
Tex1D(sampler1D tex, float2 sz) | 一维纹理查询,并进行深度值比较 |
Tex1D(sampler1D tex, float2 sz, float dsdx,float dsdy) | 使用导数值(derivatives)查询一维纹理, 并进行深度值比较 |
Tex1Dproj(sampler1D tex, float2 sq) | 一维投影纹理查询 |
Tex1Dproj(sampler1D tex, float3 szq) | 一维投影纹理查询,并比较深度值 |
Tex2D(sampler2D tex, float2 s) | 二维纹理查询 |
Tex2D(sampler2D tex, float2 s, float2 dsdx, float2 dsdy) | 使用导数值(derivatives)查询二维纹理 |
Tex2D(sampler2D tex, float3 sz) | 二维纹理查询,并进行深度值比较 |
Tex2D(sampler2D tex, float3 sz, float2 dsdx,float2 dsdy) | 使用导数值(derivatives)查询二维纹理,并进行深度值比较 |
Tex2Dproj(sampler2D tex, float3 sq) | 二维投影纹理查询 |
Tex2Dproj(sampler2D tex, float4 szq) | 二维投影纹理查询,并进行深度值比较 |
tex2Dlod(textureMap, float4(texCoord.xy, 0, lod)) | 使用 mipmap 对 2D 纹理进行采样。 mipmap LOD 在 t.w 中指定,越小细节采样越清晰,默认写0。 |
texRECT(samplerRECT tex, float2 s) | 二维非投影矩形纹理查询(OpenGL独有) |
texRECT (samplerRECT tex, float3 sz, float2 dsdx,float2 dsdy) | 二维非投影使用导数的矩形纹理查询(OpenGL独有) |
texRECT (samplerRECT tex, float3 sz) | 二维非投影深度比较矩形纹理查询(OpenGL独有) |
texRECT (samplerRECT tex, float3 sz, float2 dsdx,float2 dsdy) | 二维非投影深度比较并使用导数的矩形纹理查询(OpenGL独有) |
texRECT proj(samplerRECT tex, float3 sq) | 二维投影矩形纹理查询(OpenGL独有) |
texRECT proj(samplerRECT tex, float3 szq) | 二维投影矩形纹理深度比较查询(OpenGL独有) |
Tex3D(sampler3D tex, float s) | 三维纹理查询 |
Tex3D(sampler3D tex, float3 s, float3 dsdx, float3 dsdy) | 结合导数值(derivatives)查询三维纹理 |
Tex3Dproj(sampler3D tex, float4 szq) | 查询三维投影纹理,并进行深度值比较 |
texCUBE(samplerCUBE tex, float3 s) | 查询立方体纹理 |
texCUBE (samplerCUBE tex, float3 s, float3 dsdx, float3 dsdy) | 结合导数值(derivatives)查询立方体纹理 |
texCUBEproj (samplerCUBE tex, float4 sq) | 查询投影立方体纹理 |
类型 | 描述 |
---|---|
UnityObjectToClipPos(float4) | 将点从对象空间变换到裁剪空间 |
clip(any) | 判断参数的xyzw值,其中有一个小于1,便舍弃该片元 |
UnityCG.cginc
类型 | 描述 |
---|---|
UnityViewToClipPos(float3) | 将点从视图空间变换到裁剪空间 |
UnityObjectToViewPos(float3) | 将点从对象空间变换到摄像机空间 |
UnityObjectToWorldNormal(float3) | 将本对象法线从对象空间变换到世界空间 [单位化] |
UnityObjectToWorldDir(float3) | 将本对象方向从对象空间变换到世界空间 [单位化] |
UnityWorldSpaceViewDir(float3) | 根据世界空间位置计算到摄像机的向量 |
UnityWorldSpaceLightDir(float3) | 根据世界空间位置计算到光的向量 |
WorldSpaceViewDir(float3) | 根据对象空间位置计算到摄像机的向量 |
WorldSpaceLightDir(float3) | 根据对象空间位置计算到光的向量 |
TRANSFORM_TEX(float4[texcoord], name[str]) | 按比例/偏移特性变换2D UV |
ObjSpaceLightDir(float4) | 计算在对象空间中光的向量 |
ObjSpaceViewDir(float4) | 计算在对象空间中视图的向量 |
UnpackNormal(fixed4) | 根据不同平台解包法线贴图信息,获取法线 |
UnpackScaleNormal(half4, half[scale]) | 根据不同平台自动对法线贴图使用正确的解码,并缩放法线 |
名称 | 数据类型 | 描述 |
---|---|---|
_WorldSpaceLightPos0 | float4 | 方向光:(世界空间方向,0),其他光源:(世界空间位置,1) |
UNITY_LIGHTMODEL_AMBIENT | fixed4 | 环境光照颜色(梯度环境情况下的天空颜色)(旧版变量) |
_LightColor0 | fixed4 | 光源颜色(UnityLightingCommon.cginc) |
_WorldSpaceCameraPos | float3 | 摄像机的世界空间位置。 |
unity_ObjectToWorld | float4x4 | 当前模型矩阵。用它乘以对象上的点,可得到对应世界空间的位置 |
_Time | float4 | 自关卡加载以来的时间 (t/20, t, t*2, t*3),用于将着色器中的内容动画化。 |
_Sin[Cos]Time | float4 | 时间正【余】弦:(t/8, t/4, t/2, t) |
更多变量:https://docs.unity3d.com/cn/current/Manual/SL-UnityShaderVariables.html
// application to vert
struct a2v
{
float4 v:POSITION;
float3 m:NORMAL;
// 纹理坐标(用模型的第一套uv填充TEXCOORD)
float4 texcoord:TEXCOORD0;
};
float4 vert (a2v v):SV_POSITION
{
return UnityObjectToClipPos(v.v);
}
// vert to frag struct v2f { // 裁剪空间的位置信息 float4 pos:SV_POSITION; // 颜色信息 float3 color:COLOR0; }; v2f vert (a2v v) { v2f o; // 保存顶点信息 o.pos = UnityObjectToClipPos(v.vertex); // 将法线方向转变为颜色表现。(-1~1)转变为(0,1)区间,因为法线的向量可能为负数。 o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); return o; } // 结构体一经定义便可在其他函数中作为参数调用,shader会自动将定义的结构体作为参数传入。 fixed4 frag (v2f i) : SV_TARGET { // 颜色信息 return fixed4(i.color, 1); }
语句 | 描述 |
---|---|
discard | 舍弃片元 |
clip(any) | 判断参数的xyzw值,其中有一个小于1,便舍弃该片元 |
Unity会自动导入一些cginc基础库。若想引入其他库,需在CGPROGRAM下使用include注释
CGPROGRAM
#include "UnityCG.cginc"
ENDCG
这样以来,我们便可使用该库内的函数或结构体。如UnityCG.cginc下的appdata_base:
struct appdata_base { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct appdata_tan { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct appdata_full { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; fixed4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID };
在顶点着色器中作为入参使用
float4 vert (appdata_base v)
{
return UnityObjectToClipPos(v.vertex);
}
窗口->分析->帧调试器
Legacy Shaders/Particles/Additive
实现部分叠加效果,并非完全叠加,没有Addtive明亮,颜色更加接近贴图自身的颜色。 半透渲染效果。Sprites/Diffuse
使图像接受全局光前6种类型为常用类型,string类型几乎不使用。此外,Cg还提供了内置的向量数据类型 (built-in vector data types) ,内置的向量数据类型基于基础数据类型。 例如: float4, 表示 float 类型的 4 元向量; bool4, 表示 bool类型 4 元向量。
数组类型在Cg中的作用:作为函数的形参,用于大量数据的传递,例如:顶点参数数组、光照参数数据等。
float a[10];//声明了一个数组,包含 10 个 float 类型数据
float a[4] = {1.0, 2.0, 3.0, 4.0}; //初始化一个数组
int length = a.length;//获取数组长度
float b[2][3] = {{0.0, 0.0, 0.0},{1.0, 1.0, 1.0}};
int length1 = b.length; // length1 值为 2
int length2 = b[0].length; // length2 值为 3
结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,接下来是一个大括号,并以分号结尾(不要忘了分号) 。大括号中是结构体的定义,分为两大类:成员变量和成员函数。
注意:不要忘记结尾的分号!!!
struct v2f
{
float4 pos:SV_POSITION;
float3 color:COLOR0;
};
直接发光进入摄像机(看起来更亮)。
为简化模型,环境光不适用原本的完全间接反射光,而仅包括全局环境光。
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
兰伯特漫反射:
Cdiffuse = C光(color)C物(color)( n(法线向量) · l(光源向量) )
注:原本应该是| n | | l | cosθ,但同时 | n | | l | cosθ = n · l(点乘)
shader示例:
struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; }; // 顶点着色器计算此漫发射 v2f vert (appdata_base v) { v2f o; // 保模型观察矩阵全部投影到裁剪空间 o.vertex = UnityObjectToClipPos(v.vertex); // 将法线转为世界坐标下的向量,因为只有同坐标系下的向量才能计算,而光源在世界坐标下,法线在模型坐标下。 fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 世界方向光 fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); // 计算漫反射 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight)); // 添加环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; o.color = diffuse + ambient; return o; }
// 片元着色器逐像素计算此漫发射 struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldLightDir, i.worldNormal)); fixed3 color = ambient + diffuse; return fixed4(color, 1); }
Phong高光反射:
Cdiffuse = C光(color)C物(color)( (2 (n(法线向量) · l(光源向量)) n(法线向量) - l(光源向量) ) · v(光源向量) )mgless
// 顶点着色器计算高光 v2fvert (appdata_base v) { v2f o; // 保模型观察矩阵全部投影到裁剪空间 o.vertex = UnityObjectToClipPos(v.vertex); // 将法线转为世界坐标下的向量,因为只有同坐标系下的向量才能计算,而光源在世界坐标下,法线在模型坐标下。 fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 世界方向光 fixed3 worldLight = WorldSpaceLightDir(v.vertex); // 计算漫反射 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight)); // 添加环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 高光反射单位向量(r向量) fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal)); // 从物体到摄像机的单位向量(v向量) fixed3 viewDir = normalize(WorldSpaceViewDir(v.vertex)); // 计算高光颜色 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss); o.color = diffuse + ambient + specular; return o; }
// 片元着色器逐像素计算高光 struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); // 对应世界空间的位置 o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldLightDir, i.worldNormal)); // 高光反射单位向量(r向量) fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal)); // 从物体到摄像机的单位向量(v向量) fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 计算高光颜色 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss); fixed3 color = ambient + diffuse + specular; return fixed4(color, 1); }
Blinn-Phong高光反射:
CBlinn-Phong-diffuse = (C光(color)C物(color)) (n(法线向量) · h(光源和摄像机向量的平均单位向量))mgless
// 片元着色器逐像素计算高光 struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldLightDir, i.worldNormal)); // 从物体到摄像机的单位向量 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 光源和摄像机向量的平均单位向量(h向量) fixed3 halfDir = normalize(worldLightDir + viewDir); // 计算高光颜色 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal, halfDir)), _Gloss); fixed3 color = ambient + diffuse + specular; return fixed4(color, 1); }
_MainTex ("MainTex", 2D) = "white" {}
即2D类型变量可用于赋值一个2D纹理材质。sampler2D _MainTex;
来获取到这个纹理材质。私有变量+_ST
的fixed4变量,作为纹理材质的纹理信息(Tiling与Offset)。TEXCOORD0
变量会储存有该顶点的纹理坐标信息。tex2D(sampler2D tex, float2 s)
来获得对应图像在指纹理坐标的颜色。// 纹理材质 sampler2D _MainTex; // 自动赋值对应的纹理信息(Tiling与Offset) float4 _MainTex_ST; struct v2f { float4 vertex: SV_POSITION; float2 uv: TEXCOORD0; }; // 顶点着色器计算纹理坐标映射 v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // v.texcoord.xy 为指定点的纹理坐标,与材质xy相乘模拟缩放,与zw相加模拟偏移 // o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } // 片元着色器渲染颜色 fixed4 frag (v2f i) : SV_Target { // 查询对应图像在指纹理坐标的颜色 fixed3 albedo = tex2D(_MainTex, i.uv).rgb; return fixed4(albedo, 1); }
主要对图片的两个部分进行设置
参数 | 描述 |
---|---|
重复 | 重复拼接贴图 |
钳制 | 贴图只渲染一次,超出的部分重复渲染最后一像素 |
镜像 | 重复拼接贴图,但每次重复都和上次的y轴反向 |
每轴 | 自定义xy的上述选项 |
过滤模式
简单来说,滤波越高,透视远处的部分越清晰。
法线贴图:从灰度创建
转化为高度映射。
通过改变法线的值(长度),获得不同的凹凸效果,也就是(r,g,b,a)->1[白]: 高,0[黑]: 低。
纹理中的法线,分量范围在(-1,1)中,而贴图的RGB在(0,1)内。
因此需转换公式:
pixel = n * 0.5 + 0.5
n = 2 * pixel -1。
一个绝对的法线信息集:意味着使用它时不能换模型。(因为模型之间的法线不可能相同,而绝对的法线信息集与原本的法线信息每个点都一一对应)
n = (0,1,0)-> 纹理RGB(0.5,1,0.5)
n = (0,-1,0) -> 纹理RGB(0.5,0,0.5)
n = (1,1,0) -> 纹理RGB(1,1,0.5)
切线空间:模型某个顶点的切面作为(x,y)轴,向上的垂直法线作为z轴。
该空间下:我们定义法线为n,切线(x轴)为t,副切线(y轴)为b。,则:
n = b × t
b = t × n
在未发生变动时,切线空间下新的法线:z轴(0,0,1) => RGB(0.5,0.5,1),大部分法线是不变的,Z轴总是正方向,可以只存储XY轴,推到得到Z轴。
世界空间->切线空间法线纹理映射
TRANSFORM_TEX(v.texcoord, _BumpMap);
// 等效于:
// cross(v.normal, v.tangent.xyz * v.tangent.w)
cross(v.normal, v.tangent.xyz) * v.tangent.w;
float3x3(v.tangent.xyz, binormal, v.normal);
mul(rotation, ObjSpaceLightDir(v.vertex));
mul(rotation, ObjSpaceViewDir(v.vertex));
tex2D(_BumpMap, i.normaluv);
// 没有设置成 normal map
// fixed3 tangentNormal;
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// 设置成 normal map,使用系统函数UnpackNormal:根据不同平台解包法线贴图信息,获取法线
fixed3 tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
参考示例:
fixed4 _Diffuse; fixed4 _Specular; float _Gloss; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; fixed4 _BumpMap_ST; float _BumpScale; // 片元着色器逐像素计算此漫发射 struct v2f { float4 vertex: SV_POSITION; fixed3 lightDir: TEXCOORD0; float3 viewDir: TEXCOORD1; float2 uv: TEXCOORD2; float2 normaluv : TEXCOORD3; }; v2f vert (appdata_tan v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.normaluv = TRANSFORM_TEX(v.texcoord, _BumpMap); // 求副切线向量 // float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w; // float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal); // 系统已经宏定义求副切线向量以及旋转矩阵的此行为 TANGENT_SPACE_ROTATION; // 求切线空间光源方向及视角方向 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 tangentLightDir = normalize(i.lightDir); fixed3 tangentViewDir = normalize(i.viewDir); // 法线贴图 fixed4 packedNormal = tex2D(_BumpMap, i.normaluv); // 法线纹理没有设置成 normal map // fixed3 tangentNormal; // tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale; // tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); // 法线纹理设置成 normal map fixed3 tangentNormal = UnpackNormal(packedNormal); tangentNormal.xy *= _BumpScale; // 环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 albedo = tex2D(_MainTex, i.uv).rgb; // 漫反射 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * (saturate(dot(tangentLightDir, tangentNormal)) * 0.5 + 0.5); fixed3 reflectDir = normalize(reflect(-tangentLightDir, tangentNormal)); // 高光反射 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, tangentViewDir)), _Gloss); fixed3 color = ambient + diffuse + specular; return fixed4(color, 1); }
切线空间->世界空间法线纹理映射
TRANSFORM_TEX(v.texcoord, _BumpMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
TRANSFORM_TEX(v.texcoord, _BumpMap);
o.tanToWorld0 = float3(worldTangent.x, worldBinormal.x, worldNormal.x);
o.tanToWorld1 = float3(worldTangent.y, worldBinormal.y, worldNormal.y);
o.tanToWorld2 = float3(worldTangent.z, worldBinormal.z, worldNormal.z);
float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
fixed3tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
fixed3 worldNormal = normalize(float3(dot(i.tanToWorld0, tangentNormal), dot(i.tanToWorld1, tangentNormal), dot(i.tanToWorld2, tangentNormal)));
fixed3 worldNormal = normalize(mul(float3x3(i.tanToWorld0, i.tanToWorld1, i.tanToWorld2), tangentNormal));
参考示例:
fixed4 _Diffuse; fixed4 _Specular; float _Gloss; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; fixed4 _BumpMap_ST; float _BumpScale; // 片元着色器逐像素计算此漫发射 struct v2f { float4 vertex: SV_POSITION; float4 uv: TEXCOORD0; float3 tanToWorld0: TEXCOORD1; float3 tanToWorld1: TEXCOORD2; float3 tanToWorld2: TEXCOORD3; float3 worldPos: TEXCOORD4; }; v2f vert (appdata_tan v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // 图像信息 o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex); // 法线纹理信息 o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap); // 计算世界坐标下的顶点位置,法线,切线,副法线 float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; float3 worldNormal = UnityObjectToWorldNormal(v.normal); float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; o.worldPos = worldPos; o.tanToWorld0 = float3(worldTangent.x, worldBinormal.x, worldNormal.x); o.tanToWorld1 = float3(worldTangent.y, worldBinormal.y, worldNormal.y); o.tanToWorld2 = float3(worldTangent.z, worldBinormal.z, worldNormal.z); return o; } fixed4 frag (v2f i) : SV_Target { // 世界坐标 float3 worldPos = i.worldPos; // 计算世界空间下的光照和视角 float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos)); float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // 获得法线纹理 fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw); fixed3 tangentNormal = UnpackNormal(packedNormal); tangentNormal.xy *= _BumpScale; // 切线空间法线转换到世界坐标(由于矩阵乘法性质,以下两个式子等价) // fixed3 worldNormal = normalize(float3(dot(i.tanToWorld0, tangentNormal), dot(i.tanToWorld1, tangentNormal), dot(i.tanToWorld2, tangentNormal))); fixed3 worldNormal = normalize(mul(float3x3(i.tanToWorld0, i.tanToWorld1, i.tanToWorld2), tangentNormal)); // 环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb; // 漫反射 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * (saturate(dot(lightDir, worldNormal)) * 0.5 + 0.5); fixed3 reflectDir = normalize(reflect(-lightDir, worldNormal)); // 高光反射 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss); fixed3 color = ambient + diffuse + specular; return fixed4(color, 1); }
让影印部的颜色值根据图片约束变化。
fixed halfLambert = saturate(dot(worldLightDir, i.worldNormal)) * 0.99 + 0.005;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * tex2D(_RampTex, fixed2(halfLambert, 0.5)).rgb;
示例:
struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed halfLambert = saturate(dot(worldLightDir, i.worldNormal)) * 0.99 + 0.005; fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * tex2D(_RampTex, fixed2(halfLambert, 0.5)).rgb; fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal)); fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss); fixed3 color = diffuse + specular + ambient; return fixed4(color, 1); }
让打出的光在对象上形成图像,效果有点类似于法线贴图,但本质是遮罩,也就无法形成法线纹理那样:在光源或摄像机移动时,反光点也随之移动的立体感。
TRANSFORM_TEX(v.texcoord, _SpecularMask);
tex2D(_SpecularMask, i.maskuv).r * _SpecularScale;
示例:
fixed4 _Diffuse; fixed4 _Specular; sampler2D _MainTex; float4 _MainTex_ST; // 高光范围 float _Gloss; // 高光强度 float _SpecularScale; // 高光遮罩 sampler2D _SpecularMask; fixed4 _SpecularMask_ST; struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; float2 uv: TEXCOORD2; float2 maskuv : TEXCOORD3; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.maskuv = TRANSFORM_TEX(v.texcoord, _SpecularMask); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 albedo = tex2D(_MainTex, i.uv).rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * (saturate(dot(worldLightDir, i.worldNormal)) * 0.5 + 0.5); // 高光反射单位向量(r向量) fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal)); // 从物体到摄像机的单位向量(v向量) fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 高光遮罩 fixed3 specularMask = tex2D(_SpecularMask, i.maskuv).r * _SpecularScale; // 计算高光颜色 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss) * specularMask; fixed3 color = ambient + diffuse + specular; return fixed4(color, 1); }
让地面或建筑上形成雪斑效果,模拟雪景。
if (dot(worldNormal, _SnowDir.xyz) > lerp(1, -1, _Snow))
{
color.rgb += _SnowColor.rgb;
}
示例:
fixed4 frag (v2f i) : SV_Target { // 材质 fixed4 albedo = tex2D(_MainTex, i.uv); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 获得法线纹理 fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw); fixed3 tangentNormal = UnpackNormal(packedNormal); tangentNormal.xy *= _BumpScale; // 逆矩阵将切线空间法线旋转到世界空间 fixed3 worldNormal = normalize(mul(float3x3(i.tanToWorld0, i.tanToWorld1, i.tanToWorld2), tangentNormal)); // 环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 漫反射 float difLight = saturate(dot(worldLightDir, worldNormal)) * 0.5 + 0.5; // 层次阴影 float toon = floor(difLight * _Step) / _Step; difLight = lerp(difLight, toon, _ToonEffect); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * difLight; float4 color = float4(ambient + diffuse, 1); // 此处添加下雪效果 if (dot(worldNormal, _SnowDir.xyz) > lerp(1, -1, _Snow)) { color.rgb += _SnowColor.rgb; } return color; }
名称 | 队列索引号 | 描述 |
---|---|---|
Background | 1000 | 这个渲染队列会在其他任何队列之前被渲染,通常用来渲染背景 |
Geometry | 2000 | 默认的渲染队列,大多数物体使用这个队列,不透明物体使用这个队列 |
AlphaTest | 2450 | 需要透明度测试的物体使用的队列 |
Transparent | 3000 | 这个队列的物体会在所有Geometry和AlphatTest物体渲染之后,再按照从后往前的顺序进行渲染。任何透明物体都使用该队列 |
Overlay | 4000 | 该队列实现一些叠加效果。任何最后渲染的物体在该队列。 |
if ((albedo.a - _Cutoff) < 0)
{
// 舍弃片元
discard;
}
示例:
Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "UnityLightingCommon.cginc" sampler2D _MainTex; fixed4 _MainTex_ST; fixed4 _Diffuse; float _Cutoff; // 片元着色器逐像素计算此漫发射 struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; float2 uv: TEXCOORD2; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); // 对应世界空间的位置 o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 albedo = tex2D(_MainTex, i.uv).rgba; if ((albedo.a - _Cutoff) < 0) { discard; } fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo.rgb * (saturate(dot(worldLightDir, i.worldNormal)) * 0.5 + 0.5); fixed3 color = ambient + diffuse; return fixed4(color, 1); } ENDCG }
源颜色:该片元颜色
目标颜色:已经存在于颜色缓冲的颜色
Render 设置中只要有关键字Blend,就代表开启混合(除Blend off以外)
Blend SrcFactor DstFactor : 开启混合,并设置混合因子。源颜色乘以SrcFactor,目标颜色会乘以DstFactor,然后把两者相加后在存入颜色缓冲区。
Blend SrcFactor DstFactor,SrcFactorA DstFactorA : 与上面一样,只是使用不同的因子来混合透明通道。
混合设置
参数 | 描述 |
---|---|
One | 因子为1 |
Zero | 因子为0 |
SrcColor | 因子为元颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于混合A的混合公式的时候,使用SrcColor的A分量作为混合因子。 |
SrcAlpha | 因子为源颜色的透明度值(A通道) |
DstColor | 因子目标颜色值。相当于混合RGB通道的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用DstColor的A分量作为混合因子。 |
DstAlpha | 因子为目标颜色的透明度值(A通道) |
OneMinusSrcColor | 因子为(1-源颜色)。相当于混合RGB通道的混合等式时,使用结果RGB分量作为混合因子;当用于混合A通道的混合等式时,使用结果的A分量作为混合因子。 |
OneMinusSrcAlpha | 因子为(1-源颜色的透明度值) |
OneMinusDstColor | 因子为(1-目标颜色)。相当于混合RGB通道的混合等式时,使用结果RGB分量作为混合因子;当用于混合A通道的混合等式时,使用结果的A分量作为混合因子。 |
OneMinusDstAlpha | 因子为(1-目标颜色的透明度值) |
常见混合操作类型
//正常(Normal)透明度混合 Blend SrcAlpha OneMinusSrcAlpha //柔和相加 Blend OneMinusDstColor One //正片叠底 Blend DstColor Zero //两倍相乘 Blend DstColor SrcColor //变暗 BlendOp min Blend One One //变亮 Blend OneMinusDstColor One Blend One OneMinusSrcColor //线性减淡 Blend One One Blend SrcAlpha One
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Tags
{
"LightMode" = "ForwardBase"
}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask 0
来确保这个pass通道不会渲染任何颜色。Pass
{
ZWrite On
// 意味着不写入任何颜色通道
ColorMask 0
}
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } LOD 100 Pass { ZWrite On // 意味着不写入任何颜色通道 ColorMask 0 } Pass { Tags { "LightMode" = "ForwardBase" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "UnityLightingCommon.cginc" sampler2D _MainTex; fixed4 _MainTex_ST; fixed4 _Diffuse; float _AlphaScale; // 片元着色器逐像素计算此漫发射 struct v2f { float4 vertex: SV_POSITION; fixed3 worldNormal: TEXCOORD0; float3 worldPos: TEXCOORD1; float2 uv: TEXCOORD2; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); // 对应世界空间的位置 o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 texColor = tex2D(_MainTex, i.uv); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * texColor.rgb * (saturate(dot(worldLightDir, i.worldNormal)) * 0.5 + 0.5); fixed3 color = ambient + diffuse; return fixed4(color, texColor.a * _AlphaScale); } ENDCG }
ZWrite On
的pass通道在下方,将会导致透明混合被叠加,如图ZWrite On
的pass通道放在上方。由于模型正反两面中,正面总是会覆盖背面的特性,可用模型背面来渲染描边。
Cull Front
仅渲染背面。物体空间外拓:
v2f vert(appdata_base v)
{
v2f o;
v.vertex.xyz += v.normal * _Outline;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
视角空间外拓
v2f vert(appdata_base v) { v2f o; // 从 UnityObjectToViewPos 演变而来 // UNITY_MATRIX_V 当前视图矩阵。 float4 pos = mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, v.vertex)); // 计算视图法线 // normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal)); // UNITY_MATRIX_IT_MV 模型逆转置 * 视图矩阵。 // 从模型空间 -> 逆转置到非模型空间 -> 视图空间 float3 normal = COMPUTE_VIEW_NORMAL; pos = pos + float4(normal, 0) * _Outline; // 从 UnityViewToClipPos 演变而来 // UNITY_MATRIX_P 当前投影矩阵 o.vertex = mul(UNITY_MATRIX_P, pos); return o; }
裁剪空间外拓
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// 计算视图法线
float3 normal = COMPUTE_VIEW_NORMAL;
// 从视图空间转换到裁剪空间。由于转换到裁剪空间后z轴只是简单的深度,因此没有意义。
float2 viewNormal = TransformViewToProjection(normal.xy);
o.vertex.xy += viewNormal * _Outline;
return o;
}
通过floor函数让颜色分层,实现类似三渲二的效果
floor(difLight * _Steps) / _Steps;
lerp(difLight, toon, _ToonEffect)
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed4 albedo = tex2D(_MainTex, i.uv);
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
float difLight = saturate(dot(worldLightDir, i.worldNormal)) * 0.5 + 0.5;
// floor 入参向下取整,将值分块
float toon = floor(difLight * _Steps) / _Steps;
// 分割程度
difLight = lerp(difLight, toon, _ToonEffect);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * albedo * difLight;
return float4(ambient + diffuse, 1);
}
// 法线
float3 worldNormal = UnityObjectToWorldNormal(v.normal)
// 单位化视角向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float rim = 1 - dot(worldNormal, viewDir);
fixed3 rimColor = _RimColor * pow(rim, _RimPower);
// 法线
float3 worldNormal = UnityObjectToWorldNormal(v.normal)
// 单位化视角向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float radian = 90 / 57.296;
float rim = dot(worldNormal, viewDir) * radian;
fixed3 rimColor = _RimColor * cos(min(rim * _RimPower, radian));
ZTest Greater
效果图:
示例:
Pass { Name "XRay" // 该通道无需阴影 Tags{ "ForceNoShadowCasting" = "true" } // 深度测试,原本是小于等于才会被画出,现在让它只能在大于时画出 ZTest Greater // 关闭深度写入,以防因为绘制Xray覆盖了深度写入,导致在该对象和更靠近摄像机的对象中间的对象被渲染到最上层。 ZWrite Off Blend SrcAlpha One CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float4 _XRayColor; float _XRayPower; struct v2f { float4 vertex : SV_POSITION; float3 viewDir : TEXCOORD1; float3 normal : TEXCOORD2; }; v2f vert(appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // 模型空间视图的向量 o.viewDir = normalize(ObjSpaceViewDir(v.vertex)); // 模型空间法向量 o.normal = normalize(v.normal); return o; } float4 frag(v2f i): SV_TARGET { float rim = 1 - dot(i.normal, i.viewDir); return _XRayColor * pow(rim, _XRayPower); } ENDCG }
__
必须要写,此处名字为SNOW_ON#pragma multi_compile __ SNOW_ON
#if SNOW_ON
if (dot(worldNormal, _SnowDir.xyz) > lerp(1, -1, _Snow * _SNOWHIGH))
{
color.rgb += _SnowColor.rgb;
}
#endif
Shader.IsKeywordEnabled(SNOW_ON);
Shader.EnableKeyword(SNOW_ON);
Shader.DisableKeyword(SNOW_ON);
Shader.SetGlobalFloat(_SNOWHIGH, 0.5f);
Material mat;
mat.EnableKeyword("DB_ON");
struct VertexInput { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; float4 vertexColor : COLOR; }; struct v2f { float4 vertex: SV_POSITION; float2 uv: TEXCOORD0; float4 vertexColor : COLOR; }; v2f vertBase (VertexInput v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.vertexColor = v.vertexColor; return o; } half4 fragBase (v2f i) : SV_Target { // 查询对应图像在指纹理坐标的颜色 fixed3 albedo = tex2D(_MainTex, i.uv).rgb; return i.vertexColor * fixed4(albedo, 1); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。