Share via


Introducción a la fase de salida de flujo

En esta sección se describe cómo usar un sombreador de geometría con la fase de salida de flujo.

Compilar un sombreador de geometría

Este sombreador de geometría (GS) calcula una cara normal para cada triángulo y genera la posición, los datos de coordenadas normales y de textura.

struct GSPS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : TEXCOORD0;
    float2 Tex : TEXCOORD1;
};

[maxvertexcount(12)]
void GS( triangle GSPS_INPUT input[3], inout TriangleStream<GSPS_INPUT> TriStream )
{
    GSPS_INPUT output;
    
    //
    // Calculate the face normal
    //
    float3 faceEdgeA = input[1].Pos - input[0].Pos;
    float3 faceEdgeB = input[2].Pos - input[0].Pos;
    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
    float3 ExplodeAmt = faceNormal*Explode;
    
    //
    // Calculate the face center
    //
    float3 centerPos = (input[0].Pos.xyz + input[1].Pos.xyz + input[2].Pos.xyz)/3.0;
    float2 centerTex = (input[0].Tex + input[1].Tex + input[2].Tex)/3.0;
    centerPos += faceNormal*Explode;
    
    //
    // Output the pyramid
    //
    for( int i=0; i<3; i++ )
    {
        output.Pos = input[i].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = input[i].Norm;
        output.Tex = input[i].Tex;
        TriStream.Append( output );
        
        int iNext = (i+1)%3;
        output.Pos = input[iNext].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = input[iNext].Norm;
        output.Tex = input[iNext].Tex;
        TriStream.Append( output );
        
        output.Pos = float4(centerPos,1) + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = faceNormal;
        output.Tex = centerTex;
        TriStream.Append( output );
        
        TriStream.RestartStrip();
    }
    
    for( int i=2; i>=0; i-- )
    {
        output.Pos = input[i].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = -input[i].Norm;
        output.Tex = input[i].Tex;
        TriStream.Append( output );
    }
    TriStream.RestartStrip();
}

Teniendo en cuenta ese código, tenga en cuenta que un sombreador de geometría es muy similar a un sombreador de vértices o píxeles, pero con las siguientes excepciones: el tipo devuelto por la función, las declaraciones de parámetros de entrada y la función intrínseca.

Elemento Descripción
Tipo de retorno de función
El tipo de valor devuelto de la función hace una cosa, declara el número máximo de vértices que el sombreador puede generar. En ese caso,
maxvertexcount(12)

define la salida para que sea un máximo de 12 vértices.

Declaraciones de parámetros de entrada

Esta función toma dos parámetros de entrada:

triangle GSPS_INPUT input[3] , inout TriangleStream<GSPS_INPUT> TriStream

El primer parámetro es una matriz de vértices (3 en este caso) definida por una estructura GSPS_INPUT (que define los datos por vértice como una posición, una normal y una coordenada de textura). El primer parámetro también usa la palabra clave triángulo, lo que significa que la etapa del ensamblador de entrada debe enviar datos al sombreador de geometría como uno de los tipos primitivos de triángulo (lista de triángulos o tira de triángulos).

El segundo parámetro es una secuencia de triángulos definida por el tipo TriangleStream<GSPS_INPUT>. Esto significa que el parámetro es una matriz de triángulos, cada uno de los cuales se compone de tres vértices (que contienen los datos de los miembros de GSPS_INPUT).

Use las palabras clave triangle y trianglestream para identificar triángulos individuales o una secuencia de triángulos en un GS.

Función intrínseca

Las líneas de código de la función de sombreador usan funciones intrínsecas de HLSL de núcleo común, excepto las dos últimas líneas, que llaman a Append y RestartStrip. Estas funciones solo están disponibles para un sombreador de geometría. Append informa al sombreador de geometría para anexar la salida a la franja actual; RestartStrip crea una nueva tira primitiva. Se crea implícitamente una nueva franja en cada invocación de la fase de GS.

El resto del sombreador tiene un aspecto muy similar a un sombreador de vértices o píxeles. El sombreador de geometría usa una estructura para declarar parámetros de entrada y marca el miembro de posición con la semántica de SV_POSITION para indicar al hardware que es datos posicionales. La estructura de entrada identifica los otros dos parámetros de entrada como coordenadas de textura (aunque una de ellas contendrá una cara normal). Puede usar su propia semántica personalizada para la cara normal si lo prefiere.

Después de diseñar el sombreador de geometría, llame a D3DCompile para compilar como se muestra en el ejemplo de código siguiente.

DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob** ppShader;

D3DCompile( pSrcData, sizeof( pSrcData ), 
  "Tutorial13.fx", NULL, NULL, "GS", "gs_4_0", 
  dwShaderFlags, 0, &ppShader, NULL );

Al igual que los sombreadores de vértices y píxeles, necesita un indicador de sombreado para indicarle al compilador cómo desea que se compile el sombreador (para depurar, optimizar la velocidad, etc.), la función de punto de entrada y el modelo de sombreado con el que validar. En este ejemplo se crea un sombreador de geometría creado a partir del archivo Tutorial13.fx mediante la función GS. El sombreador se compila para el modelo de sombreador 4.0.

