Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2633

.pdf
Скачиваний:
6
Добавлен:
07.01.2021
Размер:
54.26 Mб
Скачать

объекта в сцене. Любая поверхность объекта, на которую падает прямо свет, будет очень яркой и области, куда свет падает под углом, будут темнее.

Свет создается так же как цвет. Поэтому в строке ниже, задается значение белого фонового света половиной интенсивности (0.5f):

GLfloat LightAmbient[]={0.5f, 0.5f, 0.5f, 1.0f};

// Значения

фонового света

 

Поскольку все числа – 0.5f, получится свет средней яркости между черным (выключен свет) и белым (полная яркость). Смешанные в равных значениях красный, синий и зеленый дадут оттенки от черного (0.0f) до белого (1.0f). Без фонового света пятна, где нет диффузного света, будут очень темными.

Все значения – 1.0f. Это означает, что свет настолько яркий, насколько возможно получить его. Диффузный свет эти яркое пятно перед кубом.

В следующей строке задается значение для яркого, полного

интенсивного диффузного света:

 

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f,

1.0f };

// Значения диффузного света

 

Наконец задается позиция света:

 

GLfloat LightPosition[]={0.0f,0.0f,2.0f,1.0f};

// Позиция света

Первые три числа совпадают с тремя

первыми аргументами

функции glTranslate. Первое число смещение влево или вправо по оси x, второе число – для перемещения вверх или вниз по оси y, и третье число для перемещения к или от экрана по оси z. Поскольку в данном примере необходимо, чтобы свет, падал прямо на переднею часть куба, не стоит сдвигаться влево или вправо, поэтому первое значение – 0.0f (нет движения пооси x), а также не стоит сдвигаться и вверх или вниз, поэтому второе значение – 0.0f. Третье значение задается так, чтобы свет был всегда перед кубом. Поэтому свет помещается вне экрана по отношению к наблюдателю.

Последнее число необходимо оставить в 1.0f. Это говорит о том, что данные координаты – позиция источника света.

Переменная filter должна отслеживать, каким образом будут отображаться текстуры. Первая текстура (texture[0]) использует gl_nearest (без сглаживания). Вторая текстура (texture[1]) использует фильтрацию gl_linear, которая немного сглаживает изображение. Третья текстура (texture[2]) использует текстуры с мип-наложением (mipmapped, или множественное наложение), что повышает качество отображения. Переменная filter будет равна 0, 1 или 2 в зависимости от текстуры.

73

Объявление GLuint texture[3] создает место для хранения трех разных текстур. Текстуры будут сохранены в texture[0], texture[1] и

texture[2]:

 

GLuint filter;

// Какой фильтр использовать

GLuint texture[3];

// Место для хранения 3 текстур

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); // Декларация WndProc

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

Сразу же после кода выше, и до ReSizeGLScene(), необходимо добавить следующую часть кода:

AUX_RGBImageRec *LoadBMP(char *Filename) // Загрузка

картинки

 

{

 

FILE *File=NULL;

// Индекс файла

if (!Filename)

// Проверка имени файла

{

 

Return NULL;

// Если нет вернем NULL

}

 

File=fopen(Filename,"r");

Далее проверим, существует ли файл:

If (File)

// Файл существует?

{

 

fclose(File);

// Закрыть файл

return auxDIBImageLoad(Filename); // Загрузка картинки и

вернем на нее указатель

 

}

 

return NULL;

// Если загрузка не удалась вернем NULL

}

 

В этой части кода загружается картинка (вызов кода выше) и производится конвертирование ее в 3 текстуры. Переменная Status используется, чтобы следить, действительно ли текстура была

загружена и создана.

 

 

int LoadGLTextures()

// Загрузка картинки и конвертирование в

текстуру

 

 

{

 

 

int Status=FALSE;

// Индикатор состояния

AUX_RGBImageRec *TextureImage[1];

// Создать место для

текстуры

 

 

74

memset(TextureImage,0,sizeof(void *)*1); // Установить указатель в NULL

На данном этапе происходит загрузка картинки и конвертирование ее в текстуру.

Выражение TextureImage[0]=LoadBMP ("Data/Crate.bmp") будет вызывать код LoadBMP(). Файл по имени Crate.bmp в каталоге Data будет загружен. Если все пройдет хорошо, данные изображения сохранены в TextureImage[0], переменная Status установлена в TRUE, и начинается строить нашу текстуру.

// Загрузка картинки, проверка на ошибки, если картинка не найдена - выход

if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))

