Новые уроки OpenGL и мультимедиаОсновы освещения в OpenGL


Урок 2. Основы освещения в OpenGL
Ну вот наконец-то и пришел 2 урок =). Задержался он немного, так как были проблемы с жестким диском и с инетом. Но вот вышел второй урок =). В этом уроке я раскажу про освещение.

Часть 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);
    glEnable(GL_LIGHTING);
    glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glEnable(GL_NORMALIZE);
}


Тут мы инициализируем всякое =)

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

    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light0_direction);
}


Здесь инициализируется освещение.

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


Здесь происходит вся прорисовка.

init_l();//выполняем инициализацию освещения
GLfloat x, y;//высота и ширина плоскости
glBegin(GL_QUADS);//начинаем рисовать плоскость
glNormal3f(0.0, 0.0, -1.0);//указываем направление нормалей (это обязательно)
//тут рисуем плоскость
glEnd();//закончили рисовать
glDisable(GL_LIGHT0);//отключаем освещение

Ну, вот вроде с примером разобрались. Далее будем изучать освещение.

Часть 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. Изучаем параметры света

1.Цвет

Diffuse
Параметр GL_DIFFUSE, наверное, наиболее точно совпадает с тем, что вы привыкли называть «цветом света». Он определяет RGBA цвет диффузного света, который отдельный источник света добавляет к сцене.

Ambient
Параметр GL_AMBIENT влияет на цвет зеркального блика на объекте. В реальном мире на объектах вроде стеклянной бутылки имеется зеркальный блик соответствующего освещению цвета (часто белого).

Specular
Параметр GL_SPECULAR влияет на интенсивность зеркального блика на объектах.

2.Позиция

Position
Параметр GL_POSITION имеет 3 значения положения и одно, указывающее на то, какой источник света будет использоваться.
GL_POSITION (x,y,z,w)
Первые 3 параметра понятны — это положение, а вот 4-й параметр указывает, будет ли использоваться бесконечно удаленный свет или точечный. Если значение w = 0, то источник света бесконечно удаленный (что-то вроде солнца). Если w = 1, то этот источник света точечный (что то вроде лампочки).
Если w = 0, то первые 3 параметра — это вектор от центра системы координат (0,0,0).

3.Прожектор

GL_SPOT_DIRECTION
Направление света прожектора

GL_SPOT_EXPONENT
Концентрация светового луча

GL_SPOT_CUTOFF
Угловая ширина светового луча

Я думаю тут все понятно, единственное, нужно уточнить, что позиция должна быть с w = 1.
4.Ослабление

Если вам нужно ослаблять интенсивность света от центра(тоесть чем дальше от центра, тем тускнее), то вам надо настроить параметры: 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*() – это команда, используемая для задания всех параметров модели освещения. 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
вычисляется ли зеркальный цвет отдельно от фонового и диффузного

Часть 7. Заключение

Ну вот и все, с освещением закончили =)
Теперь вы можете создавать отличные источники света.

Вот тут есть отличный пример использования всех источников света, найденый мною в недрах интернета.
Lesson2_Light(Linux, Windows).
В архиве лежит исходный код программы (мною подредактированый) и исполняемые файлы под Linux и под Windows.

Также, если вы компилируете под Windows с помощю компилятора mingw, то там компиляция немного отличается.Надо писать:

i586-mingw32msvc-g++ file.cpp -lopengl32 -lglu32 -lglut32win -o file.exe


Также вам надо будет скачать библиотеку glut для mingw (в гугле есть точно),
а если под линукс с помощью gcc, то там как обычно:
g++ file.cpp -lglut -lGLU -o file
  • +3
  • isaer
  • 25 августа 2010, 23:27

Вставка изображения

Комментарии (21)

rss свернуть / развернуть
  • avatar
  • isaer
  • 25 августа 2010, 23:28
  • #
  • +2
понравилась статья? поставь плюс =))

свернуть ветку
Отлично)

свернуть ветку
  • avatar
  • KinsT
  • 26 августа 2010, 18:46
  • #
  • 0
По-моему статья отличная — достаточно информативная!
Только код без комментариев меня немного смутил :) Все таки не для профи пишем, а для людей, которым что-то не ясно, которые разобраться хотят (имхо).

