Skip to content

Latest commit

 

History

History
514 lines (297 loc) · 22.1 KB

07.md

File metadata and controls

514 lines (297 loc) · 22.1 KB

七、HLSL 概述

到目前为止,我们在代码中稍微使用了高级着色器语言,现在我们将更详细地了解该语言。这是一种基于 C 语言的语言,但它是专门为 GPU 的并行架构设计的。在 HLSL 编写的代码通常一次运行多次。例如,一个顶点着色器的代码可能会执行数千次,一次针对三维场景中的每个顶点。GPU 不是按顺序执行,而是成千上万个并行执行。

HLSL 也有特殊的数据类型和功能,旨在使三维编程更容易。GPU 有自己的与 CPU 完全不同的机器码,其指令集充满了向量和矩阵运算的快速方法。

数据类型

来自 C 的大多数常规基本数据类型都可以在 HLSL 获得。根据您的目标硬件,有些可能不可用。例如,double仅在较新的硬件上可用。

大多数 HLSL 变量是结构、向量或矩阵。与处理单一值相比,HLSL 在处理大量类似结构的数据方面要好得多。这和 CPU 是相反的。在 SIMD,中央处理器倾向于将一个 4x4 浮点矩阵视为 16 个不同的浮点变量,而图形处理器倾向于同时在 16 个浮点上运行。

标量类型

标量类型是单一值,如intbool。它们或者作为单个值使用,或者作为矩阵、向量和结构在小集合中使用:

  • 布尔:标准布尔值,真或假。
  • int: 32 位有符号整数。
  • uint/dword: 32 位无符号整数。
  • 一半:16 位 IEEE 浮点值。不要使用这些,它们已被弃用。
  • 浮点:32 位 IEEE 浮点值。
  • double: 64 位 IEEE 浮点值。

| | 注意:16 位 IEEE 半浮点型是压缩 32 位浮点的一种方式。精度降低后,32 位浮点可以压缩到 16 位。该数据类型非常适合存储大量数据,但现在已被弃用,建议您不要使用它。 |

语义名称

我们已经使用了 HLSL 语义名称;这些名称描述了一个结构的特定元素的用途。当我们在 C++代码中指定数据布局时,我们描述了语义名称,我们还在着色器 HLSL 代码的结构定义中描述了它。例如,以下代码用于我们的VertexShader类。到最后一章结束时,我们还没有包括下面代码表中显示的 NORMAL 元素。我们将在第 8 章【照明】中添加这个元素,当我们看照明的时候。

    // Describe the layout of the data
    const D3D11_INPUT_ELEMENT_DESC vertexDesc[] =
    {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
    D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
    D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
    D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };

这里的语义名称是POSITIONNORMALTEXCOORD。这些名字不言自明。带有POSITION语义的元素通常用于指定顶点的位置,我相信您可以猜到其他两种语义的用法。有关语义名称的完整列表,请访问 MSDN 网站:

http://msdn . Microsoft . com/en-us/library/windows/desktop/bb 509647% 28v = vs . 85% 29 . aspx

在前面的代码中,每个元素的第二个参数是一个数字,可以用来区分相同语义被多次使用的值。例如,如果一个顶点类型使用多个纹理,如凹凸贴图或各种其他技巧,则可以在布局描述中包含两个TEXCOORD元素。在这种情况下,第一个可能是数字 0,第二个可能是数字 1。这些数字在着色器中的语义名称后面被引用。请注意下面代码中语义名称后面的零。该代码是对应于上面 C++描述的 HLSL 结构,因为它可能出现在顶点着色器中,如下代码表所示。

    // The input vertices
    struct VertexShaderInput
    {
    float3 position : POSITION0;
    float3 normal : NORMAL0;
    float2 tex : TEXCOORD0;
    };

您可能已经注意到顶点着色器输入指定POSITION作为语义,输出指定SV_POSITION。这个SV_POSITION也被用作像素着色器的 HLSL 代码中的语义。SV 代表系统价值。顶点着色器的输出或像素着色器的输入必须至少指定SV_POSITION的所有四个组件。管道本身就知道顶点着色器输出的SV_POSITION的含义。当SV_POSITION是顶点着色器的输出时,它已经被顶点着色器转换到其最终状态;它已准备好由管线的光栅化器级光栅化。

