Opengl игорь Тарасов Часть I основы OpenGL



бет4/4
Дата29.05.2016
өлшемі2.21 Mb.
#100716
1   2   3   4

5.5  Повторение тектуры

Размножить текстуру на плоскости не составляет большого труда. Давайте немного отредактируем программу из предыдущего раздела. Для того чтобы иметь возможность повторять текстуру, нужно установить параметр GL_REPEAT для ее S и T координат. S-координата текстуры - это горизонтальная координата, T-координата - вертикальная. Второй параметр, который может быть установлен для координат, - GL_CLAMP. Он гарантирует, что текстура не будет размножена. По умолчанию установлено GL_REPEAT. Но я все-таки приведу соответствующий код, чтобы вы представляли, как устанавливать этот параметр. В функцию main добавьте следующие строки:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Теперь отредактируйте функцию display.

void CALLBACK display(void)

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


glEnable(GL_TEXTURE_2D);

glColor3d(1,1,1);


glBindTexture(GL_TEXTURE_2D, space_tex );

glBegin(GL_QUADS);

glTexCoord2d(0,0); glVertex3d(-5,-5, -0.1);

glTexCoord2d(0,1); glVertex3d(-5, 5, -0.1);

glTexCoord2d(1,1); glVertex3d( 5, 5, -0.1);

glTexCoord2d(1,0); glVertex3d( 5,-5, -0.1);

glEnd();
glBindTexture(GL_TEXTURE_2D, photo_tex);

glBegin(GL_QUADS);

glTexCoord2d(0,0); glVertex2d(-4,-4);

glTexCoord2d(0,2); glVertex2d(-4, 4);

glTexCoord2d(3,2); glVertex2d( 4, 4);

glTexCoord2d(3,0); glVertex2d( 4,-4);

glEnd();
glDisable(GL_TEXTURE_2D);
auxSwapBuffers();

}
Функция glTexCoord привязывает координаты текстуры к вершинам объекта. Как я уже говорил, левый нижний угол текстуры имеет координату (0,0), а правый верхний - (1,1). Если вы указываете в качестве привязки значение больше единицы, то текстура повторяется. В нашем примере, координату (0,0) текстуры мы привязали к левой нижней вершине плоскости с координатой (-4,-4), а координату (3,2) текстуры к правой верхней вершине (4,4). Тем самым, мы получили размножение текстуры по горизонтали в количестве трех штук и по вертикали в количестве двух штук. Другие две вершины мы связали соответсвующим образом. Если там указать не те числа, то изображение наклонится.


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


5.6  Упражнение: "Вращаем текстуру"

Завращайте плоскость с фотографией вокруг оси X и Y. Также пусть она равномерно колеблется вдоль оси Z от 3 до 7.


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



5.7  Текстура на сфере

Здесь я покажу, как работать с одной единственной текстурой и накладывать текстуры на сферы. Создайте новый проект с именем sphere. Добавьте глобальную переменную.


AUX_RGBImageRec* photo_image;
В функции main загрузите изображение и создайте текстуру. Поскольку текстура у нас в этом приложении всего одна, то создавать идентификатор для нее не надо.
void main()

{

auxInitPosition( 50, 10, 400, 400);



auxInitDisplayMode( AUX_RGB | AUX_DEPTH | AUX_DOUBLE );

auxInitWindow( "Shapes" );

auxIdleFunc(display);

auxReshapeFunc(resize);

glEnable(GL_DEPTH_TEST);

glEnable(GL_TEXTURE_2D);


photo_image = auxDIBImageLoad("photo.bmp");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glTexImage2D(GL_TEXTURE_2D, 0, 3,

photo_image->sizeX,

photo_image->sizeY,

0, GL_RGB, GL_UNSIGNED_BYTE,

photo_image->data);


glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


auxMainLoop(display);

}
Отредактируйте функцию display. Здесь все вам знакомо см. 4.1, кроме gluQuadricTexture. Эта функция разрешает или запрещает наложение текстуры на трехмерный объект. Второй параметр GL_TRUE или GL_FALSE. По умолчанию наложение текстуры запрещено.


void CALLBACK display(void)

{

GLUquadricObj *quadObj;


glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
quadObj = gluNewQuadric();

gluQuadricTexture(quadObj, GL_TRUE);

gluQuadricDrawStyle(quadObj, GLU_FILL);

glColor3d(1,1,1);


glRotated(5, 0,1,0);
glPushMatrix();

glRotated(-90, 1,0,0);

gluSphere(quadObj, 3, 16, 16);

glPopMatrix();


gluDeleteQuadric(quadObj);

auxSwapBuffers();

}

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


5.8  Упражнение

Наложите текстуру на цилиндр, конус, диски и частичный диск.


5.9  Текстура на чайнике

Текстуру можно наложить на объект любой сложности. Для этого надо разрешить автоматически генерировать координаты текстуры - glEnable(GL_TEXTURE_GEN_S) и glEnable(GL_TEXTURE_GEN_T). Далее, вы должны установить один из трех алгоритмов генерации координат текстур.


GL_OBJECT_LINEAR

GL_EYE_LINEAR

GL_SPHERE_MAP
Алгоритм генерации координат устанавливается с помощью функции glTexGeni. Первый параметр функции указывает тип координаты, для которой будет установлен алгоритм. GL_S -горизонтальная координата, GL_T - вертикальная. Второй параметр этой функции должен быть GL_TEXTURE_GEN_MODE. И третий параметр - один из перечисленных выше алгоритмов. Создайте очередной проект с именем teapot. Отредактируйте функцию main, как в предыдущей программе, где мы накладывали изображение на сферу. Только добавьте там строчку glEnable(GL_AUTO_NORMAL), чтобы чайник лучше выглядел. Этот режим разрешает расчет векторов нормалей, что позволяет получать улучшенные изображения, однако занимает некоторое время. А функцию display отредактируйте, как показано ниже.
void CALLBACK display(void)

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


glEnable(GL_TEXTURE_2D);

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);
glColor3d(1,1,1);

glRotated(5,0,1,0);


glPushMatrix();

glTranslated(0,3,0);

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

auxSolidTeapot(2);
glTranslated(0,-3,0);

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);

auxSolidTeapot(2);


glTranslated(0,-3,0);

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

auxSolidTeapot(2);

glPopMatrix();
glDisable(GL_TEXTURE_GEN_S);

glDisable(GL_TEXTURE_GEN_T);

glDisable(GL_TEXTURE_2D);
auxSwapBuffers();

}

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

Конечно, фотография накладывается не самым лучшим образом. А что вы хотели? Машина сама сгенерировать правильно координаты на кривой поверхности не может. Тем не менее, если в качестве текстуры взять изображение в горошек, то оно довольно неплохо ляжет на чайник.




Chapter 6

Освещение и все что с ним связано

6.1  Общие понятия

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

По умолчанию освещение отключено. Включается оно командой glEnable(GL_LIGHTING). В базовом шаблоне освещение я включил, потому что без освещения работать практически невозможно. Сфера всегда будет показываться как круг, а конус - как круг или треугольник. Если монотонное тело у вас равномерно освещено, то вы не можете увидеть его рельеф. Поэтому нам нужно использовать источники света. Сначала рассмотрим функцию, которая устанавливает базовые настройки. Когда вы разрешили освещение, то вы можете уже устанавливать фоновую освещенность. По умолчанию, значение фоновой освещенности равно (0.2, 0.2, 0.2, 1). Создайте новый проект, скопируйте туда шаблонный файл и отключите освещение. Вы с трудом сможете различить сферу на экране. С помощью функции glLightModel вы можете установить фоновое освещение. Если вы повысите его до (1,1,1,1), т.е. до максимума, то включать источники света вам не понадобится. Вы их действия просто не заметите, т.к. объект уже максимально освещен. И получится, что вы как бы отключили освещение. В общем, добавьте в main вызов следующей функции:


float ambient[4] = {0.5, 0.5, 0.5, 1};

...


glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
Попробуйте изменить параметры и посмотрите на результат. Нулевую лампу (glEnable(GL_LIGHT0)) лучше отключить.


Исходный файл смотрите здесь. Исполняемый файл здесь.
6.3 Материал
Материал может рассеивать, отражать и излучать свет. Свойства материала устанавливаются при помощи функции
glMaterialfv(GLenum face, GLenum pname, GLtype* params)
Первый параметр определяет грань, для которой устанавливаются свойства. Он может принимать одно из следующих значений:

GL_BACK задняя грань

GL_FONT передняя грань

GL_FRONT_AND_BACK обе грани


Второй парметр функции glMaterialfv определяет свойство материала, которое будет установлено, и может принимать следующие значения.
GL_AMBIENT рассеянный свет

