
2633
.pdf
Первая точка должна располагаться справа вверху, вторая точка слева вверху, третья точка слева внизу, и последняя справа внизу.
В примере куб сдвинут немного вглубь экрана, поэтому размер куба будет казаться меньше размера пирамиды. Если переместить куб на 6 единиц к экрану, то куб будет казаться больше чем пирамида, и часть куба будет за пределами экрана (рис.6).
Рис. 6. – Пример расположения объектов, когда размер куба казжется больше размера пирамиды
Это происходит из-за перспективы. Объекты на расстоянии
кажутся меньше: |
|
glLoadIdentity(); |
|
glTranslatef(1.5f,0.0f,-7.0f); |
// Сдвинуть вправо и вглубь |
экрана |
|
glRotatef(rquad,1.0f,1.0f,1.0f); |
// Вращение куба по X, Y & Z |
glBegin(GL_QUADS); |
// Рисуем куб |
Рисование куба в примере начинается сверху. Следует отметить, что по оси Y всегда единица. Затем рисуется квадрат на Z плоскости. Рисование начинается с правой точки вверху экрана. Правая верхняя точка должна быть на одну единицу справа, и на одну единицу вглубь экрана. Вторая точка будет на одну единицу влево и на единицу вглубь экрана. Здесь производится рисование той части квадрата, которая
ближе к зрителю: |
|
glColor3f(0.0f,1.0f,0.0f); |
// Синий |
glVertex3f( 1.0f, 1.0f,-1.0f); |
// Право верх квадрата (Верх) |
glVertex3f(-1.0f, 1.0f,-1.0f); |
// Лево верх |
glVertex3f(-1.0f, 1.0f, 1.0f); |
// Лево низ |
glVertex3f( 1.0f, 1.0f, 1.0f); |
// Право низ |
63
Нижняя часть квадрата рисуется таким же образом, как и верхняя, но поскольку это низ, требуется сдвинуться вниз на одну единицу от центра куба. Стоит заметить, что ось Y всегда минус единица. Если взглянуть на дно куба, можно заметить, что правый верхний угол – это угол ближний к зрителю. Поэтому вместо того чтобы рисовать дальше от зрителя в начале, необходимо рисовать ближе к зрителю, тогда левая сторона ближе к зрителю. И затем сдвигаться вглубь экрана, для того
чтобы нарисовать дальние две точки: |
|
glColor3f(1.0f,0.5f,0.0f); |
// Оранжевый |
glVertex3f( 1.0f,-1.0f, 1.0f); |
// Верх право квадрата (Низ |
glVertex3f(-1.0f,-1.0f, 1.0f); |
// Верх лево |
glVertex3f(-1.0f,-1.0f,-1.0f); |
// Низ лево |
glVertex3f( 1.0f,-1.0f,-1.0f); |
// Низ право |
Теперь следует нарисовать передний квадрат. Для этого необходимо сдвинуться на одну единицу ближе к экрану, и дальше от центра для того чтобы нарисовать переднею грань. Заметим, что ось Z всегда равна единице. В гранях пирамиды ось Z не всегда единица.
Вверху, ось Z равна нулю: |
|
glColor3f(1.0f,0.0f,0.0f); |
// Красный |
glVertex3f( 1.0f, 1.0f, 1.0f); |
// Верх право квадрата (Перед) |
glVertex3f(-1.0f, 1.0f, 1.0f); |
// Верх лево |
glVertex3f(-1.0f,-1.0f, 1.0f); |
// Низ лево |
glVertex3f( 1.0f,-1.0f, 1.0f); |
// Низ право |
Задняя грань квадрата такая же, как передняя грань, но сдвинута |
|
вглубь экрана: |
|
glColor3f(1.0f,1.0f,0.0f); |
//Желтый |
glVertex3f( 1.0f,-1.0f,-1.0f); |
// Верх право квадрата (Зад) |
glVertex3f(-1.0f,-1.0f,-1.0f); |
// Верх лево |
glVertex3f(-1.0f, 1.0f,-1.0f); |
// Низ лево |
glVertex3f( 1.0f, 1.0f,-1.0f); |
// Низ право |
Отметим, что ось Z всегда минус один во всех точках. |
|
На данном этапе осталось нарисовать только два квадрата: |
|
glColor3f(0.0f,0.0f,1.0f); |
// Синий |
glVertex3f(-1.0f, 1.0f, 1.0f); |
// Верх право квадрата (Лево) |
glVertex3f(-1.0f, 1.0f,-1.0f); |
// Верх лево |
glVertex3f(-1.0f,-1.0f,-1.0f); |
// Низ лево |
glVertex3f(-1.0f,-1.0f, 1.0f); |
// Низ право |
И последняя грань завершит куб. Для нее ось X всегда равна |
|
единице. Рисовать следует против часовой стрелки: |
|
glColor3f(1.0f,0.0f,1.0f); |
// Фиолетовый |
glVertex3f(1.0f, 1.0f,-1.0f); |
// Верх право квадрата (Право) |
glVertex3f( 1.0f, 1.0f, 1.0f); |
// Верх лево |
64

glVertex3f( 1.0f,-1.0f, 1.0f); |
// Низ лево |
glVertex3f( 1.0f,-1.0f,-1.0f); |
// Низ право |
glEnd(); |
// Конец рисование квадратов |
rtri+=0.2f; // Увеличение переменную вращения для треугольника
rquad-=0.15f;// Уменьшение переменную вращения для квадрата
}
Ререзультат работы программы представлен на рис. 7,8.
Рис. 7. Вращение пирамиды и куба
Рис. 8. Вращение куба
65
Контрольные вопросы
1.Какова будет реакция OpenGL, если в рисовании треугольника создать более трех точек?
2.Почему при рисовании куба и пирамиды одинаковых размеров в данной лабораторной работе куб выглядит меньше при демонстрации результата?
3.Почему в данной лабораторной работе не было необходимости выводить дно нарисованной пирамиды?
4.Какой командой реализуется вращение пирамиды по оси Y?
2.7.Наложение текстур на объекты
Вкачестве объекта для наложения текстуры выберем куб. Далее следует добавить новые строки в код самого первого проекта инициализации окна.
Первые три строки задают четыре вещественных переменных - xrot, yrot и zrot. Эти переменные будут использованы для вращения куба по осям x, y, z. В четвертой строке резервируется место для одной текстуры. Если планируется загружать более чем одну текстуру, изменяется число один на число текстур, которые загружаются:
#include <windows.h> // Заголовочный файл для Windows
#include <gl\gl.h> |
// |
Заголовочный |
файл |
для |
OpenGL32 |
|
библиотеки |
|
|
|
|
|
|
#include <gl\glu.h> |
//Заголовочный |
файл |
для |
GLu32 |
||
библиотеки |
|
|
|
|
|
|
#include <gl\glaux.h> |
//Заголовочный |
файл |
для |
GLaux |
||
библиотеки |
|
|
|
|
|
|
static HGLRC hRC; |
// Постоянный контекст рендеринга |
|||||
static HDC hDC |
// Приватный контекст устройства GDI |
|||||
BOOL |
keys[256]; |
// |
Массив для |
процедуры |
обработки |
|
клавиатуры |
|
|
|
|
|
|
GLfloat |
xrot; |
// Вращение X |
|
|
|
|
GLfloat |
yrot; |
// Вращение Y |
|
|
|
|
GLfloat |
zrot; |
// Вращение Z |
|
|
|
|
GLuint |
texture[1];// Место для одной текстуры |
|
|
Сразу после этого кода, до InitGL, нужно добавить следующую часть кода. Этот код загружает файл картинки, и конвертирует его в текстуру. Такое изображение должно иметь высоту и ширину кратной двум. При этом высота и ширина изображения должна быть не меньше чем 64 пикселя, и по причинам совместимости, не более 256 пикселов.
66
AUX_RGBImageRec *texture1 задает указатель на структуру для хранения первой картинки, которая загружается используется как текстура.
//Загрузка картинки и конвертирование в текстуру
GLvoid LoadGLTextures()
{
//Загрузка картинки
AUX_RGBImageRec *texture1;
texture1 = auxDIBImageLoad("Data/….bmp");
Структура содержит красную, зеленую и синию компоненты цвета, которые используются при создании изображения. Так размещается в памяти загруженная картинка. Структура AUX_RGBImageRec определена в библиотеке glAux, и делает возможной загрузку картинки в память. В следующей строке происходит непосредственная загрузка. Файл картинки будет загружен и сохранен в структуре texture1, которую задали выше с помощью
AUX_RGBImageRec.
Вызов glGenTextures(1, &texture[0]) передает OpenGL то, что построение текстуры будет в нулевом элементе массива texture[] (Первая действительная область для сохранения имеет номер 0).
Во второй строке вызов glBindTexture(GL_TEXTURE_2D, texture[0]) говорит OpenGL, что texture[0] (первая текстура) будет 2D
текстурой. 2D текстуры имеют и высоту (по оси Y) и ширину (по оси X). Основная задача glGenTexture указать OpenGL на доступную память. Память доступна в &texture[0]. Затем создается текстура, и она будет сохранена в этой памяти:
// Создание текстуры glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
В следующих двух строках указывается какой тип фильтрации надо использовать, когда изображение больше на экране, чем оригинальная текстура (GL_TEXTURE_MAG_FILTER), или когда оно меньше на экране, чем текстура (GL_TEXTURE_MIN_FILTER). для обоих случаев используется GL_LINEAR:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
При этом текстура выглядит сглаженной на расстоянии, и вблизи. Использование GL_LINEAR требует много работы для
процессора/видеокарты.
67
Взавершении создается фактическая текстура. Текстура будет двухмерной (GL_TEXTURE_2D). Ноль задает уровень детализации, это обычно ноль. Три – число компонент цветовых данных, так как изображение сделано из трех цветовых компонент (красный, зеленый, синий). texture1->sizeX – это ширина текстуры, автоматически. texture1- >sizeY – высота текстуры. Ноль – это бордюр. GL_RGB означает, что данные изображения представлены в порядке следования красных, зеленых и голубых компонент цвета. GL_UNSIGNED_BYTE означает, что данные, из которых состоит изображение имеют размер байта и все числа без знака, и в конце texture1->data указывает на то, где брать сами данные. В этом случае указатель на данные в записи texture1:
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1- >sizeY, 0,
GL_RGB, GL_UNSIGNED_BYTE, texture1->data);
}
Впервой строке происходит вызов процедуры LoadGLTextures(), которая загружает изображение и делает из него текстуру.
Вторая строка glEnable(GL_TEXTURE_2D) разрешает наложение текстуры:
GLvoid InitGL(GLsizei Width, GLsizei Height)
{
LoadGLTextures(); |
// Загрузка текстур |
glEnable(GL_TEXTURE_2D); |
// Разрешение наложение |
текстуры |
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
|
glClearDepth(1.0); |
|
glDepthFunc(GL_LESS); |
|
glEnable(GL_DEPTH_TEST); |
|
glShadeModel(GL_SMOOTH); |
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW);
}
Далее первые две строки glClear() и glLoadIdentity() взяты из оригинального кода первого проекта:
GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();
glTranslatef(0.0f,0.0f,-5.0f);
68
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
очищает экран цветом, который выбран в InitGL(). В этом случае, экран будет очищен в синий цвет. Буфер глубины будет также очищен. Просмотр будет сброшен с помощью glLoadIdentity().
Следующие три строки кода вращают куб по оси X, затем по оси
Y, и в конце по оси Z: |
|
glRotatef(xrot,1.0f,0.0f,0.0f); |
// Вращение по оси X |
glRotatef(yrot,0.0f,1.0f,0.0f); |
// Вращение по оси Y |
glRotatef(zrot,0.0f,0.0f,1.0f); |
// Вращение по оси Z |
Насколько велико будет вращение (угол) по каждой оси будет зависеть от значения указанного в xrot, yrot и zrot.
В следующей строке кода происходит выбор – какую текстуру использовать для наложения текстуры:
glBindTexture(GL_TEXTURE_2D, texture[0]);
Первый аргумент glTexCoord2f – координата X. 0.0f – левая сторона текстуры. 0.5f – середина текстуры, и 1.0f – правая сторона текстуры, второе значение glTexCoord2f – это Y координата. 0.0f – низ текстуры. 0.5f – середина текстуры, и 1.0f – верх текстуры:
glBegin(GL_QUADS);
// Передняя грань glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Низ лево
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Низ право
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Верх право
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Верх лево
// Задняя грань
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Низ право
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Верх право
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Верх лево
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Низ лево
// Верхняя грань glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Верх лево
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Низ лево
69
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Низ право
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Верх право
// Нижняя грань glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Верх право
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Верх лево
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Низ лево
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Низ право
// Правая грань glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Низ право
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Верх право
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Верх лево
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Низ лево
// Левая грань glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
// Низ лево |
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, |
1.0f); |
||
// Низ право |
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, |
1.0f, |
1.0f); |
|
// Верх право |
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, |
1.0f, -1.0f); |
||
// Верх лево |
|
|
|
glEnd(); |
|
|
|
Насколько велико будет вращение (угол) по каждой оси будет |
|||
зависеть от значения указанного в xrot, yrot и zrot. |
|
||
Далее увеличиваем значений xrot, yrot и zrot: |
|
||
xrot+=0.3f; |
// Ось вращения X |
|
|
yrot+=0.2f; |
// Ось вращения Y |
|
|
zrot+=0.4f; |
// Ось вращения Z |
|
|
} |
|
|
|
Результат работы программы представлен на рис.9.
70

