Opengl приложение a


Содержание

Портирование приложений OpenGL* на Andro >

Опубликовано: 28 апреля 2013 г.

Возможность портирования игр и других приложений, интенсивно использующих 3D-графику на основе стандартов OpenGL, на устройства Android на базе Intel® Atom, имеет для нас огромную важность, поскольку существует множество уже изданных приложений, игровых движков и других программ на базе OpenGL; OpenGL обеспечивает переносимость программ, а Android постоянно расширяет поддержку OpenGL ES и C/C++. Многие приложения на базе OpenGL даже доступны в виде открытого исходного кода, например серия Quake компании Id Software. В данной статье, состоящей из двух частей, показано, как начать портирование, и подробно описаны препятствия, существующие при портировании компонентов отрисовки в приложениях, собранных в более ранних версиях OpenGL, на платформу Android на базе процессоров Intel Atom. Кроме того, здесь рассматривается портирование кода OpenGL из настольных операционных систем, таких как Windows* и Linux*, и из приложений, использующих внедренные версии OpenGL ES, с системой управления окнами или без нее.

В первой части данной статьи содержится вводная информация об использовании OpenGL ES на платформе Android с помощью пакета средств разработки программного обеспечения (SDK) или пакета Android NDK (Native Development Kit) и о том, по какому принципу выбирается тот или иной пакет. Здесь описаны различные примеры приложений OpenGL ES в SDK и NDK и интерфейс JNI (Java* Native Interface), позволяющий комбинировать компоненты Java и C/C++. Также здесь обсуждается выбор версии OpenGL ES — 1.1 или 2.0.

Во второй части рассматриваются препятствия, существующие при портировании игр OpenGL, которые необходимо изучить, прежде чем начинать подобный проект, в том числе различия расширений OpenGL, поддержка операций с плавающей запятой, сжатые форматы текстур и библиотека GLU. Кроме того, здесь описана процедура настройки системы разработки для Android на базе процессоров Intel Atom и пути достижения максимально возможной производительности при эмуляции виртуального устройства Android при использовании OpenGL ES.

Графика на платформе Android

Существуют четыре различных способа отрисовки графики на платформе Android, и у каждого из них есть свои сильные стороны и ограничения (см. табл. 1). В задачи данной статьи не входит подробное описание всех этих вариантов. С точки зрения портирования кода OpenGL с других платформ для нас важны только два из них: классы SDK Wrapper для OpenGL ES и NDK для разработки OpenGL ES в C/C++. Что касается других вариантов, SDK Canvas представляет собой мощный API-интерфейс для создания 2D-графики, который можно использовать в сочетании с OpenGL ES, однако его возможности ограничены 2D-графикой и для его использования требуется написание нового кода.

API-интерфейс Renderscript для Android изначально поддерживал OpenGL ES, но поддержка прекратилась в версии API уровня 16 (Jelly Bean) и была недоступна для новых проектов. Наилучшей областью применения Renderscript в настоящее время является повышение производительности алгоритмов с большими объемами вычислений, но низкой потребностью в выделении памяти и передаче данных, таких как расчет физических параметров в играх-симуляторах.

Таблица 1. Четыре способа отрисовки графики на платформе Android

Способ Ограничения
SDK Canvas API Только графика Java и 2D
SDK Wrapper classes for OpenGL ES Функции OpenGL ES 1.1 и 2.0 могут вызываться из Java, но с дополнительным потреблением ресурсов интерфейсом JNI
NDK OpenGL ES Функции OpenGL ES 1.1 и 2.0 с нативным кодом C/C++ могут вызываться из Java
Renderscript для OpenGL ES Поддержка OpenGL ES прекращена в версии API уровня 16

Портирование приложений OpenGL в более ранние версии Android было затруднено, поскольку старый код OpenGL был чаще всего написан на C или C++, а Android поддерживал только Java до выпуска NDK в версии Android 1.5 (Cupcake). OpenGL ES 1.0 и 1.1 поддерживались с самого начала, но производительность была неустойчива, поскольку ускорение не являлось обязательным. Однако за последние годы Android совершил огромный скачок вперед. В SDK версии Android 2.2 (Froyo) и NDK редакции 3 была добавлена поддержка OpenGL ES 2.0, а в NDK редакции 7 появилась улучшенная поддержка расширений OpenGL ES. На сегодняшний день OpenGL ES 2.0 так интенсивно используется в собственной графической платформе Android, что его наличие стало считаться обязательным. OpenGL ES 1.1 и 2.0 с ускорением теперь в обязательном порядке присутствуют на всех новых устройствах Android, что в особенности проявилось с ростом размера экранов. Сегодня Android обеспечивает устойчивую и надежную производительность приложений с интенсивным использованием 3D-графики, созданных на основе кода OpenGL ES 1.1 или 2.0 на языке Java или C/C++, и у разработчиков появилось несколько средств для упрощения процесса портирования.

Использование Android Framework SDK с классами OpenGL ES Wrapper

Платформа Android SDK предоставляет набор классов-оболочек для трех версий OpenGL ES, поддерживаемых на Android (1.0, 1.1 и 2.0). Данные классы позволяют с легкостью вызывать в системе Android драйверы OpenGL ES из кода Java, даже при том, что эти драйверы выполняются нативно. Если вы создаете новую игру OpenGL ES Android с нуля или хотите преобразовать старый код C/C++ в Java, то этот способ, вероятно, будет для вас наиболее простым. Хотя язык Java предназначен для обеспечения переносимости, при портировании приложений Java могут возникать сложности, поскольку Android не поддерживает полный набор утвержденных классов, библиотек и функций API Java Platform Standard Edition (Java SE) и Java Platform Micro Edition (Java ME). Интерфейс Android Canvas API включает в себя широкий набор функций 2D API, но он поддерживается только в Android и не обеспечивает совместимость со старым кодом.

В Android SDK есть несколько дополнительных классов, упрощающих использование OpenGL ES, например GLSurfaceView и TextureView . Класс GLSurfaceView аналогичен классу SurfaceView, который используется с Canvas API, за исключением нескольких специальных дополнительных функций для OpenGL ES. Он обрабатывает необходимую инициализацию библиотеки EGL (Embedded System Graphics Library), размещает отображаемую в Android поверхность в определенном месте на экране и создает поток. Кроме того, в него включены полезные функции для трассировки и отладки вызовов OpenGL ES. Можно быстро создать новое приложение OpenGL ES, реализовав всего три метода для интерфейса GLSurfaceView.Renderer() , как показано в табице 2.

Таблица 2. Минимально необходимые методы для интерфейса GLSurfaceView.Renderer

Метод Описание
onSurfaceCreated() Вызывается один раз, когда приложение запускается для инициализации
onSurfaceChanged() Запускается каждый раз, когда происходит изменение размера или ориентации GLSurfaceView
onDrawFrame() Вызывается повторно для отрисовки каждого кадра графической сцены

Начиная с версии Android 4.0, можно использовать класс TextureView вместо GLSurfaceView , позволяющий отрисовывать поверхности OpenGL ES с дополнительными возможностями, хотя для этого потребуется написать больше кода. Поверхности TextureView работают аналогично обычным проекциям (Views) и могут использоваться для отрисовки поверхностей за пределами экрана. Эта функция позволяет перемещать, преобразовывать, анимировать поверхности и выполнять наложение полупрозрачных изображений TextureView , когда они соединяются на экране. Класс TextureView также позволяет сочетать отрисовку OpenGL ES и Canvas API.

Классы Bitmap и GLUtils упрощают создание текстур для OpenGL ES благодаря использованию функций Android Canvas API и позволяют загружать текстуры из файлов PNG, JPG и GIF. Экземпляры Bitmap используются для размещения отрисованных поверхностей Android Canvas API. Класс GLUtils позволяет преобразовывать изображения из растрового формата в текстуры OpenGL ES. Благодаря этой интеграции с помощью Canvas API можно отрисовывать 2D-изображения, используемые затем в качестве текстур в OpenGL ES. Это особенно полезно при создании графических элементов, которые невозможно создать в OpenGL ES, например виджетов графического интерфейса и шрифтов. Но для использования этих функций, конечно, необходимо написать новый код Java.

Класс Bitmap был разработан прежде всего для использования с Canvas API, и при его использовании для загрузки текстур в OpenGL ES возникает несколько серьезных ограничений. Функции Canvas API соответствуют спецификации наложения по альфа-каналу Портера-Даффа, и класс Bitmap оптимизирует изображения со значениями альфа-канала для каждого пикселя, сохраняя их в формате с премультипликацией (A, R*A, G*A, B*A). Этот механизм подходит для алгоритма Портера-Даффа, но не для OpenGL, где требуется формат матрицы без премультипликации (ARGB). Это означает, что класс Bitmap можно использовать только с полностью непрозрачными текстурами (или с текстурами без значений альфа-канала для каждого пикселя). В трехмерных играх обычно требуются текстуры с альфа-каналом, поэтому вместо растровых изображений следует использовать для загрузки текстур байтовые массивы или NDK.

Еще одним недостатком класса Bitmap является поддержка загрузки только изображений в формате PNG, JPG или GIF, тогда как в играх OpenGL обычно используются сжатые форматы текстур, которые распаковываются графическим процессором и часто зависят от его архитектуры, например ETC1 и PVRTC. Классы Bitmap и GLUtils не поддерживают текстуры с зависимым форматом сжатия и мипмаппинг. Поскольку данные функции интенсивно используются в большинстве 3D-игр, это существенно усложняет портирование старых игр OpenGL на Android с использованием SDK. До тех пор, пока Google не решит эти проблемы, лучше всего просто отказаться от загрузки текстур с помощью классов Bitmap и GLUtils . Форматы текстур обсуждаются далее в разделе «Сжатые форматы текстур».

