Опубликован: 08.07.2007 | Доступ: свободный | Студентов: 1431 / 183 | Оценка: 4.43 / 4.02 | Длительность: 13:47:00
Специальности: Программист
Лекция 8:

Использование шейдеров с помощью языка HLSL. Графический процессор

Ниже приведен пример компиляции вершинного шейдера, хранящегося в файле vertex.vsh.

C++
LPD3DXBUFFER Code = NULL;
LPD3DXBUFFER BufferErrors = NULL;
LPD3DXCONSTANTTABLE ConstantTable = NULL;
...
D3DXCompileShaderFromFile( "vertex.vsh", NULL, NULL, 
  "main", "vs_1_1", 0, &Code, &BufferErrors, 
    &ConstantTable );
Pascal
var
  Code: ID3DXBuffer;
  BufferErrors: ID3DXBuffer;
  ConstantTable: ID3DXConstantTable;
...
D3DXCompileShaderFromFile('vertex.vsh', nil, nil, 'main', 
  'vs_1_1', 0, @Code, @BufferErrors, 
    @ConstantTable);

Следующий шаг – получение указателя на откомпилированный код шейдера. Для этого используется метод CreateVertexShader() интерфейса IDirect3DDevice9. Метод имеет два параметра: указатель на буфер, в котором хранится скомпилированный код шейдера и переменная интерфейсного типа IDirect3DVertexShader9, в которую будет помещен результат вызова.

C++
LPD3DXBUFFER Code = NULL;
LPDIRECT3DVERTEXSHADER9 VertexShader = NULL;
...
device->CreateVertexShader( (DWORD*)Code->GetBufferPointer(),
                            &VertexShader );
Pascal
var 
  Code: ID3DXBuffer;
  VertexShader: IDirect3DVertexShader9;
...
device.CreateVertexShader(Code.GetBufferPointer, VertexShader);

И заключительный шаг – установка вершинного шейдера, реализуемая через вызов метода SetVertexShader() интерфейса IDirect3DDevice9. Как правило, данный метод вызывается в процедуре вывода сцены ( Render ).

C++
LPDIRECT3DVERTEXSHADER9 VertexShader = NULL;
...
device->SetVertexShader( VertexShader );
Pascal
var 
  VertexShader: IDirect3DVertexShader9;
...
device.SetVertexShader(VertexShader);

Теперь разберем, что из себя представляет шейдер на языке HLSL. Вершинный шейдер есть не что иное, как обычный текстовый файл, содержащий программный код. Этот программный код можно разбить на несколько секций:

  • Секция глобальных переменных и констант
  • Секция, описывающая входные данные вершины
  • Секция, описывающая выходные данные вершины
  • Главная процедура в шейдере (точка входа)

В секции глобальных переменных и констант описываются данные, которые не содержатся в вершинных атрибутах: матрицы преобразований, положения источников света и др. Ниже приведен пример описания глобальной матрицы и статичной переменной.

float4x4 WorldViewProj;
static float4 col = {1.0f, 1.0f, 0.0f, 1.0f};

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

struct VS_INPUT
{
	float4 position  : POSITION;
	float4 color0    : COLOR0;
};

Аналогично определяется выходная структура данных шейдера.

struct VS_OUTPUT
{
	float4 position : POSITION;
	float4 color0    : COLOR0;
};

Используемые здесь семантические конструкции ( POSITION и COLOR0 ) указывают на принадлежность того или иного атрибута вершины.

Так же как и в программах на языке C++, программа на языке HLSL должна иметь точку входа (главную процедуру). Здесь точка входа может быть описана следующим образом.

VS_OUTPUT main( VS_INPUT IN )
{
    VS_OUTPUT OUT;
    …
    return OUT;
}

Вообще говоря, использование входных и выходных структур не является обязательным в языке HLSL. Можно использовать привычный для любого программиста подход передачи параметров без структур.

float4 main(in float2 tex0 : TEXCOORD0,
            in float2 tex1 : TEXCOORD1) : COLOR
{
    return …;
}

Разберем теперь, как осуществляется преобразование вершины в вершинном шейдере. Как мы уже знаем, трансформация вершины осуществляется путем умножения вектор-строки, описывающей компоненты вершины, на матрицу преобразования. В языке HLSL данный шаг осуществляется с помощью функции mul.

OUT.position = mul( IN.position, WorldViewProj );

Таким образом, каждая вершина трехмерной модели подвергается обработке с помощью данного преобразования, а результат передается дальше по конвейеру. Ниже приведен пример полного текста кода шейдера.

float4x4 WorldViewProj;

struct VS_INPUT
{
	float4 position  : POSITION;
	float4 color0    : COLOR0;
;

struct VS_OUTPUT
{
	float4 position : POSITION;
	float4 color0    : COLOR0;
};

VS_OUTPUT main( VS_INPUT IN )
{
    VS_OUTPUT OUT;
    OUT.position = mul( IN.position, WorldViewProj );
    OUT.color0 = IN.color0;
    return OUT;
}