В этом уроке мы научимся создавать вершинное освещение.
Исходные файлы main.cpp, main.h, init.h взяты из урока «Начало — инициализация».

Обьявим глобальную переменную позиции света в файле main.h:

extern float g_LightPosition[4];    // Позиция источника света

 

Итак, в первую очередь нам нужно инициализировать освещение.

Сделаем мы это в файле init.cpp:

// Сразу переходим к функции InitializeOpenGL().
// В конец функции, перед закрывающейся скобкой, дописываем новый код.// Лучше всего устанавливать цвет пространства в цвет «темноты» — отсутствия освещения.
// Так как мы будем использовать белый как цвет освещения, установим цвет
// пространства в черный:glClearColor(0, 0, 0, 1);// Ниже создадим два массива цветов. Первый, ambience[] — цвет «рассеянного света», когда
// на полигон не падает прямое освещение. Если мы не установим этот цвет, не освещенные полигоны
// будут совершенно черными, и мы их не увидим. Цвет освещения мы сделаем белый, а рассеянный
// свет установим в половину этого значения.
// Второй массив, diffuse[], это цвет направленного света. Мы выбрали белый. Первые три
// цифры массивы — R,G,B, последняя — значение альфа. Пока что не беспокойтесь о нём.
//
// Для рассеянного света выберем глубоко серый цвет, для дифузного — среднее между
// белым и черным.

float ambience[4] = {0.3f, 0.3f, 0.3f, 1.0};    // Цвет мирового света
float diffuse[4] = {0.5f, 0.5f, 0.5f, 1.0}; // Цвет позиционного света

// Чтобы установить мировое освещене, нужно передать OpenGL наши массивы.
// OpenGL даёт нам возможность использовать несколько источников света. Общее количество
// источников зависит от переменной GL_MAX_LIGHTS. Для первого используемого источника будем
// использовать дефайн OpenGL GL_LIGHT0. После указания используемого источника передаём
// OpenGL флаг говорящий, что мы устанавливаем значение ambience, и массив ambience-цветов.
// Точно то же делаем с diffuse и GL_DIFFUSE.

// Устанавливаем цвет рассеянного цвета (без направленного света)
glLightfv( GL_LIGHT0, GL_AMBIENT,  ambience );
// И диффузный цвет (цвет света)
glLightfv( GL_LIGHT0, GL_DIFFUSE,  diffuse );

// Далее нам нужно установить позицию источника света. У нас может быть много простых
// источников в разных местах, но в этом уроке будет использоваться только один.
// Для последующих источников нужно использовать GL_LIGHT1, GL_LIGHT2, GL_LIGHT3 и т.д…
// Мы сможем изменять позицию источника клавишами ‘+’ и ‘-‘. Позиция по умолчанию — (0,1,0,1).
// Это расположит источник прямо над центральной пирамидой. Последнее значение в массиве
// g_LightPosition сообщает OpenGL, нужен ли нам ПОЗИЦИОННЫЙ или НАПРАВЛЕННЫЙ свет.
// Если значение = 1, цвет будет позиционным, иначе он будет направлен от камеры.
// Если мы установим направленный свет, он будет освещать всё «по дороге» от нашего «глаза».
// Если же свет будет позиционным, он «повиснет» в координатах x,y,z и будет освещать
// всё вокруг себя. Это то, что нам нужно, так что установим значение в единицу.

glLightfv( GL_LIGHT0, GL_POSITION, g_LightPosition );

// После инициализации источника света нам нужно включить его:
glEnable(  GL_LIGHT0   );

// Но недостаточно включить один источник; кроме этого нужно включить само
// освещение в OpenGL:
glEnable(GL_LIGHTING);

// Следующая строка позволяет закрашивать полигоны цветом при включенном освещении:
glEnable(GL_COLOR_MATERIAL);

 

Помните, если мы используем функции glColor*() вместе с освещением, они будут проигнорированы, пока мы не включим GL_COLOR_MATERIALS.

В init.cpp мы инициализировали освещение в функции InicializeOpenGL().

Теперь используем освещение в main.cpp