В Android ApiDemos содержится образец приложения с именем StaticTriangleRenderer , на примере которого показана загрузка непрозрачной текстуры из ресурсного файла PNG с использованием оболочки GLES10 для OpenGL ES 1.0 и классов GLSurfaceView , Bitmap и GLUtils . Аналогичная версия с именем GLES20TriangleRenderer , напротив, использует класс-оболочку GLES20 для OpenGL ES 2.0. Эти образцы служат хорошими отправными точками для разработки игр OpenGL ES, если вы рассчитываете использовать Android Framework SDK и классы-оболочки. Не используйте оригинальную версию с именем TriangleRenderer , поскольку в ней применяется оболочка для более старой версии привязок OpenGL ES к Java с именем javax.microedition.khronos.opengles . Компания Google выпустила новые привязки, чтобы обеспечить статический интерфейс для OpenGL ES специально под Android. У этих статических привязок более высокая производительность, они поддерживают больше функций OpenGL ES и позволяют использовать модель программирования, более близкую к использованию OpenGL ES с кодом C/C++, что упрощает повторное использование кода.

Android Framework SDK обеспечивает поддержку OpenGL ES с помощью нескольких привязок для Java из Google и Khronos, как показано в таблице 3.

Таблица 3. Сводная информация о классах-оболочках для OpenGL ES и образцах приложений

Привязки Java для OpenGL ES Описание Образцы приложений
javax.microedition.khronos.egl Стандартное определение Khronos
javax.microedition.khronos.opengles Стандартное определение Khronos TriangleRenderer, Kube, CubeMap
android.opengl Специальные статические интерфейсы для Android

Специальные статические привязки для Android имеют более высокую производительность и должны по мере возможности использоваться вместо привязок Khronos. Статические привязки предоставляют соответствующие классы-оболочки для всех версий OpenGL ES, доступных для разработки приложений под Android. Эти классы перечислены в таблице 4.

Таблица 4. Сводная информация о классах-оболочках Android для OpenGL ES и образцах приложений

Версия API Класс Java Образцы приложений
OpenGL ES 1.0 android.opengl.GLES10 StaticTriangleRenderer, CompressedTexture
OpenGL ES 1.0 android.opengl.GLES10Ext
OpenGL ES 1.1 android.opengl.GLES11
OpenGL ES 1.1 android.opengl.GLES11Ext
OpenGL ES 2.0 android.opengl.GLES20 GLES20TriangleRenderer, BasicGLSurfaceView, HelloEffects

Эти классы-оболочки устроены так, что большинство вызовов OpenGL ES в старом коде C/C++ можно преобразовать в Java, просто добавив к именам функций и символов OpenGL ES в виде префикса класс-оболочку подходящей версии API. См. примеры в таблице 5.

Таблица 5. Примеры преобразования вызовов OpenGL ES из C в Java

Язык C Язык Java
glDrawArrays(GL_TRIANGLES, 0, count) GLES11.glDrawArrays(GLES11.GL_TRIANGLES, 0, count)
glDrawArrays(GL_TRIANGLES, 0, count) GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, count)

При портировании игр OpenGL на Android с помощью этих классов-оболочек имеются три значительных ограничения: дополнительное потребление ресурсов средой Java Virtual Machine и JNI и работа по преобразованию старого кода C/C++ в Java. Java представляет собой интерпретируемый язык, и любой код Java на Android выполняется в виртуальной машине Dalvik и поэтому работает медленнее, чем скомпилированный код C/C++. Поскольку драйверы OpenGL ES всегда выполняются нативно, каждый вызов функции OpenGL ES через эти оболочки влечет за собой дополнительное потребление ресурсов интерфейсом JNI, что ограничивает производительность отрисовки игровой графики. Чем больше вызовов OpenGL ES выполняет ваше приложение, тем больше ресурсов потребляет интерфейс JNI. К счастью, структура OpenGL ES помогает минимизировать число необходимых обычно вызовов в ресурсоемких циклах отрисовки. Если производительность действительно ухудшается, вы всегда можете преобразовать ресурсоемкий код в C/C++ с помощью NDK. Разумеется, если изначально код был написан на C/C++, лучше всего сразу выбрать вариант с NDK.

Должны ли вы преобразовывать код C/C++ в Java — это зависит от особенностей вашего проекта. Если кода C/C++ сравнительно немного, он понятен и у вас нет задачи достичь сверхпроизводительности, то преобразование кода в Java будет оправданным. Иначе при большом объеме малопонятного кода C/C++ и высоких требованиях к производительности лучше использовать NDK.

Использование Android NDK

В июне 2009 года компания Google представила пакет NDK для Android, позволяющий выполнять в приложениях код C/C++ как нативный с более высокой производительностью по сравнению с Java. А поскольку большая часть имеющегося кода OpenGL написана на C или C++, в NDK предусмотрен более простой способ портирования приложений, особенно при наличии больших объемов кода C/C++, преобразование которых в Java нецелесообразно. Фактически, основная причина, по которой компания Google решила сделать пакет NDK общедоступным — это упрощение портирования игр OpenGL на Android. Благодаря этим преимуществам NDK становится наиболее популярным вариантом реализации приложений, требующих быстрой отрисовки графики в Android.

Пакет NDK позволяет компилировать код C/C++ в библиотеки общих объектов Linux, статично привязанные к приложению для Android. Библиотеки собираются с помощью средств GNU, которые предоставляются в пакете NDK от компании Google и могут запускаться в системах разработки для Windows, Mac OS* X или Linux при использовании интегрированной среды разработки Eclipse* или интерфейса командной строки. Этот набор средств поддерживает три процессорные архитектуры: ARM, Intel Atom (x86) и MIPS. Доступны все возможности C/C++, но большинство функций Linux API недоступно. Фактически непосредственная поддержка распространяется на API-интерфейсы OpenGL ES, OpenMAX* AL, OpenSL ES*, zlib, API ввода-вывода файлов Linux и немногие другие, которые Google относит к категории стабильных API-интерфейсов. Однако доступна документация по портированию других библиотек Linux в проект NDK, если это необходимо.

NDK позволяет произвольно распределять код между Java и C/C++ в соответствии с требованиями вашего приложения. В NDK поддерживается механизм вызова кода C/C++ из Java, называемый Java Native Interface. Но вызовы JNI приводят к дополнительному расходованию ресурсов, поэтому важно соблюдать осторожность при распределении приложений и использовать нативный код для минимизации вызовов через JNI. Обычно для наилучшей производительности и простоты портирования большая часть кода OpenGL ES остается на C/C++, а новый код Java пишется для использования класса GLSurfaceView и других классов SDK с целью управления событиями жизненного цикла приложения и поддержки других игровых функций. Android поддерживает JNI для процессоров Intel Atom начиная с NDK редакции 6b.

Пакет NDK поддерживает OpenGL ES 1.1 и 2.0 и включает в себя образцы приложений для обеих версий, которые также демонстрируют сочетание функции C и Java при использовании JNI. Эти приложения отличаются распределением кода между Java и C и реализацией многопоточности. Все эти приложения используют NDK и нативный код C, но в образце native-media весь код OpenGL ES остается на языке Java, в образцах san-angeles и native-activity весь код OpenGL ES на языке C, а в образце hello-gl2 код EGL и OpenGL ES распределен между Java и C. Использовать образец hello-gl2 не рекомендуется, поскольку в нем присутствует это разделение и не реализован предпочтительный метод конфигурирования GLSurfaceView для поверхностей OpenGL ES 2.0, состоящий в вызове метода setEGLContextClientVersion(2) . См. в таблице 6.

Таблица 6. Сводная информация об образцах приложений OpenGL ES в NDK

Используемый API-интерфейс Образец приложения Распределение между SDK и NDK
OpenGL ES 1.1 san-angeles Весь код EGL и OpenGL ES на C.
OpenGL ES 1.1 native-activity Весь код на C, и используется класс NativeActivity.
OpenGL ES 2.0 hello-gl2 Настройка EGL на Java, а код OpenGL ES на C.
OpenGL ES 2.0 native-media Весь код EGL и OpenGL ES на Java.

Образец bitmap-plasma не использует OpenGL ES, но он также интересен тем, что демонстрирует использование библиотеки jnigraphics для реализации нативных функций, непосредственно обрабатывающих пиксели растровых изображений Android.

События жизненного цикла Activity и многопоточность

Android требует, чтобы все вызовы OpenGL ES выполнялись из одного потока, поскольку контекст EGL может быть связан только с одним потоком, а отрисовка графики в главном потоке пользовательского интерфейса категорически не приветствуется. Лучше создать отдельный специальный поток для всего вашего кода OpenGL ES, который всегда будет выполняться из одного этого потока. Если в вашем приложении используется класс GLSurfaceView , этот выделенный поток отрисовки OpenGL ES создается автоматически. В противном случае приложение должно будет само создать поток отрисовки.

В образцах приложений san-angeles и native-activity весь код OpenGL ES на C сохранен, но в san-angeles используется часть кода Java и класс GLSurfaceView для создания потока отрисовки и управления жизненным циклом Activity, а в native-activity кода Java нет. Управление событиями жизненного цикла Activity осуществляется на C без использования GLSurfaceView , и используется поток отрисовки, предоставляемый классом NativeActivity. NativeActivity — это удобный класс из NDK, позволяющий реализовать обработчики событий жизненного цикла Activity в виде нативного кода, например onCreate() , onPause() и onResume() . К некоторым службам и поставщикам контента Android невозможно получить непосредственный доступ из нативного кода, но можно реализовать это через JNI.

Образец native-activity является хорошей отправной точкой для портирования игр OpenGL ES, поскольку в нем показано использование класса NativeActivity и статической библиотеки android_native_app_glue для обработки событий жизненного цикла в нативном коде. Эти классы предоставляют отдельный поток отрисовки для кода OpenGL ES, поверхность отрисовки и окно на экране, поэтому вам не нужны классы GLSurfaceView и TextureView . Основной точкой входа этого нативного приложения является функция android_main() , выполняемая в своем собственном потоке и имеющая свой собственный цикл событий, позволяющий принимать входящие события. К сожалению, NDK не включает в себя версию этого образца для OpenGL ES 2.0, но вы можете заменить весь код версии 1.1 в этом образце на свой код версии 2.0.

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

Java Native Interface

