Verwenden von Tiefe und Effekten auf Grundtypen
Hier zeigen wir Ihnen, wie Sie Tiefe, Perspektive, Farbe und andere Effekte auf Grundtypen verwenden.
Ziel: Um ein 3D-Objekt zu erstellen und grundlegende Vertexbeleuchtung und Farbgebung darauf anzuwenden.
Voraussetzungen
Wir gehen davon aus, dass Sie mit C++ vertraut sind. Außerdem benötigen Sie grundlegende Erfahrungen mit Grafikprogrammierungskonzepten.
Wir gehen auch davon aus, dass Sie schnellstart: Einrichten von DirectX-Ressourcen und Anzeigen eines Bilds und Erstellen von Shadern und Zeichnen von Grundtypen.
Zeit bis zum Abschluss: 20 Minuten.
Anweisungen
1. Definieren von Cubevariablen
Zunächst müssen wir die SimpleCubeVertex - und ConstantBuffer-Strukturen für den Würfel definieren. Diese Strukturen geben die Vertexpositionen und -farben für den Würfel und die Darstellung des Würfels an. Wir deklarieren ID3D11DepthStencilView und ID3D11Buffer mit ComPtr und deklarieren eine Instanz von ConstantBuffer.
struct SimpleCubeVertex
{
DirectX::XMFLOAT3 pos; // Position
DirectX::XMFLOAT3 color; // Color
};
struct ConstantBuffer
{
DirectX::XMFLOAT4X4 model;
DirectX::XMFLOAT4X4 view;
DirectX::XMFLOAT4X4 projection;
};
// This class defines the application as a whole.
ref class Direct3DTutorialFrameworkView : public IFrameworkView
{
private:
Platform::Agile<CoreWindow> m_window;
ComPtr<IDXGISwapChain1> m_swapChain;
ComPtr<ID3D11Device1> m_d3dDevice;
ComPtr<ID3D11DeviceContext1> m_d3dDeviceContext;
ComPtr<ID3D11RenderTargetView> m_renderTargetView;
ComPtr<ID3D11DepthStencilView> m_depthStencilView;
ComPtr<ID3D11Buffer> m_constantBuffer;
ConstantBuffer m_constantBufferData;
2. Erstellen einer Tiefenschablonenansicht
Zusätzlich zum Erstellen der Renderzielansicht erstellen wir auch eine Tiefenschablonenansicht. Die Tiefenschablonenansicht ermöglicht Direct3D das effiziente Rendern von Objekten, die der Kamera vor Objekten weiter von der Kamera entfernt sind. Bevor wir eine Ansicht für einen Tiefenschablonenpuffer erstellen können, müssen wir den Tiefenschablonenpuffer erstellen. Wir füllen eine D3D11_TEXTURE2D_DESC auf, um den Tiefenschablonenpuffer zu beschreiben, und rufen dann ID3D11Device::CreateTexture2D auf, um den Tiefenschablonenpuffer zu erstellen. Zum Erstellen der Tiefenschablonenansicht füllen wir eine D3D11_DEPTH_STENCIL_VIEW_DESC auf, um die Tiefenschablonenansicht zu beschreiben und die Beschreibung der Tiefenschablonenansicht und den Tiefenschablonenpuffer an ID3D11Device::CreateDepthStencilView zu übergeben.
// Once the render target view is created, create a depth stencil view. This
// allows Direct3D to efficiently render objects closer to the camera in front
// of objects further from the camera.
D3D11_TEXTURE2D_DESC backBufferDesc = {0};
backBuffer->GetDesc(&backBufferDesc);
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = backBufferDesc.Width;
depthStencilDesc.Height = backBufferDesc.Height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
ComPtr<ID3D11Texture2D> depthStencil;
DX::ThrowIfFailed(
m_d3dDevice->CreateTexture2D(
&depthStencilDesc,
nullptr,
&depthStencil
)
);
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
depthStencilViewDesc.Format = depthStencilDesc.Format;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Flags = 0;
depthStencilViewDesc.Texture2D.MipSlice = 0;
DX::ThrowIfFailed(
m_d3dDevice->CreateDepthStencilView(
depthStencil.Get(),
&depthStencilViewDesc,
&m_depthStencilView
)
);
3. Aktualisieren der Perspektive mit dem Fenster
Wir aktualisieren die Perspektivische Projektionsparameter für den Konstantenpuffer abhängig von den Fensterabmessungen. Wir korrigieren die Parameter auf ein 70-Grad-Sichtfeld mit einem Tiefenbereich von 0,01 bis 100.
// Finally, update the constant buffer perspective projection parameters
// to account for the size of the application window. In this sample,
// the parameters are fixed to a 70-degree field of view, with a depth
// range of 0.01 to 100. For a generalized camera class, see Lesson 5.
float xScale = 1.42814801f;
float yScale = 1.42814801f;
if (backBufferDesc.Width > backBufferDesc.Height)
{
xScale = yScale *
static_cast<float>(backBufferDesc.Height) /
static_cast<float>(backBufferDesc.Width);
}
else
{
yScale = xScale *
static_cast<float>(backBufferDesc.Width) /
static_cast<float>(backBufferDesc.Height);
}
m_constantBufferData.projection = DirectX::XMFLOAT4X4(
xScale, 0.0f, 0.0f, 0.0f,
0.0f, yScale, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, -0.01f,
0.0f, 0.0f, -1.0f, 0.0f
);
4. Erstellen von Vertex- und Pixelshadern mit Farbelementen
In dieser App erstellen wir komplexere Vertex- und Pixelshader als das, was wir im vorherigen Lernprogramm beschrieben haben: Erstellen von Shadern und Zeichnen von Grundtypen. Der Vertex-Shader der App wandelt jede Vertexposition in den Projektionsbereich um und übergibt die Vertexfarbe an den Pixelshader.
Das Array der App mit D3D11_INPUT_ELEMENT_DESC Strukturen, die das Layout des Vertex-Shadercodes beschreiben, weist zwei Layoutelemente auf: ein Element definiert die Vertexposition, und das andere Element definiert die Farbe.
Wir erstellen Vertex-, Index- und Konstantenpuffer, um einen orbitierenden Würfel zu definieren.
So definieren Sie einen orbitierenden Würfel
- Zunächst definieren wir den Würfel. Wir weisen jedem Scheitelpunkt zusätzlich zu einer Position eine Farbe zu. Dadurch kann der Pixelshader jedes Gesicht unterschiedlich färben, sodass das Gesicht unterschieden werden kann.
- Als Nächstes beschreiben wir die Vertex- und Indexpuffer (D3D11_BUFFER_DESC und D3D11_SUBRESOURCE_DATA) mithilfe der Cubedefinition. Wir rufen ID3D11Device::CreateBuffer einmal für jeden Puffer auf.
- Als Nächstes erstellen wir einen Konstantenpuffer (D3D11_BUFFER_DESC) zum Übergeben von Modell-, Ansichts- und Projektionsmatrizen an den Vertex-Shader. Später können wir den Konstantenpuffer verwenden, um den Würfel zu drehen und eine perspektivische Projektion darauf anzuwenden. Wir rufen ID3D11Device::CreateBuffer auf, um den Konstantenpuffer zu erstellen.
- Als Nächstes geben wir die Ansichtstransformation an, die einer Kameraposition von X = 0, Y = 1, Z = 2 entspricht.
- Schließlich deklarieren wir eine Gradvariable , mit der wir den Würfel animieren, indem wir ihn jeden Frame drehen.
auto loadVSTask = DX::ReadDataAsync(L"SimpleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(L"SimplePixelShader.cso");
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::XMFLOAT3 vector defining the vertex position, and
// a DirectX::XMFLOAT3 vector defining the vertex color.
const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, 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 unit cube.
auto createCubeTask = (createPSTask && createVSTask).then([this] () {
// In the array below, which will be used to initialize the cube vertex buffers,
// each vertex is assigned a color in addition to a position. This will allow
// the pixel shader to color each face differently, enabling them to be distinguished.
SimpleCubeVertex cubeVertices[] =
{
{ float3(-0.5f, 0.5f, -0.5f), float3(0.0f, 1.0f, 0.0f) }, // +Y (top face)
{ float3( 0.5f, 0.5f, -0.5f), float3(1.0f, 1.0f, 0.0f) },
{ float3( 0.5f, 0.5f, 0.5f), float3(1.0f, 1.0f, 1.0f) },
{ float3(-0.5f, 0.5f, 0.5f), float3(0.0f, 1.0f, 1.0f) },
{ float3(-0.5f, -0.5f, 0.5f), float3(0.0f, 0.0f, 1.0f) }, // -Y (bottom face)
{ float3( 0.5f, -0.5f, 0.5f), float3(1.0f, 0.0f, 1.0f) },
{ float3( 0.5f, -0.5f, -0.5f), float3(1.0f, 0.0f, 0.0f) },
{ float3(-0.5f, -0.5f, -0.5f), float3(0.0f, 0.0f, 0.0f) },
};
unsigned short cubeIndices[] =
{
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7,
3, 2, 5,
3, 5, 4,
2, 1, 6,
2, 6, 5,
1, 7, 6,
1, 0, 7,
0, 3, 4,
0, 4, 7
};
D3D11_BUFFER_DESC vertexBufferDesc = {0};
vertexBufferDesc.ByteWidth = sizeof(SimpleCubeVertex) * ARRAYSIZE(cubeVertices);
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 = cubeVertices;
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(cubeIndices);
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 = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
ComPtr<ID3D11Buffer> indexBuffer;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&indexBufferDesc,
&indexBufferData,
&indexBuffer
)
);
// Create a constant buffer for passing model, view, and projection matrices
// to the vertex shader. This will allow us to rotate the cube and apply
// a perspective projection to it.
D3D11_BUFFER_DESC constantBufferDesc = {0};
constantBufferDesc.ByteWidth = sizeof(m_constantBufferData);
constantBufferDesc.Usage = D3D11_USAGE_DEFAULT;
constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantBufferDesc.CPUAccessFlags = 0;
constantBufferDesc.MiscFlags = 0;
constantBufferDesc.StructureByteStride = 0;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&constantBufferDesc,
nullptr,
&m_constantBuffer
)
);
// Specify the view transform corresponding to a camera position of
// X = 0, Y = 1, Z = 2. For a generalized camera class, see Lesson 5.
m_constantBufferData.view = DirectX::XMFLOAT4X4(
-1.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
0.00000000f, 0.89442718f, 0.44721359f, 0.00000000f,
0.00000000f, 0.44721359f, -0.89442718f, -2.23606800f,
0.00000000f, 0.00000000f, 0.00000000f, 1.00000000f
);
});
// This value will be used to animate the cube by rotating it every frame.
float degree = 0.0f;
5. Drehen und Zeichnen des Würfels und Darstellen des gerenderten Bilds
Wir geben eine Endlosschleife ein, um die Szene kontinuierlich zu rendern und anzuzeigen. Wir rufen die RotationY-Inlinefunktion (BasicMath.h) mit einer Drehungsmenge auf, um Werte festzulegen, die die Modellmatrix des Würfels um die Y-Achse drehen. Anschließend rufen wir ID3D11DeviceContext::UpdateSubresource auf, um den Konstantenpuffer zu aktualisieren und das Cubemodell zu drehen. Wir rufen ID3D11DeviceContext::OMSetRenderTargets auf, um das Renderziel als Ausgabeziel anzugeben. In diesem OMSetRenderTargets-Aufruf übergeben wir die Tiefenschablonenansicht . Wir rufen ID3D11DeviceContext::ClearRenderTargetView auf, um das Renderziel auf eine vollblaue Farbe zu löschen und ID3D11DeviceContext::ClearDepthStencilView aufzurufen, um den Tiefenpuffer zu löschen.
In der Endlosschleife zeichnen wir auch den Würfel auf der blauen Oberfläche.
So zeichnen Sie den Würfel
- Zunächst rufen wir ID3D11DeviceContext::IASetInputLayout auf, um zu beschreiben, wie Vertexpufferdaten in die Eingabeassemblerphase gestreamt werden.
- Als Nächstes rufen wir ID3D11DeviceContext::IASetVertexBuffers und ID3D11DeviceContext::IASetIndexBuffer auf, um die Vertex- und Indexpuffer an die Eingabeassemblerphase zu binden.
- Als Nächstes rufen wir ID3D11DeviceContext::IASetPrimitiveTopology mit dem D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP Wert auf, der für die Eingabeassemblerphase angegeben werden soll, um die Vertexdaten als Dreiecksstreifen zu interpretieren.
- Als Nächstes rufen wir ID3D11DeviceContext::VSSetShader auf, um die Vertex-Shaderphase mit dem Vertex-Shadercode und ID3D11DeviceContext::P SSetShader zu initialisieren, um die Pixelshaderphase mit dem Pixelshadercode zu initialisieren.
- Als Nächstes rufen wir ID3D11DeviceContext::VSSetConstantBuffers auf, um den Konstantenpuffer festzulegen, der von der Vertex-Shaderpipelinephase verwendet wird.
- Schließlich rufen wir ID3D11DeviceContext::D rawIndexed auf, um den Cube zu zeichnen und an die Renderingpipeline zu übermitteln.
Wir rufen IDXGISwapChain::P resent auf, um das gerenderte Bild im Fenster darzustellen.
// Update the constant buffer to rotate the cube model.
m_constantBufferData.model = XMMatrixRotationY(-degree);
degree += 1.0f;
m_d3dDeviceContext->UpdateSubresource(
m_constantBuffer.Get(),
0,
nullptr,
&m_constantBufferData,
0,
0
);
// Specify the render target and depth stencil we created as the output target.
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
m_depthStencilView.Get()
);
// Clear the render target to a solid color, and reset the depth stencil.
const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
m_d3dDeviceContext->ClearRenderTargetView(
m_renderTargetView.Get(),
clearColor
);
m_d3dDeviceContext->ClearDepthStencilView(
m_depthStencilView.Get(),
D3D11_CLEAR_DEPTH,
1.0f,
0
);
m_d3dDeviceContext->IASetInputLayout(inputLayout.Get());
// Set the vertex and index buffers, and specify the way they define geometry.
UINT stride = sizeof(SimpleCubeVertex);
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->VSSetConstantBuffers(
0,
1,
m_constantBuffer.GetAddressOf()
);
m_d3dDeviceContext->PSSetShader(
pixelShader.Get(),
nullptr,
0
);
// Draw the cube.
m_d3dDeviceContext->DrawIndexed(
ARRAYSIZE(cubeIndices),
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)
);
Zusammenfassung und nächste Schritte
Wir haben Tiefe, Perspektive, Farbe und andere Effekte auf Grundtypen verwendet.
Als Nächstes wenden wir Texturen auf Grundtypen an.
Anwenden von Texturen auf Grundtypen