Shader 语义

在书写HLSL shader程序时,输入和输出变量需要拥有他们 含义来表明语义。这在HLSL shader中是一个标准的做法。

Vertex shader 输入语义

主顶点着色器函数(被指令 #pragma vertex 标记)需要在所有的输入参数中加上语义。这些对应于单个网格数据元素,比如顶点位置,网格法线,还有贴图坐标等。简单例子如下:

Shader "Unlit/Show UVs"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0 // first texture coordinate input
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uv = uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv, 0, 0);
            }
            ENDCG
        }
    }
}

 

Fragment shader 输出语义

大多数情况下,片段着色器输出一个额颜色,并且含有 SV_Target语义。就比如:

fixed4 frag(v2f i): SV_Target

函数 frag 有一个返回类型为 fixed4 的值。因为它只会返回一个值,那么语义就可以直接定义在函数上。

当然,输出返回一个结构体也是可以的。上面的片段着色器可以被重写为下面这个样子,并且他们的功能一致:

struct fragOutput
{
    fixed4 color : SV_Target;
};

fragOutput frag(v2f i)
{
    fragOutput o;
    o.color = fixed4(i.uv,0,0);
    return o;
}

从片段着色器中返回结构体,对于那些不单单返回一个颜色的着色器非常有用。支持片段着色器输出的额外的语义如下:

SV_TargetN: 多个渲染目标

  SV_Target1,SV_Target2等等:这些是shader输出的额外的颜色。这种做法在一次性渲染多个目标时使用(也叫作多重渲染目标渲染技术,MRT)。当然,SV_Target0 就等于SV_Target

SV_Depth:像素着色器深度输出 

  通常情况,片段着色器不会覆写 Z 缓冲区的值,在三角形光栅化阶段,一般都用的是默认值。然而,对于一些特效,对每个像素使用自己的z缓冲区值是非常有帮助的。

  注意在许多GPU上,深度缓冲优化是关闭了的,所以在没有必须的理由时,最好不要重写深度缓冲区的值。由 SV_Depth 引发的性能消耗 根据GPU架构不同而不同,但是总体上来说这个消耗与alpha 测试相当。

Vertex shader 输出和 fragment shader 输入

一个vertex shader需要输出 一个顶点的 最终剪切空间坐标,这样GPU才知道在屏幕上的哪个地方、多少深度来光栅化它,这个输出需要有 SV_POSITION 语义,而且它的类型是一个 float4类型的数据。

vertex shader产生的任何其他输出都是你特殊着色器所需要的。从顶点着色器输出的值将被用来渲染三角形面,并且每个像素的值将被会作为输入数据传给片段着色器。

许多现代的GPU不需要真正关心这些变量有哪些语义;然而一些老系统却是需要一些特别的语义:

  TEXCOORD0,TEXCOORD1等等用来表明任意高精度数据,比如坐标或者位置。

  COLOR0,COLOR1 语义在顶点着色器输出和片段着色器输入中代表 低精度,0-1范围内的数据。

为了最好的跨平台支持, 在顶点着色器的输出与片段说色器的输入中,最好打上 TEXCOORDn语义标签。

插入器数量限制

将信息从顶点着色器传递到片段着色器中时,插入器变量可以使用的总量有着数量限制。限制数量与平台和GPU有关:

  • 最高8个:OpenGL ES 2.0 (iOS/Android), Direct3D 11 9.x level (Windows Phone) and Direct3 9 shader model 2.0 (old PCs).虽然插入器数量被限制了,但是每个插入器可以是一个4分量的向量,一些着色器将数据打包到一起来不超过限制。比如,两个贴图坐标可以被一个float4变量传递。
  • 最高10个:Direct3D 9 shader model 3.0 (#pragma target 3.0).
  • 最高16个:OpenGL ES 3.0 (iOS/Android), Metal (iOS).
  • 最高32个:Direct3D 10 shader model 4.0 (#pragma target 4.0).

为了性能着想,不管你是啥平台,最好都尽量使用更低数量的插入器。

其它特别的语义

Screen space pixel position:VPOS

一个片段着色器可以接受一个作为特别 VPOS语义呈现的像素的位置。这个特性只有在以 model 3.0开头的着色器中才存在。所以着色器需要编译指令:#pragma target 3.0

 

在不同的平台,屏幕空间位置的输入在底层上是不同的,所以为了最大限度的可移植性,在使用屏幕空间位置时,使用 UNITY_VPOS_TYPE标签。(大多数情况为float4,但是在D3D9 为float2)。

 

此外, 使用像素位置语义让在一个顶点到片段的结构体 同时拥有 剪切空间位置(SV_POSITION)和VPOS(屏幕空间位置)变得困难。所以顶点着色器需要将 剪切空间位置(SV_POSITION)作为一个单独的输出变量。请看下面的例子:

 

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // note: no SV_POSITION in this struct
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0, // texture coordinate input
                out float4 outpos : SV_POSITION // clip space position output
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy will contain pixel integer coordinates.
                // use them to implement a checkerboard pattern that skips rendering
                // 4x4 blocks of pixels

                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // clip HLSL instruction stops rendering a pixel if value is negative
                clip(checker);

                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

 

 

Face Orientation:VFACE

片段着色器可以接受一个变量,这个变量表明当前渲染的表面是否面对着摄像机,或者背对着摄像机。这一点在渲染那些两面都可以被看到的几何体时非常有用-比如渲染叶子。语义 VFACE修饰的 输入变量将会包含一个正值表明正对着的三角形,一个负数来表明背对这的三角形。

 

这个特性只有在 model 3.0 的着色器中才有,所以着色器必须包含编译指令: #pragma target 3.0,例子如下:

Shader "Unlit/Face Orientation"
{
    Properties
    {
        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
    }
    SubShader
    {
        Pass
        {
            Cull Off // turn off backface culling

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 _ColorFront;
            fixed4 _ColorBack;

            fixed4 frag (fixed facing : VFACE) : SV_Target
            {
                // VFACE input positive for frontbaces,
                // negative for backfaces. Output one
                // of the two colors depending on that.
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

  

Vertex ID: SV_VertexID

顶点着色器可以接收一个具有顶点数作为无符号整数的变量。当你想从贴图或者计算缓冲区中拿到额外的每个顶点数据时,这样就非常有用。

 

这个特性只有在DX10 和GLCore/OpenGL ES 3中才支持,所以需要引入编译指令 #pragma target 3.5,例子如下:

Shader "Unlit/VertexID"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.5

            struct v2f {
                fixed4 color : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                uint vid : SV_VertexID // vertex ID, needs to be uint
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // output funky colors based on vertex ID
                float f = (float)vid;
                o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}

  

 

posted @ 2018-02-21 14:26  gameDesigner  阅读(2262)  评论(0编辑  收藏  举报