Если вы реализуете свое приложение преимущественно на C/C++, вам будет сложно избежать использования некоторых классов Java в случае крупных и более профессиональных проектов. Это, например, доступные только в SDK API-интерфейсы Android AssetManager и Resources, являющиеся предпочтительными для обработки интернационализации, различных размеров экрана и других ситуаций. Интерфейс JNI также является хорошим решением, поскольку он позволяет вызывать не только функции C/C++ из кода Java, но и классы Java из кода C/C++. Поэтому, несмотря на дополнительное расходование ресурсов, интерфейсом JNI не следует пренебрегать. Он обеспечивает наилучший способ доступа к важным системным функциям, предлагаемым только в SDK, особенно когда для этих функций не обязательно добиваться высокой производительности. Мы не будем приводить в данной статье полное описание использования JNI, но выделим три основных этапа, необходимых для создания вызова C/C++ из Java с помощью JNI:

  1. Добавление объявления функции C/C++ в файл класса Java в качестве нативного типа.
  2. Добавление статического инициализатора библиотеки общих объектов, содержащей нативную функцию.
  3. Добавление функции с соответствующим именем, согласованным с применяемой схемой именования, в файл с нативным исходным кодом программы.

Примечание. Дополнительные сведения об использовании JNI с NDK см. по адресу http://developer.android.com/training/articles/perf-jni.html.

OpenGL ES 1.1 или 2.0?

Итак, на какую версию OpenGL ES вы должны ориентироваться в приложении для Android? Версия OpenGL ES 1.0 была заменена на 1.1, поэтому выбирать приходится только из двух версий: 1.1 и 2.0. Компании Khronos и Google, возможно, будут поддерживать обе эти версии в течение неограниченного срока, но OpenGL ES 2.0 по многим параметрам превосходит версию 1.1. Она гораздо более универсальна и обеспечивает более высокую производительность благодаря функциям программирования шейдеров (обработчиков теней) в языке GLSL (OpenGL Shading Language) ES. В ней даже может быть снижена потребность к коде и памяти для текстур. Но у Khronos и Google остается, по крайней мере, одна веская причина для того, чтобы продолжать поддержку версии 1.1, — эта версия по внешнему виду намного ближе к оригинальному стандарту OpenGL 1.x, применяемому в настольных ПК и игровых консолях во всем мире на протяжении десятилетий. Поэтому старые игры может быть проще портировать на OpenGL ES 1.1, чем на 2.0; и, чем старее игра, тем вероятность этого выше.

Если в портируемой игре нет кода шейдера, можно выбрать OpenGL ES версии как 1.1, так и 2.0, хотя работать с версией 1.1, вероятно, будет проще. Если в игре уже есть код шейдера, то, безусловно, лучше выбрать OpenGL ES 2.0, особенно если учесть, что в последних версиях Android используется преимущественно версия 2.0. Как сообщает компания Google, по состоянию на октябрь 2012 года более 90 процентов устройств Android, обращающихся на веб-сайт Google Play, поддерживают OpenGL ES версии как 1.1, так и 2.0.

Заключение

Отрисовку графики OpenGL ES на Android можно реализовать с помощью Android SDK, NDK или их комбинации с использованием JNI. При использовании пакета SDK потребуется написание кода на Java, и этот способ лучше всего подходит для разработки новых приложений, тогда как NDK делает целесообразным портирование уже имеющегося кода OpenGL на языке C/C++. Для большинства проектов портирования игр потребуется сочетание компонентов SDK и NDK. В новых проектах рекомендуется ориентироваться на версию OpenGL ES 2.0, а не 1.1, кроме случаев, когда имеющийся код OpenGL настолько старый, что не использует функции шейдеров GLSL.


Во второй части этой серии статей рассматриваются препятствия, существующие при портировании игр OpenGL, которые необходимо изучить, прежде чем начинать подобный проект, в том числе различия расширений OpenGL, поддержка операций с плавающей запятой, сжатые форматы текстур и библиотека GLU. Кроме того, здесь описана настройка системы разработки для Android на базе процессоров Intel Atom и пути достижения максимально возможной производительности в средствах эмуляции виртуального устройства Android при использовании OpenGL ES.

Простейший пример с OpenGL (C++)

09.05.2012, 12:32

Простейший пример с комментариями
Всем доброго времени суток! Моя просьба заключается в следующем: так как я полный новичок в OpenGl.

Простой пример OpenGL
Здравствуйте! У меня Visual C++ 6. Так вот. У меня не запускаются не один пример OpenGL, дайте мне.

Дайте полный пример на OpenGL
Пусть будет полноэкранка с одной простенькой моделькой в паре простых не расширенных буферов.

OpenGL С чего начать? Простой пример
Всем привет! С чего нужно начать, что скачать, что подключить.. для работы в VS C++ с OpenGL.

OpenGL рисование с VBO пример кода на голом WinAPI
Изучение VBO оказалось муторным занятием, хотя опытные программисты говорят, что от массива вершин.

Opengl приложение a

В примере с помощью SDL2 создаётся окно и программируется основной событийный цикл, использующий OpenGL.

Содержание

Почему OpenGL?

Для программирования 3D-графики вам потребуется программный интерфейс 3D-рисования. За прошедшие десятилетия наибольшее распространение получили два стандарта — OpenGL и DirectX. OpenGL даёт возможность писать код, работающий на любой платформе и любом устройстве, поэтому мы сосредоточимся на изучении OpenGL.

Что такое OpenGL

OpenGL — это спецификация API для рисования трёхмерной графики. Он позволяет переслать данные для рисования одного кадра, которые затем будут растеризованы в двумерный кадр силами конкретной реализации OpenGL. После растеризации кадр можно отправить для вывода в рамке окна или получить его как изображение.

В основном OpenGL оперирует треугольниками, изображениями и состояниями драйвера рисования.

На современных платформах API OpenGL реализует либо драйвер видеокарты, либо программный растеризатор в составе операционной системы. Реализации в драйвере работают быстрее, потому что все сложные вычислительные задачи они переносят на видеокарту. Видеокарта содержит множество процессорных ядер, хорошо приспособленных для параллельной обработки множества векторов и матриц, поэтому рисование трёхмерного мира она выполняет гораздо быстрее центрального процессора.

Возможности OpenGL сильно зависят от операционной системы и от производителя драйвера. OpenGL на Linux и на Windows имеют разные возможности. OpenGL в драйверах от NVIDIA и в драйверах от Intel также различаются. Тем не менее, можно писать код, одинаково качественно работающий на любой реализации OpenGL.

Версии OpenGL

Разработка стандарта OpenGL началась в 1992 году на основе существовавших ранее разработок компании Silicon Graphics. С тех пор вышли версии OpenGL 1.1, OpenGL 1.2, а затем OpenGL 2.x, OpenGL 3.x и серия OpenGL 4.x: от 4.0 до 4.6.

За это время взгляды на 3D-графику изменились. Было обнаружено, что программный интерфейс, разработанный для OpenGL 1.0, имеет недостаточную гибкость и потворствует потерям производительности при рисовании графики. Начиная с OpenGL 3.0, была представлена полностью новая модель программирования с использованием OpenGL, и старый способ был объявлен устаревшим, но оставлен ради обратной совместимости.

В наших уроках мы будем опираться на возможности OpenGL 3.0 и выше, в том числе применим новую модель рисования 3D-графики. Следует учесть, что на платформе Windows функции версий OpenGL выше 1.1 нужно получать через механизм расширений. Это ограничение не действует на остальных платформах и может быть прозрачно скрыто от программиста, как мы покажем позднее.

От SDL2 к OpenGL

OpenGL работает с графическим контекстом OpenGL. Проблема в том, что OpenGL сам не умеет создавать свой контекст — это ограничение наложено для максимальной кроссплатформенности и гибкости. Для создания контекста OpenGL нам нужно:

  • создать окно в оконной операционной системе
  • попросить операционную систему создать для этого окна контекст OpenGL, реализуемый драйвером или программно — на усмотрение ОС

Детали взаимодействия с системой может спрятать мультимедийная библиотека, такая как SDL2, SFML, Cocos2d-x, Cinder или OpenScehegraph. Мы будем использовать SDL2, потому что она

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

Базовый пример кода на SDL2 приведён в документации функции SDL_GL_CreateContext:

Основной цикл программы

Будучи библиотекой, а не Фреймворком, SDL2 не навязывает программисту ни стиль программирования, ни архитектуру программы. Поэтому, основной цикл следует написать самостоятельно. Простейший вариант выглядит так:

Отслеживаем интервалы времени

Обновление игрового состояния не должно зависеть от реальной частоты кадров, которую может обеспечить устройство. Поэтому функция UpdateCurrentScene должна принимать время, прошедшее с момента предыдущего обновления. Реализовать это можно с помощью замера интервалов времени через std::chrono::system_clock:

Библиотека линейной алгебры GLM

В компьютерной графике широко применяется линейная алгебра с векторами из 2-х, 3-х, 4-х элементов, матрицам 3×3 и 4×4, квантерионами. При этом вычисления с применением линейной алгебры происходят как на центральном процессоре в приложении, так и на видеокарте, управляемой видеодрайвером. OpenGL 3.0 не содержит в себе полноценного API для работы с векторами и матрицами, но существует библиотека GLM (OpenGL Mathematics library), которая предоставляет удобные C++ классы.

Как мы увидим позже, классы библиотеки GLM похожи на типы данных специального языка GLSL, используемый вместе с OpenGL для написания шейдеров. Сейчас мы просто будем использовать GLM как удобную и надёжную библиотеку:

Вспомогательный модуль Utils

Для упрощения дальнейшей разработки напишем вспомогательный модуль Utils, выраженный в заголовочных файлах “Utils.h” и “Utils.cpp”.

Прежде всего, позаботимся об автоматическом удалении объектов SDL_Window* и SDL_GLContext . Мы могли бы написать собственные RAII-классы с перегруженными конструкторами, деструкторами, операторами “*” и “->”. Однако, этот подход похож на переизобретение колеса: нужный RAII-класс уже давно входит в состав библиотеки STL, осталось лишь его применить.

Класс “unique_ptr” позволяет задать вторым шаблонным параметром тип функтора, способного удалить объект. Функтором может быть указатель на функцию, лямбда-функция или структура с перегруженным оператором “()”. Мы могли бы специализировать и начать использовать unique_ptr следующим образом:

Однако, такой подход неудобен:

  • у типов SDLWindowPtr и SDLGLContextPtr нет конструктора по умолчанию
  • из-за этого каждый член класса такого типа придётся явно инициализировать, а затем присваивать с помощью reset