свернуть ветку
я вроде все коментировал
там вначале пример весь закоментирован
да и дальше почти каждую строчку пояснял

если что не так то напиши что исправить я подправлю

свернуть ветку
У тебя все классно получилось!
Просто в самом коде комментов нет и если кто-то очень ленивый вначале копи-пастит, а потом только разбираться начинает, то туго ему бедненькому придется :)
Хотя это уже его проблемы мы для лентяев не стараемся :)))

Кстати по-поводу лентяев, а у тебя есть такой же код только на C#? :)))

свернуть ветку
нет на шарп нету
так а там изменять почти ничего не надо
только перед gl ставить GL. и все (вроде)
я на шарпе уже давно не программировал
так как мне больше С++ понравился =)
хотя так скучаю по формачкам и кнопочкам =)

свернуть ветку
«только перед gl ставить GL. и все (вроде)»
Вот-вот я про лентяев и говорю :)))
Там перед каждым из параметров внутри функции например glutInitDisplayMode(...) или др. нужно Gl. ставить. Ну ни чего, блокнот мне в руки ;)

свернуть ветку
=)
ты скачай пример, там класно =) мне понравился
правда я там убрал один прикол
а зря, с ним лучше было, ты просто делай так:
напиши в первой строчке GL. и скопируй ее и потом просто везде Ctrl + V
ну и на стрклочках строчку ниже и Home чт бы в начало строчки

там быстро тогда

свернуть ветку
ах да я же забыл =)) там если на форму делать то надо менять код (и давольнотаки много)
ты сделай на консоле, почти ничего не надо

и вобще чет меня занесло, может ты не про пример тот говоришь =))
там просто есть исполняемый файл для винды и для линукса

свернуть ветку
Все нормально, я сам код по созданию освещения перенесу на Шарп в ВинФормс и посмотрю как работает, а потом если вопросы появятся, опять сюда и буду спрашивать ;)))

Кстати, если захочешь, когда код будет готов я могу тебе скинуть (или сразу Anvi), чтобы он в конце урока ссылку на C#+TaoFramework проект сделал. Мало ли, вдруг людям пригодится :)

свернуть ветку
кидай anvi пусть он льет, меня просто может долго не быть

свернуть ветку
Перенес в «Новые уроки OpenGL». Это можно сделать и самостоятельно, имея соответствующий рейтинг (по моему, если не подводит память ~0.8)

Еще, просьба ко всем, кто создает новые уроки, ознакомиться с пожеланиями к урокам. Это поможет не упустить несколько важных вещей для создания качественных, полных уроков.

свернуть ветку
  • avatar
  • darkx
  • 01 февраля 2011, 01:08
  • #
  • +3
А его сразу не нашел, извиняюсь, что требовал продолжения первого урока, когда нужно было просить продолжения второго)))

свернуть ветку
все собираюсь сделать про текстурирование

свернуть ветку
Еще один классный урок!!! Я его тоже раньше не находил.(
Ща попробую в mingw, а то у меня OGl там не компилился. После чего, я mingw и забросил.
Побольше бы Linux-а в нашу жизнь!;)))

свернуть ветку
  • avatar
  • isaer
  • 01 февраля 2011, 02:42
  • #
  • +1
Linux — крут =)

может быть когда нить сделаю что нить по типу блога про линукс =) там как компилировать в разных средах, как подключать файлы, Makefiles
сейчас просто времени не очень много
но вскоре думаю сделаю

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

свернуть ветку
Это надо=)

свернуть ветку
Linux — это сила!)
может быть когда нить сделаю что нить по типу блога про линукс =)
Это очень классно.
Буду надеяться на то, что времени у тебя станет побольше.))
Ну и у меня заодно)))

свернуть ветку
  • avatar
  • isaer
  • 02 февраля 2011, 02:03
  • #
  • 0
люди
вот смотрю я тут и не могу понять к чему эти разные приставки например
m_Surface — что означает эта m_ ??
и какие еще приставки знаете? а то я как то все называю без приставок, может если кто читает и ему будет с приставками понятнее

свернуть ветку
Отношение к кому-то классу или объект наверное, ну как сделаешь, чтоб не путать добавляй.

свернуть ветку
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.