Soluciones de Ejercicios del Libro de DirectX 12 - Capítulo 6: Dibujando Geometría con Direct3D

1. Crear un array D3D12_INPUT_ELEMENT_DESC correspondiente a la siguiente estructura de vértices:

2. Modificar el programa de demostración del cubo colorido para utilizar dos buffers de vértices (y dos ranuras de entrada) para enviar datos de vértices al pipeline de renderizado. Un buffer almacenará elementos de posición y el otros elementos de color.

Necesitaremos modificar el siguiente código fuente:

2.1. Crear dos nuevas estructuras

struct DatosPosicion
{
    XMFLOAT3 Posicion;
};
struct DatosColor
{
    XMFLOAT4 Color;
};

2.2. Modificar D3D12_INPUT_ELEMENT_DESC

 mDiseñoEntrada =
    {
        { "POSICION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
    };

2.3. Modificar la función Draw para engresar vértices dos veces y vincular ranuras diferentes

mListaComandos->IASetVertexBuffers(0, 1, &mGeometriaCubo->VistaVertexBuffer());
mListaComandos->IASetVertexBuffers(1, 1, &mGeometriaCubo->VistaVertexBuffer());

2.4. Modificar el Shader

struct DatosPosicion
{
    float3 PosLocal : POSICION;
};
struct DatosColor
{
    float4 Color : COLOR;
};
cbuffer cbPorObjeto : register(b0)
{
    float4x4 gVistaProyeccionMundo; 
    float tiempo;
};

struct VerticeSalida
{
    float4 PosProyeccion : SV_POSITION;
    float4 Color : COLOR;
};
VerticeSalida VS(DatosPosicion vp, DatosColor vc)
{
    VerticeSalida vsalida;

    // Transformar al espacio de recorte homogéneo.
    vsalida.PosProyeccion = mul(float4(vp.PosLocal, 1.0f), gVistaProyeccionMundo);

    // Pasar el color del vértice al shader de píxeles.
    vsalida.Color = vc.Color;

    return vsalida;
}

float4 PS(VerticeSalida pin) : SV_Target
{
    return pin.Color;
}

4. Construir la lista de vértices e índices de la pirámide mostrada en la Figura 6.8 y renderizarla: el vértice superior debe ser rojo y los vértices de la base verde.

    std::array<Vertice, 5> verticesPiramide =
    {
        Vertice({ XMFLOAT3(-1.0f, 0.0f, 1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(-1.0f,0.0f, -1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(+1.0f, 0.0f, +1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(0.0f, 2.0f, 0.0f),   XMFLOAT4(Rojo) }),
    };
    std::array<std::uint16_t, 18> indicesPiramide =
    {
        // cara frontal
        0, 1, 2,
        0, 2, 3,

        0,4,1,

        1,4,2,

        2,4,3,

        3,4,0
    };

    // Modificar vértices e índices a subir<br></br>    mGeometriaCubo = std::make_unique<GeometriaMalla>();
    mGeometriaCubo->Nombre = "geometriaCubo";

    ThrowIfFailed(D3DCreateBlob(tamanoBytesVB, &mGeometriaCubo->VertexBufferCPU));
    CopyMemory(mGeometriaCubo->VertexBufferCPU->GetBufferPointer(), verticesPiramide.data(), tamanoBytesVB);

    ThrowIfFailed(D3DCreateBlob(tamanoBytesIB, &mGeometriaCubo->IndexBufferCPU));
    CopyMemory(mGeometriaCubo->IndexBufferCPU->GetBufferPointer(), indicesPiramide.data(), tamanoBytesIB);

    mGeometriaCubo->VertexBufferGPU = d3dUtil::CrearBufferPredeterminado(dispositivoD3D.Get(),
        listaComandos.Get(), verticesPiramide.data(), tamanoBytesVB, mGeometriaCubo->VertexBufferUploader);

    mGeometriaCubo->IndexBufferGPU = d3dUtil::CrearBufferPredeterminado(dispositivoD3D.Get(),
        listaComandos.Get(), indicesPiramide.data(), tamanoBytesIB, mGeometriaCubo->IndexBufferUploader);

    mGeometriaCubo->VertexByteStride = sizeof(Vertice);
    mGeometriaCubo->VertexBufferByteSize = tamanoBytesVB;
    mGeometriaCubo->IndexFormat = DXGI_FORMAT_R16_UINT;
    mGeometriaCubo->IndexBufferByteSize = tamanoBytesIB;

    SubgeometriaMalla subgeometria;
    subgeometria.IndexCount = (UINT)indicesPiramide.size();
    subgeometria.StartIndexLocation = 0;
    subgeometria.BaseVertexLocation = 0;

    mGeometriaCubo->DrawArgs["piramide"] = subgeometria;

7. Combinar los vértices del cubo y la pirámide del ejercicio 4 en un único buffer de vértices grande, y fusionar ambos índices en un único buffer de índices (sin actualizar los valores de índice). Luego, ajustar los parámetros del método ID3D12GraphicsCommandList::DrawIndexedInstanced para renderizar correctamente el cubo y la pirámide. Establecer matrices de transformación mundial para que ambos objetos no se intersequen en el espacio mundial.

7.1. Modificación del método ConstruirGeometriaCubo

void AplicacionCubo::ConstruirGeometriaCubo()
{
    std::array<Vertice, 8> verticesCubo =
    {
        Vertice({ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Blanco) }),
        Vertice({ XMFLOAT3(-1.0f, +1.0f, -1.0f), XMFLOAT4(Negro) }),
        Vertice({ XMFLOAT3(+1.0f, +1.0f, -1.0f), XMFLOAT4(Rojo) }),
        Vertice({ XMFLOAT3(+1.0f, -1.0f, -1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(-1.0f, -1.0f, +1.0f), XMFLOAT4(Azul) }),
        Vertice({ XMFLOAT3(-1.0f, +1.0f, +1.0f), XMFLOAT4(Amarillo) }),
        Vertice({ XMFLOAT3(+1.0f, +1.0f, +1.0f), XMFLOAT4(Cian) }),
        Vertice({ XMFLOAT3(+1.0f, -1.0f, +1.0f), XMFLOAT4(Magenta) })
    };

    std::array<std::uint16_t, 36> indicesCubo =
    {
        // cara frontal
        0, 1, 2,
        0, 2, 3,

        // cara posterior
        4, 6, 5,
        4, 7, 6,

        // cara izquierda
        4, 5, 1,
        4, 1, 0,

        // cara derecha
        3, 2, 6,
        3, 6, 7,

        // cara superior
        1, 5, 6,
        1, 6, 2,

        // cara inferior
        4, 0, 3,
        4, 3, 7
    };
    
    std::array<Vertice, 5> verticesPiramide =
    {
        Vertice({ XMFLOAT3(-1.0f, 0.0f, 1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(-1.0f,0.0f, -1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(+1.0f, 0.0f, +1.0f), XMFLOAT4(Verde) }),
        Vertice({ XMFLOAT3(0.0f, 2.0f, 0.0f),   XMFLOAT4(Rojo) }),
    };
    
    std::array<std::uint16_t, 18> indicesPiramide =
    {
        // cara frontal
        0, 1, 2,
        0, 2, 3,

        0,4,1,

        1,4,2,

        2,4,3,

        3,4,0
    };

    UINT offsetVerticesCubo = 0;
    UINT offsetVerticesPiramide = (UINT)verticesCubo.size();

    UINT offsetIndicesCubo = 0;
    UINT offsetIndicesPiramide = (UINT)indicesCubo.size();

    SubgeometriaMalla subgeometriaCubo;
    subgeometriaCubo.IndexCount = indicesCubo.size();
    subgeometriaCubo.StartIndexLocation = offsetIndicesCubo;
    subgeometriaCubo.BaseVertexLocation = offsetVerticesCubo;

    SubgeometriaMalla subgeometriaPiramide;
    subgeometriaPiramide.IndexCount = indicesPiramide.size();
    subgeometriaPiramide.StartIndexLocation = offsetIndicesPiramide;
    subgeometriaPiramide.BaseVertexLocation = offsetVerticesPiramide;

    std::vector<Vertice> todosVertices;
    todosVertices.insert(todosVertices.end(), std::begin(verticesCubo), std::end(verticesCubo));
    todosVertices.insert(todosVertices.end(), std::begin(verticesPiramide), std::end(verticesPiramide));

    std::vector<std::uint16_t> todosIndices;
    todosIndices.insert(todosIndices.end(), std::begin(indicesCubo), std::end(indicesCubo));
    todosIndices.insert(todosIndices.end(), std::begin(indicesPiramide), std::end(indicesPiramide));

    const UINT tamanoBytesVB = (UINT)todosVertices.size() * sizeof(Vertice);
    const UINT tamanoBytesIB = (UINT)todosIndices.size() * sizeof(std::uint16_t);
    
    mGeometriaCubo = std::make_unique<GeometriaMalla>();
    mGeometriaCubo->Nombre = "geometriaCubo";

    ThrowIfFailed(D3DCreateBlob(tamanoBytesVB, &mGeometriaCubo->VertexBufferCPU));
    CopyMemory(mGeometriaCubo->VertexBufferCPU->GetBufferPointer(), todosVertices.data(), tamanoBytesVB);

    ThrowIfFailed(D3DCreateBlob(tamanoBytesIB, &mGeometriaCubo->IndexBufferCPU));
    CopyMemory(mGeometriaCubo->IndexBufferCPU->GetBufferPointer(), todosIndices.data(), tamanoBytesIB);

    mGeometriaCubo->VertexBufferGPU = d3dUtil::CrearBufferPredeterminado(dispositivoD3D.Get(),
        listaComandos.Get(), todosVertices.data(), tamanoBytesVB, mGeometriaCubo->VertexBufferUploader);

    mGeometriaCubo->IndexBufferGPU = d3dUtil::CrearBufferPredeterminado(dispositivoD3D.Get(),
        listaComandos.Get(), todosIndices.data(), tamanoBytesIB, mGeometriaCubo->IndexBufferUploader);

    mGeometriaCubo->VertexByteStride = sizeof(Vertice);
    mGeometriaCubo->VertexBufferByteSize = tamanoBytesVB;
    mGeometriaCubo->IndexFormat = DXGI_FORMAT_R16_UINT;
    mGeometriaCubo->IndexBufferByteSize = tamanoBytesIB;

    mGeometriaCubo->DrawArgs["cubo"] = subgeometriaCubo;
    mGeometriaCubo->DrawArgs["piramide"] = subgeometriaPiramide;
}

7.2. Modificación del método Dibujar

 listaComandos->DrawIndexedInstanced(mGeometriaCubo->DrawArgs["cubo"].IndexCount, 1, mGeometriaCubo->DrawArgs["cubo"].StartIndexLocation, mGeometriaCubo->DrawArgs["cubo"].BaseVertexLocation, 0);
    listaComandos->DrawIndexedInstanced(mGeometriaCubo->DrawArgs["piramide"].IndexCount, 1, mGeometriaCubo->DrawArgs["piramide"].StartIndexLocation, mGeometriaCubo->DrawArgs["piramide"].BaseVertexLocation, 0);

7.3. Tarea: Modificar posiciones

8. Modificar el programa de demostración "Cubo" para renderizar el cubo en modo alambre.

Modificar el método ConstruirPSO

auto descRasterizador = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
descRasterizador.FillMode = D3D12_FILL_MODE_WIREFRAME;
descPSO.RasterizerState = descRasterizador;

9. Modificar el programa de demostración "Cubo" para deshabilitar primero el descarte de caras traseras (D3D12_CULL_MODE_NONE) y ejecutar el programa; luego, probar con el descarte de caras frontales (D3D12_CULL_MODE_FRONT). Usar el modo alambre para renderizar faiclita la observación de las diferencias entre los modos de descarte.

   auto descRasterizador = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
    //descRasterizador.FillMode = D3D12_FILL_MODE_WIREFRAME;
    //descRasterizador.CullMode = D3D12_CULL_MODE_NONE;
    descRasterizador.CullMode = D3D12_CULL_MODE_FRONT;
    descPSO.RasterizerState = descRasterizador;

13. Utilizar la prueba de recorte para descartar todos los píxeles fuera del área rectangular del centro del búfer de reserva, con encho mClientWidth/2 y alto mClientHeight/2. Para esto, necesitamos describir el área de recorte con una estructura D3D12_RECT y llamar al método RSSetScissorRects.

mRectRecorte ya está declarado en d3dApp.cpp

mRectRecorte = { 0, 0, mAnchoCliente/2, mAltoCliente/2 };

14. Implementar un efecto de cubo de color variable usando el shader de píxeles. En el shader de vértices y píxeles, usar un búfer de constantes junto con una función de control simple para hacer que el color del cubo cambie suavemente con el tiempo.

14.1. Añadir constantes

struct ConstantesObjeto
{
    XMFLOAT4X4 VistaProyeccionMundo = MathHelper::Identity4x4();
    float tiempo;
};

14.2. Método Actualizar

ConstantesObjeto constantesObjeto;
constantesObjeto.tiempo = gt.TotalTime();

14.3. Descomentar la variable tiempo en el shader anterior

Etiquetas: DirectX12 Direct3D renderizado geometría Shaders

Publicado el 6-3 04:03