GL_DIFFUSE тоже рассеянный свет, пояснения смотри ниже

GL_SPECULAR отраженный свет

GL_EMISSION излучаемый свет

GL_SHININESS степень отраженного света

GL_AMBIENT_AND_DIFFUSE оба рассеянных света


Ambient и diffuse переводятся на русский как "рассеянный". Разница между ними не очень понятна. Я использую только GL_DIFFUSE. Третий параметр определяет цвет соответствующего света, кроме случая GL_SHININESS.

Цвет задается в виде массива из четырех элементов - RGBA. В случае GL_SHININESS params указывает на число типа float, которое должно быть в диапазоне от 0 до 128. Я написал простенький пример с цилиндром и раскрасил его грани в разные цвета. Вам надо всего лишь модифицировать функцию display.


void CALLBACK display(void)

{

GLUquadricObj *quadObj;



GLfloat front_color[] = {0,1,0,1};

GLfloat back_color[] = {0,0,1,1};


quadObj = gluNewQuadric();
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMaterialfv(GL_FRONT, GL_DIFFUSE, front_color);

glMaterialfv(GL_BACK, GL_DIFFUSE, back_color);


glPushMatrix();

glRotated(110, -1,1,0);

gluCylinder(quadObj, 1, 0.5, 2, 10, 10);

glPopMatrix();


gluDeleteQuadric(quadObj);

auxSwapBuffers();

}
И вы должны разрешить режим освещенности для двух граней. По умолчанию он запрещен. Добавьте в функцию main следующую строчку.
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

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



  1. Лампы и их свойства


6.5  Тени
Chapter 7
Инициализация или как написать приложение с нуля

7.1  Общие положения

В этой главе вы познакомитесь с самой главной частью приложения - начальной инициализацией. На мой взгляд, это очень сложная тема. Я решил оставить ее на конец книги, когда вы уже будете знакомы с OpenGL. В противном случае, если бы я поместил эту главу в самом начале, я боюсь вы многого не поняли бы. Да и вообще, может не стали бы читать эту книжку.

Сначала, я расскажу в общих чертах, что нужно для инициализации OpenGL. Далее мы рассмотрим несколько частных реализаций в среде Windows, Linux и межплатформенный вариант для Java.

Для программирования графики в OpenGL вы должны иметь контекст воспроизведения. Это что-то типа контекста устройства в Windows или же магического адреса 0xA000 для графического или же 0xB800 для текстого режима MSDOS. Первое, что вы должны сделать, это подобрать и установить нужные вам параметры контекста воспроизведения. Потом создать сам контекст воспроизведения. И последнее, вы должны сделать его активным. Вообще говоря, вы можете иметь несколько контекстов воспроизведения, но активным может быть только один. Теперь вы можете уже что-нибудь рисовать. Но, возможно, вам не подходят настройки по умолчанию, которые предлагает OpenGL. Так что, придется еще немного потрудиться. Нужно сделать две вещи. Первое - это разрешить нужные вам опции с помощью glEnable и, здесь же, настроить параметры ламп, если вы используете освещение. Второе - надо обрабатывать сообщение об изменениях размера вашего окна. Тут вы указываете ту часть окна, где у вас будет располагаться контекст OpenGL. До этой главы у нас во всех примерах размер нашего окна и окна OpenGL совпадали, но, вообще говоря, это необязательно. Вывод OpenGL может занимать только часть окна, а в остальной части вы можете разместить свои компоненты: кнопки, поля ввода и т.п. Далее, вы должны указать тип проекции: перпективная или параллельная. В перспективной проекции две параллельных прямых будут сходиться вдалеке. В параллельной же проекции они всегда будут оставаться параллельными. И последнее, вы устанавливаете точку, где находится ваш глаз; точку, куда вы смотрите, и вектор, который принимается за направление вверх. У меня этот вектор во всех примерах - (0,1,0), т.е. ось Y направлена вверх.
7.2  Консольное приложение - Win32 Console Application

Достоинством приложений данного типа является переносимость на другие платформы при условии, что вы не пользовались другими платформенно зависимыми библиотеками. Реализация OpenGL Auxilary Library существует для большинства платформ. Также здесь значительно упрощена начальная инициализация, т.е. вы быстро можете начать программировать. Идеально подходит для начинающих. Именно поэтому, все примеры в этой книге я привязал к этому варианту приложения OpenGL. Хотя, как я уже говорил, вы можете вставлять приводимый код в WinAPI-приложение и MFC-приложение, и все будет работать точно так же. Для Java-приложения вам придется добавить префиксы к функциям. Недостатком является урезанная функциональность. Вы не можете обрабатывать все сообщения, которые приходят вашему окну. Такой тип приложения идеально подходит для написания небольших портабельных утилит, размером до 25Kb.

