找回密码
 立即注册
查看: 270|回复: 0

HLSL学习笔记——核心概念整理

[复制链接]
发表于 2023-4-4 20:23 | 显示全部楼层 |阅读模式
着色器(Shader):

计算机图形学中,着色器是指一种特殊的程序,它们被用来为图形渲染管线的各个阶段提供可编程的功能,用于计算光照、纹理映射、颜色输出等等着色器是HLSL程序的核心部分,它是用来计算像素颜色和顶点位置的程序。
在HLSL中,有两种着色器,分别是顶点着色器和像素着色器。

顶点着色器(Vertex Shader):用于接收由顶点着色器传递过来的属性和纹理采样结果,处理模型的顶点数据,例如将顶点从模型空间变换到世界空间、相机空间或裁剪空间。顶点着色器可以进行光照计算,还可以将顶点属性传递给像素着色器。

像素着色器(Pixel Shader):用于对每个像素进行处理,例如对纹理进行采样和插值,计算像素的颜色值和透明度等。像素着色器也可以进行复杂的光照计算和特效处理。

常量缓冲区(Constant Buffer):

常量缓冲区是HLSL中的一种数据类型,用于在着色器中存储和管理常量数据。它通常用于存储不会在运行时更改的常量,例如变换矩阵、材质属性和灯光位置等。
常量缓冲区可以在HLSL代码中定义和使用,定义一个常量缓冲区需要指定缓冲区名称、缓冲区大小和缓冲区中数据的结构体类型。
代码:
cbuffer ConstantBuffer : register(b0)
{
    float4x4 worldMatrix;
    float4x4 viewMatrix;
    float4x4 projectionMatrix;
    float4   lightDirection;
    float4   lightColor;
    float4   ambientColor;
};定义了一个名为ConstantBuffer的常量缓冲区,使用register关键字将它绑定到寄存器b0上。缓冲区的大小是所有数据成员大小的总和,这里是(16x4)x3 + 16x3 = 208字节。
在着色器代码中使用常量缓冲区的数据时,需要通过缓冲区名称和数据成员名称来访问。例如:
float4x4 worldMatrix = ConstantBuffer.worldMatrix;
float4x4 viewMatrix = ConstantBuffer.viewMatrix;
float4x4 projectionMatrix = ConstantBuffer.projectionMatrix;
float4   lightDirection = ConstantBuffer.lightDirection;
float4   lightColor = ConstantBuffer.lightColor;
float4   ambientColor = ConstantBuffer.ambientColor;
//在使用常量缓冲区时,需要指定常量缓冲区的寄存器。
//常量缓冲区在定义时需要指定缓冲区的大小,这个大小必须是16个字节的倍数。常量缓冲区中存储的数据可以是整型、浮点型或向量类型。
//在着色器程序执行期间,常量缓冲区中的数据可以被读取,但不能被写入。
//可以同时定义多个常量缓冲区,通过不同的寄存器绑定到不同的缓冲区。
寄存器(Register):

在GPU硬件中,寄存器通常被设计用于高速缓存数据和指令,以加速程序的执行。在HLSL中,寄存器被用于存储常量缓冲区、纹理和其他输入/输出数据。
HLSL中的寄存器有多种类型,每种类型有其特定的命名规则和使用方式。以下是一些常见的HLSL寄存器及其用途:

  • 常量缓冲区寄存器(Constant Buffer Registers):常量缓冲区寄存器用于存储着色器程序中需要频繁访问的常量值。这些常量值可以是场景中的光照属性、物体的材质属性、视角的变换矩阵等等。常量缓冲区寄存器的命名规则为:cb[SlotIndex],其中SlotIndex表示常量缓冲区的索引号。
  • 纹理寄存器(Texture Registers):纹理寄存器用于存储着色器程序中需要使用的纹理图像,纹理数据可以是二维图像、三维图像、立方体贴图等。在渲染过程中,着色器会从纹理寄存器中读取纹理数据,并根据着色器代码对其进行处理。纹理寄存器的命名规则为:t[SlotIndex],其中SlotIndex表示纹理寄存器的索引号。
  • 状态寄存器(State Registers):状态寄存器用于存储GPU渲染管线的状态信息。例如,深度测试开启与否、是否启用多重采样、是否启用透明度混合等等。状态寄存器的命名规则为:s[SlotIndex],其中SlotIndex表示状态寄存器的索引号。
  • 输入输出寄存器(Input/Output Registers):输入输出寄存器用于在不同阶段之间传递数据。例如,顶点着色器的输出可以被传递到像素着色器中进行处理。输入输出寄存器的命名规则为:v[SemanticName][SemanticIndex](输入寄存器)和o[SemanticName][SemanticIndex](输出寄存器),其中SemanticName表示变量的语义名称(例如,POSITION、NORMAL、TEXCOORD等等),SemanticIndex表示语义的索引号。
以下是一些HLSL寄存器的示例:
cb0 : register(b0) // 声明一个常量缓冲区寄存器,索引号为0
t3 : register(t3) // 声明一个纹理寄存器,索引号为3
s0 : register(s0) // 声明一个状态寄存器,索引号为0
v0 : register(semantic="POSITION") //寄存器是指用于存储着色器中数据的硬件存储器单元。每个寄存器都有一个唯一的寄存器地址,着色器代码中的变量可以映射到不同类型的寄存器中,以便在GPU上执行时使用。
使用寄存器可以让着色器程序更高效地访问数据,因为它们可以直接访问硬件存储器,而不需要像变量那样经过额外的寻址操作。但是,寄存器数量有限,因此需要合理地分配寄存器资源,以最大限度地提高着色器程序的性能。
纹理(Texture):

纹理是一种存储图像数据的数据结构,在计算机图形学中,纹理是指在物体的表面上绘制的二维图像。HLSL中的纹理是指二维、三维或立体纹理贴图,它们被用来模拟物体的表面材质和纹理。纹理由像素组成,每个像素都包含红、绿、蓝和透明度信息,这些信息可以被采样器采样。HLSL提供了一些内置的纹理数据类型,例如Texture1D、Texture2D、Texture3D和TextureCube等。
其中,Texture1D和Texture2D是最常见的纹理类型。Texture1D是一维的纹理,其像素沿着水平方向排列。Texture2D是二维的纹理,其像素排列在一个二维矩形中。Texture3D是三维的纹理,它类似于Texture2D,但多了一个深度维度。TextureCube是一个立方体纹理,由6个二维纹理组成,用于渲染天空盒和反射等效果。

在HLSL中,纹理的采样由采样器(Sampler)控制。采样器定义了纹理采样的方式,包括采样过滤器、纹理地址模式、纹理坐标偏移等。常见的采样过滤器包括点过滤器(Point Filter)、线性过滤器(Linear Filter)和各向异性过滤器(Anisotropic Filter)。点过滤器只对纹理进行最近邻插值,而线性过滤器对相邻像素进行插值,可以获得更平滑的纹理。各向异性过滤器可以同时在多个方向上进行插值,可以提供更高质量的纹理采样。
纹理坐标是指用于访问纹理像素的二维、三维或立体坐标。纹理坐标通常由像素着色器计算,并通过采样器进行采样。纹理坐标范围从0到1,其中0表示纹理的左边界或底边界,1表示纹理的右边界或顶边界。纹理坐标可以通过偏移来进行微调,例如使用偏移量(0.5, 0.5)可以将纹理坐标从像素中心移到像素边缘,这通常用于减少采样误差。

采样器(Sampler):

采样器(Sampler)是一个用来描述纹理采样方式的对象,用于告诉着色器如何从纹理中取样,以获取最终的颜色值。
在渲染3D场景时,常常需要将一个或多个纹理映射到模型表面上,以模拟真实世界的材质效果。这时,采样器对象就发挥了重要作用。通过采样器,我们可以控制纹理的过滤方式、边界处理、采样坐标的偏移量等。
HLSL中提供了一些预定义的采样器状态(Sampler State),可以通过设置不同的采样器状态,来实现不同的采样效果。一些常用的采样器状态包括:

  • Filter:用于控制纹理的过滤方式,包括Point(点采样)、Linear(线性采样)和Anisotropic(各向异性采样)等。
  • Address U/V/W:用于控制纹理坐标的边界处理方式,包括Clamp(使用边界颜色进行采样)、Wrap(在纹理坐标范围内循环采样)和Mirror(在纹理坐标范围内反向循环采样)等。
  • Max Anisotropy:用于控制各向异性采样时的最大各向异性程度。
  • MipLODBias:用于控制纹理的mipmap级别偏移量,可以用来实现远近距离下的不同采样效果。
  • MaxMipLevel:用于限制纹理的最大mipmap级别,可以用来控制纹理的粗细程度。
在HLSL中,采样器对象的类型为SamplerState。我们可以通过定义一个SamplerState变量,来创建一个新的采样器对象。例如:
SamplerState MySampler
{
    Filter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};
代码定义了一个名为MySampler的采样器对象,采用线性过滤方式,使用Wrap方式处理纹理坐标的U/V分量。在着色器程序中,我们可以使用MySampler采样器对象,来从纹理中获取像素颜色值。

输入汇编(Input Assembly):

输入汇编是将顶点数据转换为图形硬件可以使用的格式的过程。在渲染三维物体时,需要将物体的顶点数据(如位置、颜色、法线和纹理坐标等)转换为GPU可识别的格式,并将这些数据传递给GPU进行处理和渲染。在HLSL中,输入汇编可以通过InputAssembler等关键字来定义。

输入汇编的过程通常包括以下步骤:

  • 定义顶点结构体(Vertex Structure):开发者需要定义一个结构体来描述每个顶点的属性,包括位置、颜色、法线和纹理坐标等。在HLSL中,结构体的定义通常放在顶点着色器的输入部分。
  • 定义输入布局(Input Layout):输入布局定义了每个顶点结构体中属性的排列方式和数据类型。在HLSL中,输入布局由一个或多个输入元素(Input Element)组成,每个输入元素指定了一个顶点属性的名称、索引、数据类型和偏移量等信息。
  • 创建输入缓冲区(Input Buffer):开发者需要创建一个输入缓冲区来存储顶点数据。在HLSL中,输入缓冲区通常使用ID3D11Buffer接口来创建和管理。
  • 绑定输入缓冲区和输入布局:开发者需要使用ID3D11DeviceContext::IASetInputLayout和ID3D11DeviceContext::IASetVertexBuffers方法将输入缓冲区和输入布局与设备上下文关联起来,以便GPU可以读取和处理顶点数据。
  • 发送绘制命令(Draw Command):最后,开发者需要使用ID3D11DeviceContext::Draw命令发送绘制命令,告诉GPU如何使用输入汇编的数据来渲染三维物体。
输出合并(Output Merger):

输出合并是将是指将像素着色器处理后的颜色值与深度值等信息合并后输出到帧缓冲区(后缓冲区)中的过程。在HLSL中,输出合并可以通过OutputMerger等关键字来定义。
输出合并的过程通常包括以下步骤:

  • 像素着色器(Pixel Shader):像素着色器是计算像素颜色值和深度值等信息的主要阶段。在HLSL中,像素着色器通常使用输入汇编中的顶点数据和纹理贴图等信息来计算每个像素的颜色值和深度值等信息。
  • 模板测试(Stencil Test):模板测试是根据模板缓冲区中的设置来决定是否应该输出当前像素的一种测试方式。在HLSL中,可以使用ID3D11DepthStencilState接口来设置模板测试的参数。
  • 深度测试(Depth Test):深度测试是用来决定当前像素是否应该被绘制的一种测试方式。在HLSL中,可以使用ID3D11DepthStencilState接口来设置深度测试的参数。
  • α混合(Alpha Blending):α混合是根据当前像素的颜色和背景颜色以及透明度等信息来计算输出颜色值的过程。在HLSL中,可以使用ID3D11BlendState接口来设置α混合的参数。
  • 输出到后缓冲区(Output to Back Buffer):最后,GPU将像素着色器处理后的颜色值和深度值等信息与背景颜色和透明度等信息进行合并,并将最终的颜色值和深度值输出到后缓冲区中。
数据类型(Data Type):

在HLSL中,数据类型是指用来表示不同类型数据的分类。HLSL中的数据类型可以分为基本数据类型和复合数据类型两种。

  • 基本数据类型(Primitive Data Types):HLSL中的基本数据类型包括标量类型、向量类型、矩阵类型和布尔类型。

    标量类型(Scalar Types):标量类型表示单个数值,包括float、double、int和uint等。

    向量类型(Vector Types):向量类型表示一组数值,通常用来表示空间中的点、向量和颜色等信息。向量类型包括float2、float3、float4、int2、int3、int4、uint2、uint3和uint4等。

    矩阵类型(Matrix Types):矩阵类型表示一个二维数组,通常用来表示空间中的变换矩阵等信息。矩阵类型包括float2x2、float3x3、float4x4等。

    布尔类型(Boolean Type):布尔类型表示真假值,只有true和false两种取值。
  • 复合数据类型(Composite Data Types):HLSL中的复合数据类型包括结构体类型和数组类型。

    结构体类型(Struct Types):结构体类型可以将多个不同类型的变量组合成一个单独的变量。结构体类型在HLSL中通常用来表示材质和灯光等复杂的属性。可以使用struct关键字定义结构体类型。

    数组类型(Array Types):数组类型是一组相同类型的变量的集合,每个变量可以通过一个索引来访问。在HLSL中,数组类型可以用来表示纹理数组等。
除了基本数据类型和复合数据类型,HLSL还提供了一些特殊的数据类型,如SamplerState类型、Texture类型和ConstantBuffer类型等。这些数据类型通常用于表示材质、纹理和常量缓冲等信息。

变量(Variable):

变量是用于存储和操作数据的基本概念(没什么好记的)。
在HLSL中,变量的作用域和生命周期与普通编程语言中的类似。变量的作用域指定了变量的可见范围,例如全局变量可以在整个HLSL文件中使用,而局部变量只能在特定的函数或代码块中使用。变量的生命周期指定了变量在内存中存在的时间,例如全局变量在程序启动时创建,在程序结束时销毁,而局部变量在函数调用时创建,在函数返回时销毁。
除了普通变量外,HLSL中还有一些特殊的变量类型。其中最常见的是输入和输出变量,在着色器中用于与管线中其他阶段交换数据。输入变量通常是由顶点着色器输出,传递给像素着色器使用,而输出变量通常是由像素着色器输出,传递给后续渲染阶段使用。
HLSL中还有常量缓冲区变量(参考常量缓冲区示例)、纹理采样器变量、结构化缓冲区变量等特殊变量类型,它们用于管理常量数据、图像数据、结构化数据等不同的数据类型。
函数(Function):

HLSL中的函数定义与C/C++语言中的定义类似,但有其特定的语法。函数定义包括函数名称、返回类型、参数列表和函数体。函数的定义语法如下:
return_type function_name(parameter_list)
{
    function_body
}

