04.开始绘制2
顶点结构与输入布局
我们制作的所有3D对象都将由具有属性(如颜色)的点组成,这些属性称为顶点。我们必须制作自己的顶点结构。这是一个重载的顶点结构(因此我们可以轻松动态地创建和编辑顶点),只有一个位置。注意XMFLOAT3。正如我在上一课中提到的,direct3d正在从d3dx数学库转移到更流行的xna数学库。之前我们会使用D3DXVECTOR3。
之后,您可以看到我们的输入布局。使用D3D11_input_ELEMENT_DESC结构的数组定义输入布局。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 :这只是一个要与元素关联的字符串。此字符串将用于将顶点结构中的元素映射到顶点着色器中的元素。
- SemanticIndex :这基本上只是语义名称后面的一个数字,用作索引。例如,如果我们在顶点结构中有两个纹理元素,而不是创建两个不同的纹理语义名称,我们可以只使用两个不同索引。如果顶点着色器代码中的语义名称后面没有索引,则默认为索引0。例如,在着色器代码中,我们的语义名称是“POSITION”,实际上与“POSITION0”相同。
- Format :这只是我们的顶点结构中组件的格式。它必须是DXGI_FORMAT枚举类型的成员。在本课中,我们有一个描述位置的三维矢量,因此我们可以使用DXGI_FORMAT:DXGI_FORMAT_R32B32_FLOAT 。如果您需要其他格式,可以在msdn上找到它们。稍后我们将使用其他的。
- InputSlot :Direct3D允许我们使用16个不同的元素槽(0-15),您可以通过这些槽放置顶点数据。如果我们的顶点结构有一个位置和颜色,我们可以将两个元素放在同一个输入槽中,或者我们可以将位置数据放在第一个槽中,将颜色数据放在第二个槽中。我们只需要使用一个,但如果你愿意,你可以进行实验。
- AlignedByteOffset :这是您所描述的元素的字节偏移量。在单个输入槽中,如果我们有位置和颜色,位置可能是0,因为它从顶点结构的开头开始,而颜色需要是顶点位置的大小,即12个字节(请记住,我们的顶点位置格式是DXGI_format_R32B32_FLOAT,它是96位,位置中的每个组件32位。一个字节中有8位,因此96/8==12)。
- InputSlotClass :现在我们只能使用D3D10_INPUT_PER_VERTEX_DATA。其他选项用于实例化,这是我们稍后将了解的高级技术。
- InstanceDataStepRate :这也仅用于实例化,因此我们现在将指定0
定义输入布局后,我们创建一个全局变量来保存输入布局数组的大小。我们这样做是为了以后我们不必记住不断更新创建输入布局的函数。
struct Vertex //Overloaded Vertex Structure
{
Vertex(){}
Vertex(float x, float y, float z) : pos(x,y,z){}
XMFLOAT3 pos;
};
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = ARRAYSIZE(layout);
清理
void CleanUp()
{
//Release the COM Objects we created
SwapChain->Release();
d3d11Device->Release();
d3d11DevCon->Release();
renderTargetView->Release();
triangleVertBuffer->Release();
VS->Release();
PS->Release();
VS_Buffer->Release();
PS_Buffer->Release();
vertLayout->Release();
}
初始化场景
这是我们初始化场景的地方。这就是我们在游戏过程中会改变的东西,但在整个场景中不会改变的地方。这节课上几乎所有的新内容都在这里。我将一次解释一部分。
bool InitScene()
{
//Compile Shaders from shader file
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_5_0", 0, 0, 0, &VS_Buffer, 0, 0);
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_5_0", 0, 0, 0, &PS_Buffer, 0, 0);
//Create the Shader Objects
hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS);
hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS);
//Set Vertex and Pixel Shaders
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);
//Create the vertex buffer
Vertex v[] =
{
Vertex( 0.0f, 0.5f, 0.5f ),
Vertex( 0.5f, -0.5f, 0.5f ),
Vertex( -0.5f, -0.5f, 0.5f ),
};
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 3;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
vertexBufferData.pSysMem = v;
hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &triangleVertBuffer);
//Set the vertex buffer
UINT stride = sizeof( Vertex );
UINT offset = 0;
d3d11DevCon->IASetVertexBuffers( 0, 1, &triangleVertBuffer, &stride, &offset );
//Create the Input Layout
hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(),
VS_Buffer->GetBufferSize(), &vertLayout );
//Set the Input Layout
d3d11DevCon->IASetInputLayout( vertLayout );
//Set Primitive Topology
d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
//Create the Viewport
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = Width;
viewport.Height = Height;
//Set the Viewport
d3d11DevCon->RSSetViewports(1, &viewport);
return true;
}
编译着色器
我们将通过创建着色器开始初始化场景。我们将从名为“Effects.fx”的效果文件编译着色器。我们可以使用函数D3DX11CompileFromFile)来完成此操作:
HRESULT WINAPI D3DX11CompileFromFile(
LPCSTR pSrcFile,
CONST D3D10_SHADER_MACRO* pDefines,
LPD3D10INCLUDE pInclude,
LPCSTR pFunctionName,
LPCSTR pProfile,
UINT Flags1,
UINT Flags2,
ID3DX11ThreadPump* pPump,
ID3D10Blob** ppShader,
ID3D10Blob** ppErrorMsgs,
HRESULT* pHResult);
- pSrcFile :着色器文件名。
- pDefines :一个数组宏指针。这里设为NULL.
- pInclude :这是一个指向include接口的指针。如果着色器在文件中使用#include,则不能在此处放置NULL,但着色器没有include,因此我们将其设置为NULL。
- pFunctionName :着色器文件中的函数名称。
- pProfile :要使用的着色器的版本。Direct3D 11支持着色器版本5.0。然而,在我的笔记本电脑上,我需要将其设置为“vs_4_0”和“ps_4_0“。
- Flags1 :编译标志,我们将其设置为NULL。
- Flags2 :效果标志。我们还将其设置为NULL。
- pPump :这与多线程有关。我们设置NULL,这样函数在完成之前不会返回。
- ppShader :这是返回的着色器。它不是实际的着色器,更像是包含着色器和有关着色器的信息的缓冲区。然后我们将使用这个缓冲区来创建实际的着色器。
- ppErrorMsgs :这将返回编译着色器时发生的错误和警告的列表。这些错误和警告与您在调试器底部看到的相同。
- pHResult :这是返回的HRESULT。我们这样做是为了“hr=”这个函数,但你也可以把“&hr”作为这个参数来做同样的事情。
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_5_0", 0, 0, 0, &VS_Buffer, 0, 0);
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_5_0", 0, 0, 0, &PS_Buffer, 0, 0);
创建着色器
创建顶点着色器和像素着色器使用的函数分别如下:
HRESULT CreateVertexShader(
[in] const void *pShaderBytecode,
[in] SIZE_T BytecodeLength,
[in] ID3D11ClassLinkage *pClassLinkage,
[in, out] ID3D11VertexShader **ppVertexShader) = 0;
);
HRESULT CreatePixelShader(
[in] const void *pShaderBytecode,
[in] SIZE_T BytecodeLength,
[in] ID3D11ClassLinkage *pClassLinkage,
[in, out] ID3D11PixelShader **ppPixelShader) = 0;
);
- pShaderBytecode :编译后的着色器字节流缓冲区指针
- BytecodeLength :字节流长度
- pClassLinkage :指向类链接接口的指针。我们将把它设置为NULL。
- ppVertexShader :顶点着色器返回
- ppPixelShader :像素着色器返回
hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS);
hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS);
设置着色器
现在我们已经编译并创建了着色器,我们需要将它们设置为当前管道着色器。如果要设置顶点着色器,我们可以通过调用ID3D11DeviceContext::VSSetShader来实现,如果要设置像素着色器,则可以调用IID3D11DeviceContext:VSSetShadow(稍后我们将学习设置其他着色器,如几何体着色器)。
大多数时候,应用程序将为不同的几何体集使用不同的着色器集,例如,稍后我们将在绘制skybox时使用单独的像素着色器。因此,您将在运行时设置着色器,而不是仅在场景设置功能中设置着色器。请记住,direct3d是一个“状态机”,它将保持当前状态和设置,直到以后更改为止,所以不要期望direct3d在代码中设置着色器后将其设置回默认值,在渲染内容之前,您需要始终设置正确的着色器。这也适用于渲染状态和其他事情。我们稍后也将讨论渲染状态。
void VSSetShader(
[in] ID3D11VertexShader *pVertexShader,
[in] (NumClassInstances) ID3D11ClassInstance *const *ppClassInstances,
[in] UINT NumClassInstances);
);
void PSSetShader(
[in] ID3D11PixelShader *pPixelShader,
[in] (NumClassInstances) ID3D11ClassInstance *const *ppClassInstances,
[in] UINT NumClassInstances);
);
参数如下:
- pVertexShader :顶点着色器指针
- pPixelShader :像素着色器指针
- ppClassInstances :仅当着色器使用类接口时才使用此选项。设置为NULL。
- NumClassInstances :这是ppClassInstances中数组中的类实例数。我们设置为0,因为没有。
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);