// В начале файла, после включения хидеров, добавим переменные:
float g_LightPosition[4] = {0, 1, 0, 1};         // Позиция источника света
float g_bLight = true;                           // Включен ли светfloat rotY = 0.0f;                           // Вращение по Y// Напишем функцию, создающую пирамиду:
void CreatePyramid(float x, float y, float z, int width, int height)
{glBegin(GL_TRIANGLES);

// Ниже мы добавим что-то новое: НОРМАЛИ. Значение, насколько сильно нужно
// осветить конкретный полигон, OpenGL рассчитывает из его нормалей. Что такоей нормаль?
// Нормаль — это направление полигона. Вы заметите, что мы присваиваем заднему полигону
// нормаль (0,1,-1). Это значит, что полигон направлен в обратную сторону по оси Z (внутрь
// экрана). Запомните, нормали — это не координаты, а только направления.
// Функция glNormal3f() позволяют нам указать нормаль для вершин, переданных за ней.
// Сейчас мы напишем нормали вручную, но вы можете вернутся к урокам камеры, где есть
// ничто иное, как функция для рассчета нормалей.

// Задняя сторона
glNormal3f(0, 1, 1);    // Полигон направлен назад и вверх
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x width, y height, z width);
glColor3ub(255, 0, 255); glVertex3f(x + width, y height, z width);

// Передняя сторона
glNormal3f(0, 1, 1);
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x + width, y height, z + width);
glColor3ub(255, 0, 255); glVertex3f(x width, y height, z + width);

// Левая сторона
glNormal3f(1, 1, 0);
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x width, y height, z + width);
glColor3ub(0, 255, 255); glVertex3f(x width, y height, z width);

// Передняя правая сторона
glNormal3f(1, 1, 0);
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x + width, y height, z width);
glColor3ub(0, 255, 255); glVertex3f(x + width, y height, z + width);

glEnd();

// Теперь отрендерим дно пирамиды

glBegin(GL_QUADS);

// Эти вершины образуют дно пирамиды
glNormal3f(0, 1, 0);
glColor3ub(0, 0, 255); glVertex3f(x width, y height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y height, z width);
glColor3ub(0, 0, 255); glVertex3f(x width, y height, z width);
glEnd();
}

//////////////////////////////////////////////////////////////////////////
// Внесём изменения в обработку клавиш, блок WM_KEYDOWN функции WinProc():

case WM_KEYDOWN:
switch (wParam)
{
// Ниже мы позволяем пользователю увеличивать и уменьшать позицию Y источника света
// клавишами ‘+’ и ‘-‘. После изменения позиции необходимо сообщить об этом
// OpenGL вызовом glLightfv(). Мы передаём номер источника (GL_LIGHT0) и флаг
// GL_POSITION, плюс саму позицию.
// Нажатием ‘L’ свет включается и выключается.

case VK_ESCAPE:
PostQuitMessage(0);
break;

case VK_ADD:    // Если нажата ПЛЮС
g_LightPosition[1] += 0.1f;  // Увеличиваем значение Y
// Убедимся, что не превысили 5
if(g_LightPosition[1] > 5) g_LightPosition[1] = 5;
break;

case VK_SUBTRACT: // Если МИНУС
g_LightPosition[1] -= 0.1f;// Уменьшим значение Y
// Убедиммся, что оно не меньше -5
if(g_LightPosition[1] < 5) g_LightPosition[1] = 5;
break;

case ‘L’:

g_bLight = !g_bLight;   // Выключим свет

if(g_bLight)        // Включим свет
glEnable(GL_LIGHTING);
else
glDisable(GL_LIGHTING); // Выключим свет
break;
}
break;

////////////////////////////////////////////////////////////////////////

// И, наконец, изменим функцию RenderScene():
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Обновим позицию света ДО вызова glLookAt(), чтобы свет обновился
// корректно. Опустите функцию вниз, чтобы увидеть, что иначе случится.
// Если источник не двигается, вызывать эту функцию заново не нужно.
glLightfv( GL_LIGHT0, GL_POSITION, g_LightPosition );

gluLookAt(0, 0, 6,     0, 0, 0,     0, 1, 0);

glRotatef(rotY, 0.0f, 1.0f, 0.0f);  // Вращаем пирамиду вокруг оси Y

// Это создаёт 3д пирамиду в центре (0,0,0)
CreatePyramid(0, 0, 0, 1, 1);

// Создадим пирамиды вокруг:

CreatePyramid(3, 0, 3, 1, 1);
CreatePyramid(3, 0, 3, 1, 1);
CreatePyramid(0, 0, 5, 1, 1);

// Увеличиваем впращение.

rotY += 0.6f;

SwapBuffers(g_hDC);
}

 

Итак, вот шаги, которые мы проделали:

1) Установили ambience-цвет мира: glLightfv( GL_LIGHT0, GL_AMBIENT, ambienceArray );

2) Установили diffuse-цвет источника света: glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseArray);

3) Установили позицию источника света: glLightfv( GL_LIGHT0, GL_POSITION, positionArray);

4) Включили источник света: glEnable( GL_LIGHT0 );

5) И освещение в OpenGL: glEnable( GL_LIGHTING).

Исходные коды к уроку