HLSL (High Level Shader Language)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 02:47, 22 мая 2016.
HPGL
Парадигма Процедурный язык программирования
Спроектировано Microsoft
Первый   появившийся 2002
Печать дисциплины Статическая типизация
Расширение файла .hlsl
Портал: HPGL
Под влиянием
Си

HLSL (англ. High Level Shader Language) — язык высокого уровня для программирования шейдеров, является C-подобный

Основы вершинного шейдера

Во время работы программируемый вершинный шейдер заменяет обработку вершин, выполненную графическим конвейером Microsoft Direct3D. Во время работы вершинного шейдера, информация о состоянии относительно трансформации и освещения деятельности игнорируется функцией фиксированного конвейера. Когда вершинный шейдер отключен, функция фиксированной обработки снова начинает работу, применяются все текущие настройки.

Тесселяция элементарных процедур высшего порядка должна быть выполнена до того, как вершинный шейдер выполнит свои функции. Реализации, которые выполняют первоначальную (поверхностную) тесселяцию после обработки шейдеров должны выполнять это таким образом, чтобы это не очевидным для применения шейдерного кода. Как минимум, вершинный шейдер должен выпускать позиции вершины в однородном пространстве графического документа.

Основы пиксельного шейдера

Обработка пикселей представлена пиксельным шейдером на отдельных пикселях. Пиксельные шейдеры работают совместно с вершинными шейдерами; выход вершинных шейдеров предоставляет вход для пиксельного шейдера. Другие операции пикселей: fog blending, stencil operations, рендеринг начинают свою работу лишь после завершения работы шейдера.

Стадии текстуры и стадии семплера

Пиксельный шейдер полностью заменяет функциональность пикселей смешивания, определенный смешиванием нескольких текстур, включая операции ранее определенныe texture stage. Текстурная стадия и фильтрации – это операции, которые контролируются стандартом текстурной стадии для уменьшения, увеличение, MIP фильтрации, которые могут быть инициализированы в шейдеры. Приложение является бесплатным для изменения этих состояний, не требуя регенерации связанных (задействованных) в настоящее время шейдеров. Установка состояния может быть еще проще, если ваши шейдеры спроектированы в качестве эффекта.

Вход пиксельных шейдеров

Для пиксельных шейдеров версии ps_1_1 - ps_2_0 диффузные и зеркальные цвета насыщенные (находятся) в диапазоне от 0 до 1 перед использованием шейдеров. Значение цветового входа в пиксельный шейдер предполагаются в перспективе правильным, но это не гарантировано (для всех устройств). Цвета, выбранные из текстурных координат, повторяются в верной форме, и находятся в диапазоне от 0 до 1 в течение цикла.

Выход пиксельных шейдеров

Для пиксельных шейдеров версии ps_1_1 - ps_1_4, результатом, выпущенным пиксельным шейдером, является содержимое регистра r0. Как бы то ни было содержание, после завершения обработки шейдеров, оно отправляется на стадию fog и рендеринга. Пиксельных шейдеров версии ps_2_0 и выше выход цвета, выпускается с oC0 - oC4.

Входы шейдера и переменные шейдера

  • Объявление переменных шейдера (или шейдер-переменные)
  • Однородные входы шейдера
  • Изменение входа шейдера и семантика
  • Образцы и объекты структуры

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

Объявление простой переменной заключается в указании типа и имени переменной. Пример объявление вещественной переменной:

float fVar;

Инициализация переменной при объявлении:

float fVar = 3.1f;

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

int iVar[3];

так же они могут быть объявлены и инициализированы на одной стадии:

int iVar[3] = {1,2,3};

Пример, демонстрирующий особенности объявления переменных в языке HLSL:

float4 color;
uniform float4 position : POSITION; 
const float4 lightDirection = {0,0,1};

При объявление данных можно использовать любой допустимый тип включая:

  • Data Types (DirectX HLSL)
  • Vector Type (DirectX HLSL)
  • Matrix Type (DirectX HLSL)
  • Shader Type (DirectX HLSL)
  • Sampler Type (DirectX HLSL)
  • User-Defined Type (DirectX HLSL)

Шейдер может иметь переменные верхнего уровня, аргументы и функции.

Переменная верхнего уровня:

float globalShaderVariable;

Функция верхнего уровня:

void function(
in float4 position: POSITION0 // аргумент верхнего уровня
              )
{
  float localShaderVariable; // локальная переменная
  function2(...)
}

