Россия, г. Кострома |
Использование шейдеров с помощью языка HLSL. Графический процессор
Следующий шаг – получение указателя на откомпилированный код шейдера. Реализуется этот шаг вызовом метода CreatePixelShader() интерфейса IDirect3DDevice9.
C++ |
LPDIRECT3DPIXELSHADER9 PixelShader = NULL; device->CreatePixelShader( (DWORD*)Code->GetBufferPointer(), &PixelShader ); |
Pascal |
var PixelShader: IDirect3DPixelShader9; ... device.CreatePixelShader(Code.GetBufferPointer, PixelShader); |
Следующий шаг заключается в установке пиксельного шейдера в функции рендеринга. Осуществляется это путем вызова метода SetPixelShader() интерфейса IDirect3DDevice9, где в качестве параметра передается указатель на пиксельный шейдер.
Рассмотрим теперь что из себя представляет код пиксельного шейдера на языке 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; |
Рассмотрим пример мультитекстурирования на примере следующих исходных данных. Две заданные текстуры и способ закраски примитива (квадрата) показаны ниже.
Пример пиксельного шейдера, реализующего мультитекстурирование показан ниже.
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; };
Некоторые способы взаимодействия двух этих поверхностей (текстуры и цветного квадрата) представлены в таблице.