Основы освещения в OpenGL (код - C/C++)
Что прочитать перед этим материалом
Основы освещения в OpenGL
Часть 1. Подготовка
Для изучения освещения необходимы:
- OpenGL;
- glut;
- IDE, хотя можно воспользоваться gedit или CodeBlocks;
- компилятор, например, gcc для Linux и mingw для Windows;
Часть 2. Пример простой программы
Рассмотрим пример программы, в которой используется освещение.
Код:
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
void init()
{
glClearColor(0.3, 0.3, 0.3, 1.0);
glEnable(GL_LIGHTING);
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glEnable(GL_NORMALIZE);
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.2, 1.2, -1.2, 1.2, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void init_l()
{
float light0_diffuse[] = {0.4, 0.7, 0.2};
float light0_direction[] = {0.0, 0.0, 1.0, 0.0};
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light0_direction);
}
void display()
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
init_l();
GLfloat x, y;
glBegin(GL_QUADS);
glNormal3f(0.0, 0.0, -1.0);
for (x = -1.0; x < 1.0; x += 0.005)
{
for (y = -1.0; y < 1.0; y += 0.005)
{
glVertex3f(x, y, 0.0);
glVertex3f(x, y + 0.005, 0.0);
glVertex3f(x + 0.005, y + 0.005, 0.0);
glVertex3f(x + 0.005, y, 0.0);
}
}
glEnd();
glDisable(GL_LIGHT0);
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(50, 100);
glutInitWindowSize(500, 500);
glutCreateWindow("Light");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
}
Часть 3. Разбор кода в примере
Комментарии даны через символ // - “слэш”.
Код:
void init()
{
glClearColor(0.3, 0.3, 0.3, 1.0); // очищаем экран в цвет, установленый параметрами R G B A
glEnable(GL_LIGHTING); // здесь включается расчет освещения
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); // делаем так, чтобы освещались обе стороны полигона
glEnable(GL_NORMALIZE); //делам нормали одинаковой величины во избежание артефактов
}
Здесь инициализируется освещение
Код:
void init_l()
{
float light0_diffuse[] = {0.4, 0.7, 0.2}; // устанавливаем диффузный цвет света
float light0_direction[] = {0.0, 0.0, 1.0, 0.0}; // устанавливаем направление света
glEnable(GL_LIGHT0); // разрешаем использовать light0
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); // устанавливаем источнику света light0 диффузный свет, который указали ранее
glLightfv(GL_LIGHT0, GL_POSITION, light0_direction); // устанавливаем направление источника света, указанное ранее
}
Здесь происходит вся прорисовка.
Код:
void display()
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
init_l(); // выполняем инициализацию освещения
GLfloat x, y; // высота и ширина плоскости
glBegin(GL_QUADS); // начинаем рисовать плоскость
glNormal3f(0.0, 0.0, -1.0); // указываем направление нормалей (это обязательно)
for (x = -1.0; x < 1.0; x += 0.005)
{
for (y = -1.0; y < 1.0; y += 0.005)
{
glVertex3f(x, y, 0.0);
glVertex3f(x, y + 0.005, 0.0);
glVertex3f(x + 0.005, y + 0.005, 0.0);
glVertex3f(x + 0.005, y, 0.0);
}
}
glEnd(); // закончили рисовать
glDisable(GL_LIGHT0); // отключаем освещение
glutSwapBuffers();
}
Часть 4. Изучаем освещение
Сначала включим расчет освещения командой glEnable(GL_LIGHTING). Затем надо разблокировать источник света командой glEnable(GL_LIGHT). GL_LIGHT может принимать только 8 значений (по крайней мере в OpenGL 2.1), то есть GL_LIGHT0..GL_LIGHT7.
Теперь надо создать источник света. У каждого источника света есть свои параметры по умолчанию, например, если вы просто разблокируете 2 источника света GL_LIGHT0 и GL_LIGHT1, то будет виден только 0, так как в нем параметры по умолчанию отличаються от остальных (у всех остальных они идентичны).
Источники света имеют несколько параметров, а именно: цвет, позиция и направление.
Команда, используемая для указания всех параметров света – это *glLight()**. Она принимает три аргумента: идентификатор источника света, имя свойства и желаемое для него значение.
Код:
void glLight{if} (GLenum light, GLenum pname, TYPE param);
void glLight{if}v (GLenum light, GLenum pname, TYPE *param);
GLenum light - это выбор источника, например, GL_LIGHT0.
GLenum pname - это параметры источника света (далее будут приведены все ее параметры).
TYPE param - это значение, которое принимает GLenum pname.
Если у вас стоит void glLight{if}v (GLenum light, GLenum pname, TYPE *param), значит используется векторная версия команды; param представляет собой вектор величин.
Например:
Код:
float light_ambient[] = {0.0,0.0,0.0,1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
Если нет, то тогда это единственное значение.
Например:
Код:
glLightf(GL_LIGHT0, GL_GL_SPOT_CUTOFF, 180);
Вот листинг значений GLenum pname.
Читается так: первая строчка - это название параметра, вторая - это значение поумолчанию, а третья - пояснение. Если вы видите что-то типа (1.0,1.0,1.0,1.0) или (0.0,0.0,0.0,1.0), то это значит, что первая скобка - это значение по умолчанию для нулевого источника, а вторая скобка - для остальных:
Код:
GL_AMBIENT
(0.0,0.0,0.0,1.0)
Интенсивность фонового света
GL_DIFFUSE
(1.0,1.0,1.0,1.0)
или
(0.0,0.0,0.0,1.0)
Интенсивность диффузного света (значение по умолчанию
для 0-го источника - белый свет, для остальных - черный)
GL_SPECULAR
(1.0,1.0,1.0,1.0)
или
(0.0,0.0,0.0,1.0)
Интенсивность зеркального света (значение по умолчанию для 0-го источника - белый свет, для остальных - черный)
GL_POSITION
(0.0,0.0,1.0,0.0)
Положение источника света (x,y,z,w)
GL_SPOT_DIRECTION
(0.0,0.0,-1.0)
Направление света прожектора (x,y,z)
GL_SPOT_EXPONENT
0.0
Концентрация светового луча
GL_SPOT_CUTOFF
180.0
Угловая ширина светового луча
GL_CONSTANT_ATTENUATION
1.0
Постоянный фактор ослабления
GL_LINEAR_ATTENUATION
0.0
Линейный фактор ослабления
GL_QUADRATIC_ATTENUATION
0.0
Квадратичный фактор ослабления
Пример использования освещения:
Код:
float light_ambient[] = {0.0,0.0,0.0,1.0};
float light_diffuse[] = {1.0,1.0,1.0,1.0};
float light_specular[] = {1.0,1.0,1.0,1.0};
float light_position[] = {1.0,1.0,1.0,0.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
Часть 5. Изучаем параметры света
- Цвет
Diffuse
Параметр GL_DIFFUSE, наверное, наиболее точно совпадает с тем, что вы привыкли называть «цветом света». Он определяет RGBA-цвет диффузного света, который отдельный источник света добавляет к сцене.
Ambient
Параметр GL_AMBIENT влияет на цвет зеркального блика на объекте. В реальном мире на объектах вроде стеклянной бутылки имеется зеркальный блик соответствующего освещению цвета (часто белого).
Specular
Параметр GL_SPECULAR влияет на интенсивность зеркального блика на объектах.
- Позиция
Параметр GL_POSITION (x, y, z, w) имеет 3 значения положения и одно, указывающее на то, какой источник света будет использоваться.
Первые 3 (x, y, z) параметра понятны, а 4-й (w) параметр указывает, будет ли использоваться бесконечно удаленный свет или точечный. Если значение w = 0, то источник света бесконечно удаленный (что-то вроде солнца). Если w = 1, то этот источник света точечный (что-то вроде лампочки).
Если w = 0, то первые 3 параметра - это вектор от центра системы координат (0,0,0).
- Прожектор
GL_SPOT_DIRECTION - направление света прожектора.
GL_SPOT_EXPONENT - концентрация светового луча.
GL_SPOT_CUTOFF - угловая ширина светового луча.
Единственное, что нужно уточнить - позиция должна быть w = 1.
- Ослабление
Если вам нужно ослаблять интенсивность света от центра (то есть чем дальше от центра, тем тускнее), то вам надо настроить параметры: GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION.
Но так не очень удобно, поэтому можно воспользоваться формулой:
Код:
Fatt = 1 / (Kc + Kl * d + Kq * (d * d))
В формуле используются следующие значения:
d - расстояние между позицией источника света и точкой.
Kc - GL_CONSTANT_ATTENUATION, постоянный фактор ослабления.
Kl - GL_LINEAR_ATTENUATION, линейный фактор ослабления.
Kq - GL_QUADRATIC_ATTENUATION, квадратичный фактор ослабления.
Если вы не совсем поняли, как это применить, обратите внимание на эту заготовку:
Код:
float kQ;
float kL;
float kC;
float radius;
float att;
attn = 1;
radius = 5;
kQ = att / (3* radius * radius);
kL = att / (3 * radius);
kC = att / 3;
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kC);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, kL);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, kQ);
Радиус задается от центра до конца.
Если вам надо уменьшить общую интенсивность, вы можете изменить параметр att.
Часть 6. Выбор модели освещения
OpenGL-понятие модели освещения разделяется на 4 компонента:
- интенсивность глобального фонового света;
- считается ли положение точки наблюдения локальным к сцене или бесконечно удаленным;
- должен ли расчет освещенности производиться по-разному для лицевых и обратных граней объектов;
- должен ли зеркальный цвет отделяться от фонового и диффузного и накладываться на объект после операций текстурирования.
glLightModel()* – это команда, используемая для задания всех параметров модели освещения. Может принимать два аргумента: имя параметра модели освещения в виде константы и значение для этого параметра.
Код:
void glLightModel{if} (GLenum pname, TYPE param);
void glLightModel{if}v (GLenum pname, TYPE *param);
Устанавливаемая характеристика модели освещения определяется аргументом pname; param задает величину, в которую устанавливается pname.
Код:
GL_LIGHT_MODEL_AMBIENT
(0.2,0.2,0.2,1.0)
RGBA интенсивность всей сцены
GL_LIGHT_MODEL_LOCAL_VIEWER
0.0 или GL_FALSE
способ вычисления углов зеркального отражения
GL_LIGHT_MODEL_TWO_SIDE
0.0 или GL_FALSE
выбор между односторонним и двухсторонним освещением
GL_LIGHT_MODEL_COLOR_CONTROL
GL_SINGLE_COLOR
вычисляется ли зеркальный цвет отдельно от фонового и диффузного
Теперь вы можете создавать отличные источники света.