向量类型

向量是同一标量数据类型的一个到四个分量之间的小数组。这里的向量这个术语与我们之前用来读取模型数据的 C++ STL 向量类完全没有关系。图形处理器对向量的运算是以类似于 SIMD 的方式执行的,它们非常高效。到目前为止,我们已经在代码中看到并使用向量来存储和操作顶点的位置、顶点的颜色和纹理坐标。这里使用的向量这个术语比数学中的标准定义更灵活。在数学中,向量通常指定大小或长度和方向。DirectX 中的向量只是数据的小集合。我们可以用任何我们喜欢的方式使用浮点向量,它们不需要指定方向和大小。我们已经在代码中使用了 DirectX 矢量来指定位置、纹理坐标和颜色。

上面提到的任何标量类型都可以用来创建向量。有两种语法,下面是第一种。

typeCount variableName;

其中 type 是标量类型之一,count 是 1、2、3 或 4,表示向量中的分量数;variableName是被声明变量的名称。您可以使用大括号初始化向量的值。这里有一些例子。

    int4 myIntVector;
    float2 fv = { 0.5f, 0.5f };
    double1 d = 0.6;

另一种语法使用 vector 关键字。除了语法,它做了完全相同的事情。

    Vector <type, count> variableName;

下面的例子和上面的一样,只是语法使用了vector关键字。

    vector<int, 4> myIntVector;
    vector<float, 2> fv = { 0.5f, 0.5f };
    vector<double, 1> d = 0.6;

您也可以使用以下语法初始化向量。

    int4 myVector = int4(1, 2, 3, 4);

访问向量元素

可以使用标准数组表示法访问元素,也可以使用颜色空间表示法(RGBA)或坐标表示法(XYZW)。这些符号有时被称为颜色组件名称空间或坐标组件名称空间。

    Vector <int, 4> myVector;
    // Referencing the first element:
    myVector[0] = 50; // These three lines all do the same thing.
    myVector.r = 50; // They set the first element of the vector to 50.
    myVector.x = 50;

    myVector[1] = 10; // These three lines all set the second element to 10.
    myVector.g = 10;
    myVector.y = 10;

    myVector[2] = 20; // These three lines set the third element to 20.
    myVector.b = 20;
    myVector.z = 20;

    myVector[3] = 42; // These lines set the final element of the vector to 42.
    myVector.a = 42;
    myVector.z = 42;

Vector Swizzles

swizzle 是一种将向量或矩阵的组成部分作为集合进行访问的方法。

    int4 v1 = { 1, 3, 9, 27 };
    int4 v2 = { 2, 4, 6, 8 ;}
    v1.rb = v2.gr;

在上面的代码中,声明了两个向量。第三行包含调酒。它将 v2 向量的 g 和 r 分量分配给 v1 向量的 r 和 b 分量。在此操作之后,v1 向量将包含{ 4,3,2,27 };向量 v2 没有改变。

您不能在 swizzle 中混合颜色和坐标组件名称空间。以下是非法的,因为它同时使用了 RGBA 的 r 和 a 以及 XYZW 的 X 和 Y。

    v1.rx = v2.ya;

您可以多次重复使用同一个元素作为源。

    v1.rgba = v2.rrrr; // Broadcast the r element of v2 across v1

您不能多次重复使用同一个元素作为目的地(下面的 v1),因为它毫无意义。

    v1.rr = v2.gb; // Illegal, cannot set v1.r to multiple values

矩阵类型

矩阵类型类似于向量,只是它们可以存储更多的值,并且可以是二维的。矩阵的每个维度可以从一个元素到四个元素,因此矩阵可以是从一个元素到十六个元素宽的任何东西(这是一个 1x1 或 4x4 矩阵)。像向量一样,矩阵被创建为标量数据类型的集合,矩阵中的每个元素必须具有相同的类型。和向量一样,有两种语法来声明矩阵,有和没有矩阵关键字。

    typeRowsxCols variableName;

