Mostrando entradas con la etiqueta pintado con directx11. Mostrar todas las entradas
Mostrando entradas con la etiqueta pintado con directx11. Mostrar todas las entradas

domingo, 7 de julio de 2013

DirectX desde 0. Capitulo 2. Pintando con DirectX11 (Triángulos y Shaders)

Artículo perteneciente al tutorial de DirectX desde 0

Hola a todos,

El capítulo de hoy se basa en parte en el capitulo número 4 de rastertek que podréis ver aquí.

Cuando se empieza a aprender una API gráfica lo primero que hay que saber es como se pinta un triangulo. En OpenGL es bastante sencillo (siempre y cuando no nos importe demasiado el rendimiento) mientras que en DirectX es un proceso más tedioso y que generalmente va cambiando su manera de implementarse entre versiones. De hecho, uno de los grandes problemas que hay con DirectX (sobretodo el 11)  es que encontrar un buen tutorial en castellano que explique de forma sencilla como pintar un triangulo casi casi es imposible.

La gracia está en que aunque la curva de aprendizaje de DirectX sea más dura que la de OpenGL nos obliga ya desde el principio a usar la tarjeta gráfica correctamente . Con OpenGL podíamos pintar triángulos mediante primitivas directas aunque fuera ineficiente (ver aquí para más información) mientras que con DirectX hemos de situar toda la información de nuestro pintado en vectores y luego pasarle esos vectores a la tarjeta gráfica ( aunque ahora mismo no os lo pueda parecer es una de las maneras más eficientes de computar gráficos ya que se trabaja directamente con memoria de video.)

A nivel de código el pintado de un cuadrado quedaría tal que así,:

Por un lado la inicialización de la información de pintado
bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;


NOTA: DirectX 11 nos obliga a pasarle a la tarjeta gráfica un vector con todos los vertices y un vector con todos los indices a esos vértices que usaremos para conformar triángulos  Esto es debido a que normalmente un vértice acostumbra a formar parte de más de un triangulo.

//Seteamos el número de vértices que vamos a pintar (4, por que es un cuadrado)
m_vertexCount = 4;

//Seteamos el número de indices que usaremos (6, por que describimos dos triángulos)
m_indexCount = 6;

// Create the vertex array.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
return false;
}

// Create the index array.
indices = new unsigned long[m_indexCount];
if(!indices)
{
return false;
}

NOTA: En la lista de vértices no solo pondremos su posición espacial sino que incluiremos todo aquello que puede modificar su pintado, como por ejemplo, el color.
vertices[0].position = D3DXVECTOR3(-1.0f, -1.0f, 0.0f);
vertices[0].color = D3DXVECTOR4(1.0f, 0.0f, 0.0f, 1.0f);

vertices[1].position = D3DXVECTOR3(-1.0f, 1.0f, 0.0f);
vertices[1].color = D3DXVECTOR4(0.0f, 1.0f, 0.0f, 1.0f);

vertices[2].position = D3DXVECTOR3(1.0f, 1.0f, 0.0f);
vertices[2].color = D3DXVECTOR4(0.5f, 0.5f, 0.5f, 0.5f);

vertices[3].position = D3DXVECTOR3(1.0f, -1.0f, 0.0f);
vertices[3].color = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 1.0f);

// Load the index array with data.
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 2;
indices[4] = 3;
indices[5] = 0;

// Set up the description of the static vertex buffer.
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;

// Give the subresource structure a pointer to the vertex data.
    vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;

// Now create the vertex buffer.
    result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if(FAILED(result))
{
return false;
}

NOTA: Le pasamos la información de vertices e indices a la tarjeta gráfica
// Set up the description of the static index buffer.
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;

// Give the subresource structure a pointer to the index data.
    indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;

// Create the index buffer.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if(FAILED(result))
{
return false;
}

// Release the arrays now that the vertex and index buffers have been created and loaded.
delete [] vertices;
vertices = 0;

delete [] indices;
indices = 0;

return true;
}


Por otro lado la petición de que esa información que hay en tarjeta gráfica se pinte de una manera u otra se hace mediante el Render. Al igual que OpenGL, DirectX usa matrices para realizar los cálculos de transformación del espacio, así que el siguiente código os debería resultar minimamente familiar.

bool GraphicsClass::Render()
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
bool result;


