一、需要存在一个渲染目标ID3D11RenderTargetView
1、创建渲染目标,通过D3Ddevice
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, nullptr, &g_pRenderTargetView );
A、获取的D3D设备(D3D设备是通过D3D11CreateDevice 接口获取而来的,但是对于不同系统、硬件、驱动等,可能支持的格式D3D12 D3D11 D3D10..)
// D3D设备类型---创建一个可用的设备,如果第一种不可用,则使用第二种。。类推
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE, // 硬件设备
D3D_DRIVER_TYPE_WARP, // 图形变换设备
D3D_DRIVER_TYPE_REFERENCE, // 基础设备
};
// 设备支持功能等级
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
// 创建设备--numDriverTypes=driverTypes个数,numFeatureLevels = featureLevels个数, g_pImmediateContext---设备上下文
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
if ( hr == E_INVALIDARG )
{
// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
}
if( SUCCEEDED( hr ) )
break;
}
B、渲染目标的backBuffer是通过交换链获取而来的
a、通过获取DXGIFactory2
// 获取到m_pBackBuffer
ID3D11Texture2D* pBackBuffer = nullptr;
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), reinterpret_cast<void**>( &pBackBuffer ) );
// 获取交换链IDXGISwapChain--from IDXGISwapChain1
hr = g_pSwapChain1->QueryInterface( __uuidof(IDXGISwapChain), reinterpret_cast<void**>(&g_pSwapChain) );
// 获取IDXGISwapChain1--通过IDXGIFactory2 g_hWnd---窗口句柄 sd=DXGI_SWAP_CHAIN_DESC1 交换链1参数
hr = dxgiFactory2->CreateSwapChainForHwnd( g_pd3dDevice, g_hWnd, &sd, nullptr, nullptr, &g_pSwapChain1 );
DXGI_SWAP_CHAIN_DESC1 sd = {};
sd.Width = width; // 窗口宽
sd.Height = height; // 窗口高
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 支持格式RGBA
sd.SampleDesc.Count = 1; // 个数
sd.SampleDesc.Quality = 0; // 图像质量等级。 质量越高,性能越低
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 类型输出
sd.BufferCount = 1; // buffer个数
// 获取DXGIFactory2--通过IDXGIFactory1
hr = dxgiFactory->QueryInterface( __uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2) );
// 获取IDXGIFactory1--通过D3D设备获取到DXGI枚举--IDXGIAdapter,然后通过IDXGIAdapter获得获取IDXGIFactory1
IDXGIFactory1* dxgiFactory = nullptr;
{
IDXGIDevice* dxgiDevice = nullptr;
hr = g_pd3dDevice->QueryInterface( __uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice) );
if (SUCCEEDED(hr))
{
IDXGIAdapter* adapter = nullptr;
hr = dxgiDevice->GetAdapter(&adapter);
if (SUCCEEDED(hr))
{
hr = adapter->GetParent( __uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory) );
adapter->Release();
}
dxgiDevice->Release();
}
}
b、通过DXGIFactory获取--优先使用第一种
// DirectX 11.0 systems
DXGI_SWAP_CHAIN_DESC sd = {};
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
hr = dxgiFactory->CreateSwapChain( g_pd3dDevice, &sd, &g_pSwapChain );
2、绑定输出--g_pImmediateContext ID3D11DeviceContext(设备上下文)
// 获取设备上下文---ID3D11DeviceContext 通过D3D11CreateDevice 参考上面的获取设备
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, nullptr ); // 获取output-merger阶段绑定的资源指针。
二、初始化渲染数据(到底需要渲染个什么东西--多大、形状、颜色等)
1、初始化渲染视口
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width; // 渲染的宽 ---分辨率
vp.Height = (FLOAT)height; // 渲染的高---分辨率
vp.MinDepth = 0.0f; // 最小深度0~1(近裁平面)
vp.MaxDepth = 1.0f; // 最大深度0~1(远裁平面)
vp.TopLeftX = 0; // 视口左侧位置
vp.TopLeftY = 0; // 视口右侧位置
g_pImmediateContext->RSSetViewports( 1, &vp );
2、初始化渲染大小、颜色---顶点数据
// 定义顶点数据结构---此内容存在Sharder中
struct VS_OUTPUT
{
float4 Pos : SV_POSITION; // 位置 X、Y、Z、W
float4 Color : COLOR0; // 颜色 R、G、B、A
};
// 编译vertex shader
//VS_OUTPUT VS( float4 Pos : POSITION, float4 Color : COLOR )
//{
// VS_OUTPUT output = (VS_OUTPUT)0;
// output.Pos = mul( Pos, World );
// output.Pos = mul( output.Pos, View );
// output.Pos = mul( output.Pos, Projection );
// output.Color = Color;
// return output;
//}
ID3DBlob* pVSBlob = nullptr;
hr = CompileShaderFromFile( L"Tutorial04.fxh", "VS", "vs_4_0", &pVSBlob );
hr = g_pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader );
3、定义输入布局
//typedef struct D3D11_INPUT_ELEMENT_DESC
//{
// LPCSTR SemanticName; // 用来描述属性的字符串,其实没什么卵用
// UINT SemanticIndex; // 表示SemanticName的属性有几个,用来补充SemanticName,0表示 索引为0 (1个)
// DXGI_FORMAT Format; // 格式
// UINT InputSlot; // GPU可以从哪个顶点缓冲区获取这个元素。索引为0 (第1个)
// UINT AlignedByteOffset; // 一个顶点保存在一个顶点缓冲区中,这个顶点缓冲区是一块连续的内存。AlignedByteOffset告诉GPU偏移量来获取这个元素。
// D3D11_INPUT_CLASSIFICATION InputSlotClass; // :为了区分顶点数据和实例数据
// UINT InstanceDataStepRate; // 指定每个实例数据有多少个实例要绘制...顶点数据设置为0
//} D3D11_INPUT_ELEMENT_DESC
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "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 },
}
//创建inputLayout numElements---layout个数
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
pVSBlob->GetBufferSize(), &g_pVertexLayout );
g_pImmediateContext->IASetInputLayout( g_pVertexLayout );
//float4 PS( VS_OUTPUT input ) : SV_Target
//{
// return input.Color;
//}
ID3DBlob* pPSBlob = nullptr;
hr = CompileShaderFromFile( L"Tutorial04.fxh", "PS", "ps_4_0", &pPSBlob );
hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader );
5、分配顶点内存
// Create vertex buffer---这里画出来的是一个立方体
SimpleVertex vertices[] = // XMFLOAT3--- x、y、z XMFLOAT4---r、g、b、a
{
{ 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 ) },
};
6、初始化D3D11_BUFFER_DESC
// typedef struct D3D11_BUFFER_DESC {
// UINT ByteWidth; // 缓冲区大小
// D3D11_USAGE Usage; // 读取和写入缓冲区类型---D3D11_USAGE_DEFAULT(默认)、D3D11_USAGE_IMMUTABLE(只读--创建后无法更改)
// D3D11_USAGE_DYNAMIC(可读可写)、D3D11_USAGE_STAGING(从GPU复制)
// UINT BindFlags; // 绑定类型分为VERTEX、Index。。。。等等
// UINT CPUAccessFlags; // CPU访问标志--使用Dynamic resources时指定可写
// UINT MiscFlags; // 其他标志--参看D3D11_RESOURCE_MISC_FLAG
// UINT StructureByteStride; // 缓冲区结构中每个元素的大小
// } D3D11_BUFFER_DESC;
D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 8;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
7、初始化D3D11_SUBRESOURCE_DATA
// typedef struct D3D11_SUBRESOURCE_DATA {
// const void *pSysMem; // 初始化后的顶点数据
// UINT SysMemPitch; // 纹理的一行开始到下一行的距离
// UINT SysMemSlicePitch; // 一个深度级别的起点到下一个深度级别的距离
// } D3D11_SUBRESOURCE_DATA;
D3D11_SUBRESOURCE_DATA InitData = {};
InitData.pSysMem = vertices;
8、创建ID3D11Buffer---Vertex Buffer
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
9、创建 index buffer(索引缓冲区)--参看MSDN,目的:为渲染做好准备
//vertices的index 每一行代表一个三角形,共十二行
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
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( WORD ) * 36; // 都是用三角形绘制出来的---四边形可以看做2个三角形合并的结果,里锥体6个面,需要12个三角形
bd.BindFlags = D3D11_BIND_INDEX_BUFFER; // index buffer
bd.CPUAccessFlags = 0;
InitData.pSysMem = indices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer );
// 设置顶点数据
g_pImmediateContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
D3D11_BUFFER_DESC bd
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; //constant buffer
bd.CPUAccessFlags = 0;
hr = g_pd3dDevice->CreateBuffer( &bd, nullptr, &g_pConstantBuffer );
11、初始化世界向量矩阵--初始化视图矩阵、初始化投影矩阵
XMMATRIX M;
M.r[0] = g_XMIdentityR0.v; // x
M.r[1] = g_XMIdentityR1.v; // y
M.r[2] = g_XMIdentityR2.v; // z
M.r[3] = g_XMIdentityR3.v; // w
return M;
// 初始化视图矩阵
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 ); // 通过at和up能确定看的角度
g_View = XMMatrixLookAtLH( Eye, At, Up );
// 初始化投影矩阵
g_Projection = XMMatrixPerspectiveFovLH( XM_PIDIV2, width / (FLOAT)height, 0.01f, 100.0f );
三、渲染
1、 根据时间获取到立方体动画
// 获取时间
static float t = 0.0f;
if( g_driverType == D3D_DRIVER_TYPE_REFERENCE )
{
t += ( float )XM_PI * 0.0125f;
}
else
{
static ULONGLONG timeStart = 0;
ULONGLONG timeCur = GetTickCount64();
if( timeStart == 0 )
timeStart = timeCur;
t = ( timeCur - timeStart ) / 1000.0f;
}
// 获取动画
g_World = XMMatrixRotationY( t );
2、清除后台缓冲区
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, Colors::MidnightBlue );
3、更新视图
ConstantBuffer cb;
cb.mWorld = XMMatrixTranspose( g_World ); // world表示当前空间物体的位置
cb.mView = XMMatrixTranspose( g_View ); // view表示我们眼睛观测点的位置
cb.mProjection = XMMatrixTranspose( g_Projection ); // 投影
// 更新buffer
g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, nullptr, &cb, 0, 0 );
4、渲染(填充数据)
g_pImmediateContext->VSSetShader( g_pVertexShader, nullptr, 0 );
g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pConstantBuffer );
g_pImmediateContext->PSSetShader( g_pPixelShader, nullptr, 0 );
g_pImmediateContext->DrawIndexed( 36, 0, 0 ); // 12个三角形 36个顶点
5、使用交换链更新缓冲区(后缓冲区更新到前缓冲区)
g_pSwapChain->Present( 0, 0 );
四、渲染图分析
从上面的渲染图形来看,像是一个三棱锥,并不是立方体,这和我们眼睛的位置,看的方向有关系
我们看到眼睛的位置,看的点,Y坐标都是一致的,我们为了更加准确的观看到图形的真实形状,我们吧eye的Y改为3,
XMVECTOR Eye = XMVectorSet( 0.0f, 3.0f, -5.0f, 0.0f );
此时看到的就是立方体了