本篇笔记要实现的是在屏幕上渲染出一个三角形,重点要学习的是渲染一个几何体的流程方式。
为了渲染几何图形,需要一个顶点缓存和一个描述顶点布局的输入层,还有着色器(主要是顶点着色器和像素着色器),下面来看看具体Demo的实现。
新建一个Win32项目 ,新建一个类我们叫做TriangleDemo,继承自前面教程我们实现的基类Dx11DemoBase。
TriangleDemo.h头文件
#pragma once #include "Dx11DemoBase.h" class TriangleDemo : public Dx11DemoBase { public: TriangleDemo(); ~TriangleDemo(); bool LoadContent() override; void UnLoadContent() override; void Update(float dt) override; void Render() override; private: ID3D11Buffer *m_pVertexBuffer; ID3D11InputLayout *m_pInputLayout; ID3D11VertexShader *m_pSolidColorVS; ID3D11PixelShader *m_pSolidColorPS; };
在类中添加了四个成员变量,用来保存着色器、顶点缓存和顶点布局的指针。
定义顶点结构体
#include "TriangleDemo.h" #include <xnamath.h> struct VertexPos { XMFLOAT3 pos; }; TriangleDemo::TriangleDemo() : m_pInputLayout(0), m_pVertexBuffer(0), m_pSolidColorPS(0), m_pSolidColorVS(0) { } TriangleDemo::~TriangleDemo() { } void TriangleDemo::UnLoadContent() { if (m_pSolidColorPS) m_pSolidColorPS->Release(); if (m_pSolidColorVS) m_pSolidColorVS->Release(); if (m_pVertexBuffer) m_pVertexBuffer->Release(); if (m_pInputLayout) m_pInputLayout->Release(); m_pSolidColorVS = 0; m_pSolidColorPS = 0; m_pVertexBuffer = 0; m_pInputLayout = 0; }
着色器的加载
为了方便我们在基类中定义了一个CompileD3DShader函数实现加载着色器文本的功能,具体实现如下
bool Dx11DemoBase::CompileD3DShader(char* filePath, char* entry, char* shaderModel, ID3DBlob** buffer) { DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; #if defined _DEBUG || defined DEBUG shaderFlags = D3DCOMPILE_DEBUG; #endif ID3DBlob *errorBuffer = 0; HRESULT result; result = D3DX11CompileFromFile(filePath, 0, 0, entry, shaderModel, shaderFlags, 0, 0, buffer, &errorBuffer,0); if (FAILED(result)) { if (errorBuffer != 0) { OutputDebugString((char*)errorBuffer->GetBufferPointer()); errorBuffer->Release(); } return false; } if (errorBuffer != 0) { return false; } return true; }
具体加载着色器的代码我们写在LoadContent函数中,下面是该函数的一部分代码
1 //载入顶点着色器 2 ID3DBlob *vsBuffer = 0; 3 bool compileResult = CompileD3DShader("SolidColor.fx", "VS_Main", "vs_4_0", &vsBuffer); 4 if (!compileResult) 5 { 6 MessageBox(0, "载入顶点着色器错误", "编译错误", MB_OK); 7 return false; 8 } 9 10 HRESULT result; 11 result = m_pd3dDevice->CreateVertexShader(vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), 12 0, &m_pSolidColorVS); 13 14 if (FAILED(result)) 15 { 16 if (vsBuffer) 17 { 18 vsBuffer->Release(); 19 } 20 return false; 21 } 22 23 D3D11_INPUT_ELEMENT_DESC solidColorLayout[] = 24 { 25 {"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0} 26 }; 27 28 UINT numLayoutElements = ARRAYSIZE(solidColorLayout); 29 result = m_pd3dDevice->CreateInputLayout(solidColorLayout, numLayoutElements, vsBuffer->GetBufferPointer(), 30 vsBuffer->GetBufferSize(), &m_pInputLayout); 31 vsBuffer->Release(); 32 33 if (FAILED(result)) 34 { 35 return false; 36 } 37 38 //载入像素着色器 39 ID3DBlob *psBuffer = 0; 40 compileResult = CompileD3DShader("SolidColor.fx", "PS_Main", "ps_4_0", &psBuffer); 41 if (!compileResult) 42 { 43 MessageBox(0, "像素着色器加载失败", "编译错误", MB_OK); 44 return false; 45 } 46 47 result = m_pd3dDevice->CreatePixelShader(psBuffer->GetBufferPointer(), psBuffer->GetBufferSize(), 48 0, &m_pSolidColorPS); 49 psBuffer->Release(); 50 if (FAILED(result)) 51 { 52 return false; 53 }
其中输入布局信息D3D11_INPUT_ELEMENT_DESC
(A description of a single element for the input-assembler stage.)
定义如下
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;
顶点信息生成及处理
TriangleDemo::LoadContent() 的 上半部分完成了 inputlayout 和 shader, 剩下的就是 vertex 这些顶点的生成及处理了。
三角形的顶点列表 vertex list 存放处在 vertices数组里, 同时这个vertices 也作为 创建vertex buffer函数CreateBuffer()的参数之一:Subresource data。
下面是LoadContent()的另外一部分代码:
1 VertexPos vertices[] = 2 { 3 XMFLOAT3(0.5f, 0.5f, 0.5f), 4 XMFLOAT3(0.5f, -0.5f, 0.5f), 5 XMFLOAT3(-0.5f,-0.5f,0.5f) 6 }; 7 8 D3D11_BUFFER_DESC vertexDesc; 9 ZeroMemory(&vertexDesc, sizeof(vertexDesc)); 10 vertexDesc.Usage = D3D11_USAGE_DEFAULT; 11 vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 12 vertexDesc.ByteWidth = sizeof(VertexPos)* 3; 13 14 D3D11_SUBRESOURCE_DATA resourceData; 15 ZeroMemory(&resourceData, sizeof(resourceData)); 16 resourceData.pSysMem = vertices; 17 18 result = m_pd3dDevice->CreateBuffer(&vertexDesc, &resourceData, &m_pVertexBuffer); 19 if (FAILED(result)) 20 { 21 return false; 22 } 23 24 return true;
其中D3D11_BUFFER_DESC定义如下:
typedef struct D3D11_BUFFER_DESC { UINT ByteWidth; D3D11_USAGE Usage; UINT BindFlags; UINT CPUAccessFlags; UINT MiscFlags; UINT StructureByteStride; } D3D11_BUFFER_DESC;
D3D11_SUBRESOURCE_DATA定义如下:
typedef struct D3D11_SUBRESOURCE_DATA { const void *pSysMem; UINT SysMemPitch; UINT SysMemSlicePitch; } D3D11_SUBRESOURCE_DATA;
渲染几何体
剩下代码是渲染几何体和 shaders 。我们在TriangleDemo::Render() 函数进行渲染几何体的工作
1 void TriangleDemo::Render() 2 { 3 if (m_pImmediateContext == 0) 4 return; 5 //清除渲染目标视图 6 float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };//背景颜色 7 m_pImmediateContext->ClearRenderTargetView(m_pRenderTargetView, clearColor); 8 9 UINT stride = sizeof(VertexPos); 10 UINT offset = 0; 11 //设置数据信息格式控制信息 12 m_pImmediateContext->IASetInputLayout(m_pInputLayout); 13 //设置要绘制的几何体信息 14 m_pImmediateContext->IASetVertexBuffers(0,1,&m_pVertexBuffer,&stride,&offset); 15 //指明如何绘制三角形 16 m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 17 m_pImmediateContext->VSSetShader(m_pSolidColorVS, 0, 0); 18 m_pImmediateContext->PSSetShader(m_pSolidColorPS, 0, 0); 19 m_pImmediateContext->Draw(3, 0); 20 //马上输出 21 m_pSwapChain->Present(0, 0); 22 }
着色器.fx文件代码
float4 VS_Main( float4 pos : POSITION ) : SV_POSITION { return pos; } float4 PS_Main( float4 pos : SV_POSITION ) : SV_TARGET { return float4( 1.0f, 0.0f, 0.0f, 1.0f ); }
我们使用的vertex shader很简单的, 仅仅是传递输入的顶点坐标到输出设备;
pixel shader的工作也很简单, 仅仅是 为每个 像素pixel 配置一种固定的颜色( 红色)。