其中,return_type是函数的返回类型;function_name是函数名;parameter_list是函数参数列表,可以包含多个参数,每个参数由参数类型和参数名组成,参数之间用逗号分隔;function_body是函数体,包含了函数的实现代码。
HLSL内置了许多函数,包括数学函数、向量和矩阵函数、纹理采样函数等。这些函数可以直接在着色器代码中调用,无需额外的库文件或头文件。
从类型上可分为:顶点着色器函数、像素着色器函数和计算着色器函数。这些函数使用不同的语法和执行方式,用于处理不同的任务。
顶点着色器函数是用于处理每个输入顶点的函数。它接受顶点数据作为输入,并输出可传递到像素着色器的顶点属性(例如顶点位置、颜色、纹理坐标等)。顶点着色器通常用于变换顶点位置、计算法向量、计算切线向量等任务。
像素着色器函数是用于处理每个像素的函数。它接受来自顶点着色器的输出和其他输入数据(例如纹理采样、常量缓冲区等)作为输入,并输出像素的最终颜色。像素着色器通常用于执行光照计算、纹理采样、颜色插值等任务。
计算着色器函数是用于执行通用计算任务的函数。它可以接受任意输入数据,并输出任意结果,通常用于执行通用的数值计算、物理模拟、计算流体动力学等任务。
以下是一些常用的内置函数:

  • abs:返回参数的绝对值
  • sin、cos、tan、asin、acos、atan:三角函数
  • pow:指数函数
  • dot、cross、normalize、transpose:向量和矩阵函数
  • tex1D、tex2D、tex3D、texCUBE:纹理采样函数
在HLSL中,可以使用预编译指令#define定义预编译函数,这些函数在编译时会被直接替换为预定义的表达式。例如:
#define MAX(a,b) (a > b ? a : b)

float a = 5;
float b = 10;
float maxvalue = MAX(a, b); // 替换为 (a > b ? a : b) 与C++语言一样,HLSL中的函数也可以进行重载,即定义多个名称相同但参数列表不同的函数。
语义(Semantic):

语义用于描述着色器输入和输出变量的含义和用途,以及它们如何与图形渲染管线的不同阶段相互作用。在着色器中定义变量或结构体成员时,使用关键字来指定其含义或作用。语义是HLSL中非常重要的概念,因为它们告诉图形处理器如何处理着色器输入和输出数据。
语义通常在变量声明中使用,它可以提供额外的信息,帮助着色器程序员和图形引擎解释变量的含义和用途。例如,输入变量可以使用语义来指示该变量表示什么类型的数据(例如,顶点位置、法线、纹理坐标等),输出变量可以使用语义指示该变量表示什么类型的数据(例如,颜色、深度、法线等),常量缓冲区变量可以使用语义指示该变量表示什么类型的数据(例如,矩阵、材质等),纹理变量可以使用语义指示该变量表示什么类型的纹理(例如,漫反射、镜面反射、环境光等)。
在HLSL中,可以使用变量语义来告诉编译器变量的用途和属性。变量语义通过在变量声明后添加冒号和语义名称来指定。例如:
float3 position : POSITION;
float2 uv : TEXCOORD0;
其中,position变量的语义是POSITION,表示这个变量用于存储顶点位置信息;uv变量的语义是TEXCOORD0,表示这个变量用于存储纹理坐标信息。
以下是HLSL中一些常见的语义及其含义:

  • SV_Position:指定顶点的位置,通常用于顶点着色器输入。
  • SV_Target:指定像素颜色输出的目标缓冲区,通常用于像素着色器输出。
  • TEXCOORD:指定纹理坐标,用于从纹理中采样颜色。
  • COLOR:指定顶点或像素的颜色。
  • NORMAL:指定顶点或像素的法线向量。
  • TANGENT:指定顶点或像素的切线向量。
  • BINORMAL:指定顶点或像素的副法线向量。
  • DEPTH:深度值
通过正确使用语义,开发人员可以确保着色器输入和输出数据的正确处理和转换,从而获得更好的图形渲染效果。
<hr/>
微软的开发文档不是给人看的吗o(╥﹏╥)o
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-23 15:50 , Processed in 0.135764 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表