Este artículo proviene de un motor de traducción automática.
Factor DirectX
Romper las barreras con efectos Direct2D
Descargar el código de muestra
Como los niños, aprendemos a pintar antes de aprender a leer y escribir, y sin duda sacar algunas lecciones de la experiencia. Descubrimos que el proceso temporal del cuadro se refleja en las capas de pintura sobre el lienzo. ¿Qué pintamos antes pueden parcialmente cubierto y oscurecida por qué pintamos más tarde.
Por esa razón, probablemente incluso alguien completamente familiarizado con la mecánica de la infografía puede adivinar cómo la imagen en figura 1 fue rendida: Obviamente, el fondo era color gris primero; Luego vino el triángulo azul, seguido por el verde, y el último por el rojo, que es ante todo lo demás. No es ninguna sorpresa que el proceso de representación de figuras de atrás hacia adelante es conocido como el "algoritmo del pintor".
Figura 1 tres triángulos superpuestos
Los tres triángulos en figura 1 también podría ser pedazos de papel de construcción color dispuestas en una pila. Si fueras a añadir más y más triángulos a la pila, se construirían para arriba en una pila, y lo que comenzó como una superficie bidimensional adquiriría una tercera dimensión.
Incluso en gráficos 2D, hay un concepto rudimentario de un eje Z — un espacio virtual ortogonal a la pantalla bidimensional o lona. Las capas de objetos planos 2D se rigen por el "orden Z" de las figuras. En entornos basados en XAML, por ejemplo, el Canvas.ZIndex propiedad adjunta determina qué elementos parecen sentar encima de otros, pero sólo controla el orden de que los elementos se representan en la pantalla.
El problema es el siguiente: Gráficos 2D, un índice Z siempre se aplica a la figura entera. No puede utilizar este tipo de pedidos Z para dibujar tres figuras como las de figura 2, con el primero sobre el segundo, el segundo sobre el tercero, pero el tercero en la cima de la primera.
Figura 2 triángulos superpuestos mutuamente
Ese cambio en una esquina del uno triángulo parece ligero, pero representa lo que un mundo de diferencia. La imagen en figura 2 puede ser fácil con papel de construcción, pero no es tan fácil con la pintura — ya sea en la vida real o en la programación de gráficos 2D. Uno de estos triángulos debe procesarse en dos partes con coordenadas cuidadosamente calculadas, o usar recorte basado en uno de los otros triángulos.
Los efectos y la GPU
La prestación de figura 2 puede ayudar enormemente al tomar prestados algunos conceptos del mundo de los gráficos 3D.
Estas cifras no pueden tener índices de Z uniformes; en cambio, las figuras debe tener coordenadas Z variables sobre sus superficies enteras. El proceso del dibujo puede mantener una colección de coordenadas Z (llamado un búfer Z o un búfer de profundidad) que abarca todos los píxeles de la superficie de representación. Como se representa cada figura, la coordenada Z de cada píxel de la cifra se compara con la Z correspondiente coordenadas en este búfer de profundidad. Si el pixel es la coordenada Z en el búfer de profundidad en la parte superior, se dibuja el pixel y la nueva coordenada Z se almacena en el búfer de profundidad. Si no, el píxel es ignorado.
Esto suena computacionalmente costoso — no sólo para la comparación de sí mismo, pero para el cálculo de Z coordina para cada píxel de cada figura gráfica — y eso es una evaluación precisa. Por eso es un trabajo ideal para entregar a las capacidades computacionales paralelas de la GPU moderna.
El cálculo de Z coordina para cada píxel de una figura expresiones es bastante fácil si la figura es un triángulo y tenga en cuenta que cada polígono puede ser descompuesto en triángulos. Todo lo que es necesario es darle a cada uno de los tres vértices del triángulo un punto de coordenadas 3D, y entonces cualquier punto dentro del triángulo puede ser calculada como un promedio ponderado de las coordenadas de tres vértices. (Esto implica baricéntrica coordenadas — no el único concepto usado en gráficos de computadora desarrollado por el matemático alemán Agosto Ferdinand Möbius.)
Ese mismo proceso de interpolación puede hacerle sombra el triángulo, también. Si cada vértice se le asigna un color específico, entonces cualquier píxel dentro de ese triángulo es un promedio ponderado de los tres colores, como se muestra en figura 3.
Figura 3 la pantalla de programa ThreeTriangles
Que tipo de degradado de color es también una característica importante de programación 3D porque permite triángulos ser sombreadas para parecerse a superficies curvas. Pero no es un tipo de degradado que es común en la programación 2D convencional.
La imagen en figura 3 fue creado por un programa descargable llamado ThreeTriangles que corre bajo Windows 8.1 y 8.1 de Windows Phone. (La solución se creó en Visual Studio 2013 Update 2 usando la nueva plantilla de aplicación Universal que permite compartir un montón de código entre 8.1 de Windows y Windows Phone 8.1).
Los gráficos en el programa ThreeTriangles se realizan totalmente en Direct2D, utilizando una función de Direct2D llamada efectos o (cuando les código usted mismo) efectos personalizados. Usando efectos personalizados usted puede conseguir mucho más cerca a la auténtica programación 3D que lo contrario posible con Direct2D.
Cuando se escribe un efecto personalizado para Direct2D, adquirir un privilegio limitado normalmente a los programadores 3D: Puede escribir código que se ejecuta en la GPU. Este código toma la forma de pequeños programas llamados a shaders, que escribe usando un lenguaje de sombreado de alto nivel (HLSL) que se asemeja a C. Estos sombreadores son compilados por Visual Studio en archivos de objeto (.cso) sombreado compilado durante la compilación del proyecto normal y luego ejecute en la GPU cuando se ejecuta el programa.
De hecho, Direct2D efectos a veces se describen como poco más que contenedores para shaders! Los efectos personalizados son la única manera que puede utilizar shaders dentro del contexto de la programación de Direct2D para lograr imágenes 3D.
Tres tipos diferentes de sombreadores están disponibles para el uso de Direct2D efectos:
- Un sombreador de vértices que realiza operaciones en los vértices. Cada triángulo tiene tres vértices. Los vértices siempre implican un punto de coordenadas pero pueden incluir otra información, como el color.
- Un sombreador de píxeles que realiza operaciones en todos los píxeles dentro de estos triángulos. Cualquier información suministrada con los vértices se interpola automáticamente sobre la superficie del triángulo en preparación para el sombreador de píxeles.
- Un sombreado de cálculo que utiliza la GPU para realizar el procesamiento paralelo pesado. Yo no estar discutiendo al sombreado de calcular en este artículo.
Los shaders utilizados para efectos de Direct2D tienen algo diferentes requisitos de sombreadores asociados con la programación de Direct3D, pero muchos de los conceptos son los mismos.
Los efectos incorporados y efectos personalizados
Direct2D incluye unos 40 efectos incorporados predefinidos, que realizan varias manipulaciones de procesamiento de imágenes de mapas de bits, como desdibujan o afilan, o varios tipos de manipulación de color.
Cada uno de estos efectos incorporados es identificado por un identificador de clase se utiliza para crear un efecto de ese tipo. Por ejemplo, supongamos que desea utilizar el efecto de la matriz de colores, que permite especificar una transformación para modificar los colores de un mapa de bits. Probablemente podrá declarar un objeto de tipo ID2D1Effect como un campo privado en su clase de representación:
Microsoft::WRL::ComPtr<ID2D1Effect> m_colorMatrixEffect;
En el método CreateDeviceDependentResources, se puede crear este efecto mediante una referencia a la ID de clase documentados:
d2dContext->CreateEffect(
CLSID_D2D1ColorMatrix, &m_colorMatrixEffect);
En ese momento, puede llamar a SetInput en el objeto efecto para establecer un mapa de bits y SetValue para especificar una matriz de transformación.Procesar esta desplazada en color de mapa de bits llamando al:
d2dContext->DrawImage(m_colorMatrixEffect.Get());
Involucran a todos los efectos incorporados entrada de mapa de bits, y una de las características de los efectos de Direct2D es que puede encadenar juntos para aplicar una serie de efectos a un mapa de bits.
Si estás interesado en escribir tus propios efectos personalizados, existe una solución Windows 8.1 Visual Studio invaluable llamada Direct2D personalizados imagen efectos muestra que incluye tres proyectos independientes para demostrar los tres tipos de sombreadores disponibles para los efectos del Direct2D. Los tres programas requieren de mapas de bits como entrada.
Por lo tanto, podría ser perdonado por suponiendo que Direct2D efectos siempre realizan operaciones en la entrada de mapa de bits. Pero esto no es así. Los ThreeTriangles programa que creó la imagen en figura 3 no requieren el aporte de mapa de bits.
También sería perdonado de asumir Direct2D efectos implican a un tipo de sombreado. Ciertamente, los efectos incorporados parecen implicar un sombreador de vértices o un sombreador de píxeles, pero no ambos. Sin embargo, es diferente a este respecto, así como el programa ThreeTriangles: Define un efecto personalizado que utiliza un sombreador de vértices y un sombreador de píxeles.
Registrarse, crear, dibujar
Debido a efectos de Direct2D están diseñados para ser registrados y creado a partir de un identificador de clase, un efecto personalizado necesario ofrecer esa misma habilidad. El efecto personalizado en el programa ThreeTriangles es una clase denominada SimpleTriangleEffect, que define un método estático para el registro de la clase. Este método es llamado por el constructor de la clase ThreeTrianglesRenderer, pero el efecto podría ser registrado en cualquier parte en el programa:
SimpleTriangleEffect::RegisterEffectAsync(d2dFactory)
Este método de registro es asincrónica porque necesita cargar en los archivos compilados de sombreado, y el único método previsto para ello en la clase de DirectXHelper es ReadDataAsync.
Al igual que cuando usando un efecto incorporado, la clase ThreeTrianglesRenderer declara un objeto ID2D1Effect como un campo privado en su archivo de encabezado:
Microsoft::WRL::ComPtr<ID2D1Effect> m_simpleTriangleEffect;
El método CreateDeviceDependentResources crea el efecto personalizado del mismo modo que un efecto incorporado:
d2dContext->CreateEffect(
CLSID_SimpleTriangleEffect, &m_simpleTriangleEffect)
El registro anterior del efecto personalizado había asociado ese ID de clase con el efecto.
El SimpleTriangleEffect no tiene ninguna entrada.(Que es parte de lo que la hace "simple"). El efecto se representa como un efecto incorporado:
d2dContext->DrawImage(m_simpleTriangleEffect.Get());
Tal vez parte de la complejidad dentro de la misma clase de efecto sugiere el simple uso de este efecto personalizado. Un efecto personalizado como SimpleTriangleEffect debe implementar la interfaz (aplicación de efecto) de ID2D1EffectImpl. Un efecto puede consistir en varios pases, que se llaman las transformaciones, y cada uno es representado generalmente por una implementación de ID2D1DrawTransform. Si se utiliza una sola clase para ambas interfaces — que es el caso de SimpleTriangleEffect — entonces necesita implementar IUnknown (tres métodos), ID2D1EffectImpl (tres métodos), ID2D1TransformNode (un método), ID2D1Transform (tres métodos) y ID2D1DrawTransform (1 método).
Es una cantidad considerable de gastos, además de un XML que identifica el efecto y su autor cuando el efecto es el primero registrado. Afortunadamente, para los efectos simples — y ésta ciertamente califica — muchos de los métodos de efecto pueden tener implementaciones bastante fáciles. Los trabajos más importantes de la clase de efecto implican carga y registrar el código compilado de sombreado (y asociar a los shaders con GUID para futuras referencias), y definir un búfer de vértice, que debe también estar asociado con un GUID.
De búfer de vértice.
Un búfer de vértice es una colección de vértices montado para su procesamiento. Cada vértice siempre incluye un punto de coordenadas 2D o 3D, pero generalmente otros artículos también. Los datos asociados a cada vértice y cómo está organizado se llama la "disposición" del búfer de vértice y, en general, el programa de ThreeTriangles define tres diferentes — pero equivalente — los tipos de datos para describir la disposición de vértice.
La primera representación de estos datos de vértice se muestra en la figura 4. Esta es una estructura simple llamada vértice que incluye un coordi 3Dpunto de nate y de color RGB. Una matriz de estas estructuras define los tres triángulos mostrados por el programa. (Esta matriz está codificado en el método Initialize requiere de la Simpleclase TriangleEffect; en un programa real la clase efecto permitiría una serie de vértices para introducirse al efecto.)
Definición de vértice Figura 4 en SimpleTriangleEffect
// Define Vertex for simple initialization
struct Vertex
{
float x;
float y;
float z;
float r;
float g;
float b;
};
// Each triangle has three points and three colors
static Vertex vertices [] =
{
// Triangle 1
{ 0, -1000, 0.0f, 1, 0, 0 },
{ 985, -174, 0.5f, 0, 1, 0 },
{ 342, 940, 1.0f, 0, 0, 1 },
// Triangle 2
{ 866, 500, 0.0f, 1, 0, 0 },
{ -342, 940, 0.5f, 0, 1, 0 },
{ -985, -174, 1.0f, 0, 0, 1 },
// Triangle 3
{ -866, 500, 0.0f, 1, 0, 0 },
{ -643, -766, 0.5f, 0, 1, 0 },
{ 643, -766, 1.0f, 0, 0, 1 }
};
// Define layout for the effect
static const D2D1_INPUT_ELEMENT_DESC vertexLayout [] =
{
{ "MESH_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12 },
};
X e y valores se basan en senos y cosenos de ángulos en incrementos de 40 grados, con un radio de 1.000. Sin embargo, observe que las coordenadas z están entre 0 y 1, listo para que los vértices rojos tienen un valor de z de 1, los vértices verdes son 0.5 y los vértices azules son 0. Más sobre este tema un poco más adelante.
Siguiendo esa matriz es otra matriz pequeña, pero éste define la información de vértice de una manera más formal necesaria para crear y registrar el búfer de vértice.
El vertex shader y el búfer de vértice se hace referencia en el método SetDrawInfo de SimpleTriangleEffect. Cada vez que se procesa el efecto, estos nueve vértices se pasan el sombreador de vértices.
... A Vertex Shader...
Figura 5 muestra el sombreador de vértices de la SimpleTriangleEffect. Se compone de tres estructuras y una función llamada principal. La función principal se llama para cada vértice en el búfer de vértice; en este caso, es solamente nueve vértices, pero a menudo hay muchos más.
Figura 5 el archivo SimpleTriangleEffectVertexShader.hlsl
// Per-vertex data input to the vertex shader
struct VertexShaderInput
{
float3 position : MESH_POSITION;
float3 color : COLOR0;
};
// Per-vertex data output from the vertex shader
struct VertexShaderOutput
{
float4 clipSpaceOutput : SV_POSITION;
float4 sceneSpaceOutput : SCENE_POSITION;
float3 color : COLOR0;
};
// Information provided for Direct2D vertex shaders
cbuffer ClipSpaceTransforms : register(b0)
{
float2x1 sceneToOutputX;
float2x1 sceneToOutputY;
}
// Called for each vertex
VertexShaderOutput main(VertexShaderInput input)
{
// Output structure
VertexShaderOutput output;
// Append a 'w' value of 1 to the 3D input position
output.sceneSpaceOutput = float4(input.position.xyz, 1);
// Standard calculations
output.clipSpaceOutput.x =
output.sceneSpaceOutput.x * sceneToOutputX[0] +
output.sceneSpaceOutput.w * sceneToOutputX[1];
output.clipSpaceOutput.y =
output.sceneSpaceOutput.y * sceneToOutputY[0] +
output.sceneSpaceOutput.w * sceneToOutputY[1];
output.clipSpaceOutput.z = output.sceneSpaceOutput.z;
output.clipSpaceOutput.w = output.sceneSpaceOutput.w;
// Transfer the color
output.color = input.color;
return output;
}
Cada una de las tres estructuras contiene campos identificados con un tipo de datos HLSL, un nombre de usuario y semántica en mayúsculas que identifican el papel del campo particular.
La estructura llamada VertexShaderInput es la entrada principal, y es el mismo que el diseño del búfer de vértice que acabas de ver, pero con tipos de datos HLSL para la posición 3D y el color RGB.
La estructura llamada VertexShaderOutput define la salida principal. Los dos primeros campos son necesarios para los efectos de Direct2D. (Un tercio requerido campo estaría presente si el efecto implicó una entrada de mapa de bits). El campo he llamado sceneSpaceOutput se basa en la coordenada de entrada. Algunos efectos del cambio que coordinan; Este efecto no lo hace y simplemente convierte las coordenadas entrada 3D en unas coordenadas homogéneas 4D con un valor de 1 w:
output.sceneSpaceOutput = float4(input.position.xyz, 1);
La salida de sombreado de vértices también incluye un campo no requeridos llamado color, que se encuentra simplemente del color del entrada:
output.color = input.color;
El campo de salida requerida he llamado clipSpaceOutput describe cada coordenada del vértice en términos de coordenadas normalizadas se utilizan en 3D. Estas coordenadas son las mismas como coordenadas generadas a partir de las transformaciones de la proyección de cámara que describí en entrega del mes pasado de esta columna. En estas coordenadas clipSpaceOutput, x los valores fluctúan entre – 1 a la izquierda de la pantalla a 1 a la derecha; y rango de los valores de – 1 en la parte inferior a 1 en la parte superior; y z los valores van de 0 a coordenadas más cercanas al espectador a 1 para las coordenadas lejos. Como lo indica el nombre del campo, estas coordenadas normalizadas se utilizan para cortar la escena 3D a la pantalla.
Para ayudarle a calcular las coordenadas clipSpaceOutput, una tercera estructura se proporciona automáticamente por ti que he llamado ClipSpaceTransforms. Estos son cuatro números basados en el ancho de pixel y alto de la pantalla, y que estén en vigor cuando DrawImage representa el efecto transforma cualquier contexto de dispositivo.
Sin embargo, las transformaciones proporcionadas son sólo para x y y coordina y eso es por qué definí coordenadas z en el búfer de vértice original tener valores entre 0 y 1. Otro enfoque es utilizar un cámara real proyección transformar en el sombreador de vértices (como voy a demostrar en una futura columna).
Estos valores de z se utilizan también automáticamente en un búfer de profundidad así píxeles con coordenadas z inferiores oscurecen los píxeles con valores más altos de z. Pero esto sólo ocurre si el método SetDrawInfo en la clase de efecto llama SetVertexProcessing con la bandera de D2D1_VERTEX_OPTIONS_USE_DEPTH_BUFFER. (Esto pasa también resultar en errores de COM que aparecen en la ventana de salida de Visual Studio mientras se está ejecutando el programa, pero eso también sucede con el código de Microsoft muestra Direct2D efecto).
… A Pixel Shader
Cada vez que se procesa el efecto (y en el caso general, que está en la velocidad de fotogramas de la pantalla de video), el sombreador de vértices se llama para cada vértice en el búfer de vértice, en este caso nueve veces.
La salida del sombreador de vértices tiene el mismo formato que la entrada en el sombreador de píxeles. Como se puede ver en el sombreador de píxeles en figura 6, la estructura PixelShaderInput es igual a la estructura VertexShaderOutput en el sombreador de vértices.
Figura 6 el archivo SimpleTriangleEffectPixelShader.hlsl
// Per-pixel data input to the pixel shader
struct PixelShaderInput
{
float4 clipSpaceOutput : SV_POSITION;
float4 sceneSpaceOutput : SCENE_POSITION;
float3 color : COLOR0;
};
// Called for each pixel
float4 main(PixelShaderInput input) : SV_TARGET
{
// Simply return color with opacity of 1
return float4(input.color, 1);
}
Sin embargo, se llama el sombreador de píxeles para cada píxel en los triángulos, y todos los campos de la estructura han sido interpolados sobre la superficie de ese triángulo. La función principal en el sombreador de píxeles debe devolver un color de cuatro componentes que incluye opacidad, así que simplemente se modifica el color RGB interpolado añadiendo un campo de opacidad. Ese color es salida de la pantalla.
Aquí está una variante interesante para el sombreador de píxeles: Las coordenadas z de la gama de campo sceneSpaceOutput de 0 a 1, así que es posible visualizar la profundidad de cada triángulo utilizando esa coordenada para construir un tono gris y volver a partir del método main:
float z = input.sceneSpaceOutput.z;
return float4(z, z, z, 1);
¿Mejoras?
El SimpleTriangleEffect algunos atajos. Debe hacerse más versátil mediante la inclusión de un método para establecer la entrada de vértice. Algunas otras características no estaría mal tampoco: El vertex shader es un gran lugar para llevar a cabo transformaciones de matriz — tales como rotación o cámara transforma — debido a las multiplicaciones de la matriz son ejecutadas en la GPU.
Algunos programadores son capaces de resistir la tentación de aplicar mejoras de código, particularmente los que convierten una imagen estática en una animación.
Charles Petzold es desde hace mucho tiempo colaborador de MSDN Magazine y el autor de "Programación Windows, 6ª edición" (Microsoft Press, 2013), un libro acerca de cómo escribir aplicaciones para Windows 8. Su sitio Web es charlespetzold.com.
Gracias al siguiente experto técnico de Microsoft por revisar este artículo: Doug Erickson
Doug Erickson es un escritor de programación de plomo para el equipo de documentación de desarrollador de Microsoft OSG. Cuando no escribir y desarrollar código gráficos DirectX y contenido, está leyendo artículos como Charles', porque así es como le gusta pasar su tiempo libre. Bueno, eso y montando motocicletas.