| Китай |
Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 22:
Свет и материалы в OpenGL
< Самостоятельная работа 21 || Самостоятельная работа 22: 123456789101112 || Самостоятельная работа 23 >
Модель самолета с равномерным освещением (JetLight)
Вернемся к нормалям. Нормали как направленные отрезки (векторы), перпендикулярные к многоугольникам, сообщают OpenGL, в каком направлении располагается лицевая сторона вершины и как правильно вычислять отраженный от поверхности цвет. Единичный вектор нормали гарантирует, что освещение объекта будет вычисляться правильно.
-
Заполните
файл следующим кодом
// Упражнение 7: "7) Самолет с равномерным освещением"
#include <math.h>
//**********************************************************
// Прототипы
void JetLight();
void SetLightJetLight();
void RenderSceneJetLight(void);
void ChangeSizeJetLight(int, int); // При изменении размеров окна
//**********************************************************
// Вспомогательный код
typedef GLfloat GLTVector3[3]; // Новый тип - вектор
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal);
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult);
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult);
void gltNormalizeVector(GLTVector3 vNormal);
GLfloat gltGetVectorLength(const GLTVector3 vVector);
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector);
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale);
//**********************************************************
// Рисовать самолет
void JetLight()
{
GLTVector3 vNormal; // Для вектора нормали
// Очищаем буфер цвета и глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Сохранить матрицу преобразования и выполнить
// последовательность поворотов
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Устанавливаем цвет материала серый для всех граней самолета
glColor3ub(128, 128, 128);
// Рисуем самолет
glBegin(GL_TRIANGLES);
// Конус носа /////////////////////////////
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f,0.0f,30.0f);
{// Блок кода
GLTVector3 vPoints[3] = {{ 15.0f, 0.0f, 30.0f},
{ 0.0f, 15.0f, 30.0f},
{ 0.0f, 0.0f, 60.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{// Блок кода
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, 60.0f },
{ 0.0f, 15.0f, 30.0f },
{ -15.0f, 0.0f, 30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Тело самолета //////////////////////////
{
GLTVector3 vPoints[3] = {{ -15.0f, 0.0f, 30.0f },
{ 0.0f, 15.0f, 30.0f },
{ 0.0f, 0.0f, -56.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 0.0f, -56.0f },
{ 0.0f, 15.0f, 30.0f },
{ 15.0f,0.0f,30.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(15.0f,0.0f,30.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);
///////////////////////////////////////////
// Левое крыло
// Большой треугольник для основания крыла
{
GLTVector3 vPoints[3] = {{ 0.0f,2.0f,27.0f },
{ -60.0f, 2.0f, -8.0f },
{ 60.0f, 2.0f, -8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 60.0f, 2.0f, -8.0f},
{0.0f, 7.0f, -8.0f},
{0.0f,2.0f,27.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{60.0f, 2.0f, -8.0f},
{-60.0f, 2.0f, -8.0f},
{0.0f,7.0f,-8.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Другое крыло верхней секции
{
GLTVector3 vPoints[3] = {{0.0f,2.0f,27.0f},
{0.0f, 7.0f, -8.0f},
{-60.0f, 2.0f, -8.0f}};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
///////////////////////////////////////////
// Хвост
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(-30.0f, -0.50f, -57.0f);
glVertex3f(30.0f, -0.50f, -57.0f);
glVertex3f(0.0f,-0.50f,-40.0f);
// Верхняя левая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f,-0.5f,-40.0f },
{30.0f, -0.5f, -57.0f},
{0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Верхняя правая сторона
{
GLTVector3 vPoints[3] = {{ 0.0f, 4.0f, -57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f,-0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
// Оборотная нижняя часть
{
GLTVector3 vPoints[3] = {{ 30.0f,-0.5f,-57.0f },
{ -30.0f, -0.5f, -57.0f },
{ 0.0f, 4.0f, -57.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f,0.5f,-40.0f },
{ 3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 0.0f, 25.0f, -65.0f },
{ -3.0f, 0.5f, -57.0f},
{ 0.0f,0.5f,-40.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
{
GLTVector3 vPoints[3] = {{ 3.0f,0.5f,-57.0f },
{ -3.0f, 0.5f, -57.0f },
{ 0.0f, 25.0f, -65.0f }};
gltGetNormalVector(vPoints[0], vPoints[1], vPoints[2], vNormal);
glNormal3fv(vNormal);
glVertex3fv(vPoints[0]);
glVertex3fv(vPoints[1]);
glVertex3fv(vPoints[2]);
}
glEnd(); // О самолете
// Восстановить матрицу
glPopMatrix();
// Переключить буфер
glutSwapBuffers();
}
//**********************************************************
// Задать параметры освещения и согласования цветов материала
void SetLightJetLight()
{
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };// Фоновый слабобелый
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };// Рассеяный среднебелый
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};// Расположение источника
glShadeModel(GL_SMOOTH);
GLfloat diffuseMaterial[] = {0.5, 0.5, 0.5, 1.0};
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);// Начальное значение диффузного материала
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// Режим согласования цветов назначаем для фонового и рассеянного
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Включаем поддержку согласования цветов
glEnable(GL_COLOR_MATERIAL);
}
//**********************************************************
// Функция обратного вызова для рисования сцены
void RenderSceneJetLight(void)
{
// Сбрасываем буфер цвета и буфер глубины
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Рисуем сцену
JetLight();
// Прокачка сообщений
glFlush();
}
//**********************************************************
void ChangeSizeJetLight(int width, int height)
{
// Устанавливаем индивидуальные настройки освещения
SetLightJetLight();
glEnable(GL_DEPTH_TEST); // Включить тест глубины
glEnable(GL_CULL_FACE); // Отображать только лицевую сторону
glFrontFace(GL_CCW); // Считать лицевым обход против часовой стрелки
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);// Цвет фона окна
// Предотвращаем деление на нуль
if(height == 0)
height = 1;
// Устанавливаем поле просмотра с размерами окна
glViewport(0, 0, width, height);
// Устанавливает матрицу преобразования в режим проецирования
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Устанавливаем размеры отсекающего объема перспективы
GLfloat aspectRatio = (GLfloat)width / (GLfloat)height; // Для соблюдения пропорций
gluPerspective(45.0f, aspectRatio, 1.0f, 250.0f); // Отсекающая перспектива
// Восстановливает матрицу преобразования в исходный режим вида
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Отодвинем сцену в отрицательную сторону оси 0z
glTranslatef(0.0f, 0.0f, -180.0f);
}
//**********************************************************
// Вспомогательный код
//**********************************************************
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal)
{
GLTVector3 vV1, vV2;
gltSubtractVectors(vP2, vP1, vV1);
gltSubtractVectors(vP3, vP1, vV2);
gltVectorCrossProduct(vV1, vV2, vNormal);
gltNormalizeVector(vNormal);
}
//**********************************************************
void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult)
{
vResult[0] = vFirst[0] - vSecond[0];
vResult[1] = vFirst[1] - vSecond[1];
vResult[2] = vFirst[2] - vSecond[2];
}
//**********************************************************
void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult)
{
vResult[0] = vU[1]*vV[2] - vV[1]*vU[2];
vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2];
vResult[2] = vU[0]*vV[1] - vV[0]*vU[1];
}
//**********************************************************
void gltNormalizeVector(GLTVector3 vNormal)
{
GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
gltScaleVector(vNormal, fLength);
}
//**********************************************************
GLfloat gltGetVectorLength(const GLTVector3 vVector)
{
return (GLfloat)sqrt(gltGetVectorLengthSqrd(vVector));
}
//**********************************************************
GLfloat gltGetVectorLengthSqrd(const GLTVector3 vVector)
{
return (vVector[0]*vVector[0]) + (vVector[1]*vVector[1]) + (vVector[2]*vVector[2]);
}
//**********************************************************
void gltScaleVector(GLTVector3 vVector, const GLfloat fScale)
{
vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale;
}
Листинг
23.24.
Код файла JetLight.h
-
Добавьте
в файл 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) Рисование с разными материалами"
#include "ColorSphere.h" // Упражнение 5: "5) Способ согласования цветов"
#include "JetNight.h" // Упражнение 6: "6) Самолет с нулевым освещением"
#include "JetLight.h" // Упражнение 7: "7) Самолет с равномерным освещением"
//**********************************************************
// Функция обратного вызова обработки выбора пользователя
void ExecuteMenu(int choice)
{
// Сбрасываем углы вращения прежнего варианта
xRot = yRot = zRot = 0;
// Выключаем освещение
glDisable(GL_LIGHTING);
// Выключаем режим согласования цветов
glDisable(GL_COLOR_MATERIAL);
// Запоминаем выбор в глобальную переменную
::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;
case 5:
ChangeSizeColorSphere(w, h);
break;
case 6:
ChangeSizeJetNight(w, h);
break;
case 7:
ChangeSizeJetLight(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;
case 5:
RenderSceneColorSphere();
break;
case 6:
RenderSceneJetNight();
break;
case 7:
RenderSceneJetLight();
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;
case 5:
ChangeSizeColorSphere(width, height);
break;
case 6:
ChangeSizeJetNight(width, height);
break;
case 7:
ChangeSizeJetLight(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);
// Для упражнения 5 смены материала
if(key == GLUT_KEY_F1) // Меняем красный
{
diffuseMaterial[0] += 0.1;
*diffuseMaterial = *diffuseMaterial > 1.0 ? 0.0 : *diffuseMaterial;
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F2) // Меняем зеленый
{
diffuseMaterial[1] += 0.1;
*(diffuseMaterial + 1) = diffuseMaterial[1] > 1.0 ? 0.0 : diffuseMaterial[1];
glColor4fv(diffuseMaterial);
}
if(key == GLUT_KEY_F3) // Меняем синий
{
diffuseMaterial[2] += 0.1;
*(diffuseMaterial + 2) = diffuseMaterial[2] > 1.0 ? 0.0 : diffuseMaterial[2];
glColor4fv(diffuseMaterial);
}
// Вызвать принудительно визуализацию с помощью 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);
glutAddMenuEntry("5) Способ согласования цветов (F1, F2, F3)", 5);
glutAddMenuEntry("6) Самолет с нулевым освещением", 6);
glutAddMenuEntry("7) Самолет с равномерным освещением", 7);
glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем
// Конец создания меню
glutMainLoop(); // Цикл сообщений графического окна
}
Листинг
23.25.
Код подключения модели самолета с равномерным освещением в файле LightAndMaterial.cpp
-
Запустите
приложение и выполните упражнение 7, результат должен быть
примерно таким
Если поуправлять поворотами модели с помощью клавиш-стрелок, то будет меняться цвет граней самолета по мере того, как их векторы нормалей будут менять свой угол по отношению к источнику света и точке наблюдения. OpenGL "на лету" будет эти цвета пересчитывать.
< Самостоятельная работа 21 || Самостоятельная работа 22: 123456789101112 || Самостоятельная работа 23 >