Crear un objeto Geometry-Shader con salida de flujo

Una vez que sepa que va a transmitir los datos desde la geometría y ha compilado correctamente el sombreador, el siguiente paso es llamar a ID3D11Device::CreateGeometryShaderWithStreamOutput para crear el objeto sombreador de geometría.

Pero en primer lugar, debe declarar la firma de entrada de la fase de salida de flujo (SO). Esta firma coincide o valida las salidas de GS y las entradas SO en el momento de la creación del objeto. El código siguiente es un ejemplo de la declaración SO.

D3D11_SO_DECLARATION_ENTRY pDecl[] =
{
    // semantic name, semantic index, start component, component count, output slot
    { "SV_POSITION", 0, 0, 4, 0 },   // output all components of position
    { "TEXCOORD0", 0, 0, 3, 0 },     // output the first 3 of the normal
    { "TEXCOORD1", 0, 0, 2, 0 },     // output the first 2 texture coordinates
};

D3D11Device->CreateGeometryShaderWithStreamOut( pShaderBytecode, ShaderBytecodesize, pDecl, 
    sizeof(pDecl), NULL, 0, 0, NULL, &pStreamOutGS );

Esta función toma varios parámetros, entre los que se incluyen:

  • Puntero al sombreador de geometría compilado (o sombreador de vértices si no hay ningún sombreador de geometría presente y los datos se transmiten directamente desde el sombreador de vértices). Para obtener información sobre cómo obtener este puntero, consulte Obtener un puntero a un sombreador compilado.
  • Puntero a una matriz de declaraciones que describen los datos de entrada de la fase de salida del flujo. (Consulte D3D11_SO_DECLARATION_ENTRY). Puede proporcionar hasta 64 declaraciones, una para cada tipo diferente de elemento que se va a generar desde la fase SO. La matriz de entradas de declaración describe el diseño de datos, independientemente de si solo se va a enlazar un único búfer o varios búferes para la salida del flujo.
  • Número de elementos escritos por la fase SO.
  • Puntero al objeto de sombreador de geometría que se crea (consulte ID3D11GeometryShader).

En esta situación, el paso del búfer es NULL, el índice de la secuencia que se enviará al rasterizador es 0 y la interfaz de enlace de clases es NULL.

La declaración de salida de flujo define la forma en que los datos se escriben en un recurso de búfer. Puede agregar tantos componentes como desee a la declaración de salida. Use la fase SO para escribir en un único recurso de búfer o en muchos recursos de búfer. Para un único búfer, la fase SO puede escribir muchos elementos diferentes por vértice. Para varios búferes, la fase SO solo puede escribir un único elemento de datos por vértice en cada búfer.

Para usar la fase so sin usar un sombreador de geometría, llame a ID3D11Device::CreateGeometryShaderWithStreamOutput y pase un puntero a un sombreador de vértices al parámetro pShaderBytecode.

Establecer los destinos de salida

El último paso es establecer los búferes de fase so. Los datos se pueden transmitir a uno o varios búferes en memoria para usarlos más adelante. En el código siguiente se muestra cómo crear un único búfer que se puede usar para los datos de vértices, así como para la fase so en la que transmitir datos.

ID3D11Buffer *m_pBuffer;
int m_nBufferSize = 1000000;

D3D11_BUFFER_DESC bufferDesc =
{
    m_nBufferSize,
    D3D11_USAGE_DEFAULT,
    D3D11_BIND_STREAM_OUTPUT,
    0,
    0,
    0
};
D3D11Device->CreateBuffer( &bufferDesc, NULL, &m_pBuffer );

Cree un búfer llamando a ID3D11Device::CreateBuffer. En este ejemplo se muestra el uso predeterminado, que es típico de un recurso de búfer que se espera que la CPU actualice con bastante frecuencia. La marca de enlace identifica la fase de canalización a la que se puede enlazar el recurso. Cualquier recurso usado por la fase so también debe crearse con la marca de enlace D3D10_BIND_STREAM_OUTPUT.

Una vez creado correctamente el búfer, establézcalo en el dispositivo actual llamando a ID3D11DeviceContext::SOSetTargets:

UINT offset[1] = 0;
D3D11Device->SOSetTargets( 1, &m_pBuffer, offset );

Esta llamada toma el número de búferes, un puntero a los búferes y una matriz de desplazamientos (un desplazamiento en cada uno de los búferes que indica dónde empezar a escribir datos). Los datos se escribirán en estos búferes de salida de streaming cuando se llame a una función de dibujo. Una variable interna realiza un seguimiento de la posición de dónde empezar a escribir datos en los búferes de salida de streaming y que las variables seguirán incrementándose hasta que se vuelva a llamar a SOSetTargets y se especifique un nuevo valor de desplazamiento.

Todos los datos escritos en los búferes de destino serán valores de 32 bits.

Fase de salida de flujo