Файл: Opengl игорь Тарасов Часть i основы OpenGL.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 12.01.2024

Просмотров: 192

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.


Исходный файл смотрите здесь. Исполняемый файл здесь.
4.5  Упражнение: "Снег"

Ранее рассматривалось приложение "Снеговик". Анимацию создавать вы тоже уже научились. Добавьте косой снег, только сделайте снеговика прозрачным, чтобы снежинки как бы пролетали сквозь него.

4.6  Плоскости отсечения

Если вам требуется нарисовать сферу или любой другой объект урезанным, то плоскости отсечения это то, что вам надо. Плоскостей отсечения может быть шесть штук. По умолчанию они все запрещены. Разрешается плоскость отсечения командой glEnable(GL_CLIP_PLANE0). \\\\Ноль на конце GL_CLIP_PLANE означает нулевую плоскость; можно указать один, два, три и т.д. Сама плоскость устанавливается функцией glClipPlane. Первый аргумент этой функции - это плоскость, второй - это массив из четырех элементов, в котором хранятся коэффициенты уравнения плоскости. Для тех, кто не помнит уравнения плоскости в трехмерном пространстве, напоминаю: A*x+B*y+C*z+D = 0. Вот эти самые A,B,C,D и являются теми четырьмя коэффициентами. Создайте новый проект с именем ClipPlane и отредактируйте функцию display, как показано ниже.
void CALLBACK display(void)

{

GLdouble equation[4] = {-1,-0.25,0,2};

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable(GL_CLIP_PLANE0);

glClipPlane(GL_CLIP_PLANE0, equation);

glColor3d(1,0,0);

auxSolidSphere( 3 );
glDisable(GL_CLIP_PLANE0);

auxSwapBuffers();

}



Исходный файл смотрите здесь. Исполняемый файл здесь.
4.7  Упражнение: "Три плоскости"

Добавьте еще две плоскости. Расположите их так, чтобы они отсекали двумерный угол [(p)/2].
4.8  Трафарет

Зачем это может понадобиться, я не очень представляю. Рисовать прерывистую линию вы уже умеете. Трафарет - это то же самое, только для многоугольников. Вы разрешаете тест трафарета командой glEnable(GL_POLYGON_STIPPLE). \\\\Аналогично, как и в случае с линиями, тут нужно загружать массив, который задает битовую маску. Размер трафарета строго оговорен - 32х32 пикселя. 32х32 равняется 1024. 1024 делим на восемь бит, получаем 128 байт, т.е., чтобы закодировать трафарет, нужен массив из 128-ми байт. Тут уже, как в случае с линиями, калькулятором не посчитаешь. Поэтому я написал утилиту - pcx_2bpp, которая конвертирует pcx-файл в формате два бита на пиксель в массив на языке Си. Запускается она так:

>pcx_2bpp filename.pcx >array.c
Знак "больше" означает перенаправление стандартного вывода в файл array.c. Эта утилита абсолютно переносима. Нарисовав в каком-нибудь графическом пакете рисунок в формате 32х32х2bpp, сохраните его и обработайте моей утилитой. Получите массив, который вставьте в свою программу. В фунции main добавьте следующий код:
glEnable(GL_POLYGON_STIPPLE);

glPolygonStipple(array);

4.9  Упражнение: "Совершенствуем Arcanoid"

Не знаю, правильно ли сказал. Мне кажется, что уродуем, а не совершенствуем. В общем, наложите трафарет на подставку и шар. Когда увидите трафарет на шаре, то поймете, почему я сказал: "Уродуем".



Исходный файл смотрите здесь. Исполняемый файл здесь.
4.10  Туман

Еще одна мелочь, которую мы рассмотрим - это включение тумана. За основу возьмем нашего снеговика. Надо сказать, что ведет туман себя довольно странно. Если его цвет установить белым, то при увелечении плотности тумана снеговик становится полностью белым, причем даже те его места, которые изначально были черными, т.к. не освещались. Когда я установил цвет тумана темно-серым - (0.25, 0.25, 0.25), то при увелечении плотности не освещенные его части как им и положено оставались черными, а видимые теряли яркость. Я скорее охарактерезовал бы средство тумана, как средство для изменения яркости, потому как на реальный туман, это к сожалению, не похоже. Итак, создайте новый проект с именем fog, скопируйте snowman.c в fog.c. Для того чтобы включить туман и поэксперементировать с его влиянием на изображение мы добавим две функции, которые будут обрабатывать события от стрелок вверх\вниз на клавиатуре. Код этих функций выглядит так, включите его сразу после включения заголовочных файлов:
float density;
void CALLBACK Key_UP(void )

{

density+=(float)0.1;

glFogf(GL_FOG_DENSITY, density);

}
void CALLBACK Key_DOWN(void )

{

density-=(float)0.1;

glFogf(GL_FOG_DENSITY, density);

}
Я также ввел глобальную переменную density, в ней хранится плотность тумана. При нажатие стрелок вверх\вниз будет вызываться соответсвующая функция и будет изменено значение тумана. Снеговик у нас все время перерисовывается, поэтому при следующей отрисовке кадра значение тумана обновится. Теперь надо отредактировать функцию main, добавленный код выделен серым фоном:


void main()

{

float pos[4] = {3,3,3,1};

float dir[3] = {-1,-1,-1};

float fogcolor[4] = {0.25,0.25,0.25,1}; // цвет тумана
auxInitPosition( 50, 10, 400, 400);

auxInitDisplayMode( AUX_RGB | AUX_DEPTH | AUX_DOUBLE );

auxInitWindow( "Snowman" );

auxIdleFunc(display);

auxReshapeFunc(resize);
auxKeyFunc(AUX_UP, Key_UP); // устанавливаем обработчик

auxKeyFunc(AUX_DOWN, Key_DOWN); // стрелок вверх/вниз
glEnable(GL_FOG); // разрешаем туман

glGetFloatv(GL_FOG_DENSITY, &density); // получаем значение плотности

glFogfv(GL_FOG_COLOR, fogcolor); // устанавливаем цвет тумана
glEnable(GL_ALPHA_TEST);

glEnable(GL_DEPTH_TEST);

glEnable(GL_COLOR_MATERIAL);

glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLightfv(GL_LIGHT0, GL_POSITION, pos);

glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);

auxMainLoop(display);

}
Я полагаю здесь все ясно, прокомментирую тут только одну функцию:
glGetFloatv(GL_FOG_DENSITY, &density);
С помощ ью этой функции я получаю значение плотности тумана, установленное по умолчанию. Первый аргумент этой функции указывает OpenGL значение какого параметра мы хотим получить. Второй аргумент - это адрес в памяти куда будет записано значение данного параметра.
4.11 Логические операции

Логические операции позволяют вам складывать цвет фрагмента находящегося в буфере с цветом, который туда поступает. Этот режим разрешается и запрещается вызовом функций glEnable\glDisable с параметром GL_COLOR_LOGIC_OP. Правило, по которому будут складываться цвета задается функцией glLogicOp. У нее один параметр - одна из следующих констант определенных в файле gl.h.
/* LogicOp */

#define GL_CLEAR 0x1500

#define GL_AND 0x1501

#define GL_AND_REVERSE 0x1502

#define GL_COPY 0x1503

#define GL_AND_INVERTED 0x1504

#define GL_NOOP 0x1505

#define GL_XOR 0x1506

#define GL_OR 0x1507

#define GL_NOR 0x1508

#define GL_EQUIV 0x1509

#define GL_INVERT 0x150A

#define GL_OR_REVERSE 0x150B

#define GL_COPY_INVERTED 0x150C

#define GL_OR_INVERTED 0x150D

#define GL_NAND 0x150E

#define GL_SET 0x150F
Мне не удалось найти разумного применения этой функции поэтому я приведу здесь лишь текст моей программы с комментариями, которая позволяет перебрать и посмотреть действие всех логических операций. Как и в предыдущем пункте у меня определена глобальная переменная и две функции реагирующие на нажатие стрелок на клавеатуре.
/*

* (c) Copyright 1995-2000, Igor Tarasov

* FidoNet: 2:5020/370.2 620.20 1103.5

* email: igor@itsoft.miem.edu.ru itarasov@rtuis.miem.edu.ru

* Phone: (095)916-89-51 916-89-63

*/
#include
#include

#include

#include
int logicop = GL_CLEAR;
void CALLBACK Key_UP(void )

{

if(logicop
logicop++;

}

void CALLBACK Key_DOWN(void )

{

if(logicop>GL_CLEAR)

logicop--;

}

void CALLBACK resize(int width,int height)

{

glViewport(0,0,width,height);

glMatrixMode( GL_PROJECTION );

glLoadIdentity();

glOrtho(-5,5, -5,5, 2,12);

gluLookAt( 0,0,5, 0,0,0, 0,1,0 );

glMatrixMode( GL_MODELVIEW );

}
void CALLBACK display(void)

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

glColor4d(1,0,0, 1);

auxSolidSphere( 1 );

glEnable(GL_COLOR_LOGIC_OP);

glLogicOp(logicop);


glColor4d(0,1,0, 0.7);

auxSolidCylinder(2,3);
glDisable(GL_COLOR_LOGIC_OP);


auxSwapBuffers();
}

void main()

{

float pos[4] = {3,3,3,1};

float dir[3] = {-1,-1,-1};
GLfloat mat_specular[] = { 1,1,1,1};
auxInitPosition( 50, 10, 400, 400);

auxInitDisplayMode( AUX_RGB | AUX_DEPTH | AUX_DOUBLE );

auxInitWindow( "Glaux Template" );

auxIdleFunc(display);

auxReshapeFunc(resize);
auxKeyFunc(AUX_UP, Key_UP);

auxKeyFunc(AUX_DOWN, Key_DOWN);
glEnable(GL_DEPTH_TEST);

glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0);
// glLightfv(GL_LIGHT0, GL_POSITION, pos);

// glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);

glMaterialf(GL_FRONT, GL_SHININESS, 128);
auxMainLoop(display);

}
4.12 Трафарет II
Ранее вы познакомились с надоложение трафарета состоящего из массива бит 32х32 точки. Недостатки такого трафарета в его небольшой площади и то, что каждые его элемент является битом, т.е. у такого трафарета всего две зоны(рисуем и не рисуем). Далее вы познакомитесь с полноценным трафаретом.

В OpenGL есть буфер трафарета, который предоставляет огромные возможности для творчества. Тест трафарета на мой взгляд бесспорно полезная вещь. С ее помощью реализуются самые разнообразные эффекты, начиная от простого вырезания одной фигуры из другой до реализации теней, отражений и прочих нетривиальных функций, требующих от вас уже не только знакомство с библиотекой OpenGL, но и понимания алгоритмов машинной графики. Здесь мы рассмотрим самое простое применение буфера трафарета. Пусть у нас есть два объекта на экране - сфера и куб, и пусть сфера находится внутри куба и немного из него выходит за его грани.


рис. 4.12.1
Мы поставим себе задачу изобразить три рисунка: куб минус сфера, сфера минус куб, пересечение куба и сферы. Как вы понимаете объединение куба и сферы выводится без всякого буфера трафарете по умолчанию. Собственно говоря, оно(объединение) изображено на рисунке 4.12.1. Теперь о том, что такое трафарет и как им пользоваться. Трафарет это двумерный массив целых переменных(тип int). Каждому пикселю в окне соответствует один элемент массива. Использование буфера трафарета происходит в два этапа. Сначала вы его заполняете, потом основываясь на его содержимом рисуете ваши объекты. Буффер трафарета заполняется следующим образом. Вы делите окно вывода на зоны и каждой зоне присваеваете свое значение. Например, для рисунка 4.12.1 область, где нет ничего будет заполнена нулями, область, где выведен куб заполнена единицами и область, где видна сфера двойками.

Обратите внимание, что буффер трафарета - это двумерный массив, а не трехмерный. Теперь вы легко можете представить себе эти области, они собственно и изображены на приведенном рисунке. Также заметьте, что цвет здесь роли не играет. Я бы мог вывести все черным цветом, а буфер трафарета заполнился в зависимости от геометричеких размеров фигур и от их пересечения. Далее мы рассмотрим функции библиотеки OpenGL для работы с трафаретом. Тест трафарета разрешается при помощи функций glEnable\glDisable с параметром GL_STENCIL_TEST. Очищается буфер трафарета при помощи функции glClear с параметром GL_STENCIL_BUFFER_BIT. Заполнение буфера трафарета происходит при помощи следующих двух функций:
void glStencilFunc(GLenum func, GLint ref, GLuint mask)

void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
Первая функция задает правило, по которому будет определяться пройден тест трафарета или нет. Переменная func может принимать одно из следующих значений:
GL_NEVER

Не проходит

GL_LESS

Проходит if ( ref & mask) < ( stencil & mask)

GL_LEQUAL

Проходит if ( ref & mask) ? ( stencil & mask)

GL_GREATER

Проходит if ( ref & mask) > ( stencil & mask)

GL_GEQUAL

Проходит if ( ref & mask) ? ( stencil & mask)

GL_EQUAL

Проходит if ( ref & mask) = ( stencil & mask)

GL_NOTEQUAL

Проходит if ( ref & mask) ? ( stencil & mask)

GL_ALWAYS

Всегда проходит
Если тест трафарета не пройден, то фрагмент(пикселы) фигуры не прорисовываются в данном месте, т.е. они не попадают в буффер кадра. Если тест пройден, то фигура рисуется. Вторая функция позволяет задать, как будет инициализироваться буфер трафарета. Параметры fail, zfail и zpass могут принимать одно из следующих значений:
GL_KEEP

Сохранить текущее значение в буфере трафарета

GL_ZERO

Установить значение буфера трафарета в ноль

GL_REPLACE

Заменить значение буфера трафарета на значение переменной ref, заданной функцие glStencilOp

GL_INCR

Увеличить на единицу

GL_DECR

Уменьшить на единицу

GL_INVERT

Поразрадно инвертировать

В случае непрохождения теста трафарета над фрагментом выполняется действие определенной парамметром fail. Например, если мы хотим заполнить область трафарета, где рисуется куб единицами, то можно использовать следующи код:
glStencilFunc(GL_NEVER, 1, 0); // значение mask не используется

glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);

auxSolidCube(2.5);
Объясняю подробней, первая функция говорит о том, что тест трафарета всегда проходит неудачно. Вторая функция задает, что в случае неудачного теста трафарета заменить значение храняцееся в буфере трафарета на значение переменной ref, а его мы задали равным единице. В результате, на экране ничего не нарисуется т.к. тест трафарета завершался неудачно, но в буфере трафарета мы получили проекию куба из единичек, т.е. буфер трафарета заполнен не только нулями. Теперь мы хотим заполнить двойками область, где прорисовывается сфера. Здесь мы уже должны учитывать буфер глубины, иначе мы заполним двойками всю область, где у нас рисуется сфера. Для того чтобы учитывать буфер глубины тест трафарета должен завершиться положительно. Третий параметр zpass функции glStencilOp как раз указывает, что делать, если тест трафарета прошел, а тест глубины нет. Поэтому код выглядит так:
glStencilFunc(GL_ALWAYS, 2, 0); // значение mask не используется

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

auxSolidSphere(1.5);
В результате получаем буфер трафарета заполненный нулями, где ничего не было, единицами, где виден куб и двойками, где видна сфера. В последнем примере тест трафарета прошел успешно, поэтому на экране была нарисована сфера. Но это нам не мешает мы очистим буфер глубины и буфер цвета, но не буфер трафарета.
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Теперь дело техники нарисовать тотже самый куб без сферы. Надо установить, что тест трафарета проходит, если значение находящееся в буфере трафарета совпадает со сзначением второго параметра функции glStencilFunc. Как вы помните куб в буфере трафарета имел значение единицы. Поэтому получаем:
glStencilFunc(GL_EQUAL, 1, 255); // куб рисуется только там, где

// в буфере трафарета лежат единицы

glColor3d(1,1,1);

auxSolidCube(2.5);
Создайте новый проект с именем stencil. Скопируйте glaux.c в stencil.c и отредактируйте функцию display следующи образом:
void CALLBACK display(void){

// очищаем все буферы

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// разрешаем тест трафарета

glEnable(GL_STENCIL_TEST);
// рисуем куб и заполняем буффер трафарета единицами

// в том месте, где рисуется куб

// тут у меня немного по другому, чем я выше было разоьрано,

// но действие выполняется анологичное

glStencilFunc(GL_ALWAYS, 1, 0);

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

auxSolidCube(2.5);
// заполняем буффер трафарета двойками

// в том месте, где сфера закрывает куб

glStencilFunc(GL_ALWAYS, 2, 0);

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

auxSolidSphere(1.5);
// очищаем буфферы цвета и глубины

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glStencilFunc(GL_EQUAL, 1, 255);

glColor3d(1,1,1);

auxSolidCube(2.5);
// вращаем сцену

glRotated(3, 1,0,0);

glRotated(5, 0,1,0);

glRotated(7, 0,0,1);
auxSwapBuffers();}


рис. 4.12.2
Теперь давайте немного приукрасим нашу программу. Давайте внутри куба нарисуем красный шар, который будут опоясывать зеленый и синий тор. Делается это следующим образом. Как и в предыдущей программе вы составляете буфер трафарета, после чего очищаете буфер глубины и цета, запрещаете тест трафарета, т.к. нам надо просто вывести фигуры без трафарета, выводите шар с торами, и, наконец, включает тест трафарета и выводите куб с отверстиями. Вот код фунции display:
void CALLBACK display(void){

// очищаем все буферы

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// разрешаем тест трафарета

glEnable(GL_STENCIL_TEST);
// рисуем куб и заполняем буффер трафарета единицами

// в том месте, где рисуется куб

// тут у меня немного по другому, чем я выше было разоьрано,

// но действие выполняется анологичное

glStencilFunc(GL_ALWAYS, 1, 0);

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

auxSolidCube(2.5);
// заполняем буффер трафарета двойками

// в том месте, где сфера закрывает куб

glStencilFunc(GL_ALWAYS, 2, 0);

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

auxSolidSphere(1.5);
// очищаем буфферы цвета и глубины

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// запрещаем тест трафарета и рисуем красную сферу

glDisable(GL_STENCIL_TEST);

glColor3d(1,0,0);

auxSolidSphere(0.5);

//синий тор

glColor3d(0,0,1);

auxSolidTorus(0.15, 0.6);

// зеленый тор, повернутый на 90 градусов относительно синего

glColor3d(0,1,0);

glPushMatrix();

glRotated(90, 1,0,0);

auxSolidTorus(0.15, 0.6);

glPopMatrix();
//снова разрешаем тест трафарета

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 1, 255);

glColor3d(1,1,1);

auxSolidCube(2.5);
// вращаем сцену

glRotated(3, 1,0,0);

glRotated(5, 0,1,0);

glRotated(7, 0,0,1);
auxSwapBuffers();}


Исходный файл смотрите здесь. Исполняемый файл здесь.
1   2   3   4   5


4.13 Упражнение "сфера минус куб"
Напишите программу, в которой будет крутиться сфера минус куб.
4.14 Упражнение "пересечение сферы и куба"
Напишите программу, в которой будет крутиться пересечение сферы и куба.
Chapter 5

Работа с картинками

5.1  Общие слова

Строить примитивные объекты вы уже научились. Но строить трехмерные сцены с использованием только примитивов вам вряд ли придется, да и выглядят они как-то схематично и тускло. Для того, чтобы их оживить, на примитивы накладывают картинки - текстуры. В качестве фона сцены тоже полезно использовать графическое изображение. Тем самым приложение сделается более живым и интересным. Так что в этой главе мы научимся работать с изображениями.
5.2  Работа с изображениями

Существует множество графических форматов - bmp, pcx, gif, jpeg и прочие. OpenGL напрямую не поддерживает не один из них. В OpenGL нет функций чтения/записи графических файлов. Но поддерживается работа с массивами пикселей. Вы загружаете графический файл, используя библиотеки других фирм, в память и работаете с ними средствами OpenGL. В массиве данные о пикселах могут располагаться разными способами: RGB, BGR, RGBA; могут присутствовать не все компоненты; каждый компонент цвета может занимать один байт, два, четыре или восемь; выравнивание может быть по байту, слову или двойному слову. В общем, форматов расположения данных о графическом изображении в памяти очень много. Я рассмотрю один из них, наиболее часто применяемый, как мне кажется. Информация о каждом пикселе хранится в формате RGB и занимает три байта, выравнивание по байту. В Auxiliary Library есть функция auxDIBImageLoad(LPCSTR), которая загружает в память bmp-файл и возвращает указатель на структуру:
typedef struct _AUX_RGBImageRec {

GLint sizeX, sizeY;

unsigned char *data;

} AUX_RGBImageRec;
Для простоты я буду пользоваться именно этой функцией. Среди прилагаемых программ вы найдете мою утилиту для загрузки файлов из форматов pcx. Исходный текст этой утилиты абсолютно переносим на любую платформу с компилятором ANSI C.

В OpenGL имеются функции для вывода массива пикселей на экран(glDrawPixels), копирования(glCopyPixels), масштабирования(gluScaleImage). Здесь мы рассмотрим только glDrawPixels. Все остальные функции работы с изображениями устроены похожим образом. Для того, чтобы отобразить графический файл в окне OpenGL, вы должны загрузить его в память,
указать выравнивание, установить точку, с которой начинается вывод изображения, и вывести его на экран. Раздобудьте где-нибудь свою фотографию в формате BMP. Можете взять фотографию своей девушки. Создайте новый проект. Объявите глобальную переменную - AUX_RGBImageRec *image. В функцию main вставьте строку: пmage = auxDIBImageLoad("photo.bmp");", перед вызовом функции auxMainLoop. Выравнивание устанавливается вызывом функции glPixelStorei с параметром GL_UNPACK_ALIGNMENT и вторым параметром - целым числом, которое указывает выравнивание. Изображения выводятся прямо на экран. Поэтому все происходит в двухмерных координатах. Позиция, с которой начинается вывод изображения, указывается при помощи функции glRasterPos2d(x,y). Также вы можете установить размер пикселя, вызвав функцию glPixelZoom. Первый параметр этой функции - ширина, второй - высота пикселя. Я вызываю эту функцию с аргументами (1,1), что соответствует нормальному пикселю. Замените (1,1) на (3,2) и вы увидите, как картинка растянется в три раза по горизонтали и в два раза по вертикали. Это случилось, потому что теперь каждый пиксель изображения соответствует прямоугольнику 3х2 в окне. И наконец, вывод осуществляет функция glDrawPixels. Первые два параметра - это ширина и высота. Далее, вы указываете формат, в котором хранится информация в памяти, и тип элементов массива. Последним указывается массив данных.

В функцию display вставьте следующий код:

glRasterPos2d(-4.5,-3); // нижний левый угол

glPixelZoom(1,1);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // выравнивание

glDrawPixels(image->sizeX, image->sizeY, // ширина и высота

GL_RGB, GL_UNSIGNED_BYTE, // формат и тип

image->data); // сами данные
Также в OpenGL имеется функция glBitmap для отображения битовых массивов. Битовый массив - это последовательность байт, которые кодируют картинку из двух цветов. Соответственно, каждый байт кодирует 8 пикселей. Среди прилагаемых программ вы найдете мою утилиту pcx_2bpp. Она читает pcx-файл формата один бит на пиксель и направляет на стандартный вывод массив на языке Си.



Исходный файл смотрите здесь. Исполняемый файл здесь. Моя фотография здесь.
5.3  Упражнение: "Фон для игры Arcanoid"

Найдите красивый фон в формате BMP. Можете опять взять свою фотографию. Подложите этот bmp-файл в качестве фона в игру Дrcanoid". Закомментируйте разрешение всех тестов, кроме GL_DEPTH_TEST, в функции main. Возможно, я уже говорил о том, что вы всегда должны помнить, что дополнительные параметры затормаживают создание объекта, поэтому устанавливайте их очень осторожно. Часть из них можно установить в функции main. Другие же лучше устанавливать и отменять непосредственно при создании объекта.