Unity shader 官网文档全方位学习(三)

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颜色值越小,就越绿,反之越红。上下的原理类似。这样当稍稍放大物体并居中,就可以得到比较清晰的颜色变化。

上效果:Unity shader 官网文档全方位学习(三)

 

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为颜色值渲染。

上效果:

Unity shader 官网文档全方位学习(三)

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的比例取对应参数。可参考这个图:Unity shader 官网文档全方位学习(三)左边为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
		}
	}
}

这个例子画圈圈。可以设定圈圈的大小和个数。

关于参数的绑定可以参考系列文章的第一篇。

 

 

转载于:https://my.oschina.net/u/138823/blog/1606583