其中type为数据类型,Rows为矩阵行数,Cols为列数。

    int3x2 neo;
    float4x4 trinity;
    float1x2 morpheus;

使用matrix关键字的另一种语法实现了完全相同的目的。

    matrix <type, rows, cols> variableName;
    For example:
    matrix <int, 3, 2> neo;
    matrix <float, 4, 4> trinity;
    matrix <float,1, 2> morpheus;

您也可以使用大括号{和}初始化矩阵中的值。

    matrix <int, 3, 2> neo = {
    1, 2, // First row
    3, 4, // Second row
    5, 6 // Third row
    };

访问矩阵元素

可以使用从零开始的表示法或从一开始的表示法来访问矩阵元素。

    matrix<float, 4, 4> myMatrix = {
    1.0f, 2.0f, 3.0f, 4.0f,
    2.0f, 3.0f, 5.0f, 7.0f,
    1.0f, 2.0f, 6.0f, 24.0f
    1.0f, 1.0f, 2.0f, 3.0f
    };
    myMatrix._m00 = 0.0f; // Change first element to 0, 0 based notation
    myMatrix._11 = 12.9f; // Change first element to 12.9, 1 based notation

元素索引以下划线开头。对于从零开始的记数法,索引以“_m”为前缀。对于一种基于符号的方法,索引只以“_”作为前缀。请注意,根据您的显示,某些下划线“_”可能会在此文本中显示为空格;小心不要把两者混在一起。在这两种情况下,前缀后面是要访问的行的索引,然后是元素的列。

要设置前一个矩阵中元素的值,当前矩阵的值为 7.0 到 100.0,我们可以执行以下任一操作。

    myMatrix._m23 = 100.0f;
    myMatrix._34 = 100.0f;

矩阵元素也可以用标准的 C 数组符号来表示。这总是从零开始的。

    myMatrix[2][3] = 100.0f;

矩阵开关

您可以使用 swizzles 来访问和设置矩阵的元素。

    someMatrix._32_12 = someOtherMatrix._11_33;

这将把元素[1][1][3][3]someOtherMatrix复制到someMatrix的元素[3][2][1][2]

同样,您不能混合命名空间,因此基于一个的索引不能与基于零的索引在一次 swizzle 中混合。这是违法的。

    someMatrix._32_m12 = someOtherMatrix._11_33;

此外,将矩阵中的同一元素设置为两个或多个不同的值也没有意义。这也是违法的。

    someMatrix._32_32 = someOtherMatrix._11_33;

其他数据类型

还有其他数据类型,其中一些我们已经见过。cbuffer用于保存恒定的数据(从图形处理器的角度来看)。Texture2D用于保存像素着色器的纹理,SamplerState用于保存控制纹理采样方式的数据。

您可以使用类似于 C 结构的语法创建结构;struct关键字后面是名称,然后是花括号中的结构元素。

    struct someStructure {
    float x;
    float y;
    };

操作员

标量、矢量和矩阵数据类型都可以使用标准运算符(+、-、、和/)来操作。对于标量数据,这些操作按预期执行,但重要的是要知道,对于向量和矩阵类型,运算符是逐个元素的。这意味着标准矩阵乘积不是使用乘法运算符“”计算的,而是该运算符将两个源操作数的相应元素相乘。要计算标准矩阵乘积,我们必须使用mul内蕴。当乘以代码中的模型、世界和投影矩阵时,我们已经看到了这一点。关于矩阵乘法的详细解释,可以看看下面的网站。

来自 Wolfram:

http://mathworld . wolfram . com/matrix multiplier . html

来自维基百科:

http://en . Wikipedia . org/wiki/matrix _ multiplier

来自汗学院:

https://www . khanacademy . org/math/代数/代数-矩阵/矩阵 _ 乘法/v/矩阵-乘法- part-1

内在

GPU 有自己的处理单元、内存和架构。它甚至有自己的机器代码,给中央处理器一套完全不同的指令。GPU 理解的许多机器代码指令都是专门为辅助三维图形而设计的。它在矩阵运算和向量数据类型运算方面非常高效。GPU 能够执行的许多指令没有运算符(像+或*可以用于加法或乘法)。使用类似于常规函数调用的内部函数调用指令。这些是特殊功能,旨在与硬件的机器代码紧密匹配。不是所有的 GPU 都能够执行相同的指令,每一代新的 GPU 通常能够执行比上一代更多的指令。

