![]() |
|
![]() |
![]() |
Новости | Конференция | Чат | База данных | Творчество | Сообщество | О сайте | English |
![]() |
Помощь сайту |
![]() |
![]() |
![]() |
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Глава 8. Шейдеры Сейчас мы поговорим о шейдерах. С их помощью делают все современные спецэффекты. Но что же это? Когда вы рисуете что-либо, вы отправляете в видеокарту информацию о вершинах. Но контролировать дальнейший процесс вы не можете. Видеокарта сама высчитывает цвет каждого пикселя, положение вершин в экранном пространстве... Шейдеры представляют нам уникальную возможность добраться и туда. Шейдеры – микропрограммы (кстати, в OpenGL они называются именно программами) на ассемблероподобном языке видеокарты. Существуют два типа шейдеров: - Пиксельный (Фрагментный в OpenGL). - Вершинный. http://www.gamedev.ru/articles/read.shtml?id=20104&page=1 http://www.gamedev.ru/community/opengl/articles/vp Здесь очень много полезной информации, ознакомьтесь с ней, прежде чем читать дальше. Вершинный вызывается для каждой вершины. Ему на вход поступает координата вершины в мировой системе координат, текстурные координаты, нормали и еще много разных параметров (в том числе и любые другие, которые вы захотите, их можно передать с помощью специальной команды glProgramLocalParameter4fARB). Скорость выполнения вершинного шейдера на процессоре видеокарты (GPU) довольно высока, по крайней мере, превышает скорость CPU в разы. Но пиксельные шейдеры – вот настоящая скорость... Пиксельный вызывается для каждого (внимание!) пикселя. Скорость его настолько высока, что у меня, например, на GeForce 6600 пиксельный шейдер, на котором сделано гауссово размытие, замедляет счетчик FPS всего лишь на 20 кадров в секунду. Ни один CPU не сравнится по скорости с GPU. Пиксельному шейдеры поступают на вход измененные данные из вершинного шейдера и из вашей программы (с помощью той же glProgramLocalParameter4fARB), а на выход должен выйти лишь один параметр – цвет пикселя (в новых версиях шейдеров это не совсем так). Существует много версий шейдеров, но некоторые только для, например, GeForce, некоторые для Radeon`ов (на самом деле родные шейдеры у радеонов это кошмар). Но мы остановимся на универсальных – ARB шейдерах. Правда за универсальность придется платить – на, например, GeForce 2 нет пиксельных ARB шейдеров, зато есть родные и Register Combiners`ы (что-то вроде пиксельных шейдеров, были даже на Riva TNT). Поэтому желательно делать разные шейдеры, выполняющие одно и то же. По крайней мере, так делают во всех крупных проектах. Значит, для начала загрузите два расширения: - GL_ARB_fragment_program - GL_ARB_vertex_program Потом вам нужно загрузить шейдер из файла, делается так: function LoadASMShader(target: Cardinal; filename: String): Cardinal; var txt: TStringList; err: String; i: Integer; s: Cardinal; begin txt := TStringList.Create; txt.LoadFromFile(filename); glGenProgramsARB(1, @ s); glBindProgramARB(target, s); glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, Length(txt.Text), PChar(txt.Text)); txt.Free; err := PChar(glGetString(GL_PROGRAM_ERROR_STRING_ARB)); if err <> '' then begin glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, @ i); raise Exception.CreateFmt('Shader program «%s» contains errors:' + #10#10 + err + #10#10 + ' (pos %d)', [ExtractFileName(filename), i]); end; Result := s; end; Это не сильно оптимальная загрузка, т.к. используется TStringList. Функция возвращает Handle шейдера, который будет использован далее. Вот список основных команд для работы с шейдером: glGenProgramsARB(1, @ Handle); сгенерировать Handle glBindProgramARB(target, Handle); сделать шейдер текущим::/b:: где target или GL_VERTEX_PROGRAM_ARB, или GL_FRAGMENT_PROGRAM_ARB glDeleteProgramsARB(1,@ Handle); удалить Handle glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, Length(txt.Text), PChar(txt.Text)); загрузить шейдер из строки GL_PROGRAM_FORMAT_ASCII_ARB – формат текста где target или GL_VERTEX_PROGRAM_ARB, или GL_FRAGMENT_PROGRAM_ARB glEnable(target); нужно включить шейдеры где target или GL_VERTEX_PROGRAM_ARB, или GL_FRAGMENT_PROGRAM_ARB glProgramLocalParameter4fARB(target, n, p1,p2,p3,1); передать в шейдер что-то своё где target или GL_VERTEX_PROGRAM_ARB, или GL_FRAGMENT_PROGRAM_ARB n – порядковый номер. Будет использован уже в шейдере p1,p2,p3 – параметры, которые передаём. Механизм шейдеров похож на механизм текстур. включаем шейдеры делаем шейдер текущим рисуем выключаем шейдеры Теперь, как же выглядят эти шейдеры? Вот вершинный: !!ARBvp1.0 ATTRIB inPos = vertex.position; ATTRIB inTex = vertex.texcoord[0]; OUTPUT outPos = result.position; OUTPUT outTex = result.texcoord[0]; PARAM mvp[4] = { state.matrix.mvp }; DP4 outPos.x, mvp[0], vec; DP4 outPos.y, mvp[1], vec; DP4 outPos.z, mvp[2], vec; DP4 outPos.w, mvp[3], vec; MOV outTex, inTex; END Итак, приступим к разбору... !!ARBvp1.0 – заголовок шейдера, сообщающий нам об использовании вершинного (vp) ARB шейдера версии 1.0 ATTRIB inPos = vertex.position; Значит ATTRIB –это обозначение входных данных inPos – название, просто чтоб было проще vertex.position – это параметр вершини – его координаты в мировом пространстве. ATTRIB inTex = vertex.texcoord[0]; Тут то же, но только vertex.position – это параметр вершини – его текстурные координаты. OUTPUT outPos = result.position; OUTPUT outTex = result.texcoord[0]; OUTPUT – исходящие данные (в пиксельный шейдер) result.position – сюда пишется уже трансформированные координаты. result.texcoord[0] пишется уже трансформированные текстурные координаты. (да, трансформировать или нет, решать вам) PARAM mvp[4] = { state.matrix.mvp }; PARAM – параметр, который передается в шейдер из вашей программы или явно (с помощью glProgramLocalParameter4fARB), или вот как этот – неявно. state.matrix.mvp – это матрица mvp, т.е. матрица modelview projection, если на неё умножить входную вершину (в мировых координатах), то получим вершину в координатах экрана. DP4 outPos.x, mvp[0], vec; DP4 outPos.y, mvp[1], vec; DP4 outPos.z, mvp[2], vec; DP4 outPos.w, mvp[3], vec; Это, собственно, и есть умножение. Если не понятно, перечитайте статью, ссылка на которую есть выше. Для простоты понимания можно представить себе это так ::/b::outPos.x := DP4(mvp[0], vec); outPos.y := DP4(mvp[1], vec); outPos.z := DP4(mvp[2], vec); outPos.w := DP4(mvp[3], vec); Но к сожалению это не имеет смысла в синтаксисе шейдеров. ::b::MOV outTex, inTex; Это просто передает текстурные координаты далее в пиксельный шейдер, без изменения. Что-то вроде outTex := inTex; Да еще кое что об особенностях записи/чтения в переменные... ATTRIB – только читать из них OUTPUT – только писать в них PARAM – только читать TEMP – и читать и писать Вот пиксельный: !!ARBfp1.0 ATTRIB Tdecal = fragment.texcoord[0]; TEMP n; TEX n, Tdecal, texture[0], 2D; MOV result.color, n; END !!ARBfp1.0 – заголовок шейдера, сообщающий нам об использовании пиксельного (fp) ARB шейдера версии 1.0 ATTRIB Tdecal = fragment.texcoord[0]; Мы берем на вход текстурные координаты. Это переменная Tdecal TEMP N; Объявляем временную переменную N TEX N, Tdecal, texture[0], 2D; Записываем в неё цвет, взятый из 2D текстуры, которая находится в нулевом слоте текстур (glActiveTextureARB(GL_TEXTURE0ARB) задает нулевой слот; по умолчанию он включен) и по текстурным координатам Tdecal. Т.е. команда TEX производит считывание из текстуры. MOV result.color, N; Помещаем пиксель из текстуры в конечный цвет. Это довольно примитивные шейдеры, но они показывают основы работы. Кроме ARB шейдеров существуют и высокоуровневые шейдеры. Когда вышел cg – один из первых таких шейдеров он упростил жизнь многим игровым программистам (в том числе и решил проблемы с совместимостью). Но когда у DirectX вышел его высокоуровневый шейдер HLSL многие всерьёз задумались о судьбе OpenGL. Однако совет ARB выпустил GLSL – шейдерный язык, который уровнял возможности этих API (хотя я считаю, что GLSL обгоняет HLSL намного). В GLSL встроена информация об стейтах OpenGL, есть возможность делать циклы, условные переходы, структуры, функции, массивы и т.д. Но что самое интересное, что в зарезервированных словах, для будущих версий, есть, например, class (ООП на шейдерах??). Я не буду особо углубляться в GLSL. Но основу опишу. Расширения следующие: GL_ARB_shader_objects GL_ARB_shading_language_100 GL_ARB_vertex_shader GL_ARB_fragment_shader У GeForce это где-то с 6 серии (5 FX не особо дружат даже с ARB). Итак, у шейдеров GLSL тоже есть Handle. glUseProgramObjectARB(Handle); установить текущий glUseProgramObjectARB(0); перестать использовать шейдеры Чтоб передать свои параметры в шейдер нужно использовать Uniform Псевдокод: glUniform4fARB(shader.GetUniformByName('blur_offset'+ #0).Handl, 0, 0, 0, 1); Теперь посмотрим на шейдеры на GLSL. Вершинный: void main(void) { gl_Position = ftransform(); gl_TexCoord[0] = gl_MultiTexCoord0; } Пиксельный: uniform sampler2D image; void main(void) { // Sample the center pixel: vec4 color = texture2D(image, gl_TexCoord[0].xy); gl_FragColor = color; } Это тот же шейдер, что и в примере для ARB. Все предельно просто. ftransform-это специальная команда, говорящая что мы ничего не будем делать с вершинами. Теперь с полученными знаниями ваша задача внедрить шейдеры себе в движок.
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
![]() |
![]() |
![]() |
Дизайн Elite Games V5 beta.18 EGM Elite Games Manager v5.17 02.05.2010 |
![]() |