Unity3D学习笔记(三十五):Shader着色器(2)- 顶点片元着色器
Alpha测试
AlphaTest Great:大于
AlphaTest Less:小于
AlphaTest Equal:等于
AlphaTest GEqual:大于等于
AlphaTest LEqual:小于等于
AlphaTest Never:全不渲染
AlphaTest Always:全部渲染
如果使用后两项的命名的情况下,比较后的数字项没有意义
AlphaTest 比较项 数字
AlphaTest Great 0.5 //表示只渲染alpha值大于0.5的部分 AlphaTest Less 0.5 //表示只渲染alpha值小于0.5的部分
混合命令
Blend SrcFactor DstFactor
SrcFactor DstFactor 可以的取值
One表示1
Zero表示0
SrcColor当前的颜色
DstColor已经存在的颜色
SrcAlpha当前的透明度
DstAlpha已经存在的颜色的透明度
OneMinusSrcColor当前的颜色取反向 1-SrcColor
OneMinusSrcAlpha当前的Alpha值取反向 1-SrcAlpha
OneMinusDstColor已经存在的颜色取反向 1-DstColor
OneMinusDstAlpha已经存在的颜色的透明度取反向 1-DstAlpha
最终颜色 = 新颜色 * SrcFactor + 旧颜色 * DstFactor
新颜色:即将写入到颜色缓冲区的颜色
旧颜色:已经在颜色缓冲区的颜色
颜色运算法则(不同于向量运算法则)
(r,g,b) * a = (r*a,g*a,b*a)
(r,g,b) * (x,y,z) = (r*x,g*y,b*z)
(r,g,b) + (x,y,z) = (r+x,g+y,b+z)
A - (r,g,b) = (a-x,a-y,a-z)
例如
Blend Zero One //仅仅显示背景的颜色,自身的效果不会显示 Blend One Zero //显示贴图的RGB颜色,即使有透明通道的部分也显示原色,不会透过去 Blend One One //贴图色与背景色叠加,没有透明通道处理,结果可能更趋近于(1,1,1) Blend SrcAlpha Zero //没有透明通道部分显示的是贴图的原色,有透明通道0的部分,显示黑色,0.5部分更趋近于黑色 Blend SrcAlpha OneMinusSrcAlpha //最终颜色 = 新颜色 * 当前透明度 + 旧颜色 * (1 - 当前透明度),最常用的透明混合方式
Shader "Lesson/AlphaTest" { Properties { _Tex("纹理", 2D) = "white"{} _Alpha("透明度", Range(0,1)) = 0 } SubShader { Pass { //表示只渲染alpha值大于0.5的部分 AlphaTest Greater 0.5 AlphaTest Greater [_Alpha] //Blend SrcAlpha OneMinusSrcAlpha Blend SrcAlpha zero SetTexture[_Tex] { Combine texture } } } FallBack "Diffuse" }
Cg语言,详见PPT
顶点/片元着色器
核心:
顶点函数:
片元函数:
顶点函数在几何阶段,能从应用程序获取顶点信息。这些信息就是通过顶点函数的参数传递进来的。顶点函数的返回值就是传递到片元函数里的,片元函数也是通过参数去接收的
Float4 vertex:顶点坐标(模型空间下)
Float4 normal:顶点的法线向量
Float4 tangent:顶点的切线向量
Float4 texcoord:第一UV颜色
Float4 texcoord1:第二UV颜色
Float4 color:顶点颜色
语义:语句的含义
顶点函数的形参后跟的语义:表示该形参的是接收应用程序传递来的是什么。
对于顶点函数的方法后跟的语义:修饰的返回值,代表该返回值表示的是什么。
对于片元函数的方法后跟的语义:修饰的是片元函数的返回值,代表的是告诉应该GPU是什么
从应用程序阶段到顶点函数阶段
1.POSITION:表示应用程序把顶点坐标传递过来并且存储在修饰的参数
2.NORMAL:表示法线,通常是float3
3TANGENT:表示切线
4.TEXCOORD0-N:从第1套UV到第7套UV
5.COLOR:顶点颜色
从顶点函数到片元函数能表示的语义
1.SV_POSITION:代表转换之后的齐次空间坐标
2.COLOR0:代表一组颜色
2.COLOR1:代表一组颜色
4.TEXCOORD0-N:从第1套UV到第7套UV
从片元函数输出的语义
SV_Target:输出的值会渲染到屏幕上
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Lesson/VFColor" { Properties { _Color("颜色", Color) = (1,0,0,1) } SubShader { //1.顶点片元程序也要写在Pass块里 Pass { //2.把Cg语言的开始和结束写出来 //表示Cg语言程序的开始 CGPROGRAM //3.定义顶点函数和片元函数的名字 //#pragma 不变 vertex 关键字,后跟的是顶点函数的名字,vert顶点函数的名字 #pragma vertex vert //定义函数名为vert的函数为顶点函数 //#pragma 不变 fragment 关键字 #pragma fragment frag //定义函数名为frag的函数为片元函数 fixed4 _Color;//如果要在Cg语言中去使用属性面板定义的一些变量,需要在Cg语言中进行重定义 //4.实现顶点函数 //POSITION修饰的是顶点函数参数vet,证明vet是顶点坐标 //SV_POSITION修饰的是vet函数的返回值,证明返回值是转换之后的坐标 float4 vert(float4 vet : POSITION, float3 normal : NORMAL) : SV_POSITION { //顶点函数的核心作用必须做的是空间坐标转换 //mul是Cg语言的提供的API,计算向量与矩阵相乘 float4 position = UnityObjectToClipPos(vet); // return position; } //5.实现片元函数 fixed4 frag() : SV_Target { //最重要的必须的做到就是返回一个颜色 return _Color; } //表示Cg语言程序的结束 ENDCG } } FallBack "Diffuse" }
Unity\Editor\Data\CGIncludes\UnityShaderUtilities.cginc里的UnityObjectToClipPos()函数
UnityCG.cginc常用的API
#ifndef UNITY_SHADER_UTILITIES_INCLUDED #define UNITY_SHADER_UTILITIES_INCLUDED // This file is always included in all unity shaders. #include "UnityShaderVariables.cginc" // Tranforms position from object to homogenous space inline float4 UnityObjectToClipPos(in float3 pos) { // More efficient than computing M*VP matrix product return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0))); } inline float4 UnityObjectToClipPos(float4 pos) // overload for float4; avoids "implicit truncation" warning for existing shaders { return UnityObjectToClipPos(pos.xyz); } #endif
UNITY_MATRIX_MVP的MVP是几何阶段的三次转变
M模型到世界
V世界到观察
P观察到剪裁
![](https://img2018.cnblogs.com/blog/1517599/201902/1517599-20190212004144017-873634158.png)
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Lesson/VFNormal" { Properties { } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag //定义一个结构体,作为顶点函数的参数 struct m2v { float4 vex : POSITION;//告诉应用程序把模型空间下的顶点坐标放在这 float3 normal : NORMAL;//告诉应用程序把模型空间下的法线放在这 float3 tangent : TANGENT;//告诉应用程序把模型空间下的切线放在这 }; //定义一个结构体,作为顶点函数的返回值和片元函数的参数 struct v2f { float4 position : SV_POSITION;//转换之后的顶点坐标 float3 normal : COLOR0;//法线 float3 tangent : COLOR1;//切线 }; //结构体作为参数和返回值 v2f vert(m2v v) { v2f f; f.position = UnityObjectToClipPos(v.vex); //必须变换顶点坐标 f.normal = v.normal;//法线赋值 f.normal = mul(UNITY_MATRIX_M, v.normal); //f.tangent = v.tangent;//切线赋值 return f; } fixed4 frag(v2f f) : SV_Target { return fixed4(f.normal.x, f.normal.y, f.normal.z, 1);//把法线作为颜色输出 //return fixed4(f.tangent.x, f.tangent.y, f.tangent.z, 1);//把切线作为颜色输出 } ENDCG } } FallBack "Diffuse" }
UV坐标,可以通过顶点的UV坐标去获取纹理对应的颜色。对于图片的纹理来说UV的(0,0)点在左下角,UV的(1,1)点在右上角
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Lesson/VFTexture" { Properties{ //定义一个图片 _MainTex("纹理", 2D) = "white"{} } SubShader{ //透明的纹理处理要把渲染队列调高 Tags{ "Queue" = "Transparent" } Pass { //Cull off //处理带有透明通道贴图的混合 Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag //重定义纹理 sampler2D _MainTex; //定义结构体,作为顶点函数的参数 struct m2v { float4 vex : POSITION; float2 uv : TEXCOORD0;//告诉应用程序要UV信息 }; //定义一个结构体,作为顶点函数的返回值和片元函数的参数 struct v2f { float4 position : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(m2v v) { v2f f; //转换顶点坐标 f.position = UnityObjectToClipPos(v.vex); f.uv = v.uv; return f; } fixed4 frag(v2f f) : SV_Target { //通过顶点的uv坐标去访问纹理对应的颜色 fixed4 color = tex2D(_MainTex, f.uv); //return fixed4(f.uv.x,f.uv.y, 0, 1); return color; } ENDCG } } FallBack "Diffuse" }
属性面板的变量,如果想在Cg程序中去使用,必须在Cg程序中重新定义:
Color - fixed4或float4
Vector - float4
Float - float
Range - float
2D - sampler2D
Rect - samplerRect
Cube - samplerCube
裁剪可以做场景切换的幕布效果
![](https://img2018.cnblogs.com/blog/1517599/201902/1517599-20190212004215503-1150180277.png)
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Lesson/VFDiscard" { Properties{ _MainTex("纹理", 2D) = "white"{} _Radius("半径", Range(0, 0.75)) = 0 } SubShader{ Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _MainTex; float _Radius; struct m2v { float4 vex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 position : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(m2v v) { v2f f; f.position = UnityObjectToClipPos(v.vex); f.uv = v.uv; return f; } fixed4 frag(v2f f) : SV_Target { //discard;//裁剪命令 fixed4 color = tex2D(_MainTex, f.uv); //0.2圆的半径 //圆心是0.5 0.5 float2 current = f.uv; float2 center = float2(0.5, 0.5); float dis = pow((current.x - center.x), 2) + pow((current.y - center.y), 2); if (dis < pow(_Radius, 2)) { //在圆内 discard; } return color; } ENDCG } } FallBack "Diffuse" }
Emission,类似RawImage的UV Rect
----Tiling:图片的左右偏移
----Offset:图片的大小比例
练习 - 实现西瓜的效果
![](https://img2018.cnblogs.com/blog/1517599/201902/1517599-20190212004233736-1582490063.png)
Shader "Lesson/VFWatermelon" { Properties { _LightColor("浅色条纹", Color) = (1,1,1,1) _DarkColor("深色条纹", Color) = (1,1,1,1) _Number("深色条纹数量", int) = 3 _Width("深色条纹宽度", Range(0.01, 0.2)) = 0.02 } SubShader{ Pass { CGPROGRAM fixed4 _LightColor; fixed4 _DarkColor; int _Number; float _Width; #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct m2v { float4 vex : POSITION; float3 uv : TEXCOORD0; }; struct v2f { float4 position : SV_POSITION; float3 uv : TEXCOORD0; }; v2f vert(m2v v) { v2f f; f.position = UnityObjectToClipPos(v.vex); f.uv = v.uv; return f; } fixed4 frag(v2f f) : SV_Target { //实现1 //浅色条纹的宽度 float width = 1.0 / _Number - _Width; for (int i = 0; i < _Number; i++) { if (f.uv.x > i * (width + _Width) && f.uv.x < width + i * (width + _Width)) { return _DarkColor; } } return _LightColor; /* //实现2 //每对条纹的宽度(1深1浅) float wid = 1.0 / _Number; float x = fmod(f.uv.x, wid); //对wid取模(余) if (x < _Width) { return _DarkColor; } return _LightColor; */ } ENDCG } } FallBack "Diffuse" }