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

      

  • 相关阅读:
    卡常技巧
    Java经典习题3
    Java经典习题4
    VC++ MFC 文件处理unicode
    批处理更换ip地址
    C#实现系统托盘
    驱动打印
    c++ vs2010 GetWindowText GetWindowTextW
    VC++ MFC ListBox 复选框
    C# 获取本机ip地址
  • 原文地址:https://www.cnblogs.com/leiGameDesigner/p/8456586.html
Copyright © 2020-2023  润新知