Drawing in Direct3D(input layout vertex buffer index buffer and so on)
A input layout
一般的顶点格式
struct Vertex1 { XMFLOAT3 Pos; XMFLOAT4 Color; }; struct Vertex2 { XMFLOAT3 Pos; XMFLOAT3 Normal; XMFLOAT2 Tex0; XMFLOAT2 Tex1; };
需要向Direct3D描述顶点结构
(This description is provided to Direct3D in the form of an input layout (ID3D11InputLayout).)
而一个input layout 是D3D11_INPUT_ELEMENT_DESC的数组
而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;
(下面以MSDN和龙书为主)
1. SemanticName:(名字)A string to associate with the element. This can be any valid variable name. Semantics are used to map elements
in the vertex structure to elements in the vertex shader input signature
2.SemanticIndex:(名字索引,例如可能一个顶点有两套纹理坐标)An index to attach to a semantic
图6.1 (来自龙书)
3.Format:(类型)A member of the DXGI_FORMAT enumerated type specifying the format (i.e., the data type) of this vertex element to
Direct3D; here are some common examples of formats used:
DXGI_FORMAT_R32_FLOAT // 1D 32-bit float scalar DXGI_FORMAT_R32G32_FLOAT // 2D 32-bit float vector DXGI_FORMAT_R32G32B32_FLOAT // 3D 32-bit float vector DXGI_FORMAT_R32G32B32A32_FLOAT // 4D 32-bit float vector DXGI_FORMAT_R8_UINT // 1D 8-bit unsigned integer scalar DXGI_FORMAT_R16G16_SINT // 2D 16-bit signed integer vector DXGI_FORMAT_R32G32B32_UINT // 3D 32-bit unsigned integer vector DXGI_FORMAT_R8G8B8A8_SINT // 4D 8-bit signed integer vector DXGI_FORMAT_R8G8B8A8_UINT // 4D 8-bit unsigned integer vector
4. InputSlot:(中文名称不清楚,大意是d3d一共有16个index为0到15的inputslot,例如一个顶点包含position和color,可以单独feed一个inputslot,或者,分成两个feed两个inputslot)
Specifies the input slot index this element will come from. Direct3D supports sixteen input slots (indexed from 0−15)
through which you can feed vertex data. For instance, if a vertex consisted of position and color elements, you could either feed
both elements through a single input slot, or you could split the elements up and feed the position elements through the first input
slot and feed the color elements through the second input slot. Direct3D will then use the elements from the different input slots to
assemble the vertices.
5. AlignedByteOffset:(对于一个inputslot,就是offset,bytes为单位)
For a single input slot, this is the offset, in bytes, from the start of the C++ vertex structure to the start of the
vertex component. For example, in the following vertex structure, the element Pos has a 0-byte offset since its start coincides with
the start of the vertex structure; the element Normal has a 12-byte offset because we have to skip over the bytes of Pos to get to
the start of Normal; the element Tex0 has a 24-byte offset because we need to skip over the bytes of Pos and Normal to get to the
start of Tex0; the element Tex1 has a 32-byte offset because we need to skip over the bytes of Pos, Normal, and Tex0 to get to
the start of Tex1.
struct Vertex2 { XMFLOAT3 Pos; // 0-byte offset XMFLOAT3 Normal; // 12-byte offset XMFLOAT2 Tex0; // 24-byte offset XMFLOAT2 Tex1; // 32-byte offset };
6. InputSlotClass:(一般为D3D11_INPUT_PER_VERTEX_DATA)
Specify D3D11_INPUT_PER_VERTEX_DATA for now; the other option is used for the advanced technique of
instancing.
7. InstanceDataStepRate:(0即可)Specify 0 for now; other values are only used for the advanced technique of instancing.
下面是用例:
D3D11_INPUT_ELEMENT_DESC desc1[] = { {"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} }; D3D11_INPUT_ELEMENT_DESC desc2[] = { {"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}, {"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0} };
下面创建input layout
HRESULT ID3D11Device::CreateInputLayout( const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs, UINT NumElements, const void *pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout);
参数
1.pInputElementDescs: An array of D3D11_INPUT_ELEMENT_DESC elements describing the vertex structure.
2.NumElements: The number of elements in the D3D11_INPUT_ELEMENT_DESC elements array.
3.(shader签名或者shader参数)pShaderBytecodeWithInputSignature: A pointer to the shader byte-code of the input signature of the vertex shader.
4.BytecodeLength: The byte size of the vertex shader signature data passed into the previous parameter
5.ppInputLayout: Returns a pointer to the created input layout
下面是用例
ID3DX11Effect* mFX; ID3DX11EffectTechnique* mTech; ID3D11InputLayout* mInputLayout; /* ...create the effect... */ mTech = mFX->GetTechniqueByName("Tech"); D3DX11_PASS_DESC passDesc; mTech->GetPassByIndex(0)->GetDesc(&passDesc); HR(md3dDevice->CreateInputLayout(vertexDesc, 4, passDesc. pIAInputSignature, passDesc.IAInputSignatureSize, &mInputLayout));
简单说就是从effect 获取Technique,然后获取passDesc,再通过passDesc获取参数和长度
既然获取了input layout
那么就可以设置input layout
对于一个inputslot
md3dImmediateContext->IASetInputLayout(mInputLayout);
对于两个inputslot
md3dImmediateContext->IASetInputLayout(mInputLayout1);
md3dImmediateContext->IASetInputLayout(mInputLayout2);
B Vertex Buffer
为了让GPU可以读取顶点数组,那么就要一个buffer,也就是VERTEX BUFFERS
1.先fill out D3D11_BUFFER_DESC
2. Fill out a D3D11_SUBRESOURCE_DATA(初始buffer的数据描述)
3.CreateBuffer
D3D11_BUFFER_DESC的定义
typedef struct D3D11_BUFFER_DESC { UINT ByteWidth; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; UINT StructureByteStride; } D3D11_BUFFER_DESC;
1.ByteWidth: The size, in bytes
2.Usage: A member of the D3D11_USAGE enumerated type specifying how the buffer will be used
A:D3D11_USAGE_DEFAULT: Specify this usage if the
GPU will be reading and writing to the resource. The
CPU cannot read or write to a resource with this usage
using the mapping API
ie
ID3D11DeviceContext::Map); however, it can use
ID3D11DeviceContext::UpdateSubresource
B:D3D11_USAGE_IMMUTABLE(也就是不变的,GPU only read CPU 不能read,也不能map)
C:(能不用就不用,因为是update from CPU)
D3D11_USAGE_DYNAMIC: Specify this usage if the
application (CPU) needs to update the data contents of
the resource frequently (e.g., on a per frame basis). A
resource with this usage can be read by the GPU and
written to by the CPU using the mapping API
Updating a GPU
resource dynamically from the CPU incurs a
performance hit, as the new data must be transferred
over from CPU memory (i.e., system RAM) to GPU
memory (i.e., video RAM); therefore, dynamic usage
should be avoided unless necessary
D:D3D11_USAGE_STAGING:(简单来说就是CPU read一份copy,也是不用就不用,因为copy from GPU)
Specify this usage if the
application (CPU) needs to be able to read a copy of
the resource (i.e., the resource supports copying data
from video memory to system memory). Copying from
GPU to CPU memory is a slow operation and should be
avoided unless necessary. Resources can be copied
with the ID3D11DeviceContext::CopyResource and
ID3D11DeviceContext::CopySubresourceRegion
methods.
3. BindFlags: For a vertex buffer, specify D3D11_BIND_VERTEX_BUFFER.
4.CPUAccessFlags:(根据usage)If the CPU needs to update the buffer by writing to it, specify D3D11_CPU_ACCESS_WRITE
A buffer with write access must have usage D3D11_USAGE_DYNAMIC or D3D11_USAGE_STAGING.
If the CPU needs to read from the buffer, specify D3D11_CPU_ACCESS_READ.
A buffer with read access must have usage D3D11_USAGE_STAGING.
5.MiscFlags(设为0)
6.StructureByteStride:(就是若一个struct 只有一个元素,指定大小,为了对齐??)
The size, in bytes, of a single element stored in the structured buffer. This property only applies for
structured buffers and can be set to 0 for all other buffers. A structure buffer is a buffer that stores elements of equal size
D3D11_SUBRESOURCE_DATA定义如下
typedef struct D3D11_SUBRESOURCE_DATA { const void *pSysMem; UINT SysMemPitch; UINT SysMemSlicePitch; } D3D11_SUBRESOURCE_DATA;
1. pSysMem: A pointer to a system memory array which contains the data to initialize the vertex buffer. If the buffer can store n
vertices, then the system array must contain at least n vertices so that the entire buffer can be initialized.
2. SysMemPitch: Not used for vertex buffers.
3. SysMemSlicePitch: Not used for vertex buffers.
代码
struct Vertex { XMFLOAT3 Pos; XMFLOAT4 Color; };
// define raw vertex data
Vertex vertices[] =
{
{ XMFLOAT3(-1.0f, -1.0f, -1.0f), (const float*)&Colors::White },
{ XMFLOAT3(-1.0f, +1.0f, -1.0f), (const float*)&Colors::Black },
{ XMFLOAT3(+1.0f, +1.0f, -1.0f), (const float*)&Colors::Red },
{ XMFLOAT3(+1.0f, -1.0f, -1.0f), (const float*)&Colors::Green },
{ XMFLOAT3(-1.0f, -1.0f, +1.0f), (const float*)&Colors::Blue },
{ XMFLOAT3(-1.0f, +1.0f, +1.0f), (const float*)&Colors::Yellow },
{ XMFLOAT3(+1.0f, +1.0f, +1.0f), (const float*)&Colors::Cyan },
{ XMFLOAT3(+1.0f, -1.0f, +1.0f), (const float*)&Colors::Magenta }
};
D3D11_BUFFER_DESC vbd;
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * 8;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
vbd.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = vertices;
ID3D11Buffer* mVB;
HR(md3dDevice->CreateBuffer(
&vbd, // description of buffer to create
&vinitData, // data to initialize buffer with
& mVB)); // return the created buffer
然后可以设置:IASetVertexBuffers
void ID3D11DeviceContext::IASetVertexBuffers( UINT StartSlot, UINT NumBuffers, ID3D11Buffer *const *ppVertexBuffers, const UINT *pStrides, const UINT *pOffsets);
1.StartSlot: The input slot in which to start binding vertex buffers. There are 16 input slots indexed from 0−15.
2.NumBuffers: The number of vertex buffers we are binding to the input slots. If the start slot has index k and we are binding n
buffers, then we are binding buffers to input slots Ik, Ik+1, …, Ik+n-1.
3. ppVertexBuffers: Pointer to the first element of an array of vertex buffers
4.pStrides(不知道理解对不对,也就是到下一个verteise[] 元素中的stride?)pStrides: Pointer to the first element of an array of strides,A stride is the size, in bytes, of an element in the corresponding vertex buffer
5.pOffsets.(vertex buffer偏移量,因为可能会有多个inputslot)
Pointer to the first element of an array of offsets (one for each vertex buffer where the ith offset corresponds to the ith
vertex buffer). This is an offset, in bytes, from the start of the vertex buffer to the position in the vertex buffer from which the
input assembly should start reading the vertex data. You would use this if you wanted to skip over some vertex data at the front of
the vertex buffer.
ID3D11Buffer* mVB1; // stores vertices of type Vertex1 ID3D11Buffer* mVB2; // stores vertices of type Vertex2 /*...Create the vertex buffers...*/ UINT stride = sizeof(Vertex1); UINT offset = 0; md3dImmediateContext->IASetVertexBuffers(0, 1, &mVB1, &stride, &offset); /* ...draw objects using vertex buffer 1... */ stride = sizeof(Vertex2); offset = 0; md3dImmediateContext->IASetVertexBuffers(0, 1, &mVB2, &stride, &offset); /* ...draw objects using vertex buffer 2... */
动态buffer
D3D11_BUFFER_DESC vbd; vbd.Usage = D3D11_USAGE_DYNAMIC; vbd.ByteWidth = sizeof(Vertex) * mWaves.VertexCount(); vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER; vbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; vbd.MiscFlags = 0; HR(md3dDevice->CreateBuffer(&vbd, 0, &mWavesVB));
Then we can use the ID3D11DeviceContext::Map function to obtain a pointer to the start of the buffer memory block and write
to it:
HRESULT ID3D11DeviceContext::Map( ID3D11Resource *pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE *pMappedResource);
1. pResource: Pointer to the resource we want to access for reading/writing. A buffer is one type of Direct3D 11 resource, but other
resources can be accessed with this method as well, such as texture resources.
2. Subresource: An index to a subresource contained in the resource. We will see how this is used later; our buffers do not have
subresources so specify 0.
3. MapType:D3D11_MAP_WRITE_DISCARD D3D11_MAP_WRITE_NO_OVERWRITE
D3D11_MAP_READ:Used with staging buffers, where you need to read a copy of the GPU buffer into system memory
4. MapFlags: Optional flag we do not use and so specify as 0;
5. pMappedResource: Returns a pointer to a D3D11_MAPPED_SUBRESOURCE from which we can access the resource data for
reading/writing.
D3D11_MAPPED_SUBRESOURCE定义
typedef struct D3D11_MAPPED_SUBRESOURCE { void *pData; UINT RowPitch; UINT DepthPitch; } D3D11_MAPPED_SUBRESOURCE;
1. pData: Pointer to the raw memory of the resource for reading/writing. You must cast this to the appropriate data format the
resource stores.
2. RowPitch: The byte size of one row of data in a resource. For example, for a 2D texture, this is the byte size of one row.
3. DepthPitch: The byte size of a page of data in a resource. For example, for a 3D texture, this is the byte size of one 2D image
subset of the 3D texture.
可以这样写,
D3D11_MAPPED_SUBRESOURCE mappedData; HR(md3dImmediateContext->Map(mWavesVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData)); Vertex* v = reinterpret_cast<Vertex*>(mappedData.pData); for(UINT i = 0; i < mWaves.VertexCount(); ++i) { v[i].Pos = mWaves[i]; v[i].Color = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f); } md3dImmediateContext->Unmap(mWavesVB, 0);
开始Draw
void ID3D11DeviceContext::Draw(UINT VertexCount, UINT StartVertexLocation);
C indeices buffer
UINT indices[24] = { 0, 1, 2, // Triangle 0 0, 2, 3, // Triangle 1 0, 3, 4, // Triangle 2 0, 4, 5, // Triangle 3 0, 5, 6, // Triangle 4 0, 6, 7, // Triangle 5 0, 7, 8, // Triangle 6 0, 8, 1 // Triangle 7 };
// Describe the index buffer we are going to create. Observe the // D3D11_BIND_INDEX_BUFFER bind flag D3D11_BUFFER_DESC ibd; ibd.Usage = D3D11_USAGE_IMMUTABLE; ibd.ByteWidth = sizeof(UINT) * 24; ibd.BindFlags = D3D11_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; ibd.StructureByteStride = 0; // Specify the data to initialize the index buffer. D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; // Create the index buffer. ID3D11Buffer* mIB; HR(md3dDevice->CreateBuffer(&ibd, &iinitData, &mIB));
//DXGI_FORMAT_R16_UINT and DXGI_FORMAT_R32_UINT are the only formats supported for index buffers.
md3dImmediateContext->IASetIndexBuffer(mIB, DXGI_FORMAT_R32_UINT, 0);
drawIndex
void ID3D11DeviceContext::DrawIndexed( UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation);
至于为什么有 StartIndexLocation 和 BaseVertexLocation,
如果每个图形有单独的VB和IB,没问题,但是如果合并了那么就不对了
本来是0, 1, ..., numBoxVertices-1
合并后
firstBoxVertexPos,
firstBoxVertexPos+1,
...,
firstBoxVertexPos+numBoxVertices-1
所以代码应该这样写
md3dImmediateContext->DrawIndexed(numSphereIndices, 0, 0); md3dImmediateContext->DrawIndexed(numBoxIndices, firstBoxIndex, firstBoxVertexPos); md3dImmediateContext->DrawIndexed(numCylIndices, firstCylIndex, firstCylVertexPos);