Начало работы с этапом stream-Output

В этом разделе описывается использование геометрического шейдера с этапом вывода потока.

Компиляция шейдера геометрии

Этот шейдер геометрии (GS) вычисляет нормальное лицо для каждого треугольника и выводит положение, обычные и текстурные данные координат.

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();
}

Учитывая этот код, учитывайте, что шейдер геометрии выглядит так же, как вершина или шейдер пикселей, но с следующими исключениями: тип, возвращаемый функцией, объявления входных параметров и встроенная функция.

Элемент Description
Тип возвращаемого значения функции
Возвращаемый тип функции делает одно, объявляет максимальное количество вершин, которые могут быть выходными шейдером. В этом случае
maxvertexcount(12)

определяет выходные данные максимум 12 вершин.

Объявления входных параметров

Эта функция принимает два входных параметра:

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

Первый параметр — это массив вершин (3 в данном случае), определенный структурой GSPS_INPUT (которая определяет данные по вершинам в качестве позиции, нормальной и координаты текстуры). Первый параметр также использует треугольник ключевое слово, что означает, что этап входного сборщика должен выводить данные в шейдер геометрии в качестве одного из типов примитивов треугольников (список треугольников или полоса треугольников).

Второй параметр — это поток треугольника, определенный типом TriangleStream<GSPS_INPUT>. Это означает, что параметр представляет собой массив треугольников, каждый из которых состоит из трех вершин (которые содержат данные из элементов GSPS_INPUT).

Используйте треугольники и треугольники ключевое слово для идентификации отдельных треугольников или потока треугольников в GS.

Встроенная функция

Строки кода в функции шейдера используют встроенные функции HLSL common-shader-core, за исключением последних двух строк, которые вызывают Append и RestartStrip. Эти функции доступны только для шейдера геометрии. Добавление сообщает геометрическому шейдеру, чтобы добавить выходные данные к текущей полосе; RestartStrip создает новую примитивную полосу. Новая полоса неявно создается в каждом вызове этапа GS.

Остальная часть шейдера выглядит очень похоже на вершину или шейдер пикселей. Шейдер геометрии использует структуру для объявления входных параметров и помечает элемент позиции с семантикой SV_POSITION, чтобы сообщить оборудованию, что это позиционные данные. Входная структура определяет другие два входных параметра как координаты текстуры (даже если одна из них будет содержать обычный аспект). Вы можете использовать собственную пользовательскую семантику для нормального лица, если вы предпочитаете.

Сконструировав шейдер геометрии, вызовите D3DCompile для компиляции, как показано в следующем примере кода.

DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob** ppShader;

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

Так же, как вершины и шейдеры пикселей, вам нужен флаг шейдера, чтобы сообщить компилятору, как скомпилирован шейдер (для отладки, оптимизировано для скорости и т. д.), функции точки входа и модели шейдера для проверки. В этом примере создается шейдер геометрии, созданный из файла Tutorial13.fx, с помощью функции GS. Шейдер компилируется для модели шейдера 4.0.

Создание объекта Geometry-Shader с потоком выходных данных

После того как вы знаете, что вы будете потоковыми данными из геометрии, и вы успешно скомпилировали шейдер, следующий шаг — вызвать id3D11Device::CreateGeometryShaderWithStreamOutput , чтобы создать объект шейдера геометрии.

Но сначала необходимо объявить входную подпись этапа вывода потока (SO). Эта подпись соответствует или проверяет выходные данные GS и входные данные SO во время создания объекта. Следующий код является примером объявления 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 );

Эта функция принимает несколько параметров, включая следующие:

  • Указатель на шейдер скомпилированной геометрии (или шейдер вершин, если шейдер геометрии не будет присутствовать, и данные будут передаваться непосредственно из шейдера вершин). Сведения о том, как получить этот указатель, см. в статье "Получение указателя на скомпилированный шейдер".
  • Указатель на массив объявлений, описывающих входные данные для этапа вывода потока. (См. раздел D3D11_SO_DECLARATION_ENTRY.) Вы можете предоставить до 64 объявлений, по одному для каждого типа элемента, который будет выводиться на этапе SO. Массив записей объявлений описывает макет данных независимо от того, должен ли быть привязан только один буфер или несколько буферов к выходным данным потока.
  • Количество элементов, записанных этапом SO.
  • Указатель на созданный объект шейдера геометрии (см. раздел ID3D11GeometryShader).

В этой ситуации шаг буфера равен NULL, индекс потока, отправляемый в растризатор, равен 0, а интерфейс компоновки классов — NULL.

Объявление выходных данных потока определяет способ записи данных в буферный ресурс. Вы можете добавить столько компонентов, сколько вы хотите, в объявление выходных данных. Используйте этап SO для записи в один буферный ресурс или многие ресурсы буфера. Для одного буфера этап SO может записывать множество различных элементов на вершину. Для нескольких буферов этап SO может записывать только один элемент данных на вершины в каждый буфер.

Чтобы использовать этап SO без использования геометрического шейдера, вызовите id3D11Device::CreateGeometryShaderWithStreamOutput и передайте указатель на шейдер вершин в параметр pShaderBytecode.

Установка целевых объектов вывода

Последний шаг — задать буферы этапов SO. Данные можно передавать в один или несколько буферов в памяти для последующего использования. В следующем коде показано, как создать один буфер, который можно использовать для данных вершин, а также для этапа SO для потоковой передачи данных.

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 );

Создайте буфер, вызвав ID3D11Device::CreateBuffer. В этом примере показано использование по умолчанию, обычное для буферного ресурса, который, как ожидается, будет обновляться довольно часто ЦП. Флаг привязки определяет этап конвейера, к которому может быть привязан ресурс. Любой ресурс, используемый этапом SO, также должен быть создан с помощью флага привязки D3D10_BIND_STREAM_OUTPUT.

После успешного создания буфера установите его на текущее устройство, вызвав ID3D11DeviceContext::SOSetTargets:

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

Этот вызов принимает количество буферов, указатель на буферы и массив смещения (по одному смещение в каждый из буферов, указывающих, где начать запись данных). Данные записываются в эти буферы выходных данных потоковой передачи при вызове функции рисования. Внутренняя переменная отслеживает положение, где начать запись данных в буферы потоковой передачи и выходных данных, и эти переменные будут продолжать увеличиваться до тех пор, пока SOSetTargets не будет вызван снова, а новое значение смещения будет указано.

Все данные, записанные в целевые буферы, будут иметь 32-разрядные значения.

Этап потоковых выходных данных