Для избавления от таких сложностей мы создадим структуру-функтор, потому что структура может иметь конструктор по умолчанию, и благодаря этому специализация класса unique_ptr не будет требовать явной передачи функции удаления в конструктор, т.е. тоже получит адекватный конструктор по умолчанию. Чтобы не захламлять общую область видимости, поместим определение вспомогательных структур в пространство имён “detail”:

Также мы добавим класс CUtils, содержащий только статические методы, и класс CChronometer, отвечающий за измерение промежутков времени между кадрами:

Реализация выглядит следующим образом:

Создаём работоспособное приложение

  • Для абстрагирования создания окна и контекста мы заведём класс CAbstractWindow , который будет предоставлять приложению так называемые “шаблонные методы” OnUpdateWindow() и OnDrawWindow().
  • Также мы применим идиому pointer to implementation, чтобы спрятать структуры SDL2 от пользователя класса CAbstractWindow .
  • Класс CAbstractWindow унаследован приватно от boost::noncopyable , чтобы запретить ненамеренное копирование объекта окна.
  • Для игр и трёхмерных приложений обычно удобнее фиксировать размер окна или даже раскрыть его на весь экран. Поэтому, в классе CAbstractWindow мы пока не будем думать об изменении размера окна.


Файл AbstractWindow.h

Файл AbstractWindow.cpp

Файл main.cpp

В файле main мы опишем также класс CWindow, который на данном этапе будет только

  • реализовывать абстрактный класс CAbstractWindow
  • обрабатывать клавиши R, G, B, чтобы менять цвет фона окна соответственно на красный, зелёный и синий

Работа с OpenGL — Минимальная программа

Delphi , Графика и Игры , OpenGL

Работа с OpenGL — Минимальная программа

Идет кинофильм, какой то душераздирающий триллер. Главная героиня в ажурном нижнем белье медленно продвигается по темному коридору. Музыка нагнетает обстановку. Главная героиня подходит к двери, протягивает руку, что бы открыть ее . И тут истошный крик из зала:
— Сохраняйся, дура. Сохраняйся.

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

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

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

Мы знаем, что ссылка на контекст устройства — величина типа HDC, для получения которой вызываем функцию GetDC. Ссылке на контекст устройства в Delphi соответствует свойство Canvas.Handle формы, принтера и некоторых компонентов. Теоретически всюду в наших примерах в строках, использующих величину DC типа HDC, вместо DC можно использовать Canvas.Handle. В первых примерах для начинающих это так и сделано. Каков же все-таки смысл контекста устройства, если он и так связан с однозначно определенным объектом — окном, областью памяти или принтером, и зачем передавать дополнительно какую-то информацию об однозначно определенном объекте?

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

рисуют один и тот же эллипс на поверхности формы и на распечатываемом документе соответственно, то есть на различных устройствах. Причем, если мы будем выводить разноцветную картинку на монохромный принтер, он справится с этой задачей, передавая цвета оттенками серого. Даже если мы рисуем только на канве формы, мы имеем дело с различными устройствами — нам неизвестно, какова графическая плата пользователя и каковы характеристики текущей установки настроек экрана. Например, имея в своем распоряжении более 16 миллионов цветов, приложение не заботится об отображении такой богатой палитры на экране, располагающем всего 256 цветами. Эти вопросы приложение перекладывает на плечи операционной системы, решающей их посредством драйверов устройств. Приложению для того, чтобы воспользоваться функциями воспроизведения Windows, необходимо только указать ссылку на контекст устройства, содержащую средства и характеристики устройства вывода.

Win32 Programmer’s Reference фирмы MicroSoft о контексте устройства сообщает следующее:

«Контекст устройства является структурой, которая определяет комплект графических объектов и связанных с ними атрибутов, и графические режимы, влияющие на вывод. Графический объект включает карандаш для изображения линии, щетку для краски и заполнения, растр для копирования или прокрутки частей экрана, палитру для определения комплекта доступных цветов, области для отсечения и других операций, и маршрута для операций рисования».

Термин «структура», встретившийся здесь, соответствует записи в терминологии Delphi. Контекст устройства Windows содержит информацию, относящуюся к графическим компонентам GDI, контекст воспроизведения содержит информацию, относящуюся к OpenGL, то есть играет такую же роль, что и контекст устройства для GDI. В частности, эти контексты являются хранилищами состояния системы, например, хранят информацию о текущем цвете карандаша.

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

Самый частый вопрос, который я получаю в связи с моими уроками, заключается в просьбе указать источники подробной информации об OpenGL на русском. К сожалению, если такие и есть, то мне они неизвестны. Главным нашим подручным станет поставляемый в составе Delphi файл помощи по OpenGL. Систему помощи Delphi для получения хороших результатов необходимо настраивать, если в помощи Delphi найти раздел по OpenGL, он не порадует обилием информации. В разных версиях Delphi настройка помощи выполняется по-разному, потребуются некоторые несложные манипуляции, но мы не будем тратить на это время. Будем использовать самый простой способ — контекстную помощь. Наберите в тексте модуля фразу «PixelFormatDescriptor», нажмите клавишу F1 и Вы получите подробную помощь об этом типе. Точно также мы будем получать помощь обо всех терминах, функциях и командах OpenGL.

Итак, мы получили обширное описание структуры PixelFormatDescriptor. Обращаю внимание, что мы видим раздел помощи MicroSoft, рассчитанной на программистов С и С++, поэтому описание использует термины и стилистику именно этих языков. Так, по традиции Delphi имена типов начинаются с префикса T, но нам не удастся найти помощь по термину TPixelFormatDescriptor. К сожалению, это не единственное неудобство, которое нам придется испытывать. Например, если сейчас мы заглянем в файл windows.pas и найдем описание записи TPixelFormatDescriptor, мы обнаружим, что в файле помощи не указаны некоторые константы, а именно: PFD_SWAP_LAYER_BUFFERS, PFD_GENERIC_ACCELERATED и PFD_DEPTH_DONTCARE. А константа, названная PFD_DOUBLE_BUFFER_DONTCARE, по-видимому, соответствует константе, описанной в модуле windows.pas как PFD_DOUBLEBUFFER_DONTCARE. Наверное, более поздние версии помощи и заголовочного файла исправят этот и другие неточности.

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

В каталоге Beginner/1 Вы найдете проект OpenGL_min.dpr, в котором я привел описание всех полей структуры TPixelFormatDescriptor на русском, в момент их первоначального заполнения. Делается это в процедуре SetDCPixelFormat, вызываемой между получением ссылки на контекст устройства и созданием контекста воспроизведения OpenGL. Посмотрим подробнее, что там делается. Полям структуры присваиваются желаемые значения, затем вызовом функции ChoosePixelFormat осуществляется запрос системе, поддерживается ли на данном рабочем месте выбранный формат пикселя, и вызовом функции SetPixelFormat устанавливаем формат пикселя в контексте устройства. Функция ChoosePixelFormat возвращает индекс формата пикселя, который нам нужен в качестве аргумента функции SetPixelFormat.

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

Обратим внимание на поле структуры «битовые флаги» — dwFlags. То, как мы зададим значение флагов, существенно может сказаться на работе нашего приложения, и наобум задавать эти значения не стоит. Тем более, что некоторые флаги совместно ужиться не могут, а некоторые могут присутствовать только в паре с другими. В этом примере флагам я присвоил значение PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL, то есть сообщаю системе, что я собираюсь осуществлять вывод в окно, и что моя система в принципе поддерживает OpenGL. Я ограничился всего двумя константами из обширного списка, приведенного в модуле windows.pas, по каждой из которых в файле помощи приведено детальное описание.

Так, константа PFD_DOUBLEBUFFER включает режим двойной буферизации, когда вывод осуществляется не на экран, а в память, затем содержимое буфера выводится на экран. Это очень полезный режим, если в любом примере на анимацию убрать режим двойной буферизации и все команды, связанные с этим режимом, хорошо будет видно мерцание при выводе кадра. Константу PFD_GENERIC_ACCELERATED имеет смысл устанавливать в случае, если компьютер оснащен графическим акселератором. Флаги, заканчивающиеся на «DONTCARE» , сообщают системе, что соответствующий режим может иметь оба значения, то есть PFD_DOUBLE_BUFFER_DONTCARE — запрашиваемый формат пикселя может иметь оба режима — одинарной и двойной буферизации. Со всеми остальными полями и константами я предоставляю Вам возможность разобраться самостоятельно, только замечу, что поле iLayerType, описанное в windows.pas типа Byte, может, согласно помощи, иметь три значения: PFD_MAIN_PLANE, PFD_OVERLAY_PLANE и PFD_UNDERLAY_PLANE, однако константа PFD_UNDERLAY_PLANE имеет значение -1, так что установить такое значение не удастся.

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

В примере битовым флагам задаем все возможные значения одновременно, числовым полям задаем заведомо нереальное значение 64, и смотрим на выбор формата пикселя, сделанным OpenGL. Результат, который Вы получите — выбранный формат пикселя, я предсказать не смогу — он индивидуален для каждой конкретной конфигурации машины и текущих настроек. Возможно, Вы получите в результате, что режим двойной буферизации не будет установлен — напоминаю, многие флаги устанавливаются только в комбинации с другими определенными. Наше приложение позволяет менять параметры формата пикселя и устанавливать его заново. Чтобы видеть, что происходит воспроизведение, небольшая площадка на экране при каждом тестировании окрашивается случайным цветом, используя функции OpenGL. Поэкспериментируйте с этим приложением, например, определите комбинацию флагов для установления режима двойной буферизации. Посмотрите значение числовых полей формата при различной палитре экрана — 16, 24, 32 бита, но не 256 цветов. О выводе при палитре экрана в 256 цветов — отдельный разговор. Это приложение, в частности, дает ответ на вопрос — как определить, оснащен ли компьютер графическим акселератором. Повозившись с этим приложением, Вы найдете ответ на вопрос, на который я Вам ответить не смогу — как надо заполнить структуру TPixelFormatDescriptor для Вашего компьютера. Обратите внимание, что в коде я установил несколько проверок на отсутствие контекста воспроизведения, который может быть потерян по ходу работы любого приложения, использующего OpenGL — редкая, но возможная ситуация в штатном режиме работы системы и очень вероятная ситуация если, например, по ходу работы приложения менять настройки экрана.

