零基础入门Unity Shader(八)

转自:https://zhuanlan.zhihu.com/p/50492287

前言

上篇我们讨论了渲染管线的大概流程,以及在Unity的ShaderLab中如何进行对应,其中主要是关于从应用程序阶段传递数值到顶点着色器,然后又经顶点着色器将数据传递给片断着色器并最终显示在屏幕上。那么这篇就来主要讨论下这些数据具体有哪些,以及如何对它们进行定义。


应用阶段传入顶点着色器的数据

当应用阶段的数据传过来时,顶点着色器怎么知道谁是模型的顶点数据谁又是模型的法线数据呢?所以我们需一种方式来告拆计算机我们定义的变量代表着什么。

    struct appdata
    {
        float4 vertex : POSITION;
    };

这里我们声明了一个float4类型的变量vertex,并给予了它顶点数据的语义(在变量后加冒号并跟一个语义),也就是说vertex变量将代表着模型的顶点数据被我们使用与传递。

那么都有哪些语义呢?如下:

	struct appdata
	{
		float4 vertex : POSITION;		//顶点
		float4 tangent : TANGENT;		//切线
		float3 normal : NORMAL;			//法线
		float4 texcoord : TEXCOORD0;	        //UV1
		float4 texcoord1 : TEXCOORD1;	        //UV2
		float4 texcoord2 : TEXCOORD2;	        //UV3
		float4 texcoord3 : TEXCOORD3;	        //UV4
		fixed4 color : COLOR;			//顶点色
	};

仔细观察,这些正好也是模型在Unity中的所有数据信息。

在UnityCG.cginc中内置定义了三种常用的结构,我们也可以直接引用并调用,有关cginc的具体用法我们后面再讨论,这里为了熟悉学习,我们尽量自己定义结构。

顶点着色器到片断着色器的数据

顶点着色器在处理完应用阶段传过来的数据后,会需要输出并传入片断着色器,这个时候我们同样需要定义一个结构来承载其中的数据,同样的,输出给片断着色器的值也需要语义来标识。

	struct v2f
	{
		float4 pos:SV_POSITION;
	};

这里声明了float4类型的变量pos,并指定为SV_POSITION语义,表示pos就是顶点着色器输出的屏幕裁剪空间下的顶点位置。这条语义是必须要有的,否则GPU无法进行接下来的光栅化处理。

其实在现代GPU上对这里的语义如何定义并不关心了(除了SV_POSITION以外),主要是部分OpenGL ES2.0上面才需要特别注意而已。

这里的语义除了SV_POSITION以外,我们还有另外两种选择:

  • TEXCOORD0~N系列

例如TEXCOORD0、TEXCOORD1、TEXCOORD2...等等,主要用于高精度数据。

COLOR0~N系列

例如COLOR0、COLOR1、COLOR2...等等,主要用于低精度数据。

虽然这两种语义我们可以根据需要自由定义,但是,它并不是可以无限定义的,不同的GPU硬件有不同的数量限制。

以下为手机平台的常见规则:

  • OpenGL ES2.0支持最多8个
  • OpenGL ES3.0支持最多16个
从性能优化角度来讲,数量越少性能越好。
另外,每个语义是4维向量,利用好这一点,可以大大节省总数量。

VFACE

在片断着色器中还有些特殊的语义的可以识别,比如VFACE,效果如下:

动图封面

如果渲染表面朝向摄像机,则Face节点输出正值1,如果远离摄像机,则输出负值-1。

完整代码如下:

Shader "Unlit/MyFirstShader"
{
	Properties
	{
		_FrontTex("FrontTex", 2d) = "white"{}
		_BackTex("BackTex", 2d) = "white"{}
	}
	
	SubShader
	{
		cull off
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0

			sampler2D _FrontTex;
			sampler2D _BackTex;

			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
			};


			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};
			
			v2f vert (appdata v)
			{
				v2f o;
				o.pos=UnityObjectToClipPos(v.vertex);
				o.uv=v.texcoord;
				return o;
			}
			
			fixed4 frag (v2f i,float face:VFACE) : SV_Target
			{
				fixed4 col=1;
				col = face > 0 ? tex2D(_FrontTex,i.uv) : tex2D(_BackTex,i.uv);
				return col;
			}
			ENDCG
		}
	}	
}
另外比较特殊的还有SV_VertexID和VPOS,有兴趣的可以自行查阅下相关资料,由于这些不常用,在入门篇章内就暂且先不讨论了。

片断着色器输出相关语义

通常情况下,片断着色器最终只需返回一个颜色值即可,也是我们最常见到的编写方式,如下:

fixed4 frag (v2f i ) : SV_TARGET

这里的SV_TARGET就是指定输出颜色到RenderTarget的语义,其实我们也可以采用Struct的方式,就是像应用阶段到顶点与顶点到片断一样,只是由于平时我们只需返回一个颜色所以就无需再用一个Struct了(你非要用也是可以的)。

当我们利用Struct时,就可以通过下列语义来输出多个内容:

  • SV_Target0〜N

默认SV_TARGET0,也就是SV_TARGET,还有SV_TARGET1,SV_TARGET2...这个在需要输出多个RenderTarget时很有用。

  • SV_Depth

一般情况下,模型的像素深度值在光栅化时会自动插值计算得出,并不需要我们做额外的处理,但这并不代表不可以修改它,通过在片断着色器中输出SV_DEPTH语义可以更改像素的深度值。

注意此功能相对会消耗性能,在没有特别需求的情况下尽量不要用!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值