着色器shader
+ -

顶点着色器VertexShader

2024-05-14 22 0

什么是顶点着色器?

  • 顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行。
  • 同一时间内,只能激活一个顶点着色器。
  • 每个源顶点着色器最多拥有128条指令(DirextX8.1),而在DirectX9,则可以达到256条。

为什么大家要使用顶点着色器?

  • 顶点着色器可以提高渲染场景速度(直接由显卡自动调用,无需人为干预)。
  • 用顶点着色器你可以做布类仿真,高级别动画,实时修改透视效果(比如水底效果),高级光亮(需要像素着色器支持)

顶点着色器如何运作?

当渲染一个顶点时,API会执行你在顶点着色器中所写的指令。依靠这种方法,你可以自己控制每个顶点,包括渲染,确定位置,是否显示在屏幕上。

如何创建一个顶点着色器?

  用一个文本编辑器就可以了!我建议你们使用notepad或者vs开发环境来创建和修改着色器。另外,必须拥有一个支持可编程着色器的显卡。写完着色器后,保存他。API就可以调用他了(Direct3D或OpenGL)。API通过一些函数来调用这些代码指令到硬件中

Vertex Shader(顶点着色器)是针对每个顶点进行操作的,它的输入是一个顶点,包含用户指定的顶点的基本信息,输出是该顶点转换后的信息。
最常见的顶点着色器即对顶点的位置坐标、纹理坐标、法线等信息进行变换,并返回新的顶点,传递给下一个阶段。一般针对顶点着色器的输入与输出,程序员各指定相应的结构来存储相应的顶点信息,如:

struct VertexIn  
{  
    float3  pos     : POSITION;  
    float4  color   : COLOR;  
};

就是一个简单的输入结构,float3和float4是HLSL内置类型,即用来存放3个float和4个float,此外还有其他很多类型诸如float4x4(4x4矩阵)等,详细可参考HLSL相关资料。pos和color即相应的成员变量名,”POSITION”和”COLOR”,同来对相应的成员变量进行语义说明,”POSITION”很显然指位置坐标,”COLOR”即顶点的颜色。这些语义说明在C++程序中创建输入布局(InputLayout)时会用来,并且必须要一致,这个在本文稍后介绍InputLayout时还会解释。

struct VertexOut  
{  
   float4 posH : SV_POSITION;  
   float4 color : COLOR;  
};

这个结构即顶点着色器的输出结构,与输入基本类似,但pos对应地变成了float4类型,这个是齐次坐标下的位置,即经过投影变换后的坐标,这时的顶点坐标必须是float4类型,还有一点重要的是,它对应的语义说明SV_POSITION,也是固定的,”SV“即”System Value”,系统值,在像素着色器阶段会需要这个值来进行裁剪操作。除了系统值必须固定外,其他的语义值都是可以任意指定的(虽然可以任意指定,但一般情况下显然是以变量真实作用为依据嘛)。

有了该两个结构,顶点着色器函数如下:

VertexOut VS(VertexIn vin)  
{  
    VertexOut vout;  
    vout.posH = mul(float4(vin.pos,1.f),g_worldViewProj);  
    vout.color = vin.color;  

    return vout;  
}

该函数名是可以任意指定的,其参数为输入顶点结构,输出为相应的输出顶点结构。在这个函数里面,它实现的是一个最简单的功能,即坐标变换和颜色的简单传递。

这里用到了几个HLSL内置函数:mul用来实现向量、矩阵的相乘,其维数是可度的,比如float3和float3x3相乘,float4和float4x4相乘,也可以进行相同维数的矩阵的相乘。由于输出坐标要求为float4类型,因些相乘时需要float4和float4x4。这里float4通过复制输入顶点后接第4个参数1.f作为构造函数生成,g_worldViewProj为一个全局变量,类型为float4x4,用于进行世界、视角、投影变换,三个变量合成到一个矩阵中实现。

顶点着色器示例

如使用如下代码
VertexShader.hlsl
174533262624

struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD;
};


//VS是顶点着色器的入口-固定的,就像main一样
VS_OUTPUT VS(VS_INPUT input)
{
    return input;
}

经过VS的HLSL Shader Compiler编译之后,生成VertexShader.h
而VertexShader.h生成的其实是一个数组:

const BYTE g_VS[] ={
...
};

这样就可以在D3D11中创建顶点着色器:

ID3D11VertexShader* m_VertexShader
ID3D11InputLayout* m_InputLayout
     HRESULT hr;

    UINT Size = ARRAYSIZE(g_VS);
    hr = m_Device->CreateVertexShader(g_VS, Size, nullptr, &m_VertexShader);
    if (FAILED(hr))
    {
        return ProcessFailure(m_Device, L"Failed to create vertex shader in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
    }

    D3D11_INPUT_ELEMENT_DESC Layout[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
    };
    UINT NumElements = ARRAYSIZE(Layout);
    hr = m_Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_InputLayout);
    if (FAILED(hr))
    {
        return ProcessFailure(m_Device, L"Failed to create input layout in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
    }
    m_DeviceContext->IASetInputLayout(m_InputLayout);

    m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0);

在顶点结构中,针对每个成员,我们要定义一个相应的D3D11_INPUT_ELEMENT_DESC。这个结构定义了该成员相应的有关信息,定义如下:

