Глава 36. Шейдеры GLSL. Использование Без компонентов. Самый примитивный шейдер 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Глава 36. Шейдеры GLSL. Использование Без компонентов. Самый примитивный шейдер



 

Все шейдеры в этой главе соответствуют стандартам GLSL 1.00 — 1.20. Это важно.

 

Сейчас мы сделаем, пожалуй, самый примитивный шейдер — покрасим сферу в красный цвет. Итак, приступим. Поместите на форму GLScene1 (инспектор объектов сцены), GLSceneViewer. В инспекторе объектов создайте камеру, ее свойству Position.Z присвойте 2; свойству GLSceneViewer. Camera присвойте GLCamera. Также в инспекторе объектов создайте GLDirectOpenGL. Последний нужен, поскольку мы будем использовать прямой доступ к OpenGL.

Подключите модуль GLContext, иначе компилятор не будет знать о TGLProgramHandle. Так как мы будем тестировать шейдер на сфере, подключите модуль GLObjects. Также нужно подключить OpenGLTokens. Теперь объявите в программе две константы для хранения вершинного (_vp) и фрагментного (_fp) шейдера. Сейчас оба текста мы поместим в код программы для облегчения редактирования. Но учтите, часто их помещают в виде отдельных файлов.

const _vp =

‘void main(void)’+

‘{‘+

‘gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;’+

‘}’;

Тут требуется пояснение касательно кода шейдеров вообще. В коде шейдера обязательно должна быть функция main(). Это касается всех типов шейдеров. Так же пользователь может объявлять свои функции. Слово void, стоящее в скобках, означает, что эта функция ничего не принимает, перед именем функции — ничего не возвращает (т.е. процедура).

float MyMain(float value)

{

return clamp(value, 0.0, 1.0);

};

Возвращаемся к нашему простому шейдеру. Прежде, чем мы сможем работать с вершинами, нам необходимо их трансформировать с учётом глобальной (мировой) матрицы. Это и выполняется командой gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;. По сути, эта команда должна присутствовать во всех вершинных шейдерах, хотя вместо неё иногда можно увидеть и устаревшую запись gl_Position = ftransform(). Ей пользоваться мы не рекомендуем, а при использовании в шейдерах с версией выше 1.20 вообще можно получить ошибку от драйвера.

const _fp =

‘void main(void)’+

‘{‘+

‘gl_FragColor = vec4(1.0, 0.0, 0.1, 1.0);’+

‘}’;

vec4 — это вектор, состоящий из 4-х компонентов — x,y,z,w. Есть еще vec3 и vec2.

Ещё всегда помните, что GLSL различает маленькие и большие буквы.

 

Иногда программисты задают версию шейдерной программы через зарезервированное в GLSL слово; вот как мы укажем, что будем использовать версию 1.10:

‘#version 110 ‘+#10#13+

