Уроки Коммент.

Создание игр » DirectX 11, DirectX 9, Featured » Shader (Шейдер)

Shader (Шейдер)

UDK shader Shader – это специальная программа для графического процессора (GPU, Graphical Processing Unit), управляющая поведением определённой (шейдерной) стадии графического конвейера видео-карты, занимающаяся обработкой входных данных и “отдающая” результат обработки этих данных. В данный момент наиболее часто используются вершинные и пиксельные шейдеры (vertex & pixel shaders), но на самом деле в Direct3D существует не два, а больше видов шейдеров, которые отличаются как назначением, так и способами их применения:

  • Вершинный шейдер (vertex shader) – шейдер, занимающиеся обработкой вершин модели
  • Пиксельный шейдер (pixel shader) – шейдер, выполняющийся для каждого выводимого на экран пиксела
  • Геометрический шейдер (geometry shader) (DirectX 10) – это шейдер, обрабатывающий геометрию. В отличие от вертексного шейдера, он обрабатывает не отдельные вершины, а наборы вершин, представляющих из себя геометрические примитивы (например, треугольники)
  • Hull-Shader – (DirectX 11) шейдер тесселяции
  • Domain-Shader – (DirectX 11) шейдер калькуляции внутри патча

Шейдеры для Direct3D пишутся на специальном языке шейдеров, который называется HLSL (High Level Shader Language) и имеет формат, весьма схожий с форматом языка C++, но со значительными упрощениями и меньшим функционалом.

Загрузка шейдера в Direct3D обычно происходит в два этапа:

  • Компиляция кода из исходника HLSL с помощью функции D3DXCompileShader
  • Создание самого шейдера, с помощью функций CreatePixelShader/CreateVertexShader (Direct3D 9)

Вот достаточно простой пример загрузки и компиляции щейдеров:

// это функция получения файла в виде строки
string GetFileAsString(const string& strFileName)
{
	string content;
	std::ifstream in(strFileName.c_str(), std::ios::binary);
	std::istreambuf_iterator<char> begin(in), end;
	while(begin != end)
		content += *begin++;
	return content;
}
 
// объявляем переменные
LPD3DXBUFFER pErrors = NULL;
LPD3DXBUFFER pShaderBuff = NULL;
LPD3DXCONSTANTTABLE pConstTableVS = NULL;
LPD3DXCONSTANTTABLE pConstTablePS = NULL;
LPDIRECT3DPIXELSHADER9 m_pPixelShader = NULL;
LPDIRECT3DVERTEXSHADER9 m_pVertexShader = NULL;
 
// вертексный шейдер
std::string srcVS = GetFileAsString("vs.hlsl");
D3DXCompileShader(srcVS.c_str(), srcVS.length(), NULL, NULL, "vertex_shader",
	"vs_2_0", D3DXSHADER_OPTIMIZATION_LEVEL3, &pShaderBuff, 
	&pErrors, &pConstTableVS);
m_pd3dDevice->CreateVertexShader(( DWORD* )pShaderBuff->GetBufferPointer(),
	&m_pVertexShader);
pShaderBuff->Release();
 
// пиксельный шейдер
std::string srcPS = GetFileAsString("ps.hlsl");
D3DXCompileShader(srcPS.c_str(), srcPS.length(), NULL, NULL, "pixel_shader",
	"ps_2_0", D3DXSHADER_OPTIMIZATION_LEVEL3, &pShaderBuff,
	&pErrors, &pConstTablePS);
m_pd3dDevice->CreatePixelShader(( DWORD* )pShaderBuff->GetBufferPointer(),
	&m_pPixelShader);
pShaderBuff->Release();

А использование шейдеровов ещё проще:

// устанавливаем шейдеры
m_pd3dDevice->SetPixelShader(m_pPixelShader); // pixel shader
m_pd3dDevice->SetVertexShader(m_pVertexShader); // vertex shader
// рисуем треугольник
m_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 1/* 1 треугольник */,
	v /* данные брать отсюда*/, sizeof(VertPosDiffuse)/*размер вертекса*/);

Код самих шейдеров. Vertex shader:

struct VS_OUTPUT
{
	float4 Pos  : POSITION;
	float4 Color: COLOR0;
};
 
void vertex_shader(float3 Pos  : POSITION, float4 Color: COLOR0, out VS_OUTPUT Out)
{
	Out.Pos = float4(Pos, 1);
	Out.Color = Color;
}

Pixel shader:

float4 pixel_shader(float4 Color: COLOR0) : COLOR                
{
	return Color;
}

Проект с исходным кодом (включая шейдеры) к статье: Скачать.

В данном случае шейдеры, фактически, выполняют самый минимум работы – просто передают исходные данные на отрисовку, даже никак их не обрабатывая. Однако, в следующих уроках (Урок Vertex Shaders, Урок Pixel Shaders) я покажу Вам, как использовать более сложные shaders и какие эффекты можно получить с их помощью.

Дополнительную информацию о шейдерах так же можно почерпнуть на следующих ресурсах:

Раздел: DirectX 11, DirectX 9, Featured · Теги: Direct3D, DirectX, Pixel shader, Shader, Vertex shader

10 комментариев на "Shader (Шейдер)"
  1. Sergey пишет:

    А как работать с шейдерами с несколькими проходами, используя чистый LPDIRECT3DPIXELSHADER9? без d3dx.

  2. Вячеслав пишет:

    Sergey, спасибо за вопрос! Это хороший повод сделать отдельную статью по теме mulipass shaders.

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

    Т.е. вам надо будет загрузить два шейдера:

    LPDIRECT3DPIXELSHADER9 m_pPixelShader1; // первый шейдер
    LPDIRECT3DPIXELSHADER9 m_pPixelShader2; // второй шейдер

    И рендерить геометрию (как и в случае ID3DXEffect) в два прохода. Код рендера станет вот таким:

    // устанавливаем шейдеры
    m_pd3dDevice->SetVertexShader(m_pVertexShader); // vertex shader
    m_pd3dDevice->SetPixelShader(m_pPixelShader1); // pixel shader 1
     
    m_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 1, v, 
    	sizeof(VertPosDiffuse)); // рендерим с первым шейдером
     
    m_pd3dDevice->SetPixelShader(m_pPixelShader1); // pixel shader 2
    m_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 1, v, 
    	sizeof(VertPosDiffuse)); // рендерим с вторым шейдером

    При этом, естественно, будут действовать те же самые правила, что и при работе с пассами ID3DXEffect, т.е. вам надо не забыть активировать блендинг, либо альфа-тест, либо делать clip пикселей во втором шейдере – иначе результат работы второго шейдера полностью затрёт собой результат работы первого шейдера.

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

    m_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);

    для второго же шейдера будет необходимо использовать вместо D3DCMP_LESS либо D3DCMP_LESSEQUAL, либо D3DCMP_EQUAL, иначе второй шейдер ничего не нарисует (т.к. сработает отсечение по Z-буферу).

  3. Sergey пишет:

    Огромное спасибо, ваш блог в закладках ;) только вот мое мнение я думаю сейчас со статьями лучше делать упор на 10 или 11 директ икс, по 9 инфы на русском много, а вот по 10 и 11 нефига толком нет.

    1. Вячеслав пишет:

      Sergey, не за что! Рад был помочь ;)

      Уроки пока только по DirectX 9, т.к. он пока ещё достаточно распространён и на нём очень просто делать несложные игры, что я и планирую продемонстрировать в первых уроках.

      Что же касается DirectX 10/11 – будут уроки и по DirectX 11 (10 не планируется), но немного позже, ближе к лету.

  4. PSM пишет:

    Компилировать шейдер можно сразу из файла с использованием функции D3DXCompileShaderFromFile.

  5. Вячеслав пишет:

    Можно. Но часто это не удобно. Например, в данный момент мы можем модифицировать функцию GetFileAsString что бы она могла читать “файлы” не только из файловой системы, но и из пак-файлов или по сети и при этом переделывать логику самой программы не придётся – код останется неизменным.

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

    Я ещё вернусь к рассмотрению этого вопроса, когда руки дойдут до статей о пак-файлах.

  6. Алексей пишет:

    У меня при запуске release.exe вылетает с ошибкой.

  7. Kiku пишет:

    1>FXC : error X4541: vertex shader must minimally write all four components of POSITION
    что же делать

  8. Yar пишет:

    Пока всё ок. Но чую скоро начну мучить вопросами…:)

  9. Александр пишет:

    Ребят прошу помощи. При компиляции пишет вот что:
    Ошибка 4 error LNK2019: unresolved external symbol _D3DXCompileShader@40 referenced in function “int __cdecl InitD3D(struct HWND__ *,int,int)” (?InitD3D@@YAHPAUHWND__@@HH@Z) axmd_engine.obj axmd_engine

    не понимаю в чем проблема. Вроде все указано везде правильно.

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>