Vertex and Fragment shader 是unity三大shader家族之一,势力最大,且范围最广。官方的对此的说法,如果不需要对光照方向进行处理,仅是对于贴图方面或者形状特效等,可以使用vertex and fragment shader。因此从这方面来看,vertex and fragment shader使用域将会更广。以下详细说明:
http://docs.unity3d.com/Documentation/Components/SL-ShaderPrograms.html
一、总述
1.作用:顾名思义,这种shader针对顶点与块,区别于surface shader的针对表面。因此更多情况用于贴图,与相关的特效处理。
2.语法:
=>总体:以Pass以外围块,写在SubShader里。
Pass { vert frag }
在CGPROGRAM 及 ENDCG之间用Cg或HLSL语言写上处理的代码。
=>常用的一些pragma函数定向:
-
#pragma vertex name - 顶点的函数定向
-
#pragma fragment name - 块的函数定向
-
#pragma geometry name - DX10 geometry shader的函数定向. 将自动打开 #pragma target 4.0.
-
#pragma hull name - DX11 hull shader的函数定向. 自动打开#pragma target 5.0.
-
#pragma domain name - DX11 domain shader的函数定向. 自动打开#pragma target 5.0.
还有一些,不多列举,这些都可从官网查到。
=>根据平台选择合适的pragma
#pragma target 默认是2.0的,对于Cg中的ARB_vertex_program 和ARB_fragment_program分别限制在256和96条指令之内。平台是在D3D9下的。
#pragma target 3.0,相对于2.0,整体有所提高,对ARB_vertex_program不再限制,但ARB_fragment_program可在1024之内,其中512材质和512算法相关的。
#pragma target 4.0,5.0分别是对于DX10和DX11的。但目前只能使用DX11来渲染才有效。
以下是unity所支持的各种渲染机制的问题:
-
d3d9 - Direct3D 9.
-
d3d11 - Direct3D 11.
-
opengl - OpenGL.
-
gles - OpenGL ES 2.0.
-
xbox360 - Xbox 360.
-
ps3 - PlayStation 3.
-
flash - Flash.
从上面了解到,如果你使用的是MAC,是没有DX的,因此好多针对于DX的shader许多渲染是出不来的。同时有时为了加快GPU的处理速度,还可以使用#pragma only_renderers 和 #pragma exclude_renderers 帮助声明只包含指定形式的的渲染机制,或者去除一些不使用的渲染机制。
二、实例学习
1.Window Coordinates
Shader "Custom/WindowCoordinates/Base" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 //1 #include "UnityCG.cginc" float4 vert(appdata_base v) : POSITION { //2 return mul (UNITY_MATRIX_MVP, v.vertex); } fixed4 frag(float4 sp:WPOS) : COLOR { return fixed4(sp.xy/_ScreenParams.xy,0.0,1.0); //3 } ENDCG } } }
这个例子是利用物体坐标与屏幕视角坐标的比值来设置物体的颜色。
第一处(//1):使用3.0的target,比默认的2.0参数的限制较少了。
第二处(//2):这里通过include进来的UnityCG.cginc的appdata_base的结构体作为输入,内容如下:
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
函数体首先将物体的点的位置转成模型视角投射三窗口上的,这样得出的坐标是与MODEL,VIEW,PROJECTION三者就关系了。就可以实际应用到。
第三处(//3):WPOS是一个CG的语义,我理解成就是一种类型标签,就是告诉显卡,我要拿这个float值作为什么样的用途。_screenParams是屏幕参数的一个结构体,注意是屏幕参数,这个值在选定模拟的窗口大小时就固定了,此处从中取出xy坐标。因此,当移动物体,而不用视角时,往左移,则x比值越小,反映到这个例子就是R颜色值越小,就越绿,反之越红。上下的原理类似。这样当稍稍放大物体并居中,就可以得到比较清晰的颜色变化。
上效果:
2.Behind Bars
Shader "Custom/WindowCoordinates/Bars" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct vertOut { float4 pos:SV_POSITION; float4 scrPos; }; vertOut vert(appdata_base v) { vertOut o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.scrPos = ComputeScreenPos(o.pos); //1 return o; } fixed4 frag(vertOut i) : COLOR0 { float2 wcoord = (i.scrPos.xy/i.scrPos.w); //2 fixed4 color; if (fmod(20.0*wcoord.x,2.0)<1.0) { //3 color = fixed4(wcoord.xy,0.0,1.0); //4 } else { color = fixed4(0.3,0.3,0.3,1.0); } return color; } ENDCG } } }
这个例子首先较第一例使用新的方式获取屏幕坐标,其次使用取模函数做一个颜色间隔出来。
第一处:unityCG里的函数:定义如下:
inline float4 ComputeScreenPos (float4 pos) { float4 o = pos * 0.5f; #if defined(UNITY_HALF_TEXEL_OFFSET) o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw; #else o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w; #endif #if defined(SHADER_API_FLASH) o.xy *= unity_NPOTScale.xy; #endif o.zw = pos.zw; return o; }
总体来说,是对x,y的坐标按当前屏幕坐标换算。这样以得到更精确的屏幕显示坐标。
第二处:齐次坐标化成世界三维坐标。此处只取xy坐标。
第三处:取当前x坐标的20倍取模,以得到间隔效果。
第四处:同例一,以xy为颜色值渲染。
上效果:
3.Vignetting
Shader "Custom/WindowCoordinates/Vignetting" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 #include "UnityCG.cginc" float4 vert(appdata_base v) : POSITION { return mul (UNITY_MATRIX_MVP, v.vertex); } float4 frag(float4 sp:WPOS) : COLOR { float2 wcoord = sp.xy/_ScreenParams.xy; float vig = clamp(3.0*length(wcoord-0.5),0.0,1.0); //1 return lerp (float4(wcoord,0.0,1.0),float4(0.3,0.3,0.3,1.0),vig ); //2 } ENDCG } } }
这个例子主要通过lerp函数,通过对界面上各点与原点的距离做为lerp的参数,进行颜色的混合。
第一处:这里的clamp函数,如果在第一参数在后两个参数(分别为左右边界值)值之间,直接返回。如果小于左边界,取左边界,大于右边界,取右边界。这个地方,首先wccord的坐标范围是0到1之间的,0点在左下角,减去0.5,保证圆心被提到中间。这样就可以取出一个以物体中心为圆心的区域。
第二处:lerp函数就是根据0-1的比例取对应参数。可参考这个图:左边为0.右边1。左右三角形分别为第一个和第二个参数。
4.Circles Mask
Shader "Custom/WindowCoordinates/CirclesMask" { Properties { _CirclesX ("Circles in X", Float) = 20 _CirclesY ("Circles in Y", Float) = 10 _Fade ("Fade", Range (0.1,1.0)) = 0.5 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 3.0 #include "UnityCG.cginc" uniform float _CirclesX; uniform float _CirclesY; uniform float _Fade; float4 vert(appdata_base v) : POSITION { return mul (UNITY_MATRIX_MVP, v.vertex); } float4 frag(float4 sp:WPOS) : COLOR { float2 wcoord = sp.xy/_ScreenParams.xy; float4 color; if (length(fmod(float2(_CirclesX*wcoord.x,_CirclesY*wcoord.y),2.0)-1.0)<_Fade) {//1 color = float4(sp.xy/_ScreenParams.xy,0.0,1.0); } else { color = float4(0.3,0.3,0.3,1.0); } return color; } ENDCG } } }
这个例子画圈圈。可以设定圈圈的大小和个数。
关于参数的绑定可以参考系列文章的第一篇。