void function2()
{
  ...
}

Переменные верхнего уровня объявлены вне функций. Аргументы верхнего уровня – это параметры (указатели) на функции высшего уровня. Функция верхнего уровня – это любая функция, которая вызывается приложением (в отличие от функции, которая вызывается другой функцией).

Входные данные шейдеров

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

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

Постоянные переменные, используемые шейдером, связаны с применением через константную таблицу. Константная таблица – это название таблицы символов, которая определяет, какие постоянные переменные, используются шейдером, а какие вписываются в константные регистры. Постоянные параметры функции появляются в константной таблице, вначале добавляется знак доллара ($), в отличие от глобальных переменных. Знак доллара требуется для того чтобы избежать конфликта имён между входными локальными постоянными переменными и глобальными переменными с таким же именем.

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

Переменные входные данные шейдера и семантика

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

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

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

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

Входная семантика может быть назначена шейдером двумя методами:

  • Добавление двоеточия и семантического имени в объявлении параметра;
  • Определение входной структуры с входной семантикой, назначенной каждому компоненту структуры.

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

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

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

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

Семантика определяет откуда поступают данные. Семантика является дополнительным идентификатором, который определяет шейдерные входы и выходы.

Семантика подставляется в одном из трех мест:

  • После элемента структуры.
  • После аргумента в списке входных аргументов функции.
  • После входного списка аргументов функции.

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

vector vClr;

struct VS_INPUT
{
    float4 vPosition : POSITION;
    float3 vNormal : NORMAL;
    float4 vBlendWeights : BLENDWEIGHT;
};

struct VS_OUTPUT
{
    float4  vPosition : POSITION;
    float4  vDiffuse : COLOR;

};

float4x4 mWld1;
float4x4 mWld2;
float4x4 mWld3;
float4x4 mWld4;

float Len;
float4 vLight;

float4x4 mTot;

VS_OUTPUT VS_Skinning_Example(const VS_INPUT v, uniform float len=100)
{
    VS_OUTPUT out;

    // Skin position (to world space)
    float3 vPosition = 
        mul(v.vPosition, (float4x3) mWld1) * v.vBlendWeights.x +
        mul(v.vPosition, (float4x3) mWld2) * v.vBlendWeights.y +
        mul(v.vPosition, (float4x3) mWld3) * v.vBlendWeights.z +
        mul(v.vPosition, (float4x3) mWld4) * v.vBlendWeights.w;
    // Skin normal (to world space)
    float3 vNormal =
        mul(v.vNormal, (float3x3) mWld1) * v.vBlendWeights.x + 
        mul(v.vNormal, (float3x3) mWld2) * v.vBlendWeights.y + 
        mul(v.vNormal, (float3x3) mWld3) * v.vBlendWeights.z + 
        mul(v.vNormal, (float3x3) mWld4) * v.vBlendWeights.w;    // Output stuff
    out.vPosition    = mul(float4(vPosition + vNormal * Len, 1), mTot);
    out.vDiffuse  = dot(vLight,vNormal);

    return out;
}

Структура входа определяет данные из буфера вершин, которые будут обеспечивать вход шейдера. Этот шейдер отображает данные из положения, normal, и все элементы буфера вершин в регистры вершинных шейдеров. Тип входных данных не должен в точности совпадать с типом заданных вершинных данных. Если в точности они не совпадают, то данные вершин будут автоматически преобразованы в тип данных в HLSL, когда они записываются в регистры шейдеров. Например, если были определены данные типа UINT, то они будут преобразованы в float3 при чтении шейдеров.

Если данные в потоке вершин содержит меньше компонентов, чем данные шейдера соответствующего типа, отсутствующие компоненты будут инициализированы нулём «0» (за исключением w, который будет инициализирован к 1).

Структура вывода определяет выходные параметры положения и цвета вершинного шейдера. Эти результаты будут использованы в конвейерной обработке для растеризации (в примитивной обработке). Вывод отмечен положением данных, которые обозначают положение вершины в однородном пространстве. Как минимум, вершинные шейдеры должны генерировать данные позиции. Пространство положения экрана вычисляется после завершения вершинных шейдеров путем деления (х, Y, Z) координат на w. В экранном пространстве,-1 и 1 это минимальное и максимальное х и у значение границ области просмотра, в то время как z используется для тестирования Z-буфера.

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

