建立著色器及繪製基本型別
在這裡,我們將向您展示如何使用 HLSL 來源檔案來編譯和建立著色器,然後您就可以使用著色器來繪製顯示器上的基本圖元。
我們使用頂點與畫素著色器來建立並繪製黃色三角形。 在建立 Direct3D 裝置、交換鏈以及彩現目標檢視之後,我們讀取來自磁碟上的二進位著色器物件檔案的資料。
目標:建立著色器並繪製基本圖元。
必要條件
我們假設您已熟悉 C++ 語言。 您還需要具備圖形程式設計概念的基本經驗。
我們也假設您已完成 Quickstart:設定 DirectX 資源並顯示影像。
完成時間:20 分鐘。
指示
1. 編譯 HLSL 來源檔案
Microsoft Visual Studio 使用 fxc.exe HLSL 程式碼編譯器將 .hlsl 原始檔 (SimpleVertexShader.hlsl 和 SimplePixelShader.hlsl) 編譯為 .cso 二進位制著色器對象檔案 (SimpleVertexShader.cso 和 SimplePixelShader.cso)。 如需有關 HLSL 程式碼編譯器的詳細資訊,請參閱 Effect-Compiler Tool。 如需有關編譯著色器程式碼的詳細資訊,請參閱 Compiling Shaders。
這是 SimpleVertexShader.hlsl 中的程式碼:
struct VertexShaderInput
{
DirectX::XMFLOAT2 pos : POSITION;
};
struct PixelShaderInput
{
float4 pos : SV_POSITION;
};
PixelShaderInput SimpleVertexShader(VertexShaderInput input)
{
PixelShaderInput vertexShaderOutput;
// For this lesson, set the vertex depth value to 0.5, so it is guaranteed to be drawn.
vertexShaderOutput.pos = float4(input.pos, 0.5f, 1.0f);
return vertexShaderOutput;
}
這是 SimplePixelShader.hlsl 中的程式碼:
struct PixelShaderInput
{
float4 pos : SV_POSITION;
};
float4 SimplePixelShader(PixelShaderInput input) : SV_TARGET
{
// Draw the entire triangle yellow.
return float4(1.0f, 1.0f, 0.0f, 1.0f);
}
2. 從磁碟讀取資料
我們使用 DirectX 11 App (Universal Windows) 範本中 DirectX Helper.h 的 DX::ReadDataAsync 函式,以從磁碟上的檔案非同步讀取資料。
3. 建立頂點與畫素著色器
我們從 SimpleVertexShader.cso 檔案讀取資料,並將資料指派給 vertexShaderBytecode 位元組陣列。 我們呼叫 ID3D11Device::CreateVertexShader (含位元組陣列) 以建立頂點著色器 (ID3D11VertexShader)。 我們將 SimpleVertexShader.hlsl 源中的頂點深度值設定為 0.5,以確保繪製我們的三角形。 我們將填入 D3D11_INPUT_ELEMENT_DESC structures 陣列以描述頂點著色器程式碼的版面配置,然後呼叫 ID3D11Device::CreateInputLayout,以建立版面配置。 陣列有一個定義頂點位置的版面配置元素。 我們從 SimplePixelShader.cso 檔案讀取資料,並將資料指派給 pixelShaderBytecode 位元組陣列。 我們呼叫 ID3D11Device::CreatePixelShader (含位元組陣列)以建立畫素著色器 (ID3D11PixelShader)。 我們將 SimplePixelShader.hlsl 來源中的畫素值設定為 (1,1,1),使三角形變黃。 您可以變更此值來變更顏色。
我們建立定義簡單三角形的頂點和索引緩衝區。 為此,我們首先定義三角形,接下來使用三角形定義描述頂點和索引緩衝區 (D3D11_BUFFER_DESC 和 D3D11_SUBRESOURCE_DATA),最後為每個緩衝區呼叫 ID3D11Device::CreateBuffer 一次。
auto loadVSTask = DX::ReadDataAsync(L"SimpleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(L"SimplePixelShader.cso");
// Load the raw vertex shader bytecode from disk and create a vertex shader with it.
auto createVSTask = loadVSTask.then([this](const std::vector<byte>& vertexShaderBytecode) {
ComPtr<ID3D11VertexShader> vertexShader;
DX::ThrowIfFailed(
m_d3dDevice->CreateVertexShader(
vertexShaderBytecode->Data,
vertexShaderBytecode->Length,
nullptr,
&vertexShader
)
);
// Create an input layout that matches the layout defined in the vertex shader code.
// For this lesson, this is simply a DirectX::XMFLOAT2 vector defining the vertex position.
const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
ComPtr<ID3D11InputLayout> inputLayout;
DX::ThrowIfFailed(
m_d3dDevice->CreateInputLayout(
basicVertexLayoutDesc,
ARRAYSIZE(basicVertexLayoutDesc),
vertexShaderBytecode->Data,
vertexShaderBytecode->Length,
&inputLayout
)
);
});
// Load the raw pixel shader bytecode from disk and create a pixel shader with it.
auto createPSTask = loadPSTask.then([this](const std::vector<byte>& pixelShaderBytecode) {
ComPtr<ID3D11PixelShader> pixelShader;
DX::ThrowIfFailed(
m_d3dDevice->CreatePixelShader(
pixelShaderBytecode->Data,
pixelShaderBytecode->Length,
nullptr,
&pixelShader
)
);
});
// Create vertex and index buffers that define a simple triangle.
auto createTriangleTask = (createPSTask && createVSTask).then([this] () {
DirectX::XMFLOAT2 triangleVertices[] =
{
float2(-0.5f, -0.5f),
float2( 0.0f, 0.5f),
float2( 0.5f, -0.5f),
};
unsigned short triangleIndices[] =
{
0, 1, 2,
};
D3D11_BUFFER_DESC vertexBufferDesc = {0};
vertexBufferDesc.ByteWidth = sizeof(float2) * ARRAYSIZE(triangleVertices);
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
vertexBufferData.pSysMem = triangleVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
ComPtr<ID3D11Buffer> vertexBuffer;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&vertexBuffer
)
);
D3D11_BUFFER_DESC indexBufferDesc;
indexBufferDesc.ByteWidth = sizeof(unsigned short) * ARRAYSIZE(triangleIndices);
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA indexBufferData;
indexBufferData.pSysMem = triangleIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
ComPtr<ID3D11Buffer> indexBuffer;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&indexBufferDesc,
&indexBufferData,
&indexBuffer
)
);
});
我們使用頂點著色器和畫素著色器、頂點著色器佈局、頂點緩衝器和索引緩衝器來繪製黃色三角形。
4. 繪製三角形,呈現渲染後的影像
我們進入無限循環不斷渲染和顯示場景。 我們呼叫 ID3D11DeviceContext::OMSetRenderTargets,將轉譯目標指定為輸出目標。 我們呼叫 ID3D11DeviceContext::ClearRenderTargetView with { 0.071f, 0.04f, 0.561f, 1.0f},將呈現目標清除為純藍色。
在循環中,我們在藍色曲面上繪製一個黃色三角形。
繪製黃色三角形
- 首先,我們呼叫 ID3D11DeviceContext::IASetInputLayout,描述頂點緩衝區資料如何流入input-assembler 階段。
- 接下來,我們會呼叫 ID3D11DeviceContext::IASetVertexBuffers 和 ID3D11DeviceContext::IASetIndexBuffer 將頂點和索引緩衝區繫結到輸入組譯工具階段。
- 接著,我們會使用 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP 值呼叫 ID3D11DeviceContext::IASetPrimitiveTopology 以指定輸入組譯工具階段將頂點資料解釋為三角形寬帶。
- 接著,我們呼叫 ID3D11DeviceContext::VSSetShader,以頂點著色器程式碼初始化頂點著色器階段,並呼叫 ID3D11DeviceContext::PSSetShader,以畫素著色器程式碼初始化畫素著色器階段。
- 最後,我們呼叫 ID3D11DeviceContext::DrawIndexed,繪製三角形並將其提交到渲染流程。
我們呼叫 IDXGI SwapChain::Present,將渲染的影像顯示到視窗中。
// Specify the render target we created as the output target.
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
// Clear the render target to a solid color.
const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
m_d3dDeviceContext->ClearRenderTargetView(
m_renderTargetView.Get(),
clearColor
);
m_d3dDeviceContext->IASetInputLayout(inputLayout.Get());
// Set the vertex and index buffers, and specify the way they define geometry.
UINT stride = sizeof(float2);
UINT offset = 0;
m_d3dDeviceContext->IASetVertexBuffers(
0,
1,
vertexBuffer.GetAddressOf(),
&stride,
&offset
);
m_d3dDeviceContext->IASetIndexBuffer(
indexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0
);
m_d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Set the vertex and pixel shader stage state.
m_d3dDeviceContext->VSSetShader(
vertexShader.Get(),
nullptr,
0
);
m_d3dDeviceContext->PSSetShader(
pixelShader.Get(),
nullptr,
0
);
// Draw the cube.
m_d3dDeviceContext->DrawIndexed(
ARRAYSIZE(triangleIndices),
0,
0
);
// Present the rendered image to the window. Because the maximum frame latency is set to 1,
// the render loop will generally be throttled to the screen refresh rate, typically around
// 60 Hz, by sleeping the application on Present until the screen is refreshed.
DX::ThrowIfFailed(
m_swapChain->Present(1, 0)
);
摘要和後續步驟
我們使用頂點著色器和畫素著色器來建立並繪製黃色三角形。
接下來,我們建立一個在軌道上的 3D 立方體,並對其應用燈光效果。
在基本圖元上使用深度和效果