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

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

Следующий шаг – получение указателя на откомпилированный код шейдера. Реализуется этот шаг вызовом метода CreatePixelShader() интерфейса IDirect3DDevice9.

C++
LPDIRECT3DPIXELSHADER9 PixelShader = NULL;

device->CreatePixelShader( (DWORD*)Code->GetBufferPointer(),
                           &PixelShader );
Pascal
var
  PixelShader: IDirect3DPixelShader9;
...
device.CreatePixelShader(Code.GetBufferPointer, PixelShader);

Следующий шаг заключается в установке пиксельного шейдера в функции рендеринга. Осуществляется это путем вызова метода SetPixelShader() интерфейса IDirect3DDevice9, где в качестве параметра передается указатель на пиксельный шейдер.

C++ device->SetPixelShader( PixelShader );
Pascal device.SetPixelShader(PixelShader);

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

struct PS_INPUT
{
  float4 color: COLOR;
};

struct PS_OUTPUT
{
  float4 color : COLOR;
};

PS_OUTPUT main (PS_INPUT input)
{
  PS_OUTPUT output;
  output.color = input.color;
  return output;
};

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

"проталкивание" пикселя output.color = input.color;

инвертирование цветов output.color = 1-input.color;

увеличение яркости output.color = 2*input.color;

уменьшение яркости output.color = 0.5*input.color;

блокирование цветового канала output.color = input.color;

output.color.r = 0;


сложная обработка output.color.r=0.5*input.color.r;

output.color.g=2.0*input.color.g;

output.color.b=input.color.b*input.color.b;


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

sampler tex0;

struct PS_INPUT
{
  float2 base : TEXCOORD0;
};

struct PS_OUTPUT
{
  float4 diffuse : COLOR0;
};

PS_OUTPUT Main (PS_INPUT input)
{
  PS_OUTPUT output;
  output.diffuse = tex2D(tex0, input.base);
  return output;
};

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

struct PS_INPUT
{
  float2 uv0 : TEXCOORD0;
  float2 uv1 : TEXCOORD1;
  float4 color : COLOR0;
};

Пусть у нас вершина описана через положение на плоскости (преобразованная вершина), цвет и две текстурные координаты:

C++
struct MYVERTEX
{
    FLOAT x, y, z, rhw;
    DWORD color;
    FLOAT u1, v1;
    FLOAT u2, v2;
}
#define MY_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX2);
Pascal
type
    MyVertex = packed record
        x, y, z, rhw: Single;
        color: DWORD;
        u1,v1: Single;   
        u2,v2: Single;   
    end;
const
      MY_FVF = D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_TEX2;

Рассмотрим пример мультитекстурирования на примере следующих исходных данных. Две заданные текстуры и способ закраски примитива (квадрата) показаны ниже.




Текстура1 Текстура2 Закраска квадрата

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

sampler tex0;
sampler tex1;

struct PS_INPUT
{
  float2 uv0 : TEXCOORD0;
  float2 uv1 : TEXCOORD1;
  float4 color: COLOR0;
};

struct PS_OUTPUT
{
  float4 diffuse : COLOR0;
};

PS_OUTPUT Main (PS_INPUT input)
{
  PS_OUTPUT output;
  float4 texel0 = tex2D(tex0, input.uv0);
  float4 texel1 = tex2D(tex1, input.uv1);
  output.diffuse = ...;
  return output;
};

Некоторые способы взаимодействия двух этих поверхностей (текстуры и цветного квадрата) представлены в таблице.

output.diffuse = texel0*texel1;

output.diffuse = texel0*texel1+input.color;

output.diffuse = texel0+texel1*input.color;

output.diffuse = texel0*texel1*input.color;