Рис. 9. Наложение текстуры на куб
Контрольные вопросы
1.Какой код загружает файл картинки, и конвертирует его в
текстуру?
2.Для чего служит AUX_RGBImageRec *texture1?
3.задает указатель на структуру для хранения первой картинки, которая загружается используется как текстура. Почему изображение должно иметь высоту и ширину кратную двум?
4.Для чего служит процедура LoadGLTextures()?
5.Какая функция разрешает наложение текстуры.
6.Зачем необходим вызов glGenTextures(1, &texture[0])?
7.Где именно в коде происходит выбор текстуры для наложения?
8.Как изменить направление вращения куба?
9.Как уменьшить скорость вращения фигуры?
2.8.Перемещения объектов с помощью клавиатуры. Режимы фильтрации текстур. Освещение
Далее рассмотрим различные режимы фильтрации текстур, применение простого освещения OpenGL сцены с основами перемещения объектов с помощью клавиатуры.
Основой программы является код первого проекта. Для начала необходимо добавить несколько новых переменных к коду программы:
71
#include <windows.h> // Заголовочный файл для Windows
#include <stdio.h> |
// Заголовочный файл для стандартного |
ввода/вывода |
|
#include<gl\gl.h> |
// Заголовочный файл для библиотеки |
OpenGL32 |
|
#include<gl\glu.h> |
// Заголовочный файл для для библиотеки |
GLu32 |
|
#include <gl\glaux.h> |
// Заголовочный файл для библиотеки GLaux |
HDC hDC=NULL; |
// Служебный контекст GDI устройства |
HGLRC hRC=NULL; // Постоянный контекст для визуализации HWND hWnd=NULL;// Содержит дискриптор для окна
HINSTANCE hInstance; |
// Содержит данные для программы |
bool keys[256]; |
// Массив, использующийся для |
сохранения состояния клавиатуры
bool active=TRUE; // Флаг состояния активности приложения
(по умолчанию: TRUE) |
|
|
bool fullscreen=TRUE; |
// Флаг полноэкранного режима (по |
|
умолчанию: полноэкранное) |
|
|
Далее необходимо добавить три логические переменные: |
||
BOOL light; |
// Свет ВКЛ / ВЫКЛ |
|
BOOL lp; |
// L нажата? |
|
BOOL fp; |
// F нажата? |
|
Здесь следует |
создать |
переменную называемую light, чтобы |
отслеживать, действительно ли освещение включено или выключено. Переменные lp и fpь используются, для отслеживания нажатия клавиш
'L' и 'F'.
На данном этапе необходимо добавить пять переменных, которые будут управлять следующими параметрами: углом по оси X(xrot), углом по оси Y(yrot), скоростью вращения ящика по оси X(xspeed), и скоростью вращения куба по оси Y(yspeed). Также следует создать переменную z, которая будет управлять, погружением куба в экран (по
оси Z): |
|
GLfloat xrot; |
// X вращение |
GLfloat yrot; |
// Y вращение |
GLfloat xspeed; |
// X скорость вращения |
GLfloat yspeed; |
// Y скорость вращения |
GLfloat z=-5.0f; |
// Сдвиг вглубь экрана |
Далее задаются |
массивы, которые будут использоваться для |
освещения. Следует учитывать, что фоновый свет не имеет никакого определенного направления. Все объекты будут освещены фоновым светом. Второй тип света – диффузный свет. Диффузный свет создается при помощи вашего источника света и отражается от поверхности
72