Unity Shader - Shader semantics 着色器语义

目录:Unity Shader - 知识点目录(先占位,后续持续更新)
原文:Shader semantics
版本:2019.1

Shader semantics

着色器语义

当编写HLSL的shader programs着色器程序时,输入和输出变量需要了解他们的用意,使用 semantics (语义)来代表。这是HLSL着色器语言中的一个标准概念;查看 Semantics documentation on MSDN 了解更多详情。

你可以下载下面已展示的例子 zipped Unity project相关项目。
(如果下载不了,或是下载很慢,可以点击(提取码: vddu)这里,从百度网盘下载)

Vertex shader input semantics

顶点着色器输入语义

顶点着色器函数(使用 #pragma vertex 编译指令指定的函数)的所有输入参数都需要指定语义。这对应着Mesh的单个顶点的数据,如:顶点坐标,法线,纹理坐标。查看 vertex program inputs 了解更多详情。

这里有个简单的顶点着色器,带有顶点坐标和纹理坐标作为输入参数。像素着色器将纹理坐标直接作为颜色显示。

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, // 输入的顶点坐标
                float2 uv : TEXCOORD0 // 输入的纹理坐标,通过语义指定在 TEXCOORD0 寄存器
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uv = uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv, 0, 0);
            }
            ENDCG
        }
    }
}

在这里插入图片描述
在顶点着色器中我们是在函数参数中一个个的定义声明的,也可以在顶点着色器函数中传入结构体,然后再结构体中一个个的声明变量,和语义指定。查看 shader program examples 学习如何实现。

Fragment shader output semantics

片段着色器输出的语义

大多数片段(像素)着色器都会输出一个颜色值,并使用 SV_Target 语义指定。像上面的例子中片段着色器是这么声明定义的:

fixed4 frag (v2f i) : SV_Target

frag 函数返回 fixed4 类型(低精度 RGBA 颜色)数据。因为仅仅返回一个参数,所以在函数后面加上 SV_Target

也有可能输出的返回是个结构体数据。像上面的着色器可以改写成这样的方式:

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

从片段着色器返回的结构体通常用于返回的数据不只一个参数的时候是相同有用的方式。片段着色器输出支持的其他语义如下。

SV_TargetN: Multiple render targets

SV_Target: 多渲染目标

SV_Target1SV_Target2,等等:这些是着色器编写的额外颜色。这用于一次性渲染到多目标(就是我们说的:Multiple Render Targets(多渲染目标)的渲染技术,简称:MRT)。SV_Target0SV_Target 一样的意思。

SV_Depth: Pixel shader depth output

SV_Depth: 像素着色器深度输出

通常片段着色器不会去覆盖 Z-Buffer的值,Z-Buffer的默认值是从常规的三角形光栅化时就得到的。然而,有些特殊的效果使用自定义输出逐像素的Z-Buffer深度值是很有用的。

在不了解用法意图的时候不要去覆盖原始的Z-Buffer数值。SV_Depth 的消耗因GPU架构的不同而不同,但总体上它与alpha测试的消耗相当类似(使用HLSL中的内置clip()函数)。渲染着色器在渲染常规不透明对象后将会修改深度(例如,使用 AlphaTestrendering queue(渲染队列))。

输出的深度值占用一个单精度 float 数值类型。

Vertex shader outputs and fragment shader inputs

顶点着色器的输出与片段着色器的输入

一个顶点着色器需要输出顶点最终裁剪空间的坐标,这样GPU才知道对应屏幕中光栅化的位置与深度。输出这些数值需要 SV_POSITION 语义来指定给一个 float4 类型的数值。

其它的输出根据你自己的着色器来定。顶点着色器输出的数值,将会被渲染出来三角面上对应的插值像素处理,然后这些所有插值出来的像素都将会传入到片段着色器中。

许多现代 GPU 都不太关心变量语义的指定;然而在一些老旧的系统(最值得注意的是,DX9的SM 2.0(着色器模型 2.0))中语义会有一些特定的规则:

  • TEXCOORD0TEXCOORD1 等等用于表示高精度的数据,如:纹理坐标或是其他顶点坐标。
  • COLOR0COLOR1 语义在顶点输出和片段输入中用于低精度,0-1范围的数据(如:简单的颜色数值)。

为了能够跨平台支持,顶点输出和片段输入都是用 TEXCOORDn 的语义来指定。

查看 shader program examples 例子。

Interpolator count limits

插值器的数量限制

将用于顶点传到片段着色器的变量插值器的数量是有限的。具体限制数量依赖于平台和GPU,通常参考如下:

  • Up to 8 interpolators (8个):OpenGL ES 2.0(iOS/Android),Direct3D 11 9.X 级别的(Windows Phone) 和 Direct3D 9 shader model 2.0(旧版PC)。所以插值器的数量是有限的,但没有插值器向量都有4个分量,shader中通常会充分利用这些分量,将打包数据放满到分量中使用。例如,两个纹理坐标可以传入到一个 float4 的变量中(.xy是第一个坐标的,.zw是第二个坐标的)。
  • Up to 10 interpolators (10个):Direct3D 9 shader model 3.0 (#pragma target 3.0)。
  • Up to 16 interpolators (16个): OpenGL ES 3.0(iOS/Android),Metal(iOS)。
  • Up to 32 interpolators (32个):Direct3D 10 shader model 4.0(#pragma target 4.0)。

不论你的具体的目标硬件是怎么样的,通常为了性能原因尽可能使用少量的插值器来处理。

Other special semantics

其他特殊的语义

Screen space pixel position: VPOS

屏幕空间像素的坐标:VPOS

片段着色器能接收一个渲染为特定的语义 VPOS 的像素坐标。这个功能仅从 shader model 3.0 开始才有的,所有需要 shader 指定 #pragma target 3.0 编译指令。

在不同平台下屏幕空间坐标输入变量的类型也有会有所不同,所以为了最大化的可移植性,使用 UNITY_VPOS_TYPE 类型(在多数平台使用的是 float4,且在 Direct3D 9使用的是 float2)。

此外,使用像素坐标语义使剪辑空间位置(SV_POSITION)和VPOS很难同时位于同一个vertext -to-fragment结构体中。所以顶点着色器应该输出裁剪空间坐标为 “out” 变量。查看下面的shader例子:

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 将包含像素的整数坐标值。
                // 用它们实现棋盘团来,替代渲染4x4的像素块(4x4纹理)

                // 棋盘图案每个格将使用4x4的像素块
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // 如果checker为负数,则使用HLSL 内置函数 clip 来停止绘制该像素
                clip(checker);

                // 保持绘制的像素将采样纹理值并输出
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

在这里插入图片描述

Face orientation: VFACE

面的朝向: VFACE

片段着色器能接收一个代表当前渲染出来的表面是否朝向相机的变量。当渲染的几何体需要显示双面时是很有用的,通常用于树叶或是其他类似很薄的对象。 VFACE 语义指定给输入变量后,正值代表该光栅出来的三角面是正面,负值是背面。

这个功能是再shader model 3.0或之后才有的,所以shader需要 #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 // 关闭背面剔除

            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 输入值 正值代表正面,负值代表背面。
                // 输出正面颜色_ColorFont 还是背面颜色_ColorBack 取决于 facing值得正负
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

上面的shader使用了 Cull 状态设置来关闭背面剔除(默认三角面的背面是不渲染的)。这里该shader 应用于一堆旋转朝向不一的四边形网格。
在这里插入图片描述

Vertex ID: SV_VertexID

顶点ID: SV_VertexID

顶点着色器能接收一个代表顶点编号的无符号整数。当你想从纹理或ComputeBuffers (计算缓存数据)中获取其他的顶点数据时是很有用的。

这个功能仅在 DX10(shader model 4.0)和 GLCore / OpenGL ES 3中存在,所以shader需要 #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, // 输入的顶点坐标
                uint vid : SV_VertexID // 顶点ID,必须为uint类型
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(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
        }
    }
}

在这里插入图片描述
你可以下载上面已展示的例子 zipped Unity project相关项目。
(如果下载不了,或是下载很慢,可以点击(提取码: vddu)这里,从百度网盘下载)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Shader语义是一种用于定义着色器代码变量的标识符。它们可以提供有关变量的更多信息,例如变量的作用、数据类型和使用方式。使用着色器语义可以使着色器代码更加简洁、易于阅读,并增加可维护性和可扩展性。 在Unity着色器语义通常用于顶点着色器和片段着色器。在顶点着色器,通过使用语义变量,可以将模型空间下的顶点位置转换为裁剪空间下的位置,并对其他语义变量进行计算和赋值。 在代码UnityObjectToClipPos函数被用来进行模型空间到裁剪空间的转换。这个函数是Unity内置的函数,它将模型空间下的坐标转换为裁剪空间下的坐标。具体实现方式可能会因Unity版本和平台而有所不同,但使用方法是一致的。 除了顶点着色器语义还可以在片段着色器使用。比如,在一个完整的代码案例,我们可以使用纹理坐标和法线语义来对表面进行着色。在表面着色器函数,我们可以将纹理坐标和主纹理的颜色值赋值给表面输出结构体,还可以将法线向量从世界空间变换到切线空间,并对其他表面输出结构体的变量进行计算和赋值。 总之,Unity Shader语义为我们提供了一种方式来定义和使用着色器代码的变量,使其更具可读性和可维护性,并且能够方便地与其他着色器和渲染管线进行交互,同时也可以更方便地与外部程序进行交互。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值