Здесь, как я уже говорил, начальная инициализация самая простая. Библиотека сделает за вас большинство действий. От вас потребуется совсем немного по сравнению с другими типами приложений. Итак, давайте для начала создадим проект. Общепризнанно, что тремя наилучшими компиляторами считаются GNU C, Microsoft C и Watcom C. Inprise(Borland) отпадает. Компиляторами других фирм я не пользовался. Все мои задачи решались вышеуказанными четырьмя компиляторами. Правда, должен заметиь, что с 1997 года я практически прекратил пользоваться компилятором фирмы Borland. Лишь изредка, когда нужно было перекомпилировать старые утилиты, написанные еще для MSDOS. Эта книга для начинающих, и я хочу сделать ее понятной большинсву читателей. Поэтому я не буду рассматривать проекты для GNU C или Watcom C. Не хочу здесь городить непонятные многим начинающим makefile'ы. Однако, в конце данной главы будет разобрано приложение для UNIX, там уже от makefile'ов никуда не деться. Теперь вернемся к нашим баранам.

1. Запустите MSVisualC++6.0

2. Щелкните меню File->New->Win32 Console Application.

3. Выберете каталог и имя проекта задайте glaux, щелкните OK.

4. Выберете An Empty Project, щелкните Finish.

5. Создайте новый текстовый файл и сохраните его с именем glaux.c.

6. Присоедините его к проекту. Project->Add To Project->Files

7. Щелкните Build->Set Active Configuration и установите тип проекта glaux - Win32 Release

8. Далее щелкаете Project->Settings->Link->Object/library modules: и добавьте туда opengl32.lib, glu32.lib и glaux.lib

Проект у нас теперь есть, давайте писать glaux.c. Файл, в котором находится исходный код программы, желательно начинать с комментария. Это необязательно, но этого требует хороший стиль. В комментариях можно указать имя автора, способ связи - обычно, адрес электронной почты. Далее, можно кратко описать, что находится в этом файле. Неплохо вести некоторый дневник здесь же: что и когда вы добавили. У меня эти комментарии выгядят так:

/*

* (c) Copyright 1995-1999, Igor Tarasov



* FidoNet: 2:5020/370.2 620.20 1103.5

* Inet: itarasov@rtuis.miem.edu.ru

* Phone: (095)942-50-97

*/

Теперь надо включить заголовочные файлы:



#include
#include

#include

#include

Давайте напишем функцию main(). Я посчитал, что наиболее правильно и понятно будет дать код и прокомментировать его подробно. У меня функция main() выглядит так:


void main()

{

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



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

// указываем координаты окна на экране

// верхний левый угол (50,10)

// ширина и высота - 400

auxInitPosition( 50, 10, 400, 400);

// устанавливаем параметры контекста OpenGL

//

auxInitDisplayMode( AUX_RGB | AUX_DEPTH | AUX_DOUBLE );



// создаем окно на экране

auxInitWindow( "Glaux Template" );

// наше окно будет получать сообщения

// от клавиатуры, мыши, таймера или любые другие

// когда никаких сообщений нет

// будет вызываться функция display

// так мы получаем анимацию

// если вам нужна статическая картинка,

// то закомментируйте следующую строку

auxIdleFunc(display);

// при изменении размеров окна

// придет соответсвующее сообщение

// в Windows - это WM_SIZE

// мы устанавливаем функцию resize,

// которая будет вызвана

// при изменении размеров окна

auxReshapeFunc(resize);
// далее, я устанавливаю ряд тестов и параметров

// тест прозрачности, т.е. будет учитываться

// четвертый параметр в glColor

glEnable(GL_ALPHA_TEST);

// тест глубины

glEnable(GL_DEPTH_TEST);

// glColor будет устанавливать

// свойства материала

// вам не надо дополнительно

// вызывать glMaterialfv

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);


// и последнее, устанавливаем

// функцию display отрисовки окна

// эта функция будет вызываться всякий раз,

// когда потребуется перерисовать окно

// например, когда вы развернете окно на весь экран

// в windows - это обработчик сообщения WM_PAINT

auxMainLoop(display);

}
Вот и все с функцией main(). Осталось написать код функции resize и функции display. Вставьте следующий код перед функцией main().


void CALLBACK resize(int width,int height)

{

// Здесь вы указываете ту чать окна,



// куда осуществляется вывод OpenGL.

glViewport(0,0,width,height);

glMatrixMode( GL_PROJECTION );

glLoadIdentity();

// Устанавливаем тип проекции.

// glOrtho - параллельная

// glFrustum - перспективная

// Параметры у этих функций одинаковые.

// Они определяют объем, который вы видите.

// левая стенка - пять единиц влево

// правая - пять единиц вправо

// далее, нижняя стенка и верхняя

// и наконец, передняя и задняя

// см. ниже картинку

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

// Устанавливаем точку, в которой

// находится наш глаз ---(0,0,5)

// направление, куда смотрим --- (0,0,0)

// вектор, принимаемый за направление вверх --- (0,1,0)

// этим вектором является ось Y

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

glMatrixMode( GL_MODELVIEW );

}

Здесь нужно сделать пояснения по поводу glMatrixMode. Функции glOrtho и glFrustum работают с матрицей, отвечающей за тип проекции. Они просто загружают соответствующую матрицу. Вы можете установить свой тип проекции, если вам это понадобится. Сначала вы говорите, что будете изменять матрицу проекции - glMatrixMode с параметром GL_PROJECTION. Потом, с помощью glLoadMatrix загружаете соответсвующую матрицу. Функции glTranslate/glRotate работают с матрицей вида. Ее мы загружаем последней строкой - glMatrixMode( GL_MODELVIEW ).



В параметрах контекста воспроизведения мы установили AUX_DOUBLE. Это значит, что рисоваться все будет сначала в буфер. Для того, что бы скопировать содержимое буфера на экран, вызывается функция auxSwapBuffers(). Если вы программировали анимацию для MSDOS или Windows, то наверняка использовали такой прием, чтобы избавиться от мерцания на экране. В функции display мы сначала очищаем буфер. Цвет, которым заполняется буфер при очищении, можно установить в функции main() вызовом glClearColor(r,g,b).
void CALLBACK display(void)

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


/* remove next tree lines

* and enter your code here

*/

glTranslated(0.01,0,0);



glColor3d(1,0,0);

auxSolidSphere( 1 );

auxSwapBuffers();

}

Вот и все.




Исходный файл смотрите здесь. Исполняемый файл здесь.
7.3  Windows-приложение - Win32 Application

Достоинством является непосредственное взаимодействие с WinAPI. Начальная инициализация несколько усложняется, но зато вы имеете полноценное windows-приложение. Такой тип приложения подходит для написания серьезных больших программ. Кто-нибудь, конечно, скажет, что приложение непереносимо. Вам нужно написать работающее приложение для windows, а не неработающее, но переносимое приложение.

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

Создайте проект Win32 Application. Инструкции смотри в предыдущем разделе. Только имена дайте win и win.c. Теперь будем писать файл win.c. Внесите комментарии, заголовочные файлы и функции display и resize, см. предыдущий раздел. Из функций display и resize уберите слово CALLBACK. А в функции display замениете auxSwapBuffers() на glFinish();


SwapBuffers(wglGetCurrentDC());
После включения заголовочных файлов объявите следующие глобальные переменные.
HWND hWnd;

HGLRC hGLRC;

HDC hDC;

Теперь вставьте код функции, которая устанавливает параметры контекста воспроизведения OpenGL.

int SetWindowPixelFormat()

{

int m_GLPixelIndex;



PIXELFORMATDESCRIPTOR pfd;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);

pfd.nVersion = 1;


pfd.dwFlags = PFD_DRAW_TO_WINDOW |

PFD_SUPPORT_OPENGL |

PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;

pfd.cColorBits = 32;

pfd.cRedBits = 8;

pfd.cRedShift = 16;

pfd.cGreenBits = 8;

pfd.cGreenShift = 8;

pfd.cBlueBits = 8;

pfd.cBlueShift = 0;

pfd.cAlphaBits = 0;

pfd.cAlphaShift = 0;

pfd.cAccumBits = 64;

pfd.cAccumRedBits = 16;

pfd.cAccumGreenBits = 16;

pfd.cAccumBlueBits = 16;

pfd.cAccumAlphaBits = 0;

pfd.cDepthBits = 32;

pfd.cStencilBits = 8;

pfd.cAuxBuffers = 0;

pfd.iLayerType = PFD_MAIN_PLANE;

pfd.bReserved = 0;

pfd.dwLayerMask = 0;

pfd.dwVisibleMask = 0;

pfd.dwDamageMask = 0;
m_GLPixelIndex = ChoosePixelFormat( hDC, &pfd);

if(m_GLPixelIndex==0) // Let's choose a default index.

{

m_GLPixelIndex = 1;



if(DescribePixelFormat(hDC,m_GLPixelIndex,sizeof(PIXELFORMATDESCRIPTOR),&pfd)==0)

return 0;

}
if (SetPixelFormat( hDC, m_GLPixelIndex, &pfd)==FALSE)

return 0;

return 1;

}
Информацию о структуре PIXELFORMATDESCRIPTOR смотрите в справочнике. Я пользуюсь MSDN. Сейчас MSDN входит в MS Developer Studio. Редактировать параметры этой структуры вам вряд ли придется. А если придется, то я не смогу тут описать все. Перевести справочник я, конечно, могу, но это вам вряд ли поможет. Книга не предназначена для этого. Здесь рассматриваются конкретные примеры и упражнения.

Теперь напишем функцию обработки сообщений нашего окна.
LRESULT CALLBACK WindowFunc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)

{

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



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

PAINTSTRUCT ps;


switch(msg)

{

// сообщение WM_CREATE приходит



// один раз при создании окна

case WM_CREATE:

// получаем контекст устройства нашего окна

hDC = GetDC(hWnd);

// устанавливаем параметры контекста воспроизведения OpenGL

SetWindowPixelFormat();

// создаем контекст воспроизведения OpenGL

hGLRC = wglCreateContext(hDC);

// делаем его текущим

wglMakeCurrent(hDC, hGLRC);


// далее см. предыдущий раздел

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);


break;
// это сообщение приходит при уничтожении окна

case WM_DESTROY:

// удаляем созданный выше

// контекст воспроизведения OpenGL

if (hGLRC)

{

wglMakeCurrent(NULL, NULL);



wglDeleteContext(hGLRC);

}

// освобождаем контекст устройства нашего окна



ReleaseDC(hWnd, hDC);

PostQuitMessage(0);

break;

// это сообщение приходит всякий раз,



// когда нужно перерисовать окно

case WM_PAINT:

BeginPaint(hWnd, &ps);

display();

EndPaint(hWnd, &ps);

break;


case WM_SIZE:

resize( LOWORD(lParam), HIWORD(lParam) );

break;
default:

return DefWindowProc(hWnd,msg,wParam,lParam);

}
return 0;

}

И последнее, осталось написать функцию WinMain.



int WINAPI WinMain(HINSTANCE hThisInst,

HINSTANCE hPrevInst,

LPSTR str,int nWinMode)

{

MSG msg;



WNDCLASS wcl;
wcl.hInstance=hThisInst;

wcl.lpszClassName = "OpenGLWinClass";

wcl.lpfnWndProc = WindowFunc;

wcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;


wcl.hIcon = NULL;

wcl.hCursor = LoadCursor(NULL,IDC_ARROW);

wcl.lpszMenuName = NULL;
wcl.cbClsExtra = 0;

wcl.cbWndExtra = 0;


wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

RegisterClass(&wcl);


hWnd = CreateWindow(

"OpenGLWinClass",

"Win API Template",

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,

200,

150,


400,

420,


HWND_DESKTOP, NULL,

hThisInst, NULL);


ShowWindow(hWnd,nWinMode);

UpdateWindow(hWnd);


while(1)

{

while( PeekMessage(&msg,NULL,0,0,PM_NOREMOVE) )



if(GetMessage(&msg,NULL,0,0))

{

TranslateMessage(&msg);



DispatchMessage(&msg);

}

else



return 0;
display();

}

return 0;



}

OpenGL требует свойства WS_CLIPCHILDREN и WS_CLIPSIBLINGS для окна в Windows. Поэтому были добавлены эти свойства при создании окна в функцию Createwindow. Также обратите внимание, что функция display вызывается в бесконечном цикле. Она вызывается, когда в очереди сообщений окна нет ничего. Эта же функция вызывается, когда нужно отрисовать окно заново - обработчик WM_PAINT.




Исходный файл смотрите здесь. Исполняемый файл здесь.
7.4  MFC-приложение - MFC AppWizard

Этот тип приложения обладаетя всеми достоинствами и недостаками WinAPI-приложения, рассмотренного выше, так как MFC - это библиотека классов С++, т.е. надстройка над WinAPI. Кто-нибудь, конечно, скажет, что приложение на плюсах немеренно большое, работает медленно и MFC для ленивых. В общем, тут у каждого свое мнение. Каждый по-своему прав. Тем не менее, я считаю, что для каждой задачи требуется свой инструмент. Где-то лучше использовать MFC, где-то WinAPI. Кричать, что первое или второе является незаменимым средством на все случаи жизни было бы неверным. У MFC есть свои особенности, отнести которые к достоинствам или недостаткам однозначно нельзя. В зависимости от решаемой задачи они идут в плюс или минус. Согласитесь,что глупо забивать сапожный гвоздь кувалдой или же скобу сапожным молотком.

Создаем проект:

1. Запустите MSVisualC++6.0

2. Щелкните меню File->New->MFC AppWizard(exe).

3. Выберете каталог и имя проекта задайте mfc, щелкните OK.

4. Step1: Поставьте переключатель на Single document, далее OK.

5. Step3: Уберите флажок ActiveX Controls, далее OK.

6. Щелкните Finish.

7. Щелкните Build->Set Active Configuration и установите тип проекта MFC - Win32 Release

8. Далее щелкаете Project->Settings->Link->Object/library modules: и добавьте туда opengl32.lib, glu32.lib и glaux.lib

В CMFCView объявите закрытую(private) переменную hGLRC типа HGLRC. Там же объявите функцию int SetWindowPixelFormat(HDC) и открытую(public) функцию display. Вот, что должно получиться:

class CMFCView : public CView

{

private:



CClientDC *pdc;

HGLRC hGLRC;

int SetWindowPixelFormat(HDC);
public:

void display();

...
Вставьте код этой функции в файл MFCView.cpp. Код возьмите из предыдущего раздела. Отредактируйте функцию CMFCView::PreCretaeWindow следующим образом:
BOOL CMFCView::PreCreateWindow(CREATESTRUCT& cs)

{

// TODO: Modify the Window class or styles here by modifying



// the CREATESTRUCT cs

cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

return CView::PreCreateWindow(cs);

}
Добавьте также функцию display сюда:


void CMFCView::display()

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


glTranslated(0.01,0,0);

glColor3d(1,0,0);

auxSolidSphere( 1 );
glFinish();

SwapBuffers(wglGetCurrentDC());

}
Теперь запустите View->Class Wizard и добавьте обработчик WM_CREATE,WM_DESTROY и WM_SIZE в класс CMFCView. Отредактируйте их следующим образом:
int CMFCView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CView::OnCreate(lpCreateStruct) == -1)



return -1;

pdc = new CClientDC(this);


if(SetWindowPixelFormat(pdc->m_hDC)==FALSE)

return -1;


hGLRC = wglCreateContext(pdc->m_hDC);

if(hGLRC == NULL)

return -1;
if(wglMakeCurrent(pdc->m_hDC, hGLRC)==FALSE)

return -1;


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);
float pos[4] = {3,3,3,1};

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

glLightfv(GL_LIGHT0, GL_POSITION, pos);

glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);


return 0;

}
void CMFCView::OnDestroy()

{

if(wglGetCurrentContext()!=NULL)



wglMakeCurrent(NULL, NULL) ;

if(hGLRC!=NULL)

{

wglDeleteContext(hGLRC);



hGLRC = NULL;

}
delete pdc;


CView::OnDestroy();

}
void CMFCView::OnSize(UINT nType, int cx, int cy)

{

CView::OnSize(nType, cx, cy);



glViewport(0,0,cx,cy);

glMatrixMode( GL_PROJECTION );

glLoadIdentity();

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

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

glMatrixMode( GL_MODELVIEW );

}
Пояснения смотри в предыдущих разделах. В StdAfx.h включите заголовочные файлы, после строчки
#include

#include

#include

#include


Запустите еще раз Class Wizard и добавьте функцию OnIdle в класс CMFCApp. Эта функция вызывается всякий раз, когда очередь сообщений окна пуста. Отредактируйте ее:
BOOL CMFCApp::OnIdle(LONG lCount)

{

( (CMFCView*) ((CMainFrame*)m_pMainWnd)->GetActiveView() )->display();



return 1;//CWinApp::OnIdle(lCount);

}


Исходный файл смотрите здесь. Исполняемый файл здесь.
7.5  Java-апплеты - Magician Library

Достоинством данного типа приложения, конечно же, является переносимость и незаменимое средство для web-программирования. Вы можете украсить свой web-сервер апплетами с трехмерной графикой. К вашим услугам все возможности OpenGL и объектно-ориентированного программирования. Недостатком является сложность программирования на языке Java. За короткое время(три месяца) работы с этим языком на меня свалилось очень много элементарых проблем: отсутствие форматированного ввода/вывода, непонятное поведение апплета - в разных броузерах по-разному; устаревшие методы, которые одним компилятором воспринимались нормально, а другой выдавал предупреждение, и прочие мелкие проблемы. Вообще, писать java-приложения, т.е. самостоятельные программы, я бы не советовал. Воспользуйтесь альтернативой - OpenGL Auxilary Library, рассмотренной в самом начале этой главы. Если же вам необходимо переносимое приложение, то возьмите его из примеров к Magician Library. Для java-апплетов - программ, исполняющихся в web-броузерах, альтернативы нет. Поэтому я и рассматриваю здесь программирование апплетов.

Комметировать java-код, я думаю, излишне после рассмотренных здесь трех примеров приложений. Я лишь приведу здесь свой шаблонный файл template.java.
import java.applet.*;

import java.awt.*;

import java.awt.event.*;

import com.hermetica.magician.*;

import com.hermetica.util3d.*;
public class template extends Applet implements GLEventListener

{

private CoreGL gl_ = new CoreGL();



private CoreGLU glu_ = new CoreGLU();

private GLComponent glc = null;

FrameRateComponent frc = new FrameRateComponent();

final int width = 400;


public void init()

{

glc = (GLComponent)GLDrawableFactory.createGLComponent(width, width);



add( "Center", glc );

/** Setup the context capabilities */

GLCapabilities cap = glc.getContext().getCapabilities();

cap.setDepthBits(12);

cap.setPixelType(GLCapabilities.RGBA);

cap.setColourBits(24);

cap.setRedBits(1);

cap.setDoubleBuffered(GLCapabilities.DOUBLEBUFFER);


frc.setSummaryOnly(true);
/** Register the GLEventListener with the GLComponent */

glc.addGLEventListener(this);


/** Initialize the component */

glc.initialize();

glc.start();

setSize(width,width);

}
public int getWidth() {

return width;

}
public int getHeight() {

return width;

}
/** Initialization stuff */

public void initialize( GLDrawable component )

{

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



float dir[] = {-1,-1,-1};
gl_.glClearColor( 1.0f, 1.0f, 0.796875f, 1.0f );

gl_.glShadeModel( GL.GL_SMOOTH );

gl_.glEnable(GL.GL_ALPHA_TEST);

gl_.glEnable(GL.GL_DEPTH_TEST);

gl_.glEnable(GL.GL_COLOR_MATERIAL);

gl_.glEnable(GL.GL_LIGHTING);

gl_.glEnable(GL.GL_LIGHT0);

gl_.glEnable(GL.GL_BLEND);

gl_.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl_.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, pos);

gl_.glLightfv(GL.GL_LIGHT0, GL.GL_SPOT_DIRECTION, dir);


}
/** Handles viewport resizing */

public void reshape( GLDrawable component, int x, int y,

int width, int height)

{

/** Setup the viewport */



gl_.glViewport( component, x, y, width, height);

gl_.glMatrixMode( GL.GL_PROJECTION );

gl_.glLoadIdentity();

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

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

gl_.glMatrixMode( GL.GL_MODELVIEW );

}
public void display( GLDrawable component )

{

frc.startSample();



gl_.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
gl_.glTranslated(0.01,0,0);

gl_.glColor3d(1,0,0);

shapes.solidSphere(0.6, 16, 16);

frc.stopSample();

}
public void stop()

{

if(glc.isRunning())



glc.suspend();

}
public void start()

{

if(glc.isRunning())



glc.resume();

}
public void destroy()

{

glc.destroy();



}

public GL getGL()

{

return gl_;



}

}

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

7.6 Linux-приложение - Mesa Library

7.7  Упражнение:"Переносим игру Arcanoid"



Перенесите во все, указанные здесь приложения, игру Arcanoid.



Достарыңызбен бөлісу:
1   2   3   4




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет