04. Begin Drawing!
Programmable Graphics Rendering Pipeline
1. Input Assembler (IA) Stage
2. Vertex Shader (VS) Stage
3. Hull Shader (HS) Stage
4. Tesselator Shader (TS) Stage
5. Domain Shader (DS) Stage
6. Geometry Shader (GS) Stage
7. Stream Output (SO) Stage
8. Rasterizer (RS) Stage
9. Pixel Shader (PS) Stage
10. Output Merger (OM) Stage
1 Input Assembler (IA) Stage
IA阶段读取几何数据,定点和索引,在发送数据给IA之前,需要create a buffer and set the Primitive Topology, Input Layout, and active buffers
首先创建两个buffer,Vertex Bufer 和 Index buffer (D3D11_BUFFER_DESC)然后创建 the input-layout object (D3D11_INPUT_ELEMENT_DESC)
1 //The vertex Structure 2 struct Vertex 3 { 4 D3DXVECTOR3 pos; 5 D3DXCOLOR color; 6 }; 7 8 9 //The input-layout description 10 D3D11_INPUT_ELEMENT_DESC layout[] = 11 { 12 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, 13 {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0} 14 };
本节的定点类型
struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z) : pos(x,y,z){} XMFLOAT3 pos; };
本节的input layout
D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, };
创建inputlayout
ID3D11Device::CreateInputLayout()
创建buffer放我们的物体的顶点 (D3D11_BUFFER_DESC)
ID3D11Device::CreateBuffer()
然后,绑定layout和vertext 给IA
ID3D11DeviceContext::IASetVertexBuffers()
ID3D11DeviceContext::IASetInputLayout()
然后set the primitive topology 这样IA就知道怎么使用顶点了
ID3D11DeviceContext::IASetPrimitiveTopology()
渲染管线准备好了之后,就可以调draw 把primitives给IA
ID3D11DeviceContext::Draw()
2 Vertex Shader (VS) Stage
VS 是第一个可编程的shader,需自己编写。 每个绘制的顶点都将通过VS. 使用VS,您可以执行诸如转换,缩放,照明,纹理的位移贴图等等。
即使程序中的顶点不需要修改,顶点着色器也必须始终实现管道工作。 流水线中的着色器使用HLSL语言编写,对于本课,我们的顶点着色器什么也不做,所以我们只是返回每个顶点的位置而不修改它。
float4 VS(float4 inPos : POSITION) : SV_POSITION { return inPos; }
3 Hull Shader (HS) Stage
HS是添加到direct3d 11图形渲染管道的三个新的可选阶段中的第一个。
Hull Shader Stage,Tessellator Stage和Domain Shader Stage三个新阶段共同实现了所谓的tesselation。
tesselation是把一个原始的对象,如一个三角形或线,把它分成许多较小的部分,以增加模型的细节,并且非常快。
它在GPU上创建所有这些新的pirvitives不会被保存到内存中,因此在CPU和内存上创建它们会节省很多时间。 你可以take a simple low mode,用tesselation使它变成a very high detailed polly
回到Hull Shader。 这是另一个可编程的阶段。这个阶段所做的是计算如何以及在哪里向基元添加新的顶点以使其更加详细。 然后它将这些数据发送到Tessellator Stage和Domain Shader Stage
4 Tessellator (TS) Stage
TS是一个固定功能阶段。 这个阶段所做的是HS的输入,进行实际的分割。 然后它将数据传递给Domain Shader.。
5 Domain Shader (DS) Stage
DS是tessellation process的第三个阶段, 这是一个可编程的功能阶段。 这个阶段所做的就是从HS阶段获取新顶点的位置,
并且转换从TS阶段接收到的顶点以创建更多细节。 然后它将顶点传递给the geometry shader stage。
6 Geometry Shader (GS) Stage
这个着色器阶段是可选的。 这也是另一个可编程功能阶段。 它接受基元作为输入,比如3个三角形顶点,2个线,一个点。
它也可以将来自边缘相邻图元的数据作为输入,例如对于一条线另外2个顶点,或对于三角形另外3个顶点。
GS的一个好处是它可以创建或者销毁primitvies 而VS不能。 这个阶段我们可以把一个点变成一个四边形或三角形。 我们可以将数据从GS传送到光栅器阶段,或者传送到内存中的顶点缓冲器。
7 Stream Output (SO) Stage
SO是用来获取vertex date 从GS或者VS(没有GS)Vertex data 发到内存中,从SO发送到存储器的顶点数据被放入一个或多个顶点缓冲区。
从SO输出的顶点数据总是以列表的形式发送出去,例如行列表或三角形列表。 不完整的图元从不发送出去,它们会被默默地剔除。
不完整的基元例如只有2个顶点的三角形或只有一个顶点的直线
8 Rasterization Stage (RS) Stage
RS阶段将向量信息(形状和图元)通过差值顶点来访问每个primitive来将它们变成像素并发送到下一阶段PS。 它也处理裁剪,最基本的切割在屏幕视图之外的primitive。
我们使用以下命令在RS中设置视口:
ID3D11DeviceContext::RSSetViewports()
9 Pixel Shader (PS) Stage
此阶段进行计算并修改屏幕上可见的每个像素,例如每个像素点的照明。这是另一个可编程功能,以及一个可选的阶段。 RS为primitive中的每个像素调用一次PS。
我们之前说过的那样,基元中每个顶点的值和属性都是在RS中的整个基元内插入的。类似VS,VS有1:1的映射(它取一个顶点并返回一个顶点),PS也有1:1的映射(它取一个像素并返回一个像素)
PS的工作是计算每个像素片段的最终颜色。 像素片段是即将被绘制到屏幕的每个潜在像素。 例如,一个圆圈后面有一个正方形。
正方形中的像素是像素片段,圆圈中的像素是像素片段。
每一个都有机会被写入屏幕,但是一旦它到达输出合并阶段,就决定了哪一个像素会被被绘制到屏幕上,如果该圆的深度值小于该深度值正方形,所以来自圆圈的像素将都被绘制。 PS输出4D颜色值。
在本课中,我们使用像素着色器通过返回等于蓝色的四维浮点值来使三角形变为蓝色。 没有计算,只是简单地将绿色返回,所以通过着色器运行的每个像素都将是蓝色的。
float4 PS() : SV_TARGET { return float4(0.0f, 0.0f, 1.0f, 1.0f); }
10 Output Merger (OM) Stage
管道的最后一个阶段是产出合并阶段。 基本上这个阶段需要像素片段和深度/模板缓冲区,并确定哪些像素实际写入渲染目标。 我们在上一课中设置渲染目标,通过调用:
ID3D11DeviceContext::OMSetRenderTargets()
在OM阶段之后,剩下要做的就是把我们的backbuffer放在屏幕上。 我们可以通过调用:
IDXGISwapChain::Present();
第一个是保存我们的顶点数据的Buffer。接下来的两个是我们的VS和PS。 然后,是我们的VS 和PS buffer,这将保存有关我们的VS和PS信息。 最后一个是我们的input layout。
ID3D11Buffer* triangleVertBuffer; ID3D11VertexShader* VS; ID3D11PixelShader* PS; ID3D10Blob* VS_Buffer; ID3D10Blob* PS_Buffer; ID3D11InputLayout* vertLayout;
Vertex Structure & Input Layout ( D3D11_INPUT_ELEMENT_DESC )
我们所生产的所有3D对象将由具有属性的点组成,例如颜色,我们称为顶点。 我们必须建立自己的顶点结构。 这是一个重载的顶点结构(所以我们可以很容易地和动态地创建和编辑一个顶点)
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 -
这只是一个与元素关联的字符串。 该字符串将用于将顶点结构中的元素映射到VS中的元素。
SemanticIndex -
这基本上只是用作索引的语义名称之后的数字。 例如,如果我们在顶点结构中有2个纹理元素,而不是创建2个不同的纹理语义名称,我们可以使用2个不同的索引。 如果顶点着色器代码中的语义名称之后没有索引,默认为索引0.
例如在我们的着色器代码中,我们的语义名称是“POSITION”,其实际上与“POSITION0”相同,
Format -
这只是我们顶点结构中组件的格式。 它需要是DXGI_FORMAT枚举类型的成员。 在本课中,我们有一个描述位置的3d向量,所以我们可以使用DXGI_FORMAT:DXGI_FORMAT_R32G32B32_FLOAT。
InputSlot -
Direct3D允许我们使用16个不同的元素插槽(0-15),您可以放置顶点数据。 如果我们的顶点结构有一个位置和颜色,我们可以把这两个元素放在同一个输入位置,或者我们可以把位置数据放在第一个位置,而把颜色数据放在第二个位置。
AlignedByteOffset -
这是你所描述的元素的字节偏移量。 在单个输入槽中,如果我们有位置和颜色,位置可以是0,因为它从顶点结构的开始处开始,颜色将需要是我们顶点位置的大小,即12个字节(请记住我们的格式 我们的顶点位置是DXGI_FORMAT_R32G32B32_FLOAT,它是96位,位置上的每个分量都是32位,一个字节有8位,所以96/8 == 12)。
InputSlotClass -
现在我们可以使用D3D10_INPUT_PER_VERTEX_DATA。 其他选项用于实例化
InstanceDataStepRate -
这也只用于实例化,所以我们现在要指定0
1 struct Vertex //Overloaded Vertex Structure 2 { 3 Vertex(){} 4 Vertex(float x, float y, float z) 5 : pos(x,y,z){} 6 7 XMFLOAT3 pos; 8 }; 9 10 D3D11_INPUT_ELEMENT_DESC layout[] = 11 { 12 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 13 }; 14 UINT numElements = ARRAYSIZE(layout);
Cleaning Up
1 void CleanUp() 2 { 3 //Release the COM Objects we created 4 SwapChain->Release(); 5 d3d11Device->Release(); 6 d3d11DevCon->Release(); 7 renderTargetView->Release(); 8 triangleVertBuffer->Release(); 9 VS->Release(); 10 PS->Release(); 11 VS_Buffer->Release(); 12 PS_Buffer->Release(); 13 vertLayout->Release(); 14 }
Initializing the Scene
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; }
Compiling the Shaders( D3DX11CompileFromFile() )
我们将通过创建着色器来开始初始化场景。 我们将从名为“Effects.fx”的效果文件中编译shader。 我们可以使用函数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 -
shader文件名。
pDefines -
指向宏数组的指针。我们可以将其设置为NULL
pInclude -
这是一个指向包含界面的指针。如果我们的着色器在文件中使用#include,我们不能在这里放置NULL,但是我们的着色器没有include,所以我们把它设置为NULL。
pFunctionName -
这是该文件名中着色器函数的名称。
pProfile -
您要使用的着色器的版本。 Direct3D 11支持着色器版本5.0。
标志1 -
编译标志,我们将这个设置为NULL。
标志2 -
效果标志。我们也将这个设置为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);
Creating the Shaders( ID3D11Device::CreateVertexShader() )
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);
Setting the Shaders( ID3D11DeviceContext::VSSetShader() )
现在我们编译并创建了着色器,我们需要将它们设置为当前的着色器。如果我们要设置顶点着色器,我们可以通过调用ID3D11DeviceContext :: VSSetShader()来实现,
而如果我们想要设置像素着色器,还可以调用ID3D11DeviceContext :: PSSetShader()(还有其他着色器,我们将稍后学习设置,比如几何着色器)。
大多数情况下,应用程序将为不同的几何图形集使用不同的着色器集合,例如,稍后我们将在绘制天空盒时使用单独的像素着色器。
因此,您将在运行时设置着色器,而不是仅在场景设置功能中。请记住,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 -
这是我们的Pixe Shader。
ppClassInstances -
这只在我们的着色器使用和接口时才使用。 设置为NULL。
NumClassInstances -
这是来自ppClassInstances的数组中的类实例的数量。 我们设置为0,因为没有一个。
d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0);
Creating the Vertex Buffer( ID3D11Buffer )
现在我们需要创建我们的顶点缓冲区。 我们首先使用我们的顶点结构创建一个顶点数组。
在我们有了一个顶点数组之后,我们将通过填充一个D3D11_BUFFER_DESC结构来描述我们的顶点缓冲区,并通过调用ZeroMemory()来确保它是空的。 D3D11_BUFFER_DESC看起来像这样:
typedef struct D3D11_BUFFER_DESC { UINT ByteWidth; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; UINT StructureByteStride; } D3D11_BUFFER_DESC;
ByteWidth -
这是我们缓冲区的大小。
Usage -
一个D3D11_USAGE类型,描述如何读取和写入我们的缓冲区。
BindFlags -
我们指定D3D11_BIND_VERTEX_BUFFER,因为这是一个顶点缓冲区。
CPUAccessFlags -
这说明我们的缓冲区将如何被CPU使用。 我们可以将其设置为NULL
MiscFlags -
额外的标志,我们不会使用,也设置为NULL
StructureByteStride -
这里不使用,将其设置为NULL。
现在我们已经描述了缓冲区,我们需要用我们需要的数据填充D3D11_SUBRESOURCE_DATA结构。 结构如下所示:
typedef struct D3D11_SUBRESOURCE_DATA { const void *pSysMem; UINT SysMemPitch; UINT SysMemSlicePitch; } D3D11_SUBRESOURCE_DATA;
pSysMem -
这是我们想要放入缓冲区的数据。
SysMemPitch -
这是纹理中从一行到下一行的字节距离。 它仅用于2D和3D纹理。
SysMemSlicePitch -
3D纹理中从一个深度级别到下一级别的距离(以字节为单位)。 仅用于3D纹理。
现在我们可以使用我们刚创建的缓冲区描述和缓冲区数据来创建缓冲区了。 为了创建缓冲区,我们所要做的就是调用ID3D11Device :: CreateBuffer()。 该函数如下所示:
HRESULT CreateBuffer( [in] const D3D11_BUFFER_DESC *pDesc, [in] const D3D11_SUBRESOURCE_DATA *pInitialData, [in] ID3D11Buffer **ppBuffer );
pDesc -
指向我们缓冲区描述的指针
pInitialData -
指向包含我们想要放在这里的数据的子资源数据结构的指针。 如果我们想稍后添加数据,我们可以将其设置为NULL。
ppBuffer -
返回的ID3D11Buffer。
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);
Setting the Vertex Buffer( ID3D11DeviceContext::IASetVertexBuffers() )
现在我们有一个Vertex buffer 我们需要把绑定到IA,调用ID3D11Devicecontext::IASetVertexBuffers function
void IASetVertexBuffers( [in] UINT StartSlot, [in] UINT NumBuffers, [in] ID3D11Buffer *const *ppVertexBuffers, [in] const UINT *pStrides, [in] const UINT *pOffsets );
StartSlot -
这是我们可以绑定到的输入插槽。 我们在这里设置0。
NumBuffers -
这是我们绑定的缓冲区的数量。 我们只有1个。
ppVertexBuffers -
这是一个指向我们实际的顶点缓冲区的指针。
pStrides -
这是每个顶点的大小。
pOffsets -
这是从开始的缓冲区开始的字节。
UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &triangleVertBuffer, &stride, &offset );
Creating the Input (Vertex) Layout ( ID3D11Device::CreateInputLayout() )
接下来,我们需要创建我们的input layout。 我们可以用函数ID3D11Device :: CreateInputLayout来做到这一点:
HRESULT CreateInputLayout( [in] const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs, [in] UINT NumElements, [in] const void *pShaderBytecodeWithInputSignature, [in] SIZE_T BytecodeLength, [out] ID3D11InputLayout **ppInputLayout );
pInputElementDescs -
这是包含我们的顶点布局的D3D11_INPUT_ELEMENT_DESC元素的数组。
NumElements -
这是我们的顶点布局中的元素的数量。
pShaderBytecodeWithInputSignature -
这是一个指向我们的顶点着色器指针。
BytecodeLength -
这是我们的顶点着色器的大小。
ppInputLayout -
这是我们输入(顶点)布局的返回指针。
hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(),
VS_Buffer->GetBufferSize(), &vertLayout );
Setting the Input (Vertex) Layout ( ID3D11DeviceContext::IASetInputLayout() )
我们已经创建了顶点布局,接下来要做的就是将其绑定到IA作为可操作的输入(顶点)布局。 我们可以通过调用函数ID3D11DeviceContext :: IASetInputLayout()来做到这一点:
void STDMETHODCALLTYPE IASetInputLayout( [in] ID3D11InputLayout *pInputLayout );
d3d11DevCon->IASetInputLayout( vertLayout );
Setting the Primitive Topology ( ID3D11DeviceContext::IASetPrimitiveTopology() )
在这里我们告诉IA我们发给他的primitives是什么类型的。我们可以设置primitive toplogy 通过ID3D11DeviceContext::IASetPrimitiveTopology()
唯一的参数是一个D3D11_PRIMITIVE_TOPOLOGY枚举类型。 以下是常用类型的列表:
Point List -
我们可以使用D3D10_PRIMITIVE_TOPOLOGY_POINTLIST。通过使用这种topology,每个顶点将被绘制为一个单独的点。
Line Strip -
我们可以使用D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP。这基本上就像“连接点”。所有的顶点将成为一条线的一部分
Line List -
我们可以使用D3D10_PRIMITIVE_TOPOLOGY_LINELIST。每两个顶点将创建一条线。这和Line Strip之间的区别在于,在一个Line Strip中,所有的顶点将被连接起来以创建线条,这是一条连续的线条。
Triangle Strip -
我们可以使用D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP。这里我们创建三角形。每个三角形与相邻的三角形共享其顶点。所有三角形将被连接。
Triangle List -
我们可以使用D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST。这就是说,每3个顶点构成一个三角形,所以并不是所有的三角形都必须连接。它比Triangle Strip更慢,因为必须使用更多的顶点,不像Triangle Strip,您可以使用4个顶点创建2个三角形。在Triangle List中,您需要有6个顶点来创建2个三角形。
Primitives with Adjacency -
一个例子是D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ。这些仅用于几何着色器。
d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
Creating the Viewport( D3D11_VIEWPORT )
现在剩下要做的就是创建并设置我们的视口。 视口会告诉管道的RS阶段要画什么。 我们可以使用D3D11_VIEWPORT结构创建一个视口。
该视口创建一个以像素为单位的正方形,光栅化器使用该正方形来查找在我们窗口的客户区域上显示几何图形的位置。
当我们引入深度缓冲区时,您也将使用视口。 我们可以设置最小和最大深度值,通常在0和1之间。然后,OM将根据深度值决定显示哪个像素“片段”。
我们希望视口覆盖整个窗口客户区域,所以我们将框的左上角设置为0,0,将框右下角的宽度,高度 单位像素。
D3D11_VIEWPORT viewport; ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height;
Setting the Viewport( ID3D11DeviceContext::RSSetViewports() )
在我们创建视口之后,我们需要使用ID3D11DeviceContext :: RSSViewports()函数将其绑定到管道的RS阶段。 第一个参数是要绑定的视口数量,第二个参数是指向视口数组的指针。 这是你可以有多个“窗口”的地方,比如玩家一和玩家二。
d3d11DevCon->RSSetViewports(1, &viewport);
Rendering the Primitive( ID3D11DeviceContext::Draw() )
现在我们回到DrawScene()函数,在这里我们使用包含4个值的浮点数组RBGA将背景颜色改回黑色。 这里的新行是Draw函数。 第一个参数是要绘制的顶点数量,第二个参数是从开始绘制的顶点数组开始的偏移量。
void DrawScene() { float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); d3d11DevCon->Draw( 3, 0 ); SwapChain->Present(0, 0); }
Effect File (Shaders)( Vertex Shader )
现在我们转到Effectfile。 我们有一个非常非常简单的effectfile,只包含一个VS和PS着色器。 而且这些功能中的每一个功能都是最小的。 在这里,我们将顶点着色器命名为“VS”,并将其参数设置为接受名为“Pos”的4d浮点值。 “位置”是IA将在我们的顶点结构中发送位置元素的位置,因为我们在输入布局中告诉它这样做。 只要你记得在你的顶点布局中改变它,你可以把“POSITION”改成其他任何东西。 我们在这里做的唯一事情是将Pos值返回到管道中的下一个活动阶段。
float4 VS(float4 inPos : POSITION) : SV_POSITION { return inPos; }
Effect File (Shaders)( Pixel Shader )
我们在本课中做的最后一件事就是创建一个像素着色器功能。 这个函数唯一的作用是从光栅阶段传入的每个像素返回蓝色。 然后,我们将返回来自顶点着色器的纹理坐标,法线和/或颜色值,并将它们作为此像素着色器中的输入。 然后,我们可以使用所有这些值来确定像素的最终颜色。 这也是我们将启用基于像素的照明的地方。
float4 PS() : SV_TARGET { return float4(0.0f, 0.0f, 1.0f, 1.0f); }
full source main.cpp
1 //Include and link appropriate libraries and headers// 2 #include <windows.h> 3 #include <d3d11.h> 4 #include <d3dx11.h> 5 #include <D3DX10.h> 6 #include <xnamath.h> 7 8 //Global Declarations - Interfaces// 9 IDXGISwapChain* SwapChain; 10 ID3D11Device* d3d11Device; 11 ID3D11DeviceContext* d3d11DevCon; 12 ID3D11RenderTargetView* renderTargetView; 13 14 ///////////////**************new**************//////////////////// 15 ID3D11Buffer* triangleVertBuffer; 16 ID3D11VertexShader* VS; 17 ID3D11PixelShader* PS; 18 ID3D10Blob* VS_Buffer; 19 ID3D10Blob* PS_Buffer; 20 ID3D11InputLayout* vertLayout; 21 ///////////////**************new**************//////////////////// 22 23 //Global Declarations - Others// 24 LPCTSTR WndClassName = L"firstwindow"; 25 HWND hwnd = NULL; 26 HRESULT hr; 27 28 const int Width = 300; 29 const int Height = 300; 30 31 //Function Prototypes// 32 bool InitializeDirect3d11App(HINSTANCE hInstance); 33 void CleanUp(); 34 bool InitScene(); 35 void UpdateScene(); 36 void DrawScene(); 37 38 bool InitializeWindow(HINSTANCE hInstance, 39 int ShowWnd, 40 int width, int height, 41 bool windowed); 42 int messageloop(); 43 44 LRESULT CALLBACK WndProc(HWND hWnd, 45 UINT msg, 46 WPARAM wParam, 47 LPARAM lParam); 48 49 ///////////////**************new**************//////////////////// 50 //Vertex Structure and Vertex Layout (Input Layout)// 51 struct Vertex //Overloaded Vertex Structure 52 { 53 Vertex(){} 54 Vertex(float x, float y, float z) 55 : pos(x,y,z){} 56 57 XMFLOAT3 pos; 58 }; 59 60 D3D11_INPUT_ELEMENT_DESC layout[] = 61 { 62 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 63 }; 64 UINT numElements = ARRAYSIZE(layout); 65 ///////////////**************new**************//////////////////// 66 67 int WINAPI WinMain(HINSTANCE hInstance, //Main windows function 68 HINSTANCE hPrevInstance, 69 LPSTR lpCmdLine, 70 int nShowCmd) 71 { 72 73 if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) 74 { 75 MessageBox(0, L"Window Initialization - Failed", 76 L"Error", MB_OK); 77 return 0; 78 } 79 80 if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D 81 { 82 MessageBox(0, L"Direct3D Initialization - Failed", 83 L"Error", MB_OK); 84 return 0; 85 } 86 87 if(!InitScene()) //Initialize our scene 88 { 89 MessageBox(0, L"Scene Initialization - Failed", 90 L"Error", MB_OK); 91 return 0; 92 } 93 94 messageloop(); 95 96 CleanUp(); 97 98 return 0; 99 } 100 101 bool InitializeWindow(HINSTANCE hInstance, 102 int ShowWnd, 103 int width, int height, 104 bool windowed) 105 { 106 typedef struct _WNDCLASS { 107 UINT cbSize; 108 UINT style; 109 WNDPROC lpfnWndProc; 110 int cbClsExtra; 111 int cbWndExtra; 112 HANDLE hInstance; 113 HICON hIcon; 114 HCURSOR hCursor; 115 HBRUSH hbrBackground; 116 LPCTSTR lpszMenuName; 117 LPCTSTR lpszClassName; 118 } WNDCLASS; 119 120 WNDCLASSEX wc; 121 122 wc.cbSize = sizeof(WNDCLASSEX); 123 wc.style = CS_HREDRAW | CS_VREDRAW; 124 wc.lpfnWndProc = WndProc; 125 wc.cbClsExtra = NULL; 126 wc.cbWndExtra = NULL; 127 wc.hInstance = hInstance; 128 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 129 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 130 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); 131 wc.lpszMenuName = NULL; 132 wc.lpszClassName = WndClassName; 133 wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 134 135 if (!RegisterClassEx(&wc)) 136 { 137 MessageBox(NULL, L"Error registering class", 138 L"Error", MB_OK | MB_ICONERROR); 139 return 1; 140 } 141 142 hwnd = CreateWindowEx( 143 NULL, 144 WndClassName, 145 L"Lesson 4 - Begin Drawing", 146 WS_OVERLAPPEDWINDOW, 147 CW_USEDEFAULT, CW_USEDEFAULT, 148 width, height, 149 NULL, 150 NULL, 151 hInstance, 152 NULL 153 ); 154 155 if (!hwnd) 156 { 157 MessageBox(NULL, L"Error creating window", 158 L"Error", MB_OK | MB_ICONERROR); 159 return 1; 160 } 161 162 ShowWindow(hwnd, ShowWnd); 163 UpdateWindow(hwnd); 164 165 return true; 166 } 167 168 bool InitializeDirect3d11App(HINSTANCE hInstance) 169 { 170 //Describe our Buffer 171 DXGI_MODE_DESC bufferDesc; 172 173 ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC)); 174 175 bufferDesc.Width = Width; 176 bufferDesc.Height = Height; 177 bufferDesc.RefreshRate.Numerator = 60; 178 bufferDesc.RefreshRate.Denominator = 1; 179 bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 180 bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; 181 bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; 182 183 //Describe our SwapChain 184 DXGI_SWAP_CHAIN_DESC swapChainDesc; 185 186 ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); 187 188 swapChainDesc.BufferDesc = bufferDesc; 189 swapChainDesc.SampleDesc.Count = 1; 190 swapChainDesc.SampleDesc.Quality = 0; 191 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 192 swapChainDesc.BufferCount = 1; 193 swapChainDesc.OutputWindow = hwnd; 194 swapChainDesc.Windowed = TRUE; 195 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; 196 197 198 //Create our SwapChain 199 hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, 200 D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); 201 202 //Create our BackBuffer 203 ID3D11Texture2D* BackBuffer; 204 hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer ); 205 206 //Create our Render Target 207 hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView ); 208 BackBuffer->Release(); 209 210 //Set our Render Target 211 d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL ); 212 213 return true; 214 } 215 216 void CleanUp() 217 { 218 //Release the COM Objects we created 219 SwapChain->Release(); 220 d3d11Device->Release(); 221 d3d11DevCon->Release(); 222 renderTargetView->Release(); 223 ///////////////**************new**************//////////////////// 224 triangleVertBuffer->Release(); 225 VS->Release(); 226 PS->Release(); 227 VS_Buffer->Release(); 228 PS_Buffer->Release(); 229 vertLayout->Release(); 230 ///////////////**************new**************//////////////////// 231 } 232 233 ///////////////**************new**************//////////////////// 234 bool InitScene() 235 { 236 //Compile Shaders from shader file 237 hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_5_0", 0, 0, 0, &VS_Buffer, 0, 0); 238 hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_5_0", 0, 0, 0, &PS_Buffer, 0, 0); 239 240 //Create the Shader Objects 241 hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS); 242 hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS); 243 244 //Set Vertex and Pixel Shaders 245 d3d11DevCon->VSSetShader(VS, 0, 0); 246 d3d11DevCon->PSSetShader(PS, 0, 0); 247 248 //Create the vertex buffer 249 Vertex v[] = 250 { 251 Vertex( 0.0f, 0.5f, 0.5f ), 252 Vertex( 0.5f, -0.5f, 0.5f ), 253 Vertex( -0.5f, -0.5f, 0.5f ), 254 }; 255 256 D3D11_BUFFER_DESC vertexBufferDesc; 257 ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); 258 259 vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; 260 vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 3; 261 vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 262 vertexBufferDesc.CPUAccessFlags = 0; 263 vertexBufferDesc.MiscFlags = 0; 264 265 D3D11_SUBRESOURCE_DATA vertexBufferData; 266 267 ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); 268 vertexBufferData.pSysMem = v; 269 hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &triangleVertBuffer); 270 271 //Set the vertex buffer 272 UINT stride = sizeof( Vertex ); 273 UINT offset = 0; 274 d3d11DevCon->IASetVertexBuffers( 0, 1, &triangleVertBuffer, &stride, &offset ); 275 276 //Create the Input Layout 277 d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(), 278 VS_Buffer->GetBufferSize(), &vertLayout ); 279 280 //Set the Input Layout 281 d3d11DevCon->IASetInputLayout( vertLayout ); 282 283 //Set Primitive Topology 284 d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); 285 286 //Create the Viewport 287 D3D11_VIEWPORT viewport; 288 ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); 289 290 viewport.TopLeftX = 0; 291 viewport.TopLeftY = 0; 292 viewport.Width = Width; 293 viewport.Height = Height; 294 295 //Set the Viewport 296 d3d11DevCon->RSSetViewports(1, &viewport); 297 298 return true; 299 } 300 ///////////////**************new**************//////////////////// 301 302 void UpdateScene() 303 { 304 305 } 306 307 ///////////////**************new**************//////////////////// 308 void DrawScene() 309 { 310 //Clear our backbuffer 311 float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; 312 d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); 313 314 //Draw the triangle 315 d3d11DevCon->Draw( 3, 0 ); 316 317 //Present the backbuffer to the screen 318 SwapChain->Present(0, 0); 319 } 320 ///////////////**************new**************//////////////////// 321 322 int messageloop(){ 323 MSG msg; 324 ZeroMemory(&msg, sizeof(MSG)); 325 while(true) 326 { 327 BOOL PeekMessageL( 328 LPMSG lpMsg, 329 HWND hWnd, 330 UINT wMsgFilterMin, 331 UINT wMsgFilterMax, 332 UINT wRemoveMsg 333 ); 334 335 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 336 { 337 if (msg.message == WM_QUIT) 338 break; 339 TranslateMessage(&msg); 340 DispatchMessage(&msg); 341 } 342 else{ 343 // run game code 344 UpdateScene(); 345 DrawScene(); 346 } 347 } 348 return msg.wParam; 349 } 350 351 LRESULT CALLBACK WndProc(HWND hwnd, 352 UINT msg, 353 WPARAM wParam, 354 LPARAM lParam) 355 { 356 switch( msg ) 357 { 358 case WM_KEYDOWN: 359 if( wParam == VK_ESCAPE ){ 360 DestroyWindow(hwnd); 361 } 362 return 0; 363 364 case WM_DESTROY: 365 PostQuitMessage(0); 366 return 0; 367 } 368 return DefWindowProc(hwnd, 369 msg, 370 wParam, 371 lParam); 372 }
Effect .fx
float4 VS(float4 inPos : POSITION) : SV_POSITION { return inPos; } float4 PS() : SV_TARGET { return float4(0.0f, 0.0f, 1.0f, 1.0f); }
学习资料
https://www.braynzarsoft.net/viewtutorial/q16390-4-begin-drawing