Глобальные переменные присваиваются регистру компилятором автоматически. Глобальные переменные также называются постоянными параметрами, потому что содержимое переменной такое же, как и в пиксельном шейдере при его вызове. Регистры содержатся в постоянной таблице, которая может быть прочитана с помощью ID3DXConstantTable интерфейса.

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

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

Входные параметры также могут являться массивами. Семантика автоматически увеличивается компилятором для каждого элемента массива. Рассмотрим, например, следующее явное объявление:

struct VS_OUTPUT
{
    float4 Position   : POSITION;
    float3 Diffuse    : COLOR0;
    float3 Specular   : COLOR1;               
    float3 HalfVector : TEXCOORD3;
    float3 Fresnel    : TEXCOORD2;               
    float3 Reflection : TEXCOORD0;               
    float3 NoiseCoord : TEXCOORD1;               
};

float4 Sparkle(VS_OUTPUT In) : COLOR

Явное объявление, приведенное выше, эквивалентно следующему объявлению, которое будет обладать семантикой автоматически увеличенной компилятором:

float4 Sparkle(float4 Position : POSITION,
                 float3 Col[2] : COLOR0,
                 float3 Tex[4] : TEXCOORD0) : COLOR0
{
   // shader statements
   ...

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

struct PS_OUTPUT
{
    float4 Color[4] : COLOR0;
    float  Depth  : DEPTH;
};

PS_OUTPUT main(void)
{
    PS_OUTPUT out;

   // Shader statements
   ...

  // Write up to four pixel shader output colors
  out.Color[0] =  ...
  out.Color[1] =  ...
  out.Color[2] =  ...
  out.Color[3] =  ...

  // Write pixel depth 
  out.Depth =  ...

    return out;
}

Выходные цвета пиксельного шейдера должны быть типа float4. При написании нескольких цветов, все выходные цвета должны быть использованы непрерывно. Другими словами, COLOR1 не может быть выведен, пока color0 еще не отрисован. Выходная глубина пиксельного шейдера должна быть типа float.

Samplers и текстурные объекты

Sampler содержит состояние сэмплера. Структура сэмплера определяет текстуру, которая будет выбрана и контролирует фильтрацию, которая делается во время выборки.

Три вещи, которые необходимы, чтобы считать данные текстуры:

  • Текстура
  • Сэмплер
  • Инструкции выборки Сэмплеры можно инициализировать с текстурами и состояниями сэмплера, как показано здесь:
sampler s = sampler_state 
{ 
  texture = NULL; 
  mipfilter = LINEAR; 
};

Пример кода считывающего данные 2D текстуры:

texture tex0;
sampler2D s_2D;

float2 sample_2D(float2 tex : TEXCOORD0) : COLOR
{
  return tex2D(s_2D, tex);
}

Текстура объявлена переменной текстуры tex0.

В этом примере, сэмплерная переменная объявлена именем s_2D. Структура сэмплера содержится внутри фигурных скобок. Она включает в себя структуру, информация о которой будет считываться и, необязательно, состояние фильтра (т.е. параметры импорта текстуры, методы фильтрации и т.д.). Если структура сэмплера не определена, то по умолчанию применяется сэмплером с линейной фильтрацией и режим имопрта текстуры: для текстурных координат. Функция сэмплер принимает два вещественных компонента тестурных координат, и возвращает два компонента цвета. Они представляются типом float2 и возвращают данные в красных и зеленых компонентах.

Определение четырех типов сэмплеров и текстурных выборок осуществляется с помощью встроенных функций: tex1D(s, t) (DirectX HLSL),tex2D(s, t) (DirectX HLSL), tex3D(s, t) (DirectX HLSL), texCUBE(s, t) (DirectX HLSL). Пример 3D сэмплинга:

texture tex0;
sampler3D s_3D;

float3 sample_3D(float3 tex : TEXCOORD0) : COLOR
{
  return tex3D(s_3D, tex);
}

В данном прмере сэмплер использует настройки фильтров и режим адресов умолчанию.

Пример сэмплинга куба:

texture tex0;
samplerCUBE s_CUBE;

float3 sample_CUBE(float3 tex : TEXCOORD0) : COLOR
{
  return texCUBE(s_CUBE, tex);
}

И в заключении пример сэмплинга 1D:

texture tex0;
sampler1D s_1D;

float sample_1D(float tex : TEXCOORD0) : COLOR
{
  return tex1D(s_1D, tex);
}

В силу того, что среда выполнения не поддерживает 1D текстуры, компилятор будет использовать 2D текстуры, зная, что у-координата не имеет значения. Так tex1D (S, T) (DirectX HLSL) реализуется как 2D текстуры поиска, компилятор волен выбирать у-компонент в эффективной манере. В некоторых редких случаях, компилятор не может выбрать эффективный у-компонент, в этом случае он будет выдавать предупреждение.

texture tex0;
sampler s_1D_float;

float4 main(float texCoords : TEXCOORD) : COLOR
{
    return tex1D(s_1D_float, texCoords);
}

Этот пример является неэффективным, потому что компилятор должен двигаться входной координате в другой регистр (поскольку 1D поиск реализован как 2D поиск, и координаты текстуры объявляются как float1). Если код переписан с использованием входа float2 вместо float1, компилятор может использовать входные координаты текстуры, потому что известно, что инициализируется что-то.

texture tex0;
sampler s_1D_float2;

float4 main(float2 texCoords : TEXCOORD) : COLOR
{
    return tex1D(s_1D_float2, texCoords);
}

Все текстурные пробники могут быть дополнены "bias" или "Proj" (то есть, tex2Dbias (DirectX HLSL), texCUBEproj (DirectX HLSL)). С префиксом "Рго", координаты текстуры делится на w-компонента. С "bias", уровень mip сдвигается w-компонентом. Таким образом, все текстурные пробники с префиксом всегда требуют ввода float4. tex1D (S, T) (DirectX HLSL) и tex2D (S, T) (DirectX HLSL) игнорирует yz- и Z-компонентов соответственно.

Сэмплеры также могут быть использованы в массиве, хотя, в настоящее время никакой back end не поддерживает динамический доступ массива сэмплеров. Таким образом, справедливо следующее, потому что это может быть выполнено во время компиляции:

tex2D(s[0],tex)

Тем не менее, этот пример не валидный:

tex2D(s[a],tex)

Динамический доступ сэмплеров в первую очередь полезен для написания программ с циклами. Следующий код иллюстрирует сэмплер массив с доступом:

sampler sm[4];

float4 main(float4 tex[4] : TEXCOORD) : COLOR
{
    float4 retColor = 1;

    for(int i = 0; i < 4;i++)
    {
        retColor *= tex2D(sm[i],tex[i]);
    }

    return retColor;
}

Примечание: использование отладки выполнения Microsoft Direct3D может помочь вам найти несоответствия между количеством компонентов в текстуре и сэмплере.

Написание функций

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

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

В отличие от C функций, функции ввода HLSL использует семантику, чтобы связать аргументы функции (HLSL функции, которые называются внутренними, игнорируют/исключают семантику). Это облегчает связь данных буфера шейдеров, и связь выходных и выходных данных шейдера.

Функция содержит объявление и тело, объявление должна предшествовать телу.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{
    return mul(inPos, WorldViewProj );
};

Функция объявления включает все то, что стоит перед фигурными скобками:

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION

Функция объявления состоит из:

  • Возвращаемый тип
  • Имя функции
  • Список аргументов (не обязательно)
  • Выходные данные семантики (не обязательно)
  • Аннотацию (не обязательно)

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

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{
   ...
}

Возвращаемый тип может быть структурой, которая уже определена:

struct VS_OUTPUT
{
    float4  vPosition        : POSITION;
    float4  vDiffuse         : COLOR;
}; 

VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
{
   ...
}

Если функция не возвращает значение, void может быть использован в качестве типа возврата.

void VertexShader_Tutorial_1(float4 inPos : POSITION )
{
   ...
}
Тип возвращаемого значения всегда указывается в объявлении функции.
float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION

Список аргументов объявляет входные аргументы функции. Он также может объявить, что значения будут возвращены. Некоторые аргументы являются входными и выходными аргументами.

Пример из шейдера, который принимает четыре входных аргумента:

float4 Light(float3 LightDir : TEXCOORD1, 
             uniform float4 LightColor,  
             float2 texcrd : TEXCOORD0, 
             uniform sampler samp) : COLOR 
{
    float3 Normal = tex2D(samp,texcrd);

    return dot((Normal*2 - 1), LightDir)*LightColor;
}

Эта функция возвращает конечный цвет, что представляет собой смесь текстуры образца и светлого цвета. Функция принимает четыре входных аргумента. Два аргумента имеют семантику: LightDir имеет TEXCOORD1 семантику и texcrd имеет TEXCOORD0 семантику. Семантика означает, что данные для этих переменных нужно взять из буфера вершин. Даже если переменная LightDir имеет TEXCOORD1 семантической параметр, вероятно, не координату текстуры. TEXCOORDn семантический тип часто используется для снабжения семантикой типа, который не предопределен (отсутствует входная семантика вершинного шейдера ввода для направления света).

Другие два входных параметра LightColor и SAMP отмечены одним ключевым словом uniform. Эти единые константы, которые не будут изменяться между вызовами отрисовки. Значения этих параметров приходят из шейдеров глобальных переменных.

Аргументы не могут быть переданы по ссылке; однако, аргумент может быть как входным так и выходным параметром, если он объявлен с ключевым словом INOUT. Аргументы, передаваемые функции, отмеченные Inout ключевым словом, считаются копиями оригинала до возврата функции, и затем они копируются обратно.

Пример использования INOUT:

void Increment_ByVal(inout float A, inout float B) 
{ 
    A++; B++;
}

Эта функция увеличивает значения в А и B, и возвращает их.

Тело функции – это весь код после объявления функции.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{		
    return mul(inPos, WorldViewProj );
};

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

Тело данного шейдера делает две вещи: выполняет матрицу умножения и возвращает результат float4. Матрица многократно выполняется с помощью функции Mul (DirectX HLSL), которая выполняет умножение матрицы 4x4. mul (DirectX HLSL) называется библиотечной (внутренней) функцией, потому что она уже встроена в библиотеку HLSL.

Матрица умножения включает в себя входной вектор Pos и композитную матрицу WorldViewProj. В результате данные о местоположении преобразовываются в пространство экрана. Это минимальный процесс обработки вершинных шейдеров, который мы можем сделать. Если бы мы использовали фиксированную функцию конвейерной обработки вместо вершинного шейдера, данные вершин могут быть отмечены после выполнения этого преобразования.

Последнее оператор в теле функции является оператор возврата. Так же, как в C, этот оператор возвращает управление из функции в оператор, который вызвал функцию.

Возвращаемый тип функции может быть любой из простых типов данных, определенных в HLSL, в том числе bool, int, half и double. Возвращаемый тип может быть одними из сложных типов данных, таких как векторы и матрицы. Типы HLSL, которые относятся к объектам, не могут быть использованы в качестве возвращаемых типов. Например: PixelShader, vertexshader, текстура и сэмплер.

Пример функции, которая которая в качестве возвращаемого значения использует структуру.

float4x4 WorldViewProj : WORLDVIEWPROJ;

struct VS_OUTPUT
{
    float4 Pos  : POSITION;
};

VS_OUTPUT VS_HLL_Example(float4 inPos : POSITION )
{
    VS_OUTPUT Out;

    Out.Pos = mul(inPos,  WorldViewProj );

    return Out;
};

Возвратный тип float4 был заменен структурой VS_OUTPUT, которая в настоящее время содержит единственный член float4.

Оператор возврата означает конец функции. Это простейший оператор возврата. Он передаёт управление из функции в вызвавшую функцию программу. И не возвращает никакого значения.

void main()
{
    return ;
}

Оператор возврата может возвращать одно или несколько значений. Этот пример возвращает значение литерала:

float main( float input : COLOR0) : COLOR0
{	
    return 0;
}

Этот пример возвращает скалярный результат выражения:

return  light.enabled = true ;

Этот пример возвращает float4, созданный из переменной и литерала:

return  float4(color.rgb, 1) ;

Этот пример возвращает float4, что создается из возвращенного результата встроенной функции и нескольких буквальных значений:

float4 func(float2 a: POSITION): COLOR
{
    return float4(sin(length(a) * 100.0) * 0.5 + 0.5, sin(a.y * 50.0), 0, 1);
}

Этот пример возвращает структуру, которая состоит из одного или нескольких членов:

float4x4 WorldViewProj;

struct VS_OUTPUT
{
    float4 Pos  : POSITION;
};

VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
{
    VS_OUTPUT out;
    out.Pos = mul(inPos, WorldViewProj );
    return out;
};

Список литературы

  1. DirectX 10 – это просто
  2. Инструментальные средства программирования и отладки шейдеров в DirectX и OpenGL