以下是 HLSL 一些有用的内部说明的小参考。根据助记符或方法名称,内部元素按字母顺序组织。总共有大约 140 种 HLSL 语言的内部语言。许多没有提到的内在因素可以节省很多时间。例如,“点亮”内在计算照明系数与一个单一的,极快的指令。有双曲三角函数,也有反正弦、反余弦和反正切。还有同时计算正弦和余弦的函数。简而言之,一旦你熟悉了这些基本的本质,你可能会想看看完整的列表,看看 GPU 的一些更高级的功能。在微软的 MSDN 可以找到所有的内在因素。

http://msdn . Microsoft . com/en-us/library/windows/desktop/ff 471376% 28v = vs . 85% 29 . aspx

短 HLSL 本征参考

绝对的

助记符 : ret abs(x) 着色器 模型 : 1.1

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:返回 x 的绝对值,绝对值是 x 中包含的标量或标量的正版本,如果使用向量或矩阵,将计算每个元素的绝对值。

天花板

助记符 : ret ceil(x) 着色器 模型 : 1.1

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数返回大于或等于 x 值的最小整数,上限将数字向上舍入到最接近的整数。如果 x 是向量或矩阵,每个元素将向上舍入到最接近的整数。

夹钳

助记符 : ret clamp(x,min,max) 着色器 模型 : 1.1

参数 : x、min、max 可以是标量、矢量或矩阵,但必须是同一类型;计算出的 ret 值将具有与输入相同的类型。

说明:该功能将 x 中元素的值限制在最小和最大参数指定的相应值之间。x 中小于 min 中相应元素的任何元素都将被设置为 min。x 中大于 max 中相应元素的任何元素都将被设置为 max 中的值。

余弦