‘void main(void){‘+

Обритите внимание, что в шейдерной программе версия указывается без точки. Разные версии имеют некоторые отличия и ограничения по железу. Но об этом вы прочитаете много позже.

Команда GL_FragColor задаёт цвет пикселя в цветовом буфере (единственном). Возможно, при знакомстве с сложными шейдерами вы не увидите это слово, но встретите вместо него GL_FragData[]. В таком случае это будет называться Multiple Render Targets. Нельзя одновременная работать с gl_FragColor и glFragData.

Подробнее о MRT можете потом почитать здесь: steps3d.narod.ru/tutorials/mrt-tutorial.html.

Возвращаемся в Delphi. Объявите глобальную переменную GLSLHandle типа TGLProgramHandle. Она нужна для управления шейдером. Также создадим экземпляр сферы: GLSphere: TGLSphere.

Создайте у формы событие OnCreate и запишите в нем

GLSphere:=TGLSphere.Create(nil);

Читатель меня не простит, если я не расскажу, почему в скобках написано nil, а не GLScene1.Objects и почему мы используем Create, а не CreateAsChild. Дело в том, что инспектор объектов сцены, в который мы наш объект заботливо не поместили, визуализирует свои объекты когда ему и только ему захочется; нам же нужно визуализировать сферу именно после команды UseProgramObject и именно перед EndUseProgramObject. Поэтому мы и не воспользовались инспектором, чтобы не рендить объект дважды.

Пришло время задействовать GLDirectOpenGL. Мы будем практиковать нашу очень интересную находку: будем иметь для этого компонента два варианта события OnRender. Первое событие будет называться GLDirectOpenGLInit; второе GLDirectOpenGLRender. Откройте инспеткор объектов сцены, выберите там GLDirectOpenGL; перейдите на вкладку Events; щёлкнув дважды в инспекторе объектов напротив текста OnRender вы создадите второе событие GLDirectOpenGLRender. Теперь создайте первое событие: перейдите в окно редактора кода и в месте объявления формы пропишите рядом с объявлением GLDirectOpenGLRender точно такую же по параметрам процедуру GLDirectOpenGLInit:

procedure GLDirectOpenGLRender(Sender: TObject;

var rci: TRenderContextInfo);

procedure GLDirectOpenGLInit(Sender: TObject;

var rci: TRenderContextInfo);

Теперь поместите в программу пустое «тело», как говорят, процедуры GLDirectOpenGLInit. В итоге у вас должно получиться:

 

procedure TForm1.GLDirectOpenGLInit(Sender: TObject;

var rci: TRenderContextInfo);

begin

 

end;

 

procedure TForm1.GLDirectOpenGLRender(Sender: TObject;

var rci: TRenderContextInfo);

begin

 

end;

В GLDirectOpenGLInit будем проводить всё, что касается инициализации шейдеров, а именно:

// Будем проверять поддержку необходимых расширений. Эти

// расширения есть даже на весьма старых компьютерах.

if not (GL.ARB_shader_objects and GL.ARB_vertex_program and

GL.ARB_vertex_shader and GL.ARB_fragment_shader) then

begin

ShowMessage(‘Ваша видеокарта не поддерживает GLSL шейдеры!’);

Halt;

end;

// Создаём и распределяем дескриптор.

GLSLHandle:=TGLProgramHandle.CreateAndAllocate;

// Загружаем вершинный и фрагментный шейдеры.

GLSLHandle.AddShader(TGLVertexShaderHandle, _vp);

GLSLHandle.AddShader(TGLFragmentShaderHandle, _fp);

// Если у драйвера возникли какие-либо нарекания,

// он оповестит о них пользователя.

if not GLSLHandle.LinkProgram then

raise Exception.Create(GLSLHandle.InfoLog);

if not GLSLHandle.ValidateProgram then

raise Exception.Create(GLSLHandle.InfoLog);

Gl.CheckError;

// Инициализация закончена, пришло время перейти

// к рендингу объекта с шейдером.

GLDirectOpenGL.OnRender:=GLDirectOpenGLRender;

// Без этой строчки рендинг объекта всё-таки наступит, но только если

// на форме есть каденсер и в нём записано GLSceneViewer.Invalidate,

// или если опустить форму за край экрана и вытащить обратно.

GLDirectOpenGL.BuildList(rci);

 

Теперь назначьте данное событие как текущее событие OnRender компонента GLDirectOpenGL, прописав в FormCreate:

GLDirectOpenGL.OnRender:=GLDirectOpenGLInit;

или сделав то же самое в design time.

Теперь зададим GLDirectOpenGLRender, где будет проходить применение шейдера к объекту — самая интересная часть:

procedure TForm1.GLDirectOpenGLRender(Sender: TObject;

var rci: TRenderContextInfo);

begin

with GLSLHandle do

begin

UseProgramObject;

MySphere.Render(rci);

EndUseProgramObject;

end;

end;

Описание вызываемых тут команд потом посмотрите внизу главы. Я лишь скажу, что если вам понадобится когда-нибудь потом использовать шейдер не только для MySphere, но и для каких-то других объектов, вызовите их метод Render рядом с таким же методом MySphere. Например:

with GLSLHandle do

begin

UseProgramObject;

MySphere.Render(rci);

MyCube.Render(rci);

MyFreeForm.Render(rci);

EndUseProgramObject;

end;

Построение примера закончено.

Ну, собственно и всё. Запускайте проект и смотрите на работу самого примитивного GLSL шейдера. Думаю, вас удивило то, что шар превратился в круг. Всё дело в том, что чтобы вернуть шару «шарообразный» вид, нужно учитывать освещение, что у нас не делается. Но в этой главе делать мы этого и не будем — этому посвящена другая глава. Небольшое замечание — вы можете иметь столько программ-шейдеров, прилинкованных и готовых к употреблению, сколько хотите, но не больше чем это позволяет GPU. Обычно это очень большое число.

Разберём использованные свойства класса TGLProgramHandle.

Свойство Описание
CreateAndAllocate Каждая программа хранится как дескриптор. А это свойство создаёт и распределяет дескриптор.
AddShader Добавление шейдера. Первый параметр — тип шейдера. Может быть: TGLVertexShaderHandle — вершинный шейдер; TGLFragmentShaderHandle — фрагментный шейдер; TGLGeometryShaderHandle — геометрический шейдер. Второй параметр — строка, содержащая текст шейдера.
LinkProgram Линкует шейдер. Перед вызовом этого оператора шейдер уже должен быть откомпилирован.
ValidateProgram Проверяет правильность программы шейдера.
InfoLog Хранит информацию о последней произведённой операции и по требованию выводит её. К сожалению, для сообщений InfoLog нет никакой спецификации, так что разные драйвера и видеокарты могут выдавать разный текст.
UseProgramObject Функция для загрузки и использования шейдера.
EndUseProgramObject Функция, обозначающая конец использования программы шейдера. Если вовремя не применить эту функцию, шейдер будет применяться ко всем объектам сцены.

 

Многие программисты после курса изучение шейдеров с лёгкостью могут использовать в своих проектах уже написанные кем-то другим шейдеры. Да, такие люди имеют о шейдерах представления, но они так их и не освоили. Они берут шейдерную программу и вставляют её методом «copy-past». Я очень надеюсь, что уважаемый читатель был внимателен к самому языку шейдеров, а не только к тому, как же этот шейдер вставить в программу. После прочтения ещё пары глав по GLSL его ждёт задание по написанию шейдера.

 

Готовый пример берите здесь: http://www.glscene.ru/download.php?view.545

 



Поделиться:


Последнее изменение этой страницы: 2016-04-19; просмотров: 481; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.14.73.229 (0.016 с.)