Прежде, чем закончить эти уроки, сделаем ещё одну вещь: используем свет OpenGL вместо переменной lightDir. Таким образом мы сможем объявлять свет в OpenGL и использовать его направление в шейдерах. Учтите, включать освещение в OpenGL функцией glEnable нет необходимости, так как мы не будем использовать глобальное освещение OpenGL.
Мы обьявим первый источник света в OpenGL (GL_LIGHT0) как направленный свет.
GLSL предоставляет доступ к части функционала OpenGL, в том числе — к параметрам освещения. В GLSL обьявлена структура для данных света, и массив для хранения структур всех источников света.
struct gl_LightSourceParameters {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
…
};
uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];
Это значит, что у нас есть доступ к направлению света (поле position для направленного источника) в вершинном шейдере. Опять же, направление света должно быть нормализовано в приложении OpenGL.
Спецификация OpenGL указывает, что когда инициализируется источник света, его позиция автоматически устанавливается в позицию нашего «глаза» — камеры. При этом вектор его направления нормализован (если мы остаемся в ортогональной матрице 3х3, а при использовании gluLookAt() это мы в ней, пока не используем glScalef() и другие scalers).
Нам нужно конвертировать нормаль в координаты нашего «взгляда», чтобы вычислить dot product и др.
Чтобы трансформировать нормаль в координаты «взгляда», мы используем заранее объявленную uniform-переменную mat3 gl_NormalMatrix. Эта матрица — результат инвертирования 3×3 право-верхней саб-матрицы из матрицы моделей. Сделаем трансформацию нормалей на каждую вершину. Таким образом вершинный шейдер получится таким:
Файл toon.vert
varying vec3 normal;
void main()
{
normal = gl_NormalMatrix * gl_Normal;
gl_Position = ftransform();
}
В пикселном шейдере нам нужно получить доступ к позиции источника света, чтобы вычислить интенсивность.
Файл toon.frag:
varying vec3 normal;
void main()
{
float intensity;
vec4 color;
vec3 n = normalize(normal);
intensity = dot(vec3(gl_LightSource[0].position),n);
if (intensity > 0.95)
color = vec4(1.0,0.5,0.5,1.0);
else if (intensity > 0.5)
color = vec4(0.6,0.3,0.3,1.0);
else if (intensity > 0.25)
color = vec4(0.4,0.2,0.2,1.0);
else
color = vec4(0.2,0.1,0.1,1.0);
gl_FragColor = color;
}
Ну а теперь перейдём к коду программы. Возьмите его из первого практического урока.
Файл main.cpp:
#pragma comment(lib, «glut32.lib»)
#include <gl/glut.h>// Инициализируем переменные:
GLuint v,f,f2,p; // Дескрипторы программ и шейдеров
float lpos[4] = {1,0.5,1,0}; // Позиция источника света
float a = 0; // Хранит угол вращения///////////////////////////////////////////////////
// В функции setShaders() изменим, название файлов шейдеров:
vs = textFileRead(«toon.vert»);
fs = textFileRead(«toon.frag»);
// Теперь в функцию RenderScene() добавим создание чайника:
glutSolidTeapot(1);
Всё, компилируем и смотрим результат.
Скачайте и посмотрите исходники, если что-то пойдет не так.