助记符 : ret cos(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:该函数以弧度表示一个或多个值的余弦,以 x 表示。

交叉乘积

助记符 : ret cross(x,y) 着色器 模型 : 1.1

参数 : x 和 y 必须是三维浮动矢量;ret 也是一个三维浮动向量。

说明:该函数计算并返回两个三维向量的叉积。返回值是垂直于两个输入的向量;它返回包含输入向量的平面的法线。

弧度到度数

助记符 : ret 度(x) 着色器 模型 : 1.1

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此功能将 x 中的角度,以弧度读取,转换为度数。如果 x 是向量或矩阵,则 x 中的所有元素都执行转换。这与 x 中的值乘以 180/Pi 相同。

距离

助记符 : ret 距离(x,y) 着色器 模型 : 1.1

参数 : x 和 y 为任意大小的向量;ret 是标量浮点。

说明:这个函数计算 x 和 y 两点之间的距离,x 和 y 必须有相同数量的元素。

点积

助记符 : ret dot(x,y) 着色器 模型 : 1

参数 : x 和 y 为任意大小的向量;ret 是标量浮点。

说明:这个函数计算两个向量 x 和 y 之间的点积,这是 x 和 y 中每个对应元素的积之和,x 可以是任意向量大小,但 y 必须匹配。

地板

助记符 : ret floor(x) 着色器 模型 : 1.1

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数返回小于或等于 x 值的最大整数,Floor 将数字向下舍入到最接近的整数。如果 x 是向量或矩阵,每个元素将向下舍入到最接近的整数。

长度

助记符 : ret length(x) 着色器 模型 : 1.1

参数 : x 为浮点向量;ret 将是标量浮点。

说明:此函数计算矢量 x 的长度或大小。

最高的

助记符 : ret max(x,y) 着色器 模型 : 1.1

参数 : x、y、ret 必须都是同一类型;它们可以是标量、矢量或矩阵。

说明:该功能选择 x 和 y 两个对应元素的最大值或更大值,并返回。

最低限度

助记符 : ret min(x,y) 着色器 模型 : 1.1

参数 : x、y、ret 必须都是同一类型;它们可以是标量、矢量或矩阵。

说明:该功能选择 x 和 y 两个对应元素的最小值或更小值,并返回。

乘;成倍增加;(使)繁殖

助记符 : ret mul(x,y) 明暗器 模型 : 1.0

参数:针对不同的输入类型,mul 有很多重载版本。

描述:此函数将矩阵、标量或向量相乘。x 和 y 不必是相同的数据类型。根据它们的数据类型,函数执行不同的操作。例如,如果 x 是标量,y 是矩阵,那么 y 中的每个分量都将乘以 x,因此矩阵将被缩放。

该函数用于执行标准矩阵乘法。如果两个输入都是矩阵,那么结果输出将是两个输入的标准矩阵乘积。请注意,以两个矩阵作为操作数的乘法运算符(*)将逐元素执行乘法;它不计算矩阵乘积。

使标准化

助记符 : ret normalize(x) 着色器 模型 : 1.1

参数 : x 为向量;ret 将具有与 x 相同的尺寸和类型。

描述:这个函数计算 x 的归一化向量,这个向量和输入向量的角度相同,但是长度正好是 1.0。这非常有用,因为许多算法和其他函数都期望归一化向量。

力量

助记符 : ret pow(x,y) 着色器 模型 : 1.1

参数:返回 x 的 y 次方;x 和 y 可以是标量、向量或矩阵。

说明:将 x 升到 y 的幂;x、y 和 ret 必须都是相同的类型和大小。

弧度度数

助记符 : ret 弧度(x) 着色器 模型 : 1.0

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此功能将 x 中的角度,以度为单位读取,转换为弧度。如果 x 是向量或矩阵,则 x 中的所有元素都执行转换。这与 x 中的值乘以π/180 相同。

近似倒数

助记符 : ret rcp(x) 着色器 模型 : 5.0

参数 : x 可以是标量、矢量或矩阵;ret 将具有与 x 相同的类型。

描述:该函数计算 x 中元素倒数的近似值;倒数是 1.0 除以参数 x 中的元素。

反射向量

助记符 : ret reflect(x,y) 着色器 模型 : 1.0

参数 : x 和 y 是大小相同的浮点向量;x 是入射的矢量,y 是 x 撞击的表面的法线。

描述:该函数计算并返回给定入射光线(x 值)和表面法线(y 值)的反射矢量。它根据以下公式计算结果:ret = x-2ydot(x,y)。

y 是曲面法向量,应该归一化,或者长度正好为 1.0。

折射矢量

助记符 : ret 折射(x,y,z) 着色器 模型 : 1.1

参数 : x 和 y 是大小相同的浮点向量;x 是入射光线的矢量,y 是光线入射表面的法线。z 是一个标量,即折射率。

说明:给定入射光线的矢量,光线正在入射的表面法线,z 为光线正在入射的物质的折射率,该函数计算并返回折射矢量。例如,水的折射率约为 1.333,空气的折射率约为 1.000,钻石的折射率约为 2.419。

轮次

助记符 : ret round(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数将 x 中的一个或多个值舍入到最接近的整数,并返回结果值。

平方根的倒数

助记符 : ret rsqrt(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数计算 x 中一个或多个值的平方根的倒数,即 1.0/sqrt(x)。

饱和

助记符 : ret 饱和(x) 着色器 模型 : 1.0

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数饱和并返回 x 中的一个或多个值。饱和是指箝位在 0.0 和 1.0 之间。任何小于 0.0 的值都将变成 0.0,任何大于 1.0 的值都将变成 1.0。

正弦

助记符 : ret sin(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数以弧度为单位正弦值或以 x 为单位的值。

正切

助记符 : ret tan(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:该函数以弧度为单位正切一个或多个 x 值。

平方根

助记符 : ret sqrt(x) 着色器 模型 : 1.1

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数计算并返回 x 中一个或多个值的平方根。

缩短

助记符 : ret trunc(x) 着色器 模型 : 1.0

参数 : x 可以是浮点标量、矢量或矩阵;ret 将具有与 x 相同的类型。

说明:此函数通过截断或截断基点右侧的任意数字,将浮点值舍入为整数;换句话说,它执行浮点到 int 的转换,然后返回浮点。它将数字四舍五入到 0.0。