{

Status=TRUE; // Установим Status в TRUE

Теперь, данные изображения загружены в TextureImage [0], и эти данные используются для построения 3 текстур:

glGenTextures(3, &texture[0]); // Создание трех текстур

Строка сообщает, что необходимо построить три текстуры, и требуется, чтобы текстура была сохранена в texture[0], texture[1] и texture[2].

В предыдущем проекте, была использована линейная фильтрация образов текстур. Это способ фильтрации требует много мощности процессора, но текстуры при этом выглядят реалистичными. Первый тип текстуры, которую необходимо создать в этом уроке, использует GL_NEAREST. Этот тип текстуры не использует фильтрацию. Единственное применение этого типа текстуры для проектов, которые будут запускаться на медленных компьютерах.

Используем GL_NEAREST, и для MIN и для MAG, поэтому появляется возможность смешивать использование GL_NEAREST с GL_LINEAR, и текстура будет смотреться немного лучше. Фильтр MIN_FILTER используется, когда изображение рисуемого полигона меньше, чем первоначальный размер текстуры. Фильтр MAG_FILTER используется, когда изображение рисуемого полигона больше, чем первоначальный размер текстуры:

// Создание текстуры с фильтром по соседним пикселям glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,

GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY,

75

0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

Следующая текстура, которую необходимо построить, имеет тот же самый тип текстуры, которую был использован прежде – линейный режим фильтрации. Изменилось только то, что сохранение этой текстуры происходит в texture[1] вместо texture[0], потому что здесь она вторая текстура:

// Создание текстуры с линейной фильтрацией glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,

GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,

GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY,

0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

Далее рассмотрим новый способ создания текстуры – мипналожнние. Когда изображение на экране очень маленькое, пропадает множество мелких деталей. Орнаменты, которые выглядят вначале отлично, далее смотрятся плохо. При использовании мип-наложения, OpenGL будет строить разного размера высококачественные текстуры. Когда выводится текстура с мип-наложением на экран, OpenGL будет выбирать наиболее лучшую для отображения текстуру из построенных текстур (текстура с таким размером, как размер полигона на экране, т.е. наиболее детальная) и нарисует ее на экран вместо масштабирования первоначального изображения (что и вызывает потерю качества).

Поскольку это – текстура номер три, храниться эта текстура будет

вtexture[2]:

//Создание текстуры с Мип-Наложением

glBindTexture(GL_TEXTURE_2D, texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,

GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,

GL_LINEAR_MIPMAP_NEAREST);

Поэтому, на данном этапе обработаны 3 текстуры: texture[0], которая не имеет никакой фильтрации, texture[1], которая использует линейную фильтрацию, и texture[2], которая использует текстуру с мипналожением. На этом построение текстур заканчивается.

Следующая строка строит текстуру с мип-наложением: gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX,

TextureImage[0]->sizeY,

GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

}

76

Создется 2D-текстура, с использованием трех цветов. TextureImage[0]->sizeX – ширина картинки, TextureImage[0] -> sizeY –

высота картинки, GL_RGB означает, используеются цвета в порядке Красный, Зеленый, Синий (Red, Green, Blue). GL_UNSIGNED_BYTE

означает что данные, из которых состоит текстура из байтов, и TextureImage[0]->data – указатель на растр картинки, из которого строится текстура.

Далее можно освободить память, которая использовалась для картинок. Здесь происходит проверка, была ли картинка сохранена в

TextureImage[0]:

 

 

 

 

If (TextureImage[0])

// Если текстура существует

 

{

 

 

 

 

If (TextureImage[0]->data);

//

Если

изображение

текстуры

существует

 

 

 

 

{

 

 

 

 

free(TextureImage[0]->data);

//

Освобождение

памяти

изображения текстуры

 

 

 

 

}

 

 

 

 

free(TextureImage[0]);

// Освобождение памяти под структуру

}

 

 

 

 

Если картинка была сохранена, то проверяется, были ли данные сохранены. Если данные были сохранены, они удаляются, тогда освобождается структура изображения.

Наконец происходит возврат статуса. Если все происходит правильно, переменная Status будет TRUE. Если что-нибудь прошло не

так, как надо, Status будет FALSE:

 

return Status;

// Возврат статуса

}

 

 

 

Теперь происходит загрузка текстуры, и инициализирование

параметров настройки OpenGL:

 

int InitGL(GLvoid)

// Все настройки для OpenGL делаются здесь

{

 

 

 

if (!LoadGLTextures())

// Переход на процедуру загрузки

текстуры

 

 

 

{

 

 

 

return FALSE;

// Если текстура не загружена возвращаем FALSE

}

 

 

 

glEnable(GL_TEXTURE_2D);

// Разрешение наложения

текстуры

 

 

 

glShadeModel(GL_SMOOTH);

// Разрешение сглаженного

закрашивания

 

 

 

glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

// Черный фон

77

glClearDepth(1.0f); // Установка буфера глубины

glEnable(GL_DEPTH_TEST);

// Разрешить тест глубины

glDepthFunc(GL_LEQUAL);

// Тип теста глубины

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) // Улучшенные вычисления перспективы

Впервой строке InitGL загружаются текстуры, используя код выше. После того, как текстуры созданы, разрешатся 2D наложение текстуры с помощью glEnable (GL_TEXTURE_2D). Режим закрашивания (shade) задается как сглаженное закрашивание. Цвет фона задан как черный, разрешается тест глубины, затем разрешаются хорошие перспективные вычисления.

Далее задается освещение. Строка ниже задает интенсивность фонового света, которое light1 будет давать:

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // Установка Фонового Света

Вначале этого проекта была задана интенсивность фонового света

вLightAmbient. Значения, которые находятся в массиве, будут использованы (фоновый свет половиной интенсивности):Затем задается интенсивность диффузного света, который источник света номер один будет давать. Интенсивность диффузного света уже задана в LightDiffuse. Значения, которые находятся в этом массиве, будут использованы (белый свет полной интенсивности):

glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // Установка Диффузного Света Теперь задается позиция источника света:

glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); // Позиция света

Данная позиция помещена в LightPosition. Значения, которые находятся в этом массиве, будут использованы (справа в центре передней грани, 0.0f по x, 0.0f по y, и 2 единицы вперед к наблюдателю {выходит за экран} по оси z):

Наконец, задается источник света номер один:

glEnable(GL_LIGHT1);

// Разрешение источника света номер

один

 

return TRUE;

// Инициализация прошла успешно

}

 

В следующей части кода, рисуется текстура, наложенная на куб:

int DrawGLScene(GLvoid)

// Здесь происходит все рисование

{

 

glClear(GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT);

// Очистка Экрана и Буфера Глубины

glLoadIdentity();

// Сброс Просмотра

78

Вданном проекте запрещен GL_LIGHTING, поэтому никакого освещения пользователь не увидит. Свет установлен, и позиционирован,

идаже разрешен, но пока не разрешен GL_LIGHTING, освещение не будет работать:

Вследующих строках кода куб с наложенной текстурой позиционируется и вращается:

glTranslatef(0.0f,0.0f,z);

// Перенос В

//Вне экрана по z

 

glRotatef(xrot,1.0f,0.0f,0.0f);

// Вращение по оси X на xrot

glRotatef(yrot,0.0f,1.0f,0.0f);

// Вращение по оси Y по yrot

Вызов glTranslatef (0.0f, 0.0f, z) перемещает куб на значение z по

оси z (от наблюдателя или к наблюдателю).

Вызов glRotatef (xrot, 1.0f, 0.0f, 0.0f) использует переменную xrot,

чтобы вращать куб по оси X. Вызов glRotatef (yrot, 1.0f, 0.0f, 0.0f)

использует переменную yrot, чтобы вращать куб по оси Y:

Следующая строка подобна строке, которая была использована в уроке шесть, но вместо связывания к texture[0], происходит

привязывание texture[filter]:

 

glBindTexture(GL_TEXTURE_2D, texture[filter]); // Выбор

текстуры основываясь на filter

 

glBegin(GL_QUADS);

// Начало рисования

четырехугольников

 

glNormal3f новая функция в данном примере.

Если нажата клавиша

'F', значение в filter увеличится. Если

значение больше чем два, переменная filter сбрасывается в ноль. Когда программа запускается, filter будет установлен в ноль – glBindTexture(GL_TEXTURE_2D, texture[0]). Если клавиша "F" нажата еще раз, переменная filter будет равна единице – glBindTexture(GL_TEXTURE_2D, texture[1]). Используя, переменную filter появляется возможность выбирать любую из трех текстур, созданных ранее.

Нормаль – линия, которая берет начало из середины полигона под 90 углом градусов. Когда в программе используется освещение, обязательно должны быть заданы нормали. Нормаль сообщает OpenGL, в каком направлении у полигона лицевая часть, какое направление верхнее. Если нормали не заданы, то возможно, например следующая ситуация: грань, которая не освещена, будет освещена, неверная сторона полигона будет освещена, и т.д. Нормаль должна указываться вне полигона.

Посмотрев на переднюю грань, можно заметить, что нормаль имеет положительное направление по оси Z. Это означает, что нормаль указывает на наблюдателя. На обратной грани, нормаль указывает от

79

наблюдателя вглубь экрана. Если куб повернут на 180 градусов или по оси X или по оси Y, передняя грань куба будет лицом вглубь экрана, и задняя грань куба будет лицом к наблюдателю. Независимо от того, какую грань видит наблюдатель, нормаль этой грани будет также направлена на наблюдателя. Поскольку свет – близко к наблюдателю, всегда нормаль, указывающая на наблюдателя, также указывает на свет. Если это сделать, то грань будет освещена. Чем больше нормалей указывает на свет, тем более яркая будет грань:

// Передняя грань

 

 

glNormal3f( 0.0f, 0.0f, 1.0f);

// Нормаль указывает на

наблюдателя

 

 

glTexCoord2f(0.0f,0.0f);

 

 

glVertex3f(-1.0f,-1.0f, 1.0f);

 

// Точка 1 (Перед)

glTexCoord2f(1.0f, 0.0f);

 

 

glVertex3f( 1.0f, -1.0f, 1.0f);

 

// Точка 2 (Перед)

glTexCoord2f(1.0f, 1.0f);

 

 

glVertex3f( 1.0f, 1.0f, 1.0f);

// Точка 3 (Перед)

glTexCoord2f(0.0f, 1.0f);

 

 

glVertex3f(-1.0f, 1.0f, 1.0f);

// Точка 4 (Перед)

// Задняя грань

 

 

glNormal3f( 0.0f, 0.0f,-1.0f);

// Нормаль указывает

направление – от наблюдателя

 

 

glTexCoord2f(1.0f, 0.0f);

 

 

glVertex3f(-1.0f, -1.0f, -1.0f);

// Точка 1 (Задняя грань)

glTexCoord2f(1.0f, 1.0f);

 

 

glVertex3f(-1.0f, 1.0f, -1.0f);

 

// Точка 2 (Зад)

glTexCoord2f(0.0f, 1.0f);

 

 

glVertex3f( 1.0f, 1.0f, -1.0f);

 

// Точка 3 (Зад)

glTexCoord2f(0.0f, 0.0f);

 

 

glVertex3f( 1.0f, -1.0f, -1.0f);

// Точка 4 (Зад)

// Верхняя грань

 

 

glNormal3f( 0.0f, 1.0f, 0.0f);

// Нормаль указывает вверх

glTexCoord2f(0.0f, 1.0f);

 

 

glVertex3f(-1.0f, 1.0f, -1.0f);

 

// Точка 1 (Верх)

glTexCoord2f(0.0f, 0.0f);

 

 

glVertex3f(-1.0f, 1.0f, 1.0f);

// Точка 2 (Верх)

glTexCoord2f(1.0f, 0.0f);

 

 

glVertex3f( 1.0f, 1.0f, 1.0f);

// Точка 3 (Верх)

glTexCoord2f(1.0f, 1.0f);

 

 

glVertex3f( 1.0f, 1.0f, -1.0f);

 

// Точка 4 (Верх)

// Нижняя грань

 

 

glNormal3f( 0.0f,-1.0f, 0.0f);

// Нормаль указывает вниз

80

glTexCoord2f(1.0f, 1.0f);

 

glVertex3f(-1.0f, -1.0f, -1.0f);

// Точка 1 (Низ)

glTexCoord2f(0.0f, 1.0f);

 

glVertex3f( 1.0f, -1.0f, -1.0f);

// Точка 2 (Низ)

glTexCoord2f(0.0f, 0.0f);

 

glVertex3f( 1.0f, -1.0f,1.0f);

// Точка 3 (Низ)

glTexCoord2f(1.0f, 0.0f);

 

glVertex3f(-1.0f, -1.0f, 1.0f);

// Точка 4 (Низ)

// Правая грань

 

 

glNormal3f( 1.0f, 0.0f, 0.0f);

// Нормаль указывает вправо

glTexCoord2f(1.0f, 0.0f);

 

glVertex3f( 1.0f, -1.0f, -1.0f);

// Точка 1 (Право)

glTexCoord2f(1.0f, 1.0f);

 

glVertex3f( 1.0f, 1.0f, -1.0f);

// Точка 2 (Право)

glTexCoord2f(0.0f, 1.0f);

 

glVertex3f( 1.0f, 1.0f, 1.0f);

// Точка 3 (Право)

glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Точка 4

(Право)

 

 

// Левая грань

 

 

glNormal3f(-1.0f, 0.0f, 0.0f);

// Нормаль указывает влево

glTexCoord2f(0.0f, 0.0f);

 

glVertex3f(-1.0f, -1.0f, -1.0f);

// Точка 1 (Лево)

glTexCoord2f(1.0f, 0.0f);

 

glVertex3f(-1.0f, -1.0f, 1.0f);

// Точка 2 (Лево)

glTexCoord2f(1.0f, 1.0f);

 

glVertex3f(-1.0f, 1.0f,

1.0f);

// Точка 3 (Лево)

glTexCoord2f(0.0f, 1.0f);

 

glVertex3f(-1.0f, 1.0f, -1.0f);

// Точка 4 (Лево)

glEnd();

// Закончили рисовать четырехугольник

В следующих строках увеличиваются значения xrot и yrot на

значения, сохраненные в xspeed, и yspeed:

xrot+=xspeed;

// Добавить в xspeed значение xrot

yrot+=yspeed;

// Добавить в yspeed значение yrot

return TRUE;

// Выйти

 

}

 

 

Если значение в xspeed или yspeed большое, xrot и yrot увеличивается быстро. Чем быстрее увеличение xrot, или yrot, тем быстрее куб вращается по соответствующей оси.

Теперь следует обратиться к WinMain (). Необходимо добавить код для включения или выключения освещения, вращения куба, смены фильтра и перемещения его ближе или дальше от экрана:

81

SwapBuffers(hDC);

//

Переключение

буферов

(Двойная

буферизация)

 

 

 

 

if (keys['L'] && !lp)

// Клавиша 'L' нажата и не удерживается?

{

 

 

 

 

Ближе к концу WinMain () необходима реализация вызова SwapBuffers (hDC). Сразу после этой строки, необходимо добавить следующий код. Этот код отслеживает нажатие клавиши 'L'. В первой строке проверяется, нажата ли 'L'. Если 'L' нажата, но lp – не ложь, что значит клавиша 'L' уже была нажата, или она удерживается нажатой, то тогда ничего не происходит.

Если lp – ложь то, это означает, что клавиша 'L' не нажата, иначе, если она уже отпущена, lp – истина. Это гарантирует, что клавишу 'L' отпустят прежде, чем этот код выполнится снова. Если не будет проверки удержания клавиши, освещение будет мерцать, постоянно включаясь и выключаясь много раз.

Переменная lp будучи равной истине, сообщает, что 'L' отпущена, происходит включение или выключение освещения. Переменная light может только быть истина или ложь:

lp=TRUE;

// lp присвоили TRUE

light=!light;

// Переключение света TRUE/FALSE

Теперь проверяется, какое значение light получилось в конце:

if(!light)

 

// Если не свет

{

 

 

glDisable(GL_LIGHTING);

// Запрет освещения

}

 

 

Else

 

// В противном случае

{

 

 

glEnable(GL_LIGHTING);

// Разрешить освещение

}

 

 

}

 

 

Первая строка означает: если light равняется ложь. Поэтому, если Вы совместите все строки вместе, то они делают следующее: если light равняется ложь, то надо запретить освещение. Это выключает все освещение. Команда 'else' означает: если light – не ложь. Поэтому, если light не была ложь то, она истинна, поэтому освещение включается.

Следующая строка отслеживает нажатие клавиши 'L':

if(!keys['L'])

// Клавиша 'L' Отжата?

{

 

lp=FALSE;

// Если так, то lp равно FALSE

}

 

Если переменной lp присвоено значение ложь, то это, означает, что клавиша 'L' не нажата.

82

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]