Минимальная программа OpenGL

Теперь мы знаем все, что необходимо для построения минимальной программы, использующей OpenGL. Я привел два варианта этой программы — одна построена исключительно на функциях Windows API, другая использует библиотеку классов Delphi (проекты каталогов Beginner/1 и Beginner/2 соответственно).

Взглянем на головной модуль второго проекта. При создании формы задаем формат пикселя, в качестве ссылки на контекст устройства используем значение Canvas.Handle формы. Создаем контекст воспроизведения OpenGL и храним в переменной типа HGLRC. При обработке события OnPaint устанавливаем контекст воспроизведения, вызываем функции OpenGL и освобождаем контекст. При завершении работы приложения удаляем контекст воспроизведения. Для полной академичности можно включить строки, проверяющие, получен ли контекст воспроизведения, и не теряется ли он по ходу работы. Признаком таких ситуаций является нулевое значение переменной hrc. В минимальной программе я просто окрашиваю окно в желтоватый оттенок. Получив помощь по команде glClearColor, Вы можете узнать, что аргументы ее — тройка вещественных чисел в интервале [0;1], задающих долю красного, зеленого и синего составляющих в цвете и еще один, четвертый аргумент, о котором мы поговорим чуть позднее. Этому аргументу я в примере задал значение 1.0. Вообще то, аргументы glClearColor, согласно помощи, имеют неведомый тип GLclampf. Для того, чтобы разобраться с этим типом, отсылаю к строке

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

Строку нашей программы

будем понимать как очистку экрана, фон при этом покрывается заданным цветом.

Проект, построенный только на функциях API, надеюсь, сейчас стал более понятным. Вместо Canvas.Handle используем собственную переменную dc, в обработчике события WM_PAINT реализуем действия, которые Delphi при обычном подходе выполняет за нас. Напоминаю, что для лучшей устойчивости работы обработчик WM_PAINT следовало бы написать так:

А в обработчике WM_DESTROY следует перед PostQuitMessage добавить строку:

То есть все используемые ссылки необходимо освобождать, а после того, как они стали не нужны — удалять.

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

Во всех своих примерах я приписал рекомендацию не запускать проекты, использующие OpenGL под управлением среды Delphi. Дело в том, что часто в таких ситуациях программа аварийно прерывается, выдавая сообщение «access violation -«. Это происходит и в случае самой аккуратной работы с контекстами, и не связано с небрежностью работы программы. Некоторые программисты вину за это возлагают на софтверные драйверы и рекомендуют обновить их. Некоторые утверждают, что дело в Windows 9X, и под NT этого не происходит. Возможно, Вы тоже ничего такого не замечали и не можете взять в толк, о чем я сейчас веду речь. У меня такие окошки вылетают через раз на одном и том же проекте, хотя откомпилированный модуль работает превосходно. Я полагаю, что если драйверы не «глюкуют», когда приложение работает без среды Delphi, дело не только в драйверах.

Вывод на поверхность компонентов

Теоретически функциями OpenGL возможно осуществлять вывод не только на поверхность формы, а и на поверхность любого компонента, если у него имеется свойство Canvas.Handle, для чего при получении контекста воспроизведения необходимо указывать именно его ссылку на контекст устройства, например, Image1.Canvas.Handle. Однако чаще всего это приводит к неустойчивой работе, вывод «то есть, то нет», хотя контекст воспроизведения присутствует и не теряется. Я советую Вам всегда пользоваться выводом исключительно на поверхность окна. OpenGL прекрасно уживается с визуальными компонентами, как видно из примера TestPFD, если же необходимо ограничить размер области вывода, для этого есть стандартные методы, о которых мы обязательно будем беседовать в будущем.

Просто ради интереса приведу пример, когда вывод OpenGL осуществляется на поверхность панели, то есть компонента, не имеющего свойства Canvas. Для этого мы пользуемся тем, что панель имеет отдельное окно, вызываем функцию GetDC с аргументом Panel1.Handle.

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

Для вывода на компонент класса TImage можете записать:

и удалить строки BeginPaint и EndPaint, поскольку TImage не имеет свойства Handle, то есть не создает отдельного окна. Однако вывод на такие компоненты как раз отличается полной неустойчивостью, так что я не гарантирую Вам надежного положительного результата.

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

В конце сегодняшнего разговора я хочу привести еще несколько проектов, появившихся за это время из под моего пера и дополняющих «ЖиЛистую Delphi».

Статья Работа с OpenGL — Минимальная программа раздела Графика и Игры OpenGL может быть полезна для разработчиков на Delphi и FreePascal.

Комментарии и вопросы

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

OpenGL: как обновить пакет драйверов простейшими методами?

Несомненно, многие геймеры знают, что для корректной работы таких известных игр, как Minecraft или CS, одним из самых основных условий является наличие в системе установленных последних версий драйверов OpenGL. Как обновить этот пакет драйверов, сейчас и будет рассмотрено, поскольку, как и любое другое программное обеспечение, они могут устаревать. Именно поэтому иногда возникают проблемы с запуском любимых игр.

OpenGL: как обновить драйверы простейшим способом?

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

Для активации процесса следует войти в стандартный «Диспетчер устройств», что можно сделать из «Панели управления», раздела администрирования компьютера или командой devmgmgt.msc через строку консоли «Выполнить», и найти там установленный видеоадаптер.


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

Как обновить OpenGL на Windows 7 или в любой другой системе при помощи специализированных утилит?

Для владельцев чипов NVIDIA и Radeon задача может быть несколько упрощена. Как правило, для них в комплекте в предустановленном виде поставляются специальные управляющие программы вроде PhysX и Catalyst. Именно поэтому обновить драйвер OpenGL можно с их помощью.

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

В принципе, если такой вариант пользователю не нравится, можно воспользоваться не менее интересными программами наподобие Driver Booster, которые без участия пользователя обновляют драйверы абсолютно для всех «железных» и программных устройств, установленных на компьютере или ноутбуке. Приложение в процессе сканирования системы само определит версию драйвера OpenGL. Как обновить его? Нужно всего лишь согласиться с предложением установки найденных апдейтов. По завершении процесса обновления в обязательном порядке потребуется полная перезагрузка.

Наконец, можно установить специальную утилиту под названием OpenGL Extensions Viewer, с помощью которой можно и узнать версию установленного пакета драйверов, и обновить его до последней версии.

Обновление DirectX

Однако обновление может и не дать желаемого положительного результата без апдейта платформы DirectX, которая является неким связующим мостом между железом и программным комплексом в плане мультимедиа.

Узнать установленную версию можно при помощи команды dxdiag, вводимой в меню «Выполнить». Загрузить новый дистрибутив можно с официального сайта поддержки Microsoft в разделе Downloads.

Как уже понятно, DirectX OpenGL обновить можно обычным стартом процесса инсталляции загруженного дистрибутива. Преимущество этого обновления состоит еще и в том, что в самом диалоговом окне DirectX можно провести ряд тестов, включая производительность DirectSound, ffdshow, Direct3D и т. д.

Почему драйверы не обновляются?

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

Кстати сказать, наиболее часто такая проблема встречается в случае с интегрированным видеочипами стандарта video-on-board, которые встроены в материнские платы. С дискретными видеокартами, как правило, таких проблем нет (естественно, при условии, что чип не является слишком устаревшим и изначально поддерживает технологию OpenGL). Как обновить драйверы для таких карт, думается, уже понятно. Но иногда может потребоваться апдейт платформ JAVA Runtime или даже .NET Framework от Microsoft – об этом тоже забывать нельзя. Но как правило, в большинстве случаев этого делать не требуется — достаточно обновить DirectX и параллельно использовать утилиту OpenGL Extensions Viewer.

Как реализовать интерфейс для Opengl приложения на Andro >

На учебе, в рамках курса «Компьютерная графика», необходимо написать приложение на OpenGL. На свою беду(а может и нет) уговорил преподавателя разрешить писать под Android с использованием OpenGL ES. Собственно, с самой библиотекой проблем нет, вопрос в реализации интерфейса. В первую очередь в голову приходит следующее: с помощью встроенной графики Android нарисовать на экране элементы UI поверх 3D сцены и отлавливать касания на них, это кажется не сложным, но вот уже отлавливать жесты масштабирования кажется не легким делом. Так как я могу ошибаться, или не знать про уже существующие решения, задаю вам этот вопрос. Спасибо всем, кто остался неравнодушным!)

OpenGL ES

Android includes support for high performance 2D and 3D graphics with the Open Graphics Library (OpenGL®), specifically, the OpenGL ES API. OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL specification intended for embedded devices. Android supports several versions of the OpenGL ES API:

    OpenGL ES 1.0 and 1.1 — This API specification is supported by Andro >Caution: Support of the OpenGL ES 3.0 API on a device requires an implementation of this graphics pipeline provided by the device manufacturer. A device running Android 4.3 or lower may not support the OpenGL ES 3.0 API. For information on checking what version of OpenGL ES is supported at run time, see Checking OpenGL ES version.

Note: The specific API provided by the Android framework is similar to the J2ME JSR239 OpenGL ES API, but is not identical. If you are familiar with J2ME JSR239 specification, be alert for variations.

The basics

Android supports OpenGL both through its framework API and the Native Development Kit (NDK). This topic focuses on the Android framework interfaces. For more information about the NDK, see the Android NDK.

There are two foundational >GLSurfaceView and GLSurfaceView.Renderer . If your goal is to use OpenGL in your Android application, understanding how to implement these classes in an activity should be your first objective.

GLSurfaceView This >View where you can draw and manipulate objects using OpenGL API calls and is similar in function to a SurfaceView . You can use this >GLSurfaceView and adding your Renderer to it. However, if you want to capture touch screen events, you should extend the GLSurfaceView class to implement the touch listeners, as shown in OpenGL training lesson, Responding to touch events. GLSurfaceView.Renderer This interface defines the methods required for drawing graphics in a GLSurfaceView . You must prov >GLSurfaceView instance using GLSurfaceView.setRenderer() .

The GLSurfaceView.Renderer interface requires that you implement the following methods:

  • onSurfaceCreated() : The system calls this method once, when creating the GLSurfaceView . Use this method to perform actions that need to happen only once, such as setting OpenGL environment parameters or initializing OpenGL graphic objects.
  • onDrawFrame() : The system calls this method on each redraw of the GLSurfaceView . Use this method as the primary execution point for drawing (and re-drawing) graphic objects.
  • onSurfaceChanged() : The system calls this method when the GLSurfaceView geometry changes, including changes in size of the GLSurfaceView or orientation of the device screen. For example, the system calls this method when the device changes from portrait to landscape orientation. Use this method to respond to changes in the GLSurfaceView container.

OpenGL ES packages

Once you have established a container view for OpenGL ES using GLSurfaceView and GLSurfaceView.Renderer , you can begin calling OpenGL APIs using the following classes:

If you want to start building an app with OpenGL ES right away, follow the Displaying graphics with OpenGL ES class.

Declaring OpenGL requirements

If your application uses OpenGL features that are not available on all devices, you must include these requirements in your AndroidManifest.xml file. Here are the most common OpenGL manifest declarations:

    OpenGL ES version requirements — If your application requires a specific version of OpenGL ES, you must declare that requirement by adding the following settings to your manifest as shown below.

For OpenGL ES 2.0:

Adding this declaration causes Google Play to restrict your application from being installed on devices that do not support OpenGL ES 2.0. If your application is exclusively for devices that support OpenGL ES 3.0, you can also specify this in your manifest:

For OpenGL ES 3.0:

For OpenGL ES 3.1:

Note: The OpenGL ES 3.x API is backwards-compatible with the 2.0 API, which means you can be more flexible with your implementation of OpenGL ES in your application. By declaring the OpenGL ES 2.0 API as a requirement in your manifest, you can use that API version as a default, check for the availability of the 3.x API at run time and then use OpenGL ES 3.x features if the device supports it. For more information about checking the OpenGL ES version supported by a device, see Checking OpenGL ES version.

Texture compression requirements — If your application uses texture compression formats, you must declare the formats your application supports in your manifest file using . For more information about available texture compression formats, see Texture compression support.

Declaring texture compression requirements in your manifest h > documentation.

Mapping coordinates for drawn objects

One of the basic problems in displaying graphics on Android devices is that their screens can vary in size and shape. OpenGL assumes a square, uniform coordinate system and, by default, happily draws those coordinates onto your typically non-square screen as if it is perfectly square.

Figure 1. Default OpenGL coordinate system (left) mapped to a typical Android device screen (right).

The illustration above shows the uniform coordinate system assumed for an OpenGL frame on the left, and how these coordinates actually map to a typical device screen in landscape orientation on the right. To solve this problem, you can apply OpenGL projection modes and camera views to transform coordinates so your graphic objects have the correct proportions on any display.

In order to apply projection and camera views, you create a projection matrix and a camera view matrix and apply them to the OpenGL rendering pipeline. The projection matrix recalculates the coordinates of your graphics so that they map correctly to Android device screens. The camera view matrix creates a transformation that renders objects from a specific eye position.

Projection and camera view in OpenGL ES 1.0

In the ES 1.0 API, you apply projection and camera view by creating each matrix and then adding them to the OpenGL environment.

    Projection matrix — Create a projection matrix using the geometry of the device screen in order to recalculate object coordinates so they are drawn with correct proportions. The following example code demonstrates how to modify the onSurfaceChanged() method of a GLSurfaceView.Renderer implementation to create a projection matrix based on the screen’s aspect ratio and apply it to the OpenGL rendering environment.

Kotlin

Kotlin

Projection and camera view in OpenGL ES 2.0 and higher

In the ES 2.0 and 3.0 APIs, you apply projection and camera view by first adding a matrix member to the vertex shaders of your graphics objects. With this matrix member added, you can then generate and apply projection and camera viewing matrices to your objects.

    Add matrix to vertex shaders — Create a variable for the view projection matrix and include it as a multiplier of the shader’s position. In the following example vertex shader code, the included uMVPMatrix member allows you to apply projection and camera viewing matrices to the coordinates of objects that use this shader.

Kotlin


Note: The example above defines a single transformation matrix member in the vertex shader into which you apply a combined projection matrix and camera view matrix. Depending on your application requirements, you may want to define separate projection matrix and camera viewing matrix members in your vertex shaders so you can change them independently.

Access the shader matrix — After creating a hook in your vertex shaders to apply projection and camera view, you can then access that variable to apply projection and camera viewing matrices. The following code shows how to modify the onSurfaceCreated() method of a GLSurfaceView.Renderer implementation to access the matrix variable defined in the vertex shader above.

Kotlin

Kotlin

Kotlin

For a complete example of how to apply projection and camera view with OpenGL ES 2.0, see the Displaying graphics with OpenGL ES class.

Shape faces and winding

In OpenGL, the face of a shape is a surface defined by three or more points in three-dimensional space. A set of three or more three-dimensional points (called vertices in OpenGL) have a front face and a back face. How do you know which face is front and which is the back? Good question. The answer has to do with winding, or, the direction in which you define the points of a shape.

Figure 1. Illustration of a coordinate list which translates into a counterclockwise drawing order.

In this example, the points of the triangle are defined in an order such that they are drawn in a counterclockwise direction. The order in which these coordinates are drawn defines the winding direction for the shape. By default, in OpenGL, the face which is drawn counterclockwise is the front face. The triangle shown in Figure 1 is defined so that you are looking at the front face of the shape (as interpreted by OpenGL) and the other side is the back face.

Why is it important to know which face of a shape is the front face? The answer has to do with a commonly used feature of OpenGL, called face culling. Face culling is an option for the OpenGL environment which allows the rendering pipeline to ignore (not calculate or draw) the back face of a shape, saving time, memory and processing cycles:

Kotlin

If you try to use the face culling feature without knowing which sides of your shapes are the front and back, your OpenGL graphics are going to look a bit thin, or possibly not show up at all. So, always define the coordinates of your OpenGL shapes in a counterclockwise drawing order.

Note: It is possible to set an OpenGL environment to treat the clockwise face as the front face, but doing so requires more code and is likely to confuse experienced OpenGL developers when you ask them for help. So don’t do that.

OpenGL versions and device compatibility

The OpenGL ES 1.0 and 1.1 API specifications have been supported since Android 1.0. Beginning with Android 2.2 (API level 8), the framework supports the OpenGL ES 2.0 API specification. OpenGL ES 2.0 is supported by most Android devices and is recommended for new applications being developed with OpenGL. OpenGL ES 3.0 is supported with Android 4.3 (API level 18) and higher, on devices that provide an implementation of the OpenGL ES 3.0 API. For information about the relative number of Android-powered devices that support a given version of OpenGL ES, see the OpenGL ES version dashboard.

Graphics programming with OpenGL ES 1.0/1.1 API is significantly different than using the 2.0 and higher versions. The 1.x version of the API has more convenience methods and a fixed graphics pipeline, while the OpenGL ES 2.0 and 3.0 APIs provide more direct control of the pipeline through use of OpenGL shaders. You should carefully consider the graphics requirements and choose the API version that works best for your application. For more information, see Choosing an OpenGL API version.

The OpenGL ES 3.0 API provides additional features and better performance than the 2.0 API and is also backward compatible. This means that you can potentially write your application targeting OpenGL ES 2.0 and conditionally include OpenGL ES 3.0 graphics features if they are available. For more information on checking for availability of the 3.0 API, see Checking OpenGL ES version

Texture compression support

Texture compression can significantly increase the performance of your OpenGL application by reducing memory requirements and making more efficient use of memory bandw >ETC1Util utility >etc1tool compression tool (located in the Andro > /tools/ ). For an example of an Andro >CompressedTextureActivity code sample in Andro > /samples/ /ApiDemos/src/com/example/android/apis/graphics/ ).

Caution: The ETC1 format is supported by most Andro >ETC1Util.isETC1Supported() method.

Note: The ETC1 texture compression format does not support textures with a transparency (alpha channel). If your application requires textures with transparency, you should investigate other texture compression formats available on your target devices.

The ETC2/EAC texture compression formats are guaranteed to be available when using the OpenGL ES 3.0 API. This texture format offers excellent compression ratios with high visual quality and the format also supports transparency (alpha channel).

Beyond the ETC formats, Android devices have varied support for texture compression based on their GPU chipsets and OpenGL implementations. You should investigate texture compression support on the devices you are are targeting to determine what compression types your application should support. In order to determine what texture formats are supported on a given device, you must query the device and review the OpenGL extension names, which identify what texture compression formats (and other OpenGL features) are supported by the device. Some commonly supported texture compression formats are as follows:

  • ATITC (ATC) — ATI texture compression (ATITC or ATC) is available on a w >GL_AMD_compressed_ATC_texture
  • GL_ATI_texture_compression_atitc
  • PVRTC — PowerVR texture compression (PVRTC) is available on a w >GL_IMG_texture_compression_pvrtc
  • S3TC (DXTn/DXTC) — S3 texture compression (S3TC) has several format variations (DXT1 to DXT5) and is less w >GL_EXT_texture_compression_s3tc
  • Some devices only support the DXT1 format variation; this limited support is represented by the following OpenGL extension name:

    • GL_EXT_texture_compression_dxt1
  • 3DC — 3DC texture compression (3DC) is a less w >GL_AMD_compressed_3DC_texture
  • Warning: These texture compression formats are not supported on all devices. Support for these formats can vary by manufacturer and device. For information on how to determine what texture compression formats are on a particular device, see the next section.

    Note: Once you decide which texture compression formats your application will support, make sure you declare them in your manifest using . Using this declaration enables filtering by external services such as Google Play, so that your app is installed only on devices that support the formats your app requires. For details, see OpenGL manifest declarations.

    Determining OpenGL extensions

    Implementations of OpenGL vary by Android device in terms of the extensions to the OpenGL ES API that are supported. These extensions include texture compressions, but typically also include other extensions to the OpenGL feature set.

    To determine what texture compression formats, and other OpenGL extensions, are supported on a particular device:

      Run the following code on your target devices to determine what texture compression formats are supported:

    Kotlin

    Warning: The results of this call vary by device model! You must run this call on several target devices to determine what compression types are commonly supported.

  • Review the output of this method to determine what OpenGL extensions are supported on the device.
  • Android Extension Pack (AEP)

    The AEP ensures that your application supports a standardized set of OpenGL extensions above and beyond the core set described in the OpenGL 3.1 specification. Packaging these extensions together encourages a consistent set of functionality across devices, while allowing developers to take full advantage of the latest crop of mobile GPU devices.

    The AEP also improves support for images, shader storage buffers, and atomic counters in fragment shaders.

    For your app to be able to use the AEP, the app’s manifest must declare that the AEP is required. In addition, the platform version must support it.


    Declare the AEP requirement in the manifest as follows:

    To verify that the platform version supports the AEP, use the hasSystemFeature(String) method, passing in FEATURE_OPENGLES_EXTENSION_PACK as the argument. The following code snippet shows an example of how to do so:

    Kotlin

    If the method returns true, AEP is supported.

    For more information about the AEP, visit its page at the Khronos OpenGL ES Registry.

    Checking the OpenGL ES version

    There are several versions of OpenGL ES available on Android devices. You can specify the minimum version of the API your application requires in your manifest, but you may also want to take advantage of features in a newer API at the same time. For example, the OpenGL ES 3.0 API is backward-compatible with the 2.0 version of the API, so you may want to write your application so that it uses OpenGL ES 3.0 features, but falls back to the 2.0 API if the 3.0 API is not available.

    Before using OpenGL ES features from a version higher than the minimum required in your application manifest, your application should check the version of the API available on the device. You can do this in one of two ways:

    1. Attempt to create the higher-level OpenGL ES context ( EGLContext ) and check the result.
    2. Create a minimum-supported OpenGL ES context and check the version value.

    The following example code demonstrates how to check the available OpenGL ES version by creating an EGLContext and checking the result. This example shows how to check for OpenGL ES 3.0 version:

    Kotlin

    If the createContext() method show above returns null, your code should create a OpenGL ES 2.0 context instead and fall back to using only that API.

    The following code example demonstrates how to check the OpenGL ES version by creating a minimum supported context first, and then checking the version string:

    Kotlin

    With this approach, if you discover that the device supports a higher-level API version, you must destroy the minimum OpenGL ES context and create a new context with the higher available API version.

    Choosing an OpenGL API version

    OpenGL ES 1.0 API version (and the 1.1 extensions), version 2.0, and version 3.0 all provide high performance graphics interfaces for creating 3D games, visualizations and user interfaces. Graphics progamming for OpenGL ES 2.0 and 3.0 is largely similar, with version 3.0 representing a superset of the 2.0 API with additional features. Programming for the OpenGL ES 1.0/1.1 API versus OpenGL ES 2.0 and 3.0 differs significantly, and so developers should carefully consider the following factors before starting development with these APIs:

    • Performance — In general, OpenGL ES 2.0 and 3.0 provide faster graphics performance than the ES 1.0/1.1 APIs. However, the performance difference can vary depending on the Android device your OpenGL application is running on, due to differences in hardware manufacturer’s implementation of the OpenGL ES graphics pipeline.
    • Device Compatibility — Developers should consider the types of devices, Android versions and the OpenGL ES versions available to their customers. For more information on OpenGL compatibility across devices, see the OpenGL versions and device compatibility section.
    • Coding Convenience — The OpenGL ES 1.0/1.1 API provides a fixed function pipeline and convenience functions which are not available in the OpenGL ES 2.0 or 3.0 APIs. Developers who are new to OpenGL ES may find coding for version 1.0/1.1 faster and more convenient.
    • Graphics Control — The OpenGL ES 2.0 and 3.0 APIs provide a higher degree of control by providing a fully programmable pipeline through the use of shaders. With more direct control of the graphics processing pipeline, developers can create effects that would be very difficult to generate using the 1.0/1.1 API.
    • Texture Support — The OpenGL ES 3.0 API has the best support for texture compression because it guarantees availability of the ETC2 compression format, which supports transparency. The 1.x and 2.0 API implementations usually include support for ETC1, however this texture format does not support transparency and so you must typically provide resources in other compression formats supported by the devices you are targeting. For more information, see Texture compression support.

    While performance, compatibility, convenience, control and other factors may influence your decision, you should pick an OpenGL API version based on what you think provides the best experience for your users.

    Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.

    Простейший пример с OpenGL (C++)

    09.05.2012, 12:32

    Простейший пример с комментариями
    Всем доброго времени суток! Моя просьба заключается в следующем: так как я полный новичок в OpenGl.

    Простой пример OpenGL
    Здравствуйте! У меня Visual C++ 6. Так вот. У меня не запускаются не один пример OpenGL, дайте мне.

    Дайте полный пример на OpenGL
    Пусть будет полноэкранка с одной простенькой моделькой в паре простых не расширенных буферов.

    OpenGL С чего начать? Простой пример
    Всем привет! С чего нужно начать, что скачать, что подключить.. для работы в VS C++ с OpenGL.

    OpenGL рисование с VBO пример кода на голом WinAPI
    Изучение VBO оказалось муторным занятием, хотя опытные программисты говорят, что от массива вершин.

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

    Оригинал: An Introduction to OpenGL Programming
    Автор: Mihalis Tsoukalos
    Дата публикации: 12 ноября 2014 г.
    Перевод: А.Панин
    Дата перевода: 7 октября 2015 г.

    OpenGL является широко известным, мощным и функциональным стандартном генерации трехмерной, а также двухмерной графики. Тексты различных версий стандарта OpenGL разрабатывается и публикуется Ревизионной комиссией по архитектуре OpenGL (OpenGL Architecture Review Board — ARB).

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

    На момент написания данной статьи новейшей версией стандарта OpenGL является версия 4.4, в которой используется техника рисования примитивов, отличная от описанной в данной статье. Тем не менее, данная статья предназначена в первую очередь для знакомства читателя с философией OpenGL, а не для обучения читателя разработке приложений на основе новейшей версии OpenGL. Исходя из вышесказанного, представленный в статье исходный код может быть скомпилирован и использован даже на машинах, работающих под управлением дистрибутивов Linux с устаревшими версиями библиотек OpenGL.

    Установка компонентов окружения разработки приложений на основе OpenGL

    Если вы выполните в системе Debian 7 следующую команду, предназначенную для поиска всех пакетов программного обеспечения со словом «opengl» в названии, вы получите многострочный вывод (Рисунок 1):

    Рисунок 1. Результат выполнения команды apt-cache search opengl

    В Linux существует множество свободных реализаций библиотек OpenGL, но нам понадобится только одна из них. Я установил FreeGLUT, так как данная библиотека является самой современной. Библиотека FreeGLUT является свободной альтернативой библиотеки OpenGL Utility Toolkit (GLUT).

    Для компиляции кода приложений на основе OpenGL также необходим компилятор языка C++.

    Наконец, в некоторых случаях не обойтись без пакета программного обеспечения mesa-utils, так как в процессе разработки приложений в любом случае придется использовать утилиту glxinfo из его состава.

    Утилита glxinfo выводит полезную информацию об установленных компонентах OpenGL в формате, представленном ниже:

    Mesa является библиотекой для работы с трехмерной графикой, предоставляющей API, который практически не отличается API OpenGL.

    Конвейер OpenGL

    Рисунок 2, взятый из книги с описанием языка шейдеров OpenGL («The Orange Book»), схематично демонстрирует программируемый конвейер OpenGL с вершинными и фрагментными обработчиками. Как вы видите, конвейер OpenGL является достаточно сложным, но вам не нужно понимать принцип работы каждого из его компонентов для того, чтобы успешно использовать OpenGL. Схематичное изображение конвейера иллюстрирует принцип работы библиотек OpenGL. Конвейеры новых версий OpenGL являются еще более сложными!

    Рисунок 2. Архитектура OpenGL

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

    Код на языке шейдеров OpenGL, который предназначен для исполнения с помощью одного из программируемых процессоров OpenGL, называется шейдером (Shader). Язык шейдеров OpenGL уходит своими корнями в язык программирования C (рассмотрение языка шейдеров OpenGL выходит за рамки данной статьи).

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

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

    Рисование треугольника

    Пришло время познакомится с реальным кодом приложения на основе OpenGL. При исполнении приложения на основе кода из Листинга 1 с помощью функций OpenGL в окне будет нарисован треугольник.

    Листинг 1. triangle.cc

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

    В системе Debian 7 следующая команда позволяет скомпилировать исходный код примера работы с OpenGL из файла triangle.cc без единого сообщения об ошибке:


    В системе Ubuntu Linux исполнение этой же команды приводит к выводу следующего сообщения об ошибке:

    Решение данной проблемы заключается в компиляции файла исходного кода программы triangle.cc со связыванием исполняемого файла с дополнительной библиотекой (-lGL):

    Библиотека libGL.so принимает команды OpenGL и выполняет действия, необходимые для отражения результатов их исполнения на экране каким-либо образом. В том случае, если ваша графическая карта не поддерживает функции аппаратного ускорения вывода трехмерной графики, библиотека libGL будет использовать функции программного вывода графики средствами оконной системы X. Аналогичным образом работает и библиотека Mesa. Библиотека libGL может также передавать информацию о командах OpenGL оконной системе X при наличии расширения GLX данной системы. После этого оконная система X может либо осуществлять программную обработку команд с помощью библиотеки Mesa, либо использовать функции аппаратного ускорения.

    При исполнении результирующего бинарного файла будет показано окно с треугольником, снимок которого приведен на Рисунке 3. Факт корректной компиляции файла исходного кода triangle.cc является доказательством того, что ваша система Linux может использоваться для разработки приложений, работающих с трехмерной графикой.

    Рисунок 3. Рисование треугольника средствами OpenGL

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

    Примечание: помните о том, что наиболее важной частью данной статьи является код!

    Рисование куба с помощью OpenGL

    Теперь давайте создадим приложение, которое будет рисовать куб с помощью OpenGL. Нам придется создать этот куб из треугольников. Куб имеет шесть плоскостей, причем каждая плоскость должна быть создана как минимум из двух треугольников. Причина, по которой я говорю «как минимум» заключается в том, что в общем случае вы можете использовать большее количество треугольников, если желаете придать трехмерному объекту более сглаженную или точную форму, но в случае рисования куба это замечание не актуально. Как вы, надеюсь, поняли, нам придется создать в общей сложности 12 треугольников.

    Кроме того, куб имеет восемь вершин. При этом каждая из вершин требует наличия трех других вершин.

    В Листинге 2 приведен полный исходный код из файла cube.cc.

    Листинг 2. cube.cc

    На Рисунке 4 показаны два различных снимка окна приложения на основе исходного кода из файла cube.cc. На снимке слева показан простой квадрат, так как при запуске приложения вы видите лишь переднюю поверхность куба и не можете видеть другие пять поверхностей. Таким образом, показанный на экране объект выглядит как двухмерный. После того, как вы начнете вращать куб с помощью клавиш со стрелками, вы можете с определенностью сказать, что объект является трехмерным.

    Рисунок 4. Графический вывод приложения на основе исходного кода из файла cube.cc

    Подробное рассмотрение кода

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

    Для иллюстрации возможности создания более сложных трехмерных объектов передняя поверхность куба создана из четырех треугольников. На Рисунке 5 показаны координаты вершин четырех треугольников передней поверхности куба. Центральная точка (0,0,-0.6) была выбрана произвольным образом. Для формирования поверхности подойдет любая точка, находящаяся на этой поверхности.

    Рисунок 5. Координаты вершин треугольников передней поверхности куба

    На Рисунке 6 показаны координаты вершин куба для точки отсчета с координатами x=0.6, y=0.6 и z=0.6. Обратите внимание на то, что вершины на концах каждого из ребер куба имеют по две одинаковые координаты из трех.

    Рисунок 6. Вершины куба

    Как вы можете видеть на Рисунке 6, координаты четырех вершин передней поверхности куба отличаются от координат четырех вершин задней поверхности куба лишь значениями по оси z.

    Описание параметров треугольников:

    Треугольники строятся на основании параметров их вершин. Для построения каждого треугольника необходимо описать параметры трех вершин. Для построения каждой из поверхностей куба необходимы два треугольника за исключением передней поверхности, для построения которой были использованы четыре треугольника. Следующие команды предназначены для создания окрашенного треугольника на основе координат x, y и z.

    Изменение цвета фигуры:

    Вы можете изменить цвет фигуры с помощью команды glColor3f(. ) . Команда glColor3f(. ) принимает три параметра, которые представляют RGB-составляющие желаемого цвета.

    Вы можете изменить перспективу сцены с помощью следующих команд:

    Угол перспективы меняется в соответствии с тем, как пользователь нажимает клавиши со стрелками.

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

    После того, как вы получите корректные координаты треугольников, их рисование не будет представлять особых сложностей. Рисование каждого треугольника должно начинаться с вызова команды glBegin(GL_TRIANGLES) и заканчиваться вызовом команды glEnd() . GL_TRIANGLES является идентификатором примитива OpenGL. Существуют и другие идентификаторы примитивов: GL_POINTS , GL_LINES , GL_LINE_STRIP , GL_LINE_LOOP , GL_TRIANGLE_STRIP , GL_TRIANGLE_FAN , GL_QUADS , GL_QUAD_STRIP и GL_POLYGON . При этом в конечном счете каждый из примитивов OpenGL формируется из одного или нескольких треугольников.

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

    Использование клавиш со стрелками:

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

    Функция обратного вызова keyboard(. ) зарегистрирована в рамках функции main(. ) с помощью следующей строки кода:

    Автоматическое вращение куба

    В качестве бонуса давайте рассмотрим пример автоматического вращения куба (Рисунок 7).

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

    Примечание: любой объект может быть разделен на отдельные треугольники, но треугольник не может быть разделен на что-либо, кроме треугольников.

    Рисунок 7. Графический вывод приложения на основе исходного кода из файла rotateCube.cc

    В Листинге 3 приведен исходный код приложения из фала rotateCube.cc

    Листинг 3. rotateCube.cc

    Обратите внимание на то, что реализации функции main(. ) в файлах исходного кода triangle.cc, cube.cc и rotateCube.cc очень схожи, несмотря на то, что три программы предназначены для выполнения различных задач.

    Наиболее важным аспектом данного приложения является использование функции glutTimerFunc(. ) . Данная функция позволяет зарегистрировать функцию обратного вызова таймера update(. ) , которая будет периодически вызываться по прошествии заданного количества миллисекунд. Функция update(. ) изменяет угол сцены при каждом вызове.

    Заключение

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

    Благодарности

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

    Дополнительные ресурсы

    Книга «Learning Modern 3D Graphics Programming»: http://www.arcsynthesis.org/gltut

    Книга «OpenGL Superbible, 6th edition, Graham Sellers, Richard S. Wright and Nicholas Haemel, Addison Wesley, ISBN: 0321902947»

    Opengl приложение a

    GLUT не идет по умолчанию в системе и в среде разработки. Вам потребуется найти и скачать последнюю версию по ссылке: GLUT Downloads . Скопируйте *. dll файлы в папку system , находящуюся в директории c Windows . Файлы *. lib положите в папку lib Visual C ++, файл glut . h в папку Include\gl .

    Прим. Версию 3.6 можно скачать по ссылке: glutdlls36.zip

    К примеру, файлы могут располагаться таким образом:

    C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Lib

    C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\gl

    Код шаблона

    Перейдем непосредственно к рассмотрению шаблона. Файл main.cpp :

    #include «engine.h»

    Engine *engine;

    GLvoid display(GLvoid)
    <
    engine->Draw();
    glutSwapBuffers();
    >

    GLvoid reshape(GLsizei width, GLsizei height)
    <
    engine->Resize(width, height);
    >

    int main( int argc, char ** argv)
    <
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(«2.1. Каркас OpenGL приложения c использованием GLUT .»);
    engine = new Engine();
    engine->Init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
    >

    Это весь код, который необходим, чтобы рисовать на экране средствами OpenGL . Команды OpenGL , которые отвечают за установку проекции и рисование вынесены в класс Engine , чтобы разделить приложение на логические части. Часть приложения из модуля main . cpp отвечает за диалог с системой, а класс Engine за вывод графики.

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

    Приведем небольшие комментарии по приведенному коду:

    glutInit(&argc, argv); Данная строка позволит библиотеке GLUT обработать специальные аргументы командной строки, предназначенные для неё. После вызова этой функции, те аргументы, которые касались библиотеки GLUT будут удалены из списка.
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(«2.1. Каркас OpenGL приложения c использованием GLUT.»);
    Здесь создается окно и указываются некоторые параметры OpenGL . Для окна указывается размер, позиция левого верхнего угла и заголовок. GLUT _ DOUBLE означает, что будет использоваться двойная буферизация; GLUT _ RGB , что будет использоваться RGB модель цвета, а не индексированный цвет.
    engine = new Engine();
    engine->Init();

    Здесь создается движок нашего приложения и производится дополнительная настройка OpenGL .
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    Здесь указываются функции нашего приложения, которые отвечают за перерисовку окна и изменение его размеров, соответственно.
    glutMainLoop(); Основной цикл работы приложения, построенного на базе GLUT . В одной из последующих статей можно посмотреть, как выполнять свои действия во время этого цикла. Например, анимацию.
    return 0; По стандарту языка С функция main должна возвращать величину типа int . 0 означает, что приложение завершилось корректно.

    Содержание модуля Engine . cpp:

    #include «engine.h»

    GLvoid Engine::SetProjectionMatrix(GLvoid) <
    glMatrixMode(GL_PROJECTION); // Действия будут производиться с матрицей проекции
    glLoad >// Текущая матрица (проекции) сбрасывается на единичную
    glFrustum(-1, 1, -1, 1, 3, 10); // Устанавливается перспективная проекция
    >

    GLvoid Engine::SetModelviewMatrix(GLvoid) <
    glMatrixMode(GL_MODELVIEW); // Действия будут производиться с матрицей модели
    glLoadIdentity(); // Текущая матрица (модели) сбрасывается на единичную
    glTranslatef(0.0, 0.0, -8.0); // Система координат переносится на 8 единиц вглубь сцены
    glRotatef(30.0, 1.0, 0.0, 0.0); // и поворачивается на 30 градусов вокруг оси x,
    glRotatef(70.0, 0.0, 1.0, 0.0); // а затем на 70 градусов вокруг оси y
    >

    GLvoid Engine::Resize(GLsizei width, GLsizei height) <
    if (height == 0)
    <
    height = 1;
    >

    glViewport (0, 0, width , height ); // Устанавливается область просмотра

    Height = height;
    W >

    SetProjectionMatrix();
    SetModelviewMatrix();
    >

    GLvoid Engine::Init(GLvoid) <
    glClearColor(0.2f, 0.5f, 0.75f, 1.0f ); // Устанавливается синий фон
    glClearDepth( 1.0f ); // Устанавливается значение для
    // заполнения буфера глубины по умолчанию
    glEnable(GL_DEPTH_TEST); // Включается тест глубины
    glDepthFunc(GL_LEQUAL); // Устанавливается значение, используемое
    // в сравнениях при использовании
    // буфера глубины

    glShadeModel(GL_SMOOTH); // Включается плавное затенение
    glEnable(GL_LINE_SMOOTH); // Включается сглаживание линий
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Выбирается самый качественный
    // режим сглаживания для линий
    glEnable(GL_BLEND); // Включается смешение цветов, необходимое
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // для работы сглаживания и задается
    // способ смешения
    >

    GLvoid Engine::Draw(GLvoid)
    <
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очищается буфер кадра и буфер глубины
    glPushMatrix(); // Запоминается матрица модели

    glColor3f(1.0f, 1.0f, 1.0f ); // Задается текущий цвет примитивов
    glutWireCube( 2.0f ); // Рисуется проволочный куб со стороной 2

    glPopMatrix(); // Восстанавливается матрица модели
    >

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

    Итак, шаблон создан. Последующие статьи будут базироваться на нём. Когда нам потребуется реагировать на действия пользователя, ввод с использованием библиотеки GLUT будет рассмотрен в отдельной статье.

    Илон Маск рекомендует:  Живые примеры
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL