Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник по GLScene.doc
Скачиваний:
255
Добавлен:
16.12.2018
Размер:
7.18 Mб
Скачать

Глава 38. Шейдеры glsl. Немного о спецификации. Типы данных и переменные

В GLSL доступны следующие простые типы данных:

  • float

  • bool

  • int

Эти типы данных - точно такие же, как в C.

 

Также доступны векторы для перечисленных выше типов данных с двумя, тремя или четырьмя компонентами.

Она обьявляются как:

  • vec{2,3,4} - вектор из 2/3/4 float

  • bvec{2,3,4} - вектор из 2/3/4 bool

  • ivec{2,3,4} - вектор из 2/3/4 int

 

 

Также доступны квадратные матрицы 2x2, 3x3 и 4x4, т.к. они часто используются в графике.

Соответствующие типы данных:

  • mat2

  • mat3

  • mat4

 

 

Также доступен ряд специальных типов данных для работы с текстурами. Они называются "samplers" и

нуждаются в доступе к значениям текстур, так же известным как "texels". Типы данных:

  • sampler1D - для 1D-текстур

  • sampler2D - для 2D-текстур

  • sampler3D - для 3D-текстур

  • samplerCube - для текстур кубических карт

  • sampler1DShadow - для карт теней

  • sampler2DShadow - для карт теней

 

 

 

Массивы в шейдерах обьявляются так же, как в C, однако они не могут быть инициализированы при обьявлении.

Доступ к элементам - такой же, как в C. 

Также в GLSL доступны структуры. Синтаксис такой же, как в C:

struct dirlight {

vec3 direction;

vec3 color;

};

Стандартные функции

Имя

Описание

Radiants(deg)

Перевод угла из градусной меры в радианы.

Degrees(rad)

Перевод угла из радиан в градусы.

sin(X)

Синус угла(задается в радианах).

cos(X)

Косинус угла(задается в радианах).

tan(X)

Тангенс угла(задается в радианах).

pow(X)

Вычисляет X в степени Y, результат не

определен для X<0 или X=0 и Y=<0 .

exp(X)

Вычисляет e в степени X.

exp2(X)

Вычисляет 2 в степени X.

log(X)

Вычисляет ln x результат не определен если

X=<0.

log2(X)

Вычисляет log2 x результат не определен если

X=<0.

sqrt(X)

Вычисляет квадратный корень из числа X,

результат не определен если X<0.

inversesqrt(X)

Вычисляет 1/sqrt(X), результат не определен

если X=<0.

abs(X)

Вычисляет модуль X

min(a,b)

Вычисляет минимум из двух величин

max(a,b)

Вычисляет максимум из двух величин

dot(a, b)

Возвращает скалярное произведение двух векторов a и b

Поддерживаемые компоненты векторов

Несмотря на маломальский размер нижеследующего материала, он довольно важен. Не следует это забывать.

Компоненты

Описание

x, y, z, w

используются при считывании позиций или нормали

r, g, b, a

используются при считывании цветов

s, t, p, q

используются при считывании текстурных координат

FAQ

Как узнать глобальные координаты вектора?

GPos = gl_ModelViewMatrix * gl_Vertex.xyz;

Как получить вектор напрвления зрения?

vec3 viewVec = camPos - gl_Vertex.xyz;

Как получить позицию камеры? camPos = gl_ModelViewMatrixInverse[3].xyz;

Глава 39. Шейдеры GLSL. Использование без компонентов. Пиксельное освещение Ambient + Diffuse + Specular по Фонгу.

Существует два общеизвестных метода освещения. Это пиксельное и вершинное (вертексное). Вертексное освещение уступает пиксельному в реалистичности, зато реализуется проще и работает быстрее.

Ещё одно небольшое пояснение, ambient – однородный фоновый цвет, diffuse – рассеянный (или диффусный) цвет. Так же существует ещё Specular – “зеркальный” свет или блики и emissive - исходящий цвет.

Фоновоее свет – это свет, который настолько распределен средой (предметами, стенами и так далее), что его направление определить невозможно – кажется, что он исходит отовсюду. Лампа дневного света имеет большой фоновый компонент, поскольку большая часть света, достигающего вашего глаза, сначала отражается от множества поверхностей. Уличный фонарь имеет маленький фоновый компонент: большая часть его света идет в одном направлении, кроме того, поскольку он находится на улице, очень небольшая часть света попадает вам в глаз после того, как отразится от других объектов. Когда фоновый свет падает на поверхность, он одинаково распределяется во всех направлениях.

Диффузный компонент – это свет, идущий из одного направления. Таким образом, он выглядит

ярче, если падает на поверхность под прямым углом, и выглядит тусклым, если касается ее

всего лишь вскользь. Однако, когда он падает на поверхность, он распределяется одинаково во всех направлениях, то есть его яркость одинакова вне зависимости от того, с какой стороны вы

смотрите на поверхность. Вероятно, любой свет, исходящий из определенного направления или

позиции, имеет диффузный компонент.

Зеркальный свет исходит из определенного направления и отражается от поверхности в определенном направлении. При отражении хорошо сфокусированного лазерного луча от качественного зеркала происходит почти 100 процентное зеркальное отражение. Блестящий метал или пластик имеет высокий зеркальный компонент, а кусок ковра или плюшевая игрушка нет. Вы можете думать о зеркальности как о том, насколько блестящим выглядит материал.

Исходящий цвет, имитирующий свет, исходящий от самого объекта. В модели освещения OpenGL исходящий свет поверхности добавляет объекту интенсивности, но на него не влияют никакие источники света, и он не производит дополнительного света для сцены в целом.

Ну что же, давайте реализуем пиксельное освещение. Снова нам придётся поместить необходимые для успешной работы компоненты. GLScene1 (инспектор объектов сцены), GLSceneViewer. В инспекторе объектов создайте камеру, свойству GLCamera.Position.Z присвойте значение 2; свойству GLSceneViewer.Camera присвойте GLCamera. Так же в инспекторе объектов создайте GLDirectOpenGL. Для того чтобы увидеть результат действия шейдера, создадим сферу с именем GLSphere.

Теперь объявите в программе константы для хранения вершинного и фрагментного шейдера.

const _vp:string =

‘uniform vec3 LightPosition;’+

‘uniform vec3 EyePosition;’+

‘varying vec3 ViewDirection;’+

‘varying vec3 LightDirection;’+

‘varying vec3 Normal;’+

‘void main(void)’+

‘{‘+

‘gl_Position = ftransform();’+

‘vec4 ObjectPosition = gl_ModelViewMatrix * gl_Vertex;’+

‘ViewDirection = EyePosition - ObjectPosition.xyz;’+

‘LightDirection = LightPosition - ObjectPosition.xyz;’+

‘Normal = gl_NormalMatrix * gl_Normal;’+

‘}’;

Думаю, ничего особенного в этом шейдере нет.

EyePosition – координаты взгляда.

LightPosition – координаты источника света.

ViewDirection и LightDirection вектора взгляда и источника света.

const _fp:string =

‘uniform vec4 SpecularColor;’+

‘uniform vec4 DiffuseColor;’+

‘uniform vec4 AmbientColor;’+

‘uniform float SpecularPower;’+

‘varying vec3 ViewDirection;’+

‘varying vec3 LightDirection;’+

‘varying vec3 Normal;’+

‘void main( void )’+

‘{‘+

‘vec3 fvLightDirection = normalize( LightDirection );’+

‘vec3 fvNormal = normalize( Normal );’+

‘float fNDotL = dot( fvNormal, fvLightDirection );’+

‘vec3 fvReflection = normalize( ( ( 2.0 * fvNormal ) * fNDotL ) - fvLightDirection );’+

‘vec3 fvViewDirection = normalize( ViewDirection );’+

‘float fRDotV = max( 0.0, dot( fvReflection, fvViewDirection ) );’+

‘vec4 fvTotalAmbient = AmbientColor;’+

‘vec4 fvTotalDiffuse = DiffuseColor * fNDotL;’+

‘vec4 fvTotalSpecular = SpecularColor * ( pow( fRDotV, SpecularPower ) );’+

‘gl_FragColor = fvTotalAmbient + fvTotalDiffuse + fvTotalSpecular;’+

‘}’;

Даю вам самим разобраться что и как делает этот шейдер. Это несложно сделать, если вы читали предыдущие главы по использованию GLSL шейдеров без компонентов.

Объявите в программе переменные:

GLSLHandle:TGLProgramHandle;

InitDGL:boolean;

Настало время записать в событие OnRender код для инициализации шейдера.

if not InitDGL then begin

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);

CheckOpenGLError;

InitDGL:=True;

end;

if InitDGL then

with GLSLHandle do begin

UseProgramObject;

Uniform1f['SpecularPower']:=2;

Uniform3f['LightPosition']:=AffineVectorMake(1, 1, 1);

Uniform3f['EyePosition']:=AffineVectorMake(GLCamera.Position.X,GLCamera.Position.Y,GLCamera.Position.Z);

Uniform4f['AmbientColor']:=VectorMake(0.1,0.2,0.2,1);

Uniform4f['DiffuseColor']:=VectorMake(2,0.3,0.1,1);

Uniform4f['SpecularColor']:=VectorMake(0.2,1,0.3,1);

GLSphere.Render(rci);

EndUseProgramObject;

end;

Поздравляю, вы успешно реализовали пиксельное освещение Ambient + Diffuse + Specular.

Должен вас предупредить. Описанный способ далеко не лучший вариант использования для сцен с большим количеством света и/или объектов. Объясню почему.

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

Кроме того для сложных сцен подобный многопроходный способ создает особенно большую нагрузку на вершинную часть конвейера - представьте себе сцену с большим количеством различной растительности, анимируемой в вершинном шейдере. Тогда на каждом проходе вся эта анимация будет вычисляться заново.

Поскольку и геометрическая сложность сцены все время растет, подобный подход становится все менее приемлемым.