因为比较看不惯商店应用使用的C++/CX,所以用win32的项目来学习,起名为D3DFirst。
项目模板会生成一堆默认的代码,用于显示一个最简单的Windows窗口,这个窗口会作为D3D画面的输出窗口。
生成的代码虽然很多,但我只关心图形绘制相关的代码,所以直接定位到
RESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
回调函数的
case WM_PAINT:
break;
所有的绘制都是在这里进行的,我的D3D代码也是写在这里。
为了不和这个默认生成的代码文件搅和在一起,自己新建一个类专门用于操作D3D,叫做D3DRender。将绘制的代码写在WM_PAINT后面。
可以声明一个全局的变量
D3DRender pD3dRender;
然后在InitInstance函数里初始化,主要是初始化D3D需要的资源,并且将窗口做为参数传入。
pD3dRender.Init(hWnd);
然后在WM_PAINT后面绘制:
case WM_PAINT:
//hdc = BeginPaint(hWnd, &ps);
TODO: Add any drawing code here...
//EndPaint(hWnd, &ps);
pD3dRender.OnRender();
break;
当然还有D3DRender类的头文件需要include。
下面介绍D3DRender类的声明:
头文件和using namespace:
#pragma once
#include "D3D11_1.h"
#include "d3dx11.h"
#include "directxmath.h"
#include "DirectXPackedVector.h"
#include "d3dCompiler.h"
using namespace DirectX;
using namespace DirectX::PackedVector;
前两个是D3D的一些核心接口。
DirectXMath.h和DirectXPackedVector.h是向量和矩阵的相关接口。
d3dCompiler.h是用于读取shader的接口。
下面是类的主要内容,基本上就是渲染流水线所需要的所有东西。
struct Vertex1
{
XMFLOAT3 pos;
XMFLOAT4 color;
};
struct Vertex2
{
XMFLOAT3 pos;
XMFLOAT3 normal;
XMFLOAT2 Tex0;
XMFLOAT2 Tex1;
};
struct ConstantBuffer
{
XMMATRIX mWorld;
XMMATRIX mView;
XMMATRIX mProjection;
};
class D3DRender
{
public:
D3DRender(void);
~D3DRender(void);
void CreateDeviceAndSwapChain(HWND);//创建设备接口和双缓冲交换接口
void Check4XMsaa();//检测多重采样的质量级别
void CreateRenderTargetView();//创建渲染目标视图(双缓冲的其中一个Buffer)
void CreateDepthView();//创建深度视图(检测遮挡)
void BindView();//绑定视图(将渲染目标视图和深度视图绑定到output stage)
void SetViewport();//绑定一组视口到Rasterizer Stage
void SetInputLayout();//绑定输入格式到input-assembler stage.
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);//从文件读取shader或effect
void CreateVertexBuffer();//绑定一组顶点Buffer到input-assembler stage.
void CreateIndexBuffer();//绑定一组索引Buffer到input-assembler stage.
void CreateVertexShader();//用文件中读取的编译过的shader创建一个顶点shader对象
void CreateConstantBuffer();//创建一个Buffer(类型可以是顶点,索引,shader常量buffer)
void SetPrimitive();//设置图元类型,数据的输入顺序,绑定到input-assembler stage.
void CreatePixelShader();//用文件中读取的编译过的shader创建一个索引shader对象
void InitWorldMatrix();
void InitViewMatrix();
void InitProjectionMatrix();
void Init(HWND);//初始化
void OnRender();//绘制
void OnResize();
private:
ID3D11Device* pDevice;
ID3D11DeviceContext* pDeviceContext;
IDXGISwapChain* pSwapChain;
ID3D11Texture2D* pDepthStencilBuffer;
ID3D11DepthStencilView* pDepthStencilView;
ID3D11Texture2D* pBackBuffer;
ID3D11RenderTargetView* pRenderTargetView;
ID3DBlob* pVSBlob;
ID3DBlob* pPSBlob;
ID3D11InputLayout* pInputLayout;
ID3D11Buffer* pVertexBuffer;
ID3D11Buffer* pIndexBuffer;
ID3D11Buffer* pConstantBuffer;
ID3D11VertexShader* pVertexShader;
ID3D11PixelShader* pPixelShader;
UINT m4xMsaaQuality;
HRESULT hr;
RECT rect;
UINT width;
UINT height;
XMMATRIX worldMatrix;
XMMATRIX viewMatrix;
XMMATRIX projectionMatrix;
};
下面是这些函数的实现:
#include "stdafx.h"
#include "D3DRender.h"
D3DRender::D3DRender(void)
{
pDevice = NULL;
pDeviceContext = NULL;
pSwapChain = NULL;
pBackBuffer= NULL;
pRenderTargetView= NULL;
}
D3DRender::~D3DRender(void)
{
pDevice->Release();
pDeviceContext->Release();
pSwapChain->Release();
pBackBuffer->Release();
pRenderTargetView->Release();
}
void D3DRender::CreateDeviceAndSwapChain(HWND hwnd)
{
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_SOFTWARE
};
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1,
};
UINT featureLevelsCount = ARRAYSIZE(featureLevels);
GetClientRect(hwnd, &rect);
width = rect.right - rect.left;
height = rect.bottom - rect.top;
DXGI_SWAP_CHAIN_DESC swapChainDesc = {0,};
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
D3D_FEATURE_LEVEL featureLevel;
hr = D3D11CreateDeviceAndSwapChain(
NULL,
driverTypes[0],
NULL,
D3D11_CREATE_DEVICE_DEBUG,
featureLevels,
featureLevelsCount,
D3D11_SDK_VERSION,
&swapChainDesc,
&pSwapChain,
&pDevice,
&featureLevel,
&pDeviceContext);
}
void D3DRender::Check4XMsaa()
{
hr = pDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM, 4, &m4xMsaaQuality);
}
void D3DRender::CreateRenderTargetView()
{
// Get a pointer to the back buffer of target view
hr = pSwapChain->GetBuffer( 0, __uuidof(ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
// Create a render-target view
hr = pDevice->CreateRenderTargetView(pBackBuffer, 0, &pRenderTargetView );
}
void D3DRender::CreateDepthView()
{
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = rect.right-rect.left;
depthStencilDesc.Height = rect.bottom = rect.top;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality - 1;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
hr = pDevice->CreateTexture2D( &depthStencilDesc, 0, &pDepthStencilBuffer);
hr = pDevice->CreateDepthStencilView( pDepthStencilBuffer, 0, &pDepthStencilView);
}
void D3DRender::BindView()
{
// Bind the view
pDeviceContext->OMSetRenderTargets( 1, &pRenderTargetView, pDepthStencilView);
}
void D3DRender::SetViewport()
{
// Setup the viewport
D3D11_VIEWPORT viewport;
viewport.Width = static_cast<float>(width);
viewport.Height = static_cast<float>(height);
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
pDeviceContext->RSSetViewports( 1, &viewport );
}
HRESULT D3DRender::CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob* pErrorBlob;
hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel,
dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
if( FAILED(hr) )
{
if( pErrorBlob != NULL )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
if( pErrorBlob ) pErrorBlob->Release();
return hr;
}
if( pErrorBlob ) pErrorBlob->Release();
return S_OK;
}
void D3DRender::CreateVertexShader()
{
// Compile the vertex shader
hr = CompileShaderFromFile( L"vs1.hlsl", "main", "vs_4_0", &pVSBlob );
//create vertex shader
hr = pDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &pVertexShader );
//pVSBlob->Release();
}
void D3DRender::SetInputLayout()
{
// Define the input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = ARRAYSIZE(layout);
hr= pDevice->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &pInputLayout);
pDeviceContext->IASetInputLayout(pInputLayout);
}
void D3DRender::CreatePixelShader()
{
pPSBlob = NULL;
// Compile the pixel shader
hr = CompileShaderFromFile( L"ps1.hlsl", "main", "ps_4_0", &pPSBlob );
// Create the pixel shader
hr = pDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &pPixelShader );
pPSBlob->Release();
}
void D3DRender::CreateVertexBuffer()
{
// Create vertex buffer
Vertex vertices[] =
{
XMFLOAT3( 0.0f, 0.5f, 0.5f ),
XMFLOAT3( 0.5f, -0.5f, 0.5f ),
XMFLOAT3( -0.5f, -0.5f, 0.5f ),
};
Vertex1 vertices1[] =
{
{ XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT4( 0.0f, 0.0f, 1.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT4( 0.0f, 1.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT4( 0.0f, 1.0f, 1.0f, 1.0f ) },
{ XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT4( 1.0f, 0.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT4( 1.0f, 0.0f, 1.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT4( 1.0f, 1.0f, 0.0f, 1.0f ) },
{ XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT4( 1.0f, 1.0f, 1.0f, 1.0f ) },
{ XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) },
};
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( Vertex ) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = vertices;
hr = pDevice->CreateBuffer( &bd, &InitData, &pVertexBuffer );
// Set vertex buffer
UINT stride = sizeof( Vertex );
UINT offset = 0;
pDeviceContext->IASetVertexBuffers( 0, 1, &pVertexBuffer, &stride, &offset );
}
void D3DRender::CreateIndexBuffer()
{
// Create index buffer
WORD indices[] =
{
3,1,0,
2,1,3,
0,5,4,
1,5,0,
3,4,7,
0,4,3,
1,6,5,
2,6,1,
2,7,6,
3,7,2,
6,4,5,
7,4,6,
};
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( WORD ) * 36; // 36 vertices needed for 12 triangles in a triangle list
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = indices;
hr = pDevice->CreateBuffer( &bd, &InitData, &pIndexBuffer );
// Set index buffer
pDeviceContext->IASetIndexBuffer( pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
}
void D3DRender::SetPrimitive()
{
// Set primitive topology
pDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
}
void D3DRender::CreateConstantBuffer()
{
// Create the constant buffer
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = pDevice->CreateBuffer( &bd, NULL, &pConstantBuffer );
}
void D3DRender::InitWorldMatrix()
{
worldMatrix = XMMatrixIdentity();
}
void D3DRender::InitViewMatrix()
{
XMVECTOR Eye = XMVectorSet( 0.0f, 1.0f, -5.0f, 0.0f );
XMVECTOR At = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
XMVECTOR Up = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
viewMatrix = XMMatrixLookAtLH( Eye, At, Up );
}
void D3DRender::InitProjectionMatrix()
{
projectionMatrix = XMMatrixPerspectiveFovLH( XM_PIDIV2, width / (FLOAT)height, 0.01f, 100.0f );
}
void D3DRender::Init(HWND hwnd)
{
CreateDeviceAndSwapChain(hwnd);
Check4XMsaa();
CreateRenderTargetView();
//CreateDepthView();
BindView();
SetViewport();
CreateVertexShader();
SetInputLayout();
CreatePixelShader();
CreateVertexBuffer();
CreateIndexBuffer();
SetPrimitive();
CreateConstantBuffer();
InitWorldMatrix();
InitViewMatrix();
InitProjectionMatrix();
}
void D3DRender::OnRender()
{
// Update our time
static float t = 0.0f;
static DWORD dwTimeStart = 0;
DWORD dwTimeCur = GetTickCount();
if( dwTimeStart == 0 )
dwTimeStart = dwTimeCur;
t = ( dwTimeCur - dwTimeStart ) / 1000.0f;
//
// Animate the cube
//
worldMatrix = XMMatrixRotationY( t );
float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };
pDeviceContext->ClearRenderTargetView( pRenderTargetView, clearColor );
//pDeviceContext->ClearDepthStencilView( pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );
//
// Update variables
//
ConstantBuffer cb;
cb.mWorld = XMMatrixTranspose( worldMatrix );
cb.mView = XMMatrixTranspose( viewMatrix );
cb.mProjection = XMMatrixTranspose( projectionMatrix );
pDeviceContext ->UpdateSubresource( pConstantBuffer, 0, NULL, &cb, 0, 0 );
//
// Renders a triangle
//
//pDeviceContext->VSSetShader(pVertexShader, NULL, 0 );
//pDeviceContext->VSSetConstantBuffers( 0, 1, &pConstantBuffer );
//pDeviceContext->PSSetShader( pPixelShader, NULL, 0 );
//pDeviceContext->DrawIndexed( 36, 0, 0 ); // 36 vertices needed for 12 triangles in a triangle list
// Render a triangle
pDeviceContext->VSSetShader( pVertexShader, NULL, 0 );
pDeviceContext->PSSetShader( pPixelShader, NULL, 0 );
pDeviceContext->Draw( 3, 0 );
pSwapChain->Present( 0, 0 );
}
最后运行的效果如下: