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

RedBook

.pdf
Скачиваний:
20
Добавлен:
11.06.2015
Размер:
7.43 Mб
Скачать

typedef void (APIENTRY * GLMINMAX) GLenum target, GLenum internalFormat, GLboolean sink);

GLMINMAX glMinmax=NULL;

typedef void (APIENTRY * GLGETMINMAX) GLenum target,GLboolean reset, GLenum format,GLenum type, const GLvoid *values);

GLGETMINMAX glGetMinmax=NULL;

void init()

{

//Проверяем, присутствует ли расширение GL_ARB_imaging (imaging subset)

if (glutExtensionSupported("GL_ARB_imaging")==0) exit(1);

glMinmax=(GLMINMAX)wglGetProcAddress("glMinmax");

glGetMinmax=(GLGETMINMAX)wglGetProcAddress("glGetMinmax");

//Загружаем любую непалитровую картинку BMP image=auxDIBImageLoad("tree.bmp"); glPixelStorei(GL_UNPACK_ALIGNMENT,1); glClearColor(0,0,0,0); glMinmax(GL_MINMAX,GL_RGB,GL_FALSE); glEnable(GL_MINMAX);

}

void deinit()

{

//Освобождаем память delete image;

}

void reshape(int w, int h)

{

glViewport(0,0,(GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,540,0,405);

}

void display()

{

GLubyte values[6]; glClear(GL_COLOR_BUFFER_BIT); glRasterPos2i(1,1); glDrawPixels(image->sizeX,image->sizeY,

GL_RGB,GL_UNSIGNED_BYTE,image->data); glGetMinmax(GL_MINMAX,GL_TRUE,GL_RGB,GL_UNSIGNED_BYTE,values); glFlush();

printf("Red : min = %d max = %d\n",values[0],values[3]); printf("Green: min = %d max = %d\n",values[1],values[4]); printf("Blue : min = %d max = %d\n",values[3],values[5]);

}

int main(int argc,char* argv[])

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(540,405);

glutCreateWindow("Computing Minimum and Maximum Pixel Values"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop();

deinit(); return 0;

}

Несмотря на то, что команда glGetMinmax() позволяет сбрасывать величины минимума и максимума в их начальные значений, вы также можете сделать это в любой момент при помощи команды glResetMinmax().

void glResetMinmax (GLenum target);

Сбрасывает величины минимума и максимума в их начальные значения. Аргумент target должен быть установлен в значение GL_MINMAX.

8.2.5.6 Комбинирование пикселей с использованием уравнений наложения

При использовании цветового наложения цвета в буфере кадра складываются с поступающими фрагментами, создавая новые цвета в буфере кадра. Подмножество обработки изображений расширяет возможности цветового наложения, определяя дополнительные математические комбинации. С помощью команды glBlendEquation() можно указать, как модифицировать операцию, используемую для цветового наложения входящих фрагментов на цвета в буфере кадра.

void glBlendEquation (GLenum mode);

Задает как цвета в буфере кадра и цвета источника должны соединяться. Допустимыми значениями для mode являются GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE, GL_MIN и GL_MAX. Значением mode по умолчанию является

GL_FUNC_ADD (сложение), которое, кроме того, является единственно возможным при отсутствии подмножества обработки изображений. Влияние различных режимов на результат цветового наложения отражен в таблице 8-10.

В таблице 8-10 и представляют цвета источника и приемника, параметры S и D в таблице представляют факторы наложения источника и приемника, чье значение определяется в соответствии с аргументами команды glBlendFunc().

Таблица 8-10. Математические операции, связанные с режимами наложения

подмножества обработки изображений

Режим наложения

Математическая операция

GL_FUNC_ADD

GL_FUNC_SUBTRACT

GL_FUNC_REVERSE_SUBTRACT

GL_MIN

GL_MAX

В примере 8-10 демонстрируются различные уравнения наложения. Для выбора режима наложения используются клавиши ‘a’, ’s’, ’r’, ’m’, ’x’. В качестве источника используется синий квадрат, в качестве приемника используется желтый квадрат на заднем фоне.

Фактор наложения для обоих цветов установлен в GL_ONEкомандой glBlendFunc(). Результирующее изображение во всех режимах вы можете увидеть на рисунке 8-22.

Рисунок 8-22. Различные уравнения цветового наложения

Пример 8-10. Демонстрация уравнений наложения: файл blendeqn.cpp

/* 'a' -> GL_FUNC_ADD

*'s' -> GL_FUNC_SUBTRACT

*'r' -> GL_FUNC_REVERSE_SUBTRACT

*'m' -> GL_MIN

*'x' -> GL_MAX

*/

#include <glut.h> #include "glext.h"

//Тип и указатель для функции смешивающего уравнения typedef void (APIENTRY * GLBLENDEQUATION) (GLenum mode); GLBLENDEQUATION glBlendEquation=NULL;

void init()

{

//Проверяем, присутствует ли расширение GL_ARB_imaging (imaging subset)

if (glutExtensionSupported("GL_ARB_imaging")==0)

{

exit(1);

}

//Получить указатель на функцию

glBlendEquation=(GLBLENDEQUATION)wglGetProcAddress("glBlendEquation");

glClearColor(1.0,1.0,0.0,0.0); glBlendFunc(GL_ONE,GL_ONE); glEnable(GL_BLEND);

}

void keyboard(unsigned char key, int x, int y)

{

switch(key)

{

case 'a': case 'A': glBlendEquation(GL_FUNC_ADD); break;

case 's': case 'S': glBlendEquation(GL_FUNC_SUBTRACT); break;

case 'r': case 'R': glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); break;

case 'm': case 'M': glBlendEquation(GL_MIN); break;

case 'x': case 'X':

glBlendEquation(GL_MAX); break;

case 27: exit(0);

}

glutPostRedisplay();

}

void reshape(int w, int h)

{

glViewport(0,0,(GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity();

gluOrtho2D(-1,1,-1,1);

}

void display()

{

glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0,0.0,1.0); glRectf(-0.5,-0.5,0.5,0.5); glFlush();

}

int main (int argc, char **argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(500,500); glutCreateWindow("Demonstrating The Blend Equation Modes"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutKeyboardFunc(keyboard);

glutMainLoop(); return 0;

}

8.2.5.7Постоянные факторы наложения

Подмножество обработки изображений предоставляет дополнительные факторы наложения, которые могут быть использованы в команде glBlendFunc(). Эти дополнительные факторы описаны в таблице 8-11.

Таблица 8-11. Факторы наложения, предоставляемые подмножеством обработки

изображений

Константа

Принадлежность

Вычисляемый фактор

GL_CONSTANT_COLOR

источник или приемник

 

GL_ONE_MINUS_CONSTANT_COLOR

источник или приемник

 

GL_CONSTANT_ALPHA

источник или приемник

 

GL_ONE_MINUS_CONSTANT_ALPHA

источник или приемник

 

 

 

 

Глава 9. Текстурирование

До сих пор мы рисовали все геометрические объекты либо одним цветом, либо с плавной закраской (когда внутренние цвета рассчитываются интерполяцией цветов в вершинах) – то есть объекты рисовались без текстурирования. Если вы хотите нарисовать массивную кирпичную стену без текстурирования, каждый кирпичик должен быть нарисован в виде отдельного полигона. Без текстурирования такая стена может потребовать тысяч полигонов, хотя сама она является прямоугольной, и даже тогда кирпичи будут выглядеть слишком гладкими и одинаковыми, то есть недостаточно реалистичными.

Текстурирование (или наложение текстур) позволяет прикрепить изображение кирпичной стены (полученное, возможно, сканированием реальной фотографии) к полигону и нарисовать всю стену в виде одного полигона. При использовании текстурирования можно быть уверенным в том, что с полигоном в процессе всех преобразований будут происходить правильные изменения. Например, если вы наблюдаете стену в перспективе, кирпичи, находящиеся дальше от вас, будут выглядеть меньше тех, что ближе. Другие примеры использования текстур включают симуляцию растительности на полигонах, представляющих землю; наложение обоев на стены или придание объектам вида физических материалов, например, мрамора, дерева или гранита. Хотя наиболее естественным является наложение текстур на полигоны, текстуру можно наложить на любой примитив точку, линию, полигон, битовую карту или изображение.

Поскольку существует так много возможностей, текстурирование это большой сложный предмет, и при работе с ним вам несколько раз придется делать выбор техники программирования. Например, большинство людей интуитивно понимают смысл двумерных текстур, но текстуры могут также быть одномерными и даже трехмерными. Вы можете накладывать текстуры на поверхности, созданные из нескольких полигонов, и на изогнутые поверхности, вы также можете повторять текстуру в одном, двух или трех направлениях (в зависимости от того в каком числе направлений текстура описана). Кроме того вы можете накладывать текстуру таким образом, чтобы она выделяла контуры других свойств рассматриваемого объекта. Блестящие объекты могут быть текстурированы таким образом, как будто они находятся в центре комнаты и отражают изображения окружающих предметов. Наконец, текстура может быть наложена на объект различными способами. Она может быть нарисована на объекте, использоваться для модуляции его цветов или комбинироваться с ними.

Текстуры это просто прямоугольные массивы данных, например, цветовых, световых или цветовых и альфа. Индивидуальные элементы (значения) текстуры часто называются тэкселями (texels). Что делает текстурирование сложным, так это то, что прямоугольная текстура может быть наложена на непрямоугольный объект, и это должно быть сделано каким-либо разумным способом.

Рисунок 9-1 иллюстрирует процесс текстурирования. Левая часть рисунка представляет собой саму текстуру, а белая линия четырехугольную форму, углы которой будут наложены на соответствующие точки текстуры. Когда четырехугольник отображается на экране, он может быть искажен за счет различных преобразований поворотов, переносов, масштабирования и проецирования. Правая часть рисунка демонстрирует, как выглядит четырехугольник на экране после этих преобразований. (Заметьте, что

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

Рисунок 9-1. Процесс текстурирования

Обратите внимание на то, что текстура изменяется, чтобы соответствовать искажениям четырехугольника. В данном случае она слегка растянута в направлении xи сжата в направлении y. В зависимости от размера текстуры, степени искажения четырехугольника и размера изображения на экране. некоторые тэксели могут быть наложены более чем на один фрагмент, а некоторые фрагменты могут покрываться несколькими тэкселями. Поскольку текстура состоит из целого числа тэкселей (в данном случае 256x256), для наложения тэкселей на фрагменты следует выполнить операцию фильтрации. Например, если несколько тэкселей соответствуют одному фрагменту, следует взять их среднее, которое уместится на фрагменте, если один тэксель попадает на несколько фрагментов, следует вычислить взвешенное среднее соседних тэкселей. Из-за этих расчетов текстурирование -- довольно дорогая операция с точки зрения объема расчетов, поэтому многие аппаратные средства включают встроенную поддержку наложения текстур.

Приложение может создавать текстурные объекты, каждый из которых содержит одну текстуру (и, возможно, связанные с ней мипмапы (mipmaps)). Некоторые реализации OpenGL поддерживают рабочее подмножество (workingset) текстурных объектов, которые работают быстрее, чем другие. Говорят, что эти объекты являются резидентными (resident) и могут иметь специальную аппаратную и/или программную поддержку. С помощью OpenGL вы можете создавать и уничтожать текстурные объекты, а также выяснять, какие из текстур могут быть размещены в рабочем подмножестве.

Несколько операций по наложению текстур появились в OpenGL версии 1.1:

Дополнительные внутренние форматы изображений текстуры.

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

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

Создание текстуры из данных буфера кадра (а также из системной памяти)

Объекты текстуры, включая резиденты и приоритеты.

Версия 1.2 добавила еще несколько операций, связанных с текстурированием:

3D текстуры.

Новый режим вычисления текстурных координат GL_CLAMP_TO_EDGE, отделяющий тэксели от ребра изображения текстуры, а не от его границы.

Больший контроль над мипмапами, представляющими разные уровни детализации (levels-of-details -- LOD).

Вычисление зеркального блика (от источника) после текстурирования.

Версия 1.2 также позволила производителям добавлять стандартизованные (принятые ARB) опциональные расширения, включая:

Мультитекстурирование, позволяющее накладывать на один примитив несколько текстур.

Если вы пытаетесь использовать одну из перечисленных операций и не можете сделать этого, проверьте версию вашей реализации OpenGL, чтобы убедиться в том, что она действительно поддерживает требуемую операцию.

9.1 Введение и пример

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

9.1.1Наложение текстуры по шагам

Чтобы использовать наложение текстуры, вы должны выполнить следующие шаги:

1.Создать текстурный объект и задать текстуру для него

2.Задать, как текстура должна воздействовать на каждый пиксель

3.Активизировать механизм текстурирования

4.Нарисовать сцену, передавая на конвейер визуализации и геометрические

координаты и координаты текстуры

Имейте в виду, что текстурирование работает только в RGBA режиме. Результат попытки применения текстурирования в индексном режиме не определен.

9.1.1.1 Создание текстурного объекта и указание текстуры для него

Обычно считается, что текстура является двумерной, как и большинство изображений, однако она может быть и одномерной, и трехмерной. Данные, описывающие текстуру могут состоять из одного, двух, трех или четырех элементов на тэксель и представлять все, что угодно от константы модуляции до четверки (R, G, B, A).

В довольно простом примере 9-1 создается один текстурный объект, управляющий одной двумерной текстурой. Данный пример предварительно не выясняет, сколько имеется доступной памяти. Поскольку создается только одна текстура, не делается никаких попыток по оптимизации работы программы (нет приоритезации или другой работы с рабочим подмножеством текстур). Другие более сложные техники, такие как границы текстуры и мипмапы, в этом простом примере также не используются.

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

Вы можете выбрать любую из 4 функций для вычисления финальной RGBA величины на основании фрагмента и текстуры. Одна из возможностей заключается в использовании цвета текстуры в качестве результирующего цвета (этот режим называется режимом замены replace; текстура просто рисуется поверх фрагмента) (Этот режим как раз и используется в примере 9-1.) Другой метод заключается в использовании текстуры для модуляции (modulate) или масштабирования цвета фрагмента; эта техника полезна при комбинировании эффектов освещения и текстурирования. Наконец, на основании величины текстуры на фрагмент может быть наложена константная величина цвета.

9.1.1.3Активизация наложения текстур

До того, как вы нарисуете свою сцену, вам следует включить текстурирование.

Текстурирование включается и выключается с использованием команд glEnable() и glDisable() с символическими константами GL_TEXTURE_1D, GL_TEXTURE_2D или GL_TEXTURE_3D в качестве аргумента для активизации одномерного, двумерного или трехмерного текстурирования соответственно. (Если активизированы два или все три режима, используется режим с большим числом измерений. Для лучшей вразумительности программы следует использовать только один режим.)

9.1.1.4 Нарисовать сцену, поставляя и текстурные, и геометрические координаты

Вам нужно указать, как текстура должна быть выровнена относительно фрагментов до того, как она будет к ним прикреплена. То есть во время передачи объекта на конвейер, вам нужно поставлять не только геометрические, но и текстурные координаты. Для двумерной карты текстуры, например, координаты текстуры лежат в диапазоне [0; 1] в обоих направлениях, однако координаты текстурируемых элементов могут быть любыми. Например, чтобы наложить кирпичную текстуру на стену, предположим, что стена имеет квадратную форму и что должна использоваться только одна копия текстуры. Тогда в коде вам, вероятно, потребуется ассоциировать текстурные координаты (0,0) (1, 0) (1, 1) и (1, 1) с четырьмя углами стены. Если стена весьма велика, вы, возможно, захотите нарисовать на ней несколько копий текстуры. Чтобы сделать это, текстура должна быть создана таким образом, чтобы кубики на левой границе точно подходили к кубикам на правой границе, и то же должно относиться к кубикам на верхней и нижней границах.

Вы также должны определиться с тем, как будут трактоваться координаты текстуры за пределами диапазона [0; 1]. Должна ли текстура повторяться, чтобы накрыть объект, или координаты следует отсечь по допустимой границе.

9.1.2Простая программа

Одной из проблем с примерами, демонстрирующими текстуры, является то, что интересные текстуры, как правило, велики. Обычно, текстуры считываются из файлов изображений, поскольку программное создание текстуры может вылиться в сотни строк кода. В примере 9-1 текстура, которая состоит из чередующихся белых и черных квадратиков, подобно шахматной доске, создается программно. Программа накладывает эту текстуру на два квадрата, которые затем выводятся в перспективе, при этом один из них перпендикулярен направлению обзора, а второй повернут на 45 градусов, как показано на рисунке 9-2. В объектных координатах оба квадрата имеют одинаковый размер.

Рисунок 9-2. Текстурированные квадраты

Пример 9-1. Текстурированная шахматная доска: файл checker.cpp

#include <glut.h>

//Параметры текстуры шахматной доски

#define checkImageWidth 64 #define checkImageHeight 64

GLubyte checkImage[checkImageHeight][checkImageWidth][4]; GLuint texName;

void makeCheckImage()

{

int i,j,c;

for (i=0;i<checkImageHeight;i++)

{

for (j=0;j<checkImageWidth;j++)

{

c=(((i&0x8)==0)^((j&0x8)==0))*255;

checkImage[i][j][0]=(GLubyte)c;

checkImage[i][j][1]=(GLubyte)c;

checkImage[i][j][2]=(GLubyte)c;

checkImage[i][j][3]=(GLubyte)255;

}

}

}

void init(void)

{

glClearColor(0.0,0.0,0.0,0.0); glShadeModel(GL_FLAT); glEnable(GL_DEPTH_TEST);

makeCheckImage(); glPixelStorei(GL_UNPACK_ALIGNMENT,1);

glGenTextures(1,&texName); glBindTexture(GL_TEXTURE_2D,texName);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); 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,GL_RGBA,checkImageWidth,checkImageHeight, 0,GL_RGBA,GL_UNSIGNED_BYTE,checkImage);

}

void display(void)

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glBindTexture(GL_TEXTURE_2D,texName);

glBegin(GL_QUADS);

glTexCoord2f(0.0,0.0); glVertex3f(-2.0,-1.0,0.0); glTexCoord2f(0.0,1.0); glVertex3f(-2.0,1.0,0.0); glTexCoord2f(1.0,1.0); glVertex3f(0.0,1.0,0.0); glTexCoord2f(1.0,0.0); glVertex3f(0.0,-1.0,0.0);

glTexCoord2f(0.0,0.0); glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,1.0); glVertex3f(1.0,1.0,0.0);

glTexCoord2f(1.0,1.0); glVertex3f(2.41421,1.0,-1.41421); glTexCoord2f(1.0,0.0); glVertex3f(2.41421,-1.0,-1.41421); glEnd();

glFlush(); glDisable(GL_TEXTURE_2D);

}

void reshape(int w, int h)

{

glViewport(0,0,(GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity();

gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,30.0); glMatrixMode(GL_MODELVIEW);

glLoadIdentity(); glTranslatef(0.0,0.0,-3.6);

}

int main(int argc, char ** argv)

{

glutInit(&argc,&argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(250,250); glutCreateWindow("Texture-Mapped Checkerboard"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Текстура в виде шахматной доски генерируется в функции makeCheckImage(), а вся инициализация текстурирования производится в функции init(). Команды glGenTextures() и glBindTexture() именуют и создают текстурный объект для изображения текстуры. Единственная карта текстуры с полным разрешением задается командой glTexImage2D(), чьи параметры индицируют размер, тип, расположение и другие параметры изображения текстуры.

Четыре вызова команды glTexParameter*() задают, как текстура должна быть наложена, и как должны фильтроваться цвета, если количество тэкселей в текстуре и пикселей на экране точно не совпадают.

В функции display() команда glEnable() включает текстурирование. Команда glTexEnv*() устанавливает режим рисования в GL_REPLACE, чтобы текстурированные полигоны рисовались с использованием цветов карты текстуры (без принятия в расчет того цвета, которым они рисовались бы без текстурирования).

Затем рисуются два полигона. Заметьте, что координаты текстуры задаются вместе с координатами вершин. Команда glTexCoord*() ведет себя аналогично glNormal*(). Команда glTexCoord*() устанавливает текущие координаты текстуры; эти координаты

ассоциируются со всеми последующими вызовами glVertex*() пока glTexCoord*() не будет вызвана снова.

Замечание: Изображение шахматной доски может неверно выглядеть на экране, если скомпилируете и запустите программу на своей машине. Например, вместо двух квадратов вы можете увидеть 4 треугольника с различно – спроецированным изображением. Если это произошло, попробуйте с помощью команды glHint()

установить параметр GL_PERSPECTIVE_CORRECTION_HINT в значение GL_NICEST.

Соседние файлы в предмете Компьютерная Графика