Usar profundidad y efectos en primitivos
Aquí te mostramos cómo usar profundidad, perspectiva, color y otros efectos en primitivos.
Objetivo: Para crear un objeto 3D y aplicar iluminación básica de vértices y colorearlo.
Se supone que está familiarizado con C++. También necesita experiencia básica con los conceptos de programación de gráficos.
También se supone que ha pasado por inicio rápido: configuración de recursos de DirectX y visualización de una imagen y creación de sombreadores y dibujos primitivos.
Tiempo de finalización: 20 minutos.
En primer lugar, es necesario definir las estructuras SimpleCubeVertex y ConstantBuffer para el cubo. Estas estructuras especifican las posiciones y colores del vértice para el cubo y cómo se verá el cubo. Declaramos ID3D11DepthStencilView e ID3D11Buffer con ComPtr y declaramos una instancia de 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;
Además de crear la vista de destino de representación, también se crea una vista de galería de símbolos de profundidad. La vista de galería de símbolos de profundidad permite a Direct3D representar de forma eficaz los objetos más cerca de la cámara delante de los objetos más allá de la cámara. Para poder crear una vista a un búfer de galería de símbolos de profundidad, debemos crear el búfer de galería de símbolos de profundidad. Rellenamos un D3D11_TEXTURE2D_DESC para describir el búfer de galería de símbolos de profundidad y, a continuación, llamamos a ID3D11Device::CreateTexture2D para crear el búfer de galería de símbolos de profundidad. Para crear la vista de galería de símbolos de profundidad, rellenamos un D3D11_DEPTH_STENCIL_VIEW_DESC para describir la vista de galería de símbolos de profundidad y pasar la descripción de la vista de galería de símbolos de profundidad y el búfer de galería de símbolos de profundidad a ID3D11Device::CreateDepthStencilView.
// 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
)
);
Actualizamos los parámetros de proyección de perspectiva para el búfer de constantes en función de las dimensiones de la ventana. Se han corregido los parámetros en un campo de vista de 70 grados con un intervalo de profundidad de 0,01 a 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
);
En esta aplicación, creamos sombreadores de vértices y píxeles más complejos que los descritos en el tutorial anterior, Creación de sombreadores y primitivos de dibujo. El sombreador de vértices de la aplicación transforma cada posición de vértice en espacio de proyección y pasa el color del vértice al sombreador de píxeles.
La matriz de la aplicación de D3D11_INPUT_ELEMENT_DESC estructuras que describen el diseño del código del sombreador de vértices tiene dos elementos de diseño: un elemento define la posición del vértice y el otro elemento define el color.
Creamos búferes de vértices, índices y constantes para definir un cubo en órbita.
Para definir un cubo en órbita
- En primer lugar, definimos el cubo. Asignamos cada vértice un color además de una posición. Esto permite que el sombreador de píxeles colore cada cara de forma diferente para que se pueda distinguir la cara.
- A continuación, se describen los búferes de vértices e índices (D3D11_BUFFER_DESC y D3D11_SUBRESOURCE_DATA) mediante la definición del cubo. Llamamos a ID3D11Device::CreateBuffer una vez para cada búfer.
- A continuación, creamos un búfer de constantes (D3D11_BUFFER_DESC) para pasar matrices de modelo, vista y proyección al sombreador de vértices. Más adelante podemos usar el búfer de constantes para girar el cubo y aplicar una proyección de perspectiva a él. Llamamos a ID3D11Device::CreateBuffer para crear el búfer de constantes.
- A continuación, especificamos la transformación de vista que corresponde a una posición de cámara de X = 0, Y = 1, Z = 2.
- Por último, declaramos una variable de grado que usaremos para animar el cubo girando cada fotograma.
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;
Entramos en un bucle sin fin para representar y mostrar continuamente la escena. Llamamos a la función insertada rotationY (BasicMath.h) con una cantidad de rotación para establecer valores que girarán la matriz del modelo del cubo alrededor del eje Y. A continuación, llamamos a ID3D11DeviceContext::UpdateSubresource para actualizar el búfer de constantes y girar el modelo de cubo. Llamamos a ID3D11DeviceContext::OMSetRenderTargets para especificar el destino de representación como destino de salida. En esta llamada a OMSetRenderTargets, pasamos la vista de galería de símbolos de profundidad. Llamamos a ID3D11DeviceContext::ClearRenderTargetView para borrar el destino de representación en un color azul sólido y llamar a ID3D11DeviceContext::ClearDepthStencilView para borrar el búfer de profundidad.
En el bucle sin fin, también dibujamos el cubo en la superficie azul.
Para dibujar el cubo
- En primer lugar, llamamos a ID3D11DeviceContext::IASetInputLayout para describir cómo se transmiten los datos del búfer de vértices a la fase del ensamblador de entrada.
- A continuación, llamamos a ID3D11DeviceContext::IASetVertexBuffers y ID3D11DeviceContext::IASetIndexBuffer para enlazar los búferes de vértices e índices a la fase del ensamblador de entrada.
- A continuación, llamamos a ID3D11DeviceContext::IASetPrimitiveTopology con el valor de D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP para especificar para la fase del ensamblador de entrada para interpretar los datos de vértices como una franja de triángulos.
- A continuación, llamamos a ID3D11DeviceContext::VSSetShader para inicializar la fase del sombreador de vértices con el código del sombreador de vértices y ID3D11DeviceContext::P SSetShader para inicializar la fase del sombreador de píxeles con el código del sombreador de píxeles.
- A continuación, llamamos a ID3D11DeviceContext::VSSetConstantBuffers para establecer el búfer de constantes que usa la fase de canalización del sombreador de vértices.
- Por último, llamamos a ID3D11DeviceContext::D rawIndexed para dibujar el cubo y enviarlo a la canalización de representación.
Llamamos a IDXGISwapChain::P resent para presentar la imagen representada en la ventana.
// 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)
);
Usamos profundidad, perspectiva, color y otros efectos en primitivos.
A continuación, aplicamos texturas a primitivos.
Aplicación de texturas a primitivos