// Clear the buffers to begin the scene.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

// Generate the view matrix based on the camera's position.
m_Camera->Render();

NOTA: Obtenemos las matrices de transformación del espacio y las almacenamos en memoria
// Get the world, view, and projection matrices from the camera and d3d objects.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);

// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
m_Model->Render(m_D3D->GetDeviceContext());

NOTA: Usamos un shader para realizar el render. Al final de este artículo explicaremos lo que es un shader de forma rápida.
result = m_ColorShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix);

if(!result)
{
return false;
}

// Present the rendered scene to the screen.
m_D3D->EndScene();

return true;
}

Un shader, concepto nuevo en estos tutoriales, es un pequeño programa usable desde DirectX o desde OpenGL  que se encarga de ejecutarse directamente en la tarjeta gráfica. Hay diferentes tipos de shaders, los más usados: pixel shader y vertex shader. El shader usado en este tutorial es del tipo vertex shader y se encarga de aplicar las matrices de transformación de nuestra escena a nuestro modelo para poderlo renderizar correctamente por pantalla. Este proceso se podría realizar mediante CPU, pero es obvio que va más rápido si se ejecuta en la GPU.

El código del shader usado es el siguiente:
////////////////////////////////////////////////////////////////////////////////
// Filename: color.vs
////////////////////////////////////////////////////////////////////////////////


/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float4 color : COLOR;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ColorVertexShader(VertexInputType input)
{
    PixelInputType output;
 

// Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

// Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
 
// Store the input color for the pixel shader to use.
    output.color = input.color;
 
    return output;
}

En breve haré un capítulo enfocado solamente a shaders para que veais toda la potencia que tienen.

Ya para finalizar, si os bajáis el código del svn, compilad y ejecutad, debería quedaros algo tal que así:


Espero que os haya gustado

Nos vemos


LordPakusBlog

lunes, 1 de julio de 2013

DirectX desde 0. Capitulo 1. Iniciando DirectX

Artículo perteneciente al tutorial de DirectX desde 0

Hola a todos,

Bienvenidos al primer capitulo del tutorial de DirectX 11 desde 0.

DirectX es un conjunto de funcionalidades proporcionadas por Microsoft para crear videojuegos. La ventaja principal respecto a OpenGL es que engloba muchas funcionalidades ( gráficos, sonidos, inputs, textos) que OpenGL no cubre (ya que es solo es una API gráfica). La principal desventaja de DirectX es que solo funciona en productos Microsoft ( es decir, Windows ) con lo que el tema portabilidad queda descartado.

Hay bastantes versiones de DirectX (ahora mismo van por la 11) y no son demasiado compatibles entre si, así que creo que vale la pena empezar directamente por la última versión.

Antes que nada deberemos preparar nuestro PC para que funcione con DirectX:

1. Instalar el SDK de DirectX11. Lo podéis descargar de aqui
2. Es posible que al finalizar la instalación del SDK de DirectX os salga un error s1023. Estad tranquilos que tiene solución, lo mejor que podéis hacer es ir al siguiente link.
3. Si no lo tenéis, bajaos el Visual Studio Express Edition. Para trabajar con DirectX es la mejor opción.
4. Bajaos el código de este tutorial, ya sea por svn  o por descarga.

Para realizar este tutorial me he basado en el tutorial número 3 de rastertek y lo he modificado ligeramente para que funcione en hardware que no admite directamente DirectX11. Un problema del tutorial de rastertek es que si tu tarjeta de video no soportaba nativamente DirectX11 no funcionaba nada, y no creo que sea plan que por un mero tema de hardware nos perdamos un buen tutorial no?

El código lo podéis mirar aquí y la modificación realizada solo ha sido lo siguiente:

if(FAILED(result))
{
result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, &featureLevel, 1, D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL, &m_deviceContext);
}

if(FAILED(result))
{
result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_SOFTWARE, NULL, 0, &featureLevel, 1, D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL, &m_deviceContext);
}

Por ahora, con que miréis el código del svn y seais capaces de instalaros el SDK y compilar, suficiente.

En cuanto lo tengáis compilado, ejecutad, os debería salir una ventana en gris.

En el siguiente capítulo empezaremos ha hablar realmente de DirectX.

Espero que os haya gustado

Nos vemos


LordPakusBlog

Entradas populares