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
        }
    }
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM