codingМихаил Фленов/ www.vr-online.ru /Х-лаба #1Создание изображений с использованиембиблиотеки OpenGLЗадание: аппроксимировать заданную поверхность полигональной сеткой и средствамиOpenGL обеспечить для нее возможностьа) изображения в параллельной и перспективной проекции,б) удаления невидимых линий и поверхностей,в) реалистичного освещения,г) каркасного изображения,д) пространственных аффинных преобразований.Исходные данные, определяющие поверхность, должны считываться из текстовогофайла. Формат представления исходных данных разрабатывается студентами самостоятельно.В зависимости от номера студента в группе предоставляются на выбор следующиеповерхности: сфера с вырезами, конус с вырезами, цилиндр с вырезами и т.д.Сегодня в самом первом выпуске нашего революционного Х-проекта :) мы рассмотримлабу, которую задают на третьем курсе факультета прикладной математикив МАИ, (спецкурс «Компьютерная гра фика»). Я немного усложнил задание, так чточитай — будет интересно.УсложняемЧто такое «аппроксимировать поверхность»?Если посмотреть на фигуры, которые нампредлагают, то видно, что все они имеют формус изгибами. Невозможно создать в компьютернойграфике сферу, можно только рисовать точкии линии, а окружности создаются с помощьюбольшого количества линий (трехмерныеобъекты — из треугольников). Чем большелиний, тем более округлой будет получатьсяформа объекта. Аппроксимировать означаетсоздать объект, максимально приближенныйк реальному. А насколько приближенным егонужно сделать в этом задании? Ладно, выберемстепень соответствия на свое усмотрение.Данные необходимо загружать изфайла, но это же серьезное упущение! Отображениедолжно происходить полигональнымметодом, поэтому какая разница, какие данныев файле — сфера, цилиндр или пышные формыПамелы Андерсон? Достаточно одному студентувыполнить задание, а все остальные должнытолько чуть изменить формат файла и переменныев исходнике, чтобы идентичность кода не бросаласьв глаза. После этого нужно создать необходимуюфигуру в 3DS Max, сохранить ее в файле, и можносчитать, что задание выполнено. Мы усложнимзадачу и будем генерить фигуру программно.ИнициализацияИтак, давай напишем программку, котораябудет динамически формировать сферу. Для началасоздадим пустое Win32‐приложениеи сразу же добавим необходимые заголовочныефайлы, а именно:#include #include Первый заголовочный файл подключает функцииOpenGL, которые нам предстоит использоватьдля отображения сферы. Вторая строкаподключает математические функции./ 126xàêåð 02 /98/ 07
codingCферa с закрашенными полигонамиКаркас сферыТак как сфера будет генериться, нам понадобятсятригонометрические функции из math.h. Теперь идем в Свойства проекта и в Установкахлинкера в строке Additional Dependenciesдобавляем библиотеку opengl32.lib. Библиотекурасширенных функций glu мы использоватьне будем (да, она упростила бы созданиесферы, цилиндра и т.д., но, судя по заданию, мыне имеем права обращаться к ней).Из глобальных переменных нам понадобитсяhrc типа HGLRC, в которой мы будем сохранятьконтекст рисования OpenGL.Теперь движемся в функцию InitInstance, гдесоздается окно. После его создания добавляемкод из листинга 1. Я думаю, стоит рассмотретьэтот листинг поподробнее, поскольку его пониманиенеобходимо для выполнения задания.После получения контекста рисования окна, мыдолжны задать формат пикселя. В данномслучае я использую RGBA-формат в 24 бита.Помимо этого, для повышения скоростивключается двойная буферизация (флагPFD_DOUBLEBUFFER). В исходнике, которыйты найдешь на DVD, задание формата пикселяя вынес в отдельную функцию SetPixelFormat.Далее идет создание контекста рисованияOpenGL с помощью функции wglCreateContext (онбудет «текущим» — wglMakeCurrent). Это стандартнаяоперация при инициализации OpenGL.Теперь самое интересное. В заданииопределена необходимость удаленияневидимых линий и поверхностей. Но какэто сделать? В OpenGL для этого нужновсего лишь включить тест глубины. Без неговыводится все подряд, и объекты, находящиесядальше, могут оказаться в одной позициис более близкими объектами. Чтобы включитьтест глубины, пишем:glEnable(GL_DEPTH_TEST);Существует множество вариантов тестовглубины. Их можно задать с помощью функцииglDepthFunc. В этом примере я задействуютест GL_LESS, который используетсяпо умолчанию. Про все остальные их видыты можешь узнать из файла справки по функцииglDepthFunc.ОсвещениеОсвещение — это отдельная история. Согласнозаданию, мы должны обеспечить реалистичноеосвещение. Но как это сделать? Реалистичноеосвещение — это целая наука, для него существуетвеликое множество алгоритмов и методов(OpenGL в этом плане может практическивсе. Самое реалистичное, на мой взгляд, освещениеможно получить только с использованиемвершинных шейдеров. Но я надеюсь, что составителизадания не пошли так далеко в своихзапросах и не испортили нам всю жизнь, и поэтомуиспользую освещение, предоставляемоефункциями OpenGL.Итак, чтобы в нашей сцене появился источникосвещения, необходимо его создать, выбратьмодель и указать его положение. В OpenGL первыйпункт достаточно прост, поскольку нам ужедоступны источники с именами GL_LIGHTi, гдеi изменяется от 0 до GL_MAX_LIGHTS. В моемзаголовочном файле эта константа равна0x0D31. Я думаю, этого будет вполнедостаточно. Мы будем использовать одинисточник освещения — GL_LIGHT0. Чтобызадать его положение, используем следующийкод:GLfloat position[] ={ 0.0, 1, — 1.5, 0.0 };glLightfv(GL_LIGHT0, GL_POSITION, position);В первой строке мы задаем массив из четырехчисел типа GLfloat, которые определятпозицию источника освещения в нашеммире. Во второй — вызываем функциюglLightfv, которая имеет 3 параметра:1. источник освещения;2. параметр, который нужно изменить; мыбудем устанавливать позицию света, поэтомууказываем константу GL_POSITION;3. вектор (массив из четырех значений), задающийпозицию.Эта позиция будет трансформирована в матрицуmodelview нашего мира.Если не задать положение источника света, топо умолчанию будет использоваться вектор(0,0,1,0). Можно еще задать тип лампочки — рассеянныйсвет, прожектор и т.д., но это уже деловкуса и цвета. В задании тип света не указан.Итак, позицию лампочки мы задали, теперьнеобходимо включить свет.Нет, для этого не нужно вызывать электрикаалкаша,нужно просто написать две следующиестроки:Листинг 2case WM_SIZE:// Определяем размеры окнаRECT r;GetWindowRect(hWnd, &r);width = r.right-r.left;height = r.bottom-r.top;// Использовать матрицу ProjectionglMatrixMode(GL_PROJECTION);// Загрузить единичную матрицупреобразованийglLoadIdentity();// В зависимости от размеров окнасоздаем параллельную проекциюif (width