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)
Вот достаточно простой пример загрузки и компиляции щейдеров:
float4 pixel_shader(float4 Color: COLOR0) : COLOR
{
return Color;
}
Проект с исходным кодом (включая шейдеры) к статье: Скачать.
В данном случае шейдеры, фактически, выполняют самый минимум работы – просто передают исходные данные на отрисовку, даже никак их не обрабатывая. Однако, в следующих уроках (Урок Vertex Shaders, Урок Pixel Shaders) я покажу Вам, как использовать более сложные shaders и какие эффекты можно получить с их помощью.
Дополнительную информацию о шейдерах так же можно почерпнуть на следующих ресурсах:
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-буфера. Как и большинство, я обычно рендерю с:
для второго же шейдера будет необходимо использовать вместо D3DCMP_LESS либо D3DCMP_LESSEQUAL, либо D3DCMP_EQUAL, иначе второй шейдер ничего не нарисует (т.к. сработает отсечение по Z-буферу).
Огромное спасибо, ваш блог в закладках только вот мое мнение я думаю сейчас со статьями лучше делать упор на 10 или 11 директ икс, по 9 инфы на русском много, а вот по 10 и 11 нефига толком нет.
Уроки пока только по DirectX 9, т.к. он пока ещё достаточно распространён и на нём очень просто делать несложные игры, что я и планирую продемонстрировать в первых уроках.
Что же касается DirectX 10/11 – будут уроки и по DirectX 11 (10 не планируется), но немного позже, ближе к лету.
Можно. Но часто это не удобно. Например, в данный момент мы можем модифицировать функцию GetFileAsString что бы она могла читать “файлы” не только из файловой системы, но и из пак-файлов или по сети и при этом переделывать логику самой программы не придётся – код останется неизменным.
По идее, так лучше работать со всеми данными: конфигами, текстурами, моделями, шейдерами и т.д. Тогда простым дополнением/изменением одной единственной функции (или 2-3, которые, скажем, позволяют получать текстовые/бинарные данные) мы сможем менять логику поведения всей программы.
Я ещё вернусь к рассмотрению этого вопроса, когда руки дойдут до статей о пак-файлах.
Ребят прошу помощи. При компиляции пишет вот что:
Ошибка 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
не понимаю в чем проблема. Вроде все указано везде правильно.
А как работать с шейдерами с несколькими проходами, используя чистый LPDIRECT3DPIXELSHADER9? без d3dx.
Sergey, спасибо за вопрос! Это хороший повод сделать отдельную статью по теме mulipass shaders.
Пока же опишу вкратце. Вообще, многопроходные шейдеры, это “наворот” системы эффектов DirectX. Если же их не использовать, каждый проход шейдера будет выглядеть просто как отдельный шейдер и рендеринг геометрии с этим шейдером.
Т.е. вам надо будет загрузить два шейдера:
И рендерить геометрию (как и в случае ID3DXEffect) в два прохода. Код рендера станет вот таким:
При этом, естественно, будут действовать те же самые правила, что и при работе с пассами ID3DXEffect, т.е. вам надо не забыть активировать блендинг, либо альфа-тест, либо делать clip пикселей во втором шейдере – иначе результат работы второго шейдера полностью затрёт собой результат работы первого шейдера.
Кроме того, надо не забыть настроить (сразу после рендера первого шейдера и перед вторым шейдером) рендер-стейт для Z-буфера. Как и большинство, я обычно рендерю с:
для второго же шейдера будет необходимо использовать вместо D3DCMP_LESS либо D3DCMP_LESSEQUAL, либо D3DCMP_EQUAL, иначе второй шейдер ничего не нарисует (т.к. сработает отсечение по Z-буферу).
Огромное спасибо, ваш блог в закладках только вот мое мнение я думаю сейчас со статьями лучше делать упор на 10 или 11 директ икс, по 9 инфы на русском много, а вот по 10 и 11 нефига толком нет.
Sergey, не за что! Рад был помочь
Уроки пока только по DirectX 9, т.к. он пока ещё достаточно распространён и на нём очень просто делать несложные игры, что я и планирую продемонстрировать в первых уроках.
Что же касается DirectX 10/11 – будут уроки и по DirectX 11 (10 не планируется), но немного позже, ближе к лету.
Компилировать шейдер можно сразу из файла с использованием функции D3DXCompileShaderFromFile.
Можно. Но часто это не удобно. Например, в данный момент мы можем модифицировать функцию GetFileAsString что бы она могла читать “файлы” не только из файловой системы, но и из пак-файлов или по сети и при этом переделывать логику самой программы не придётся – код останется неизменным.
По идее, так лучше работать со всеми данными: конфигами, текстурами, моделями, шейдерами и т.д. Тогда простым дополнением/изменением одной единственной функции (или 2-3, которые, скажем, позволяют получать текстовые/бинарные данные) мы сможем менять логику поведения всей программы.
Я ещё вернусь к рассмотрению этого вопроса, когда руки дойдут до статей о пак-файлах.
У меня при запуске release.exe вылетает с ошибкой.
1>FXC : error X4541: vertex shader must minimally write all four components of POSITION
что же делать
Пока всё ок. Но чую скоро начну мучить вопросами…:)
Ребят прошу помощи. При компиляции пишет вот что:
Ошибка 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
не понимаю в чем проблема. Вроде все указано везде правильно.