typedef struct D3D11_INPUT_ELEMENT_DESC {  
  LPCSTR                     SemanticName;  
  UINT                       SemanticIndex;  
  DXGI_FORMAT                Format;  
  UINT                       InputSlot;  
  UINT                       AlignedByteOffset;  
  D3D11_INPUT_CLASSIFICATION InputSlotClass;  
  UINT                       InstanceDataStepRate;  
} D3D11_INPUT_ELEMENT_DESC;
  • SemanticName指的是我们在着色器中定义输入顶点结构时对应的语义说明,比如对于位置坐标,为“POSITION”;
  • SemanticIndex为该语义说明的序号,是这样的,在一个顶点结构中,不同的成员可以使用相同的语义说明,这时要用不同的序号来区分他们,相应的序号就是这个SemanticIndex。比如一个顶点有两层纹理坐标,tex1和tex2,它们的语义说明都为”TEXTCOORD”,相应的序号则为0和1;
  • Format为该成员的数据类型,对于坐标即为DXGI_FORMAT_R32G32B32_FLOAT(3个float);
  • InputSlot为输入槽,一般情况下我们指定为0。此外,在个别情况下,不同的顶点信息可以通过不同的输入槽提供给GPU,这时通过该成员来指定其输入槽序号,多个输入槽的使用在以后会有学习;
  • AlignedByteOffset,即该成员在顶点结构中字节偏移量,对于这里的位置坐标,为第一个成员,因此偏移为0。对于颜色值,由于位置坐标占据了12个字节,因此颜色值对应的偏移为12,依次类推;
  • InputaSlotClass,对于一般情况,为D3D11_INPUT_PER_VERTEX_DATA,这时相应的最后一个成员InstanceDataStepRate就要设为0。

顶点结构中对应的每个成员,都对应一个D3D11_INPUT_ELEMENT_DESC,所有的D3D11_INPUT_ELEMENT_DESC存放在一个数组当中。因此,对于上面定义的顶点结构,我们可以如下数组:

D3D11_INPUT_ELEMENT_DESC inputDesc[2] =   
{  
    { "POSITION",   0, DXGI_FORMAT_R32G32B32_FLOAT,     0,  0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },  
    { "COLOR",  0, DXGI_FORMAT_R32G32B32A32_FLOAT,  0,  12, D3D11_INPUT_PER_VERTEX_DATA, 0 }  
};

定义好这个,下一步就是用它来创建Input Layout了,相应的函数如下:

HRESULT ID3D11Device::CreateInputLayout(  
  [in]   const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,  
  [in]   UINT NumElements,  
  [in]   const void *pShaderBytecodeWithInputSignature,  
  [in]   SIZE_T BytecodeLength,  
  [out]  ID3D11InputLayout **ppInputLayout  
);

第一个参数即我们刚定义好的D3D11_INPUT_ELEMENT_DESC数组;第二个参数为数组成员个数,这里为2;第三个和第四个成员需要用到编译好的Effect程序,获取方式如下:

D3DX11_PASS_DESC passDesc = {0};  
g_effect->GetTechniqueByName("BasicDraw")->GetPassByIndex(0)->GetDesc(&passDesc);  
g_device->CreateInputLayout(inputDesc,2,passDesc.pIAInputSignature,passDesc.IAInputSignatureSize,&g_inputLayout);

g_effect为事先创建好的ID3DX11Effect*类型变量,代表编译好的Effect,通过它获取相应technique11中相应的pass的描述信息,通过pass描述信息即可获取我们这里需要的两个参数,如上代码中所示,意指shader中输入结构信息,及该信息的字节长度;最后一个参数即我们要创建的input layout接口的地址。

到这里,Input Layout就创建好了。

0 篇笔记 写笔记

顶点着色器VertexShader
什么是顶点着色器顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行。同一时间内,只能激活一个顶点着色器。每个源顶点着色器最多拥有128条指令(DirextX8.1),而在DirectX9,则可以达到256条。为什么大家要使用顶点着色器顶点着色器可以提高渲染场景速度(直接由显卡自动调用......
3.4 顶点着色器-简介
渲染管道中的第一个可编程着色器阶段是顶点着色器。如上所述,可编程着色器阶段执行用HTSL编写的自定义函数。在顶点着色器阶段的情况下,顶点着色器程序是为输入装配程序生成的顶点流中的每个顶点调用一次的函数。每个输入顶点都作为顶点着色器程序的参数接收,并且处理后的顶点作为函数的结果返回。每个顶点着色器调用......
3.4.1顶点着色器管道输入
由于顶点着色器直接位于管道中的输入装配程序之后,因此它自然会从那里接收输入。配置输入装配程序的输入布局所做的所有工作都旨在使创建的顶点与顶点着色器阶段的当前程序所期望的格式相匹配。这就是为什么在创建ID3DllInputl_ayout对象时需要将编译的着色器字节代码作为输入,以确保组装的顶点与执行顶......
3.4.2顶点着色器状态配置
作为可编程着色器阶段,顶点着色器实现通用着色器核心功能。这意味着它提供了一组标准的资源接口方法,允许应用程序向着色器程序提供对所需资源的访问。与所有管道操作一样,所有可以更改顶点着色器阶段状态的方法都属于ID3DllDeviceContext接口。我们将查看这些可用资源中的每一个,并了解它们在顶点着......
3.4.3顶点着色器阶段处理
我们现在知道顶点着色器可以从输入装配程序接收哪些数据作为输入数据,以及哪些资源可以被主机应用程序绑定。顶点绘冲区常量缓冲区着色器资源视图我们还知道,顶点着色器程序提供了对单个顶点的自定义处理,而与调用管道的拓扑无关。那么,在顶点着色器程序中执行哪些类型的操作呢?某些类型的操作更适合这个阶段的组......
3.4.4顶点着色器管道输出
在决定在输出顶点结构中包括哪些信息时,需要考虑如何使用管道的其余部分。图3.18显示了渲染管道的框图。顶点着色器阶段之后是一组曲面细分阶段(外壳着色器、曲面细分器和域着色器阶段),然后是几何体着色器,然后是光栅化器阶段。根据顶点着色器和光栅化器阶段之间这些阶段中的哪一个处于活动状态,必须满足不同的......
作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!