DirectX 11:顶点着色器和像素着色器如何工作

3
我正在阅读《Practical Rendering and Computation with Direct3D 11》这本书,并查看DirectX 11示例:

CPP: https://github.com/walbourn/directx-sdk-samples/blob/master/Direct3D11Tutorials/Tutorial02/Tutorial02.cpp

HLSL: https://github.com/walbourn/directx-sdk-samples/blob/master/Direct3D11Tutorials/Tutorial02/Tutorial02.fx

这是什么?

D3D11_INPUT_ELEMENT_DESC layout[] =
 {
  { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
 };

更重要的是着色器--程序中在哪里设置了这个"Pos"?
float4 VS( float4 Pos : POSITION ) : SV_POSITION
{
    return Pos;
}

我只能看到你是通过以下方式绑定顶点数据:
// Create vertex buffer
SimpleVertex vertices[] =
{
    XMFLOAT3( 0.0f, 0.5f, 0.5f ),
    XMFLOAT3( 0.5f, -0.5f, 0.5f ),
    XMFLOAT3( -0.5f, -0.5f, 0.5f ),
};
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if( FAILED( hr ) )
    return hr;

// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

// Set primitive topology
g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

然后您需要设置着色器并绘制3个顶点:

// Render a triangle
g_pImmediateContext->VSSetShader( g_pVertexShader, nullptr, 0 );
g_pImmediateContext->PSSetShader( g_pPixelShader, nullptr, 0 );
g_pImmediateContext->Draw( 3, 0 );

这一切都有道理。但我对这个Pos输入是什么,它是如何生成的,以及为什么要使用它感到困惑?此外,还有其他类似的示例(样本7),它们使用了位置和纹理,但我看不出这些信息来自哪里。例如...
struct VS_INPUT
{
  float4 Pos : POSITION;
  float2 Tex : TEXCOORD0;
};

感谢您的时间!


在这个上下文中,POSITION 是物体空间中的顶点位置。 - Asesh
1个回答

1

D3D11_INPUT_ELEMENT_DESC数组确定从顶点缓冲区读取的数据布局。该数组传递给ID3D11Device::CreateInputLayout调用,以便使用它。然后通过调用ID3D11DeviceContext::IASetInputLayout在渲染上下文中设置它(未显示,但在您链接的代码中)。

如果您查看D3D11_INPUT_ELEMENT_DESC的成员,第一个成员是SemanticName(设置为"POSITION"),这是顶点缓冲区元素与着色器输入匹配的方式。它还包含有关数据偏移和格式的数据(在本例中,偏移量为零,格式DXGI_FORMAT_R32G32B32_FLOAT对应于3个32位浮点数)。您会注意到,在着色器中,在Pos之后有一个冒号,然后是"POSITION" - 这是语义,并与输入布局匹配。然后,着色器知道它应该从偏移零读取位置数据以及该数据的格式。
数据本身是从顶点缓冲区读取的,使用 ID3D11DeviceContext::IASetVertexBuffers 调用设置。 stride 参数告诉输入装配器从一个顶点到下一个顶点在顶点缓冲区中的距离,因此它知道从哪里开始读取下一个顶点。传入的 g_pVertexBuffer 是从 vertices 数据初始化的,你可以看到它是一组 3x32位浮点数。
对于具有其他输入(例如 TEXCOORD0)的着色器,在 D3D11_INPUT_ELEMENT_DESC 数组中将有更多条目,对应于着色器中使用的语义。

好的!如果您想使用自定义变量怎么办?您需要将其编码到纹理或顶点缓冲区中吗?谢谢! - Mike5050
只要输入布局和着色器之间匹配,您可以使用任何语义。 - MuertoExcobito
https://msdn.microsoft.com/zh-cn/library/windows/desktop/bb509647(v=vs.85).aspx#VS - 所以你必须使用语义,这就是我想说的吗? 你不能自己编造吗? 我猜语义告诉HLSL它将如何行动?例如,您可以在POSITION(3个32位值)中放置TEXCOORD0(2个32位值),并忽略z。 对吗? - Mike5050
在D3D9中,顶点语义通常必须对应于该链接中的“顶点着色器语义”列表,因为它的D3D11_INPUT_ELEMENT_DESC数组版本(D3DVERTEXELEMENT9 https://msdn.microsoft.com/en-us/library/windows/desktop/bb172630(v=vs.85).aspx)仅允许使用预定义类型。在D3D11中,这些语义仍然可以使用,但不意味着任何“特殊”的含义,除了系统值(SV_)之外,您可以使用任何想要的语义,因为它们是保留的。 - MuertoExcobito
我可以使用语义称为:“THIS_IS_NOT_A_DX11_SEMANTIC”来代替“POSITION”,这不会影响任何东西吗?如果是这样,那么为什么还需要语义呢?另外,如果您删除“POSITION”语义或根本不使用它们,输出合并器如何知道如何处理信息?我假设在某个地方必须告诉GPU如何实际处理数据以将其输出到屏幕上! - Mike5050

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接