Notifications
Article
零基础入门Unity Shader(八)
Published 5 days ago
758
3
零基础入门Unity Shader(八)

前言

上篇我们讨论了渲染管线的大概流程,以及在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语义可以更改像素的深度值。
注意此功能相对会消耗性能,在没有特别需求的情况下尽量不要用!

最后

更多干货在公众号:Unity技术美术 ( ID:gh_8b69cca044dc )
Tags:
taecg
TA - Artist
16
Comments
JY
Jack Yan
2 days ago
感谢分享,坐等更新
1
J
JOJO
4 days ago
谢谢分享
0
王孟玥
4 days ago
谢谢分享 非常有用
0