Украина, Киев |
Свет и материалы в OpenGL
Задание свойств материала
Свойства материала объекта определяют характер рассеивания света. Поскольку взаимодействие между падающим светом и поверхностью объекта довольно сложно, правильное задание свойств материала, дающих нужный результат, - целое искусство. В нашем распоряжении не только фоновый, рассеянный и отраженный цвета материала, но и блеск, то есть насколько хорошо материал отполирован. В упражнении рисования освещенной сферы ( LightSphere ) мы явно, с помощью функции glMaterialfv(), определили только два свойства материала - цвет блика и его размер.
Большинство свойств материала концептуально (по смыслу) похожи на свойства источников света. Механизм их указания помощью функции glMaterialfv() подобен установке параметров источника света. Команды задания свойств материала имеют следующий синтаксис:
void glMaterial{if}(GLenum face, GLenum pname, TYPE param); void glMaterial{if}v(GLenum face, GLenum pname, TYPE *param);
Команда задает текущие значения свойств материала для использования при расчете освещенности. Аргумент face может принимать значения GL_FRONT, GL_BACK, GL_FRONT_AND_BACK в соответствии с тем, к лицевым или оборотным сторонам объекта применяется это свойство, или и к тем и другим вместе. Устанавливаемое свойство материала идентифицируется именем pname (приведены в таблице ниже), а его значение передается в аргументе param. В векторном варианте param является указателем на вектор значений. Невекторная версия команды применяется только для параметра GL_SHININESS.
Зеркальное отражение от объекта производит блики. В отличие от фонового и рассеянного отражения, то, что видит наблюдатель здесь, зависит от положения точки обзора. Представим, что мы смотрим на металлический шарик под солнечным светом. При движении головы блик, создаваемый солнечным светом на шарике, до некоторой степени будет передвигаться вместе с нами. Но если мы слишком далеко закинем голову, блик вообще исчезнет.
OpenGL позволяет управлять воздействием, оказываемым материалом на отраженный свет. Параметр GL_SPECULAR задает цвет блика, а GL_SHININESS - площадь и яркость блика.
Излучение. Задавая RGBA - цвет для параметра GL_EMISSION, можно изобразить объект так, как будто он испускает свет данного цвета. Поскольку большинство реальных объектов (кроме источников света) сами не излучают свет, этот эффект используется в основном для имитации ламп и других источников света. Так, например, объект, нарисованный при таких текущих настройках материала, как
GLfloat mat_emission[] = {0.8, 0.0, 0.2, 0.0}; glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
будет выглядеть красноватым.
Здесь нужно понимать, что хоть объект и будет казаться светящимся, но он не будет действовать как источник света, т.е. не будет давать отблески на соседние объекты. Для того, чтобы он действовал как источник света, нужно создать источник света и поместить его внутрь этого светящегося объекта.
Первый способ задания нескольких материалов (MultiMaterial)
Ко всем примитивам, заданным после вызова функции glMaterial*(), применяются последние установленный значения, пока не будет вызвана следующая функция glMaterial*(). Для того, чтобы нарисовать несколько объектов с различными свойствами материала, непосредственно перед рисованием каждого объекта нужно настраивать текущие свойства материала вызовом функции glMaterial*() с нужными параметрами. Продемонстрируем это на примере
- Наполните файл следующим кодом
// MultiMaterial.h // Упражнение 4: "4) Рисование с разными материалами" //********************************************************** // Прототипы void SetLightMultiMaterial(); void RenderSceneMultiMaterial(); void ChangeSizeMultiMaterial(int, int); // Глобальные массивы свойств материала и освещения GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0}; GLfloat mat_ambient[] = {0.7, 0.7, 0.7, 1.0};// Параметры фонового освещения GLfloat mat_ambient_color[] = {0.8, 0.8, 0.2, 1.0}; GLfloat mat_diffuse[] = {0.1, 0.5, 0.8, 1.0}; GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};// Цвет блика белый GLfloat no_shininess[] = {0.0}; // Размер блика (обратная пропорция) GLfloat low_shininess[] = {5.0}; // Размер блика (большая площадь) GLfloat high_shininess[] = {100.0}; // Размер блика (маленькая площадь - большой фокус) GLfloat mat_emission[] = {0.8, 0.0, 0.2, 0.0};// Красноватое излучение GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};// Цвет и интенсивность // освещения, генерируемого источником GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};// Параметры фонового освещения void SetLightMultiMaterial() { glClearColor(0.0, 0.0, 0.0, 1.0); glShadeModel(GL_SMOOTH); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light); glLightfv(GL_LIGHT0, GL_SPECULAR, white_light); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } //********************************************************** void RenderSceneMultiMaterial() { // Сбрасываем буфер цвета и буфер глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Рисуем левую верхнюю сферу glPushMatrix();// Сохранить матрицу преобразования модели glTranslatef(-.75, 0.75, 0.0); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан glPopMatrix();// Восстановить матрицу преобразования модели // Рисуем правую верхнюю сферу glPushMatrix();// Сохранить матрицу преобразования модели glTranslatef(0.75, 0.75, 0.0); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан glPopMatrix();// Восстановить матрицу преобразования модели // Рисуем левую нижнюю сферу glPushMatrix();// Сохранить матрицу преобразования модели glTranslatef(-0.75, -0.75, 0.0); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан glPopMatrix();// Восстановить матрицу преобразования модели // Рисуем правую нижнюю сферу glPushMatrix();// Сохранить матрицу преобразования модели glTranslatef(0.75, -0.75, 0.0); glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); glRotatef(zRot, 0.0f, 0.0f, 1.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); glutSolidSphere(0.75, 16, 16); // Диаметр, число параллелей и меридиан glPopMatrix();// Восстановить матрицу преобразования модели // Для анимации xRot += 1.0f; yRot += 1.0f; zRot += 1.0f; xRot = (GLfloat)((const int)xRot % 360); yRot = (GLfloat)((const int)yRot % 360); zRot = (GLfloat)((const int)zRot % 360); // Переключить буфер glutSwapBuffers(); // Прокачка сообщений glFlush(); } //********************************************************** void ChangeSizeMultiMaterial(int width, int height) { // Индивидуальные настройки освещения SetLightMultiMaterial(); // Предотвращаем деление на нуль if(height == 0) height = 1; // Устанавливаем поле просмотра с размерами окна glViewport(0, 0, width, height); // Устанавливает матрицу преобразования в режим проецирования glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Устанавливаем размеры ортогонального отсекающего объема GLfloat aspectRatio = (GLfloat)width / (GLfloat)height;// Для соблюдения пропорций if (width <= height) glOrtho (-1.5, 1.5, -1.5 / aspectRatio, 1.5 / aspectRatio, -10.0, 10.0); else glOrtho (-1.5 * aspectRatio, 1.5 * aspectRatio, -1.5, 1.5, -10, 10); // Восстановливает матрицу преобразования в исходный режим вида glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }Листинг 23.12. Код файла MultiMaterial.h
Здесь мы поочередно настраиваем свойства материала перед рисованием очередной сферы, повторно вызывая функцию glMaterial*(). Обратите внимание, что для экономии временных затрат эту функцию необходимо вызывать только для тех параметров, которые изменяются.
- Измените код файла LightAndMaterial.cpp, чтобы он поддерживал новое упражнение
// LightAndMaterial.cpp : Defines the entry point for the console application. // //********************************************************** // Подключение стандартного файла с библиотекой OpenGL #include "stdafx.h" //********************************************************** // Прототипы функций void ExecuteMenu(int); // Контекстное меню первого уровня void TimerFunc(int); // Обработчик события таймера void SpecialKeys(int, int, int); // Обработка нажатия клавиш void RenderScene(void); // Функция рендеринга void ChangeSize(int, int); // Функция установки отсекающего объема // Глобальная переменная выбранного варианта основного меню int choice = 1; // Глобальные переменные для создания вращения // в градусах GLfloat xRot = 0.0f; GLfloat yRot = 0.0f; GLfloat zRot = 0.0f; GLint w, h; // Ширина и высота экрана //********************************************************** // Подключение файлов с упражнениями #include "ColorCube.h" // Упражнение 1: "1) Куб цвета" #include "Jet.h" // Упражнение 2: "2) Самолет без освещения" #include "LightSphere.h" // Упражнение 3: "3) Рисование освещенной сферы" #include "MultiMaterial.h" // Упражнение 4: "4) Рисование с разными материалами" //********************************************************** // Функция обратного вызова обработки выбора пользователя void ExecuteMenu(int choice) { // Сбрасываем углы вращения прежнего варианта xRot = yRot = zRot = 0; // Выключаем освещение glDisable(GL_LIGHTING); // Запоминаем выбор в глобальную переменную ::choice = choice; switch(::choice) { case 1: ChangeSizeColorCube(w, h); break; case 2: ChangeSizeJet(w, h); break; case 3: ChangeSizeLightSphere(w, h); break; case 4: ChangeSizeMultiMaterial(w, h); break; } // Вызвать принудительно визуализацию glutPostRedisplay(); } //********************************************************** // Функция обратного вызова для рисования сцены void RenderScene(void) { // Сохранить прежние настройки OpenGL в стеке атрибутов glPushAttrib(GL_LIGHTING_BIT); switch(::choice) { case 1: RenderSceneColorCube(); break; case 2: RenderSceneJet(); break; case 3: RenderSceneLightSphere(); break; case 4: RenderSceneMultiMaterial(); break; } // Восстановить прежние настройки OpenGL из стека атрибутов glPopAttrib(); } //********************************************************** // Вызывается библиотекой GLUT при изменении размеров окна void ChangeSize(int width, int height) { w = width; h = height; switch(::choice) { case 1: ChangeSizeColorCube(width, height); break; case 2: ChangeSizeJet(width, height); break; case 3: ChangeSizeLightSphere(width, height); break; case 4: ChangeSizeMultiMaterial(width, height); break; } } //********************************************************** // Обработчик события таймера void TimerFunc(int value) { glutPostRedisplay(); // Перерисовка сцены glutTimerFunc(30, TimerFunc, 1); // Заряжаем новый таймер } //********************************************************** // Управление с клавиатуры стрелками // для задания новых значений матрицы поворота void SpecialKeys(int key, int x, int y) { if(key == GLUT_KEY_UP) // Стрелка вверх xRot -= 5.0f; if(key == GLUT_KEY_DOWN)// Стрелка вниз xRot += 5.0f; if(key == GLUT_KEY_LEFT)// Стрелка влево yRot -= 5.0f; if(key == GLUT_KEY_RIGHT)// Стрелка вправо yRot += 5.0f; xRot = (GLfloat)((const int)xRot % 360); yRot = (GLfloat)((const int)yRot % 360); // Вызвать принудительно визуализацию с помощью RenderScene() glutPostRedisplay(); } //********************************************************** void main(int argc, char* argv[]) { glutInit(&argc, argv); // Двойная буферизация, цветовая модель RGB, буфер глубины glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(300, 300); // Начальные размеры окна glutCreateWindow("Свет и материалы"); // Заголовок окна glutDisplayFunc(RenderScene); // Обновление сцены при разрушении окна glutReshapeFunc(ChangeSize); // При изменении размера окна glutTimerFunc(100, TimerFunc, 1); // Создали таймер glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры // Создание меню и добавление опций выбора glutCreateMenu(ExecuteMenu); glutAddMenuEntry("1) Куб цвета", 1); glutAddMenuEntry("2) Самолет без освещения", 2); glutAddMenuEntry("3) Рисование освещенной сферы", 3); glutAddMenuEntry("4) Рисование с разными материалами", 4); glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем // Конец создания меню glutMainLoop(); // Цикл сообщений графического окна }Листинг 23.13. Изменения в коде файла LightAndMaterial.cpp
- Запустите приложение и получите такой результат
- Попытайтесь разобраться с кодом!!!