Мне нужно сделать что-то вроде рейтресинга, но существенно проще - просто найти депт по лучу из каждой руки, т.е. определить расстояние до обьекта от контроллера (в руке).
Что бы не делать дурную работу, я для черновой отбраковки использовал BoundingBox::Intersects.
Все работало отлично, пока я не стал вращать предметы. Сейчас у меня данный метод работает только для оригинальных обьектов. Положив предмет "на бок" находится только точка строго по оси вращения, т.е все остальное отбрасывается, что совершенно неверно. Наверно надо как-то повернуть BoundingBox, но у него нет такого метода.
Кто-то может дать совет как с этим бороться?

4 апр. 2019 18:30 Suslik

Я методом тыка нашёл ответ, но ума не приложу, почему он такой. Хочу понять, что происходит.

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

4 апр. 2019 1:52 zombihello

Привет народ!

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

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

Во-вторых , как вообще посоветуете организовывать работу с инстансингом, как Вы данную вещь делали? Опишу как у меня сейчас. Для модели (без повторений) формирую один VBO для вершин, другой VBO для инстансиг информации (по началу выделяю на 10.000 байт) и EBO для индексов вершин. При создании копии модели я в буфер инстансинга записываю матрицу трансформации с проверкой на кол-во используемой памяти в буфере, если уже нужно больше чем выделено - копирую во временный буфер все содержимое, с помощью glBufferData(.., useSize + 10000, NULL, ...) выделяю для рабочего буфера на 10.000 байт больше (учитывая текущий размер + новый элемент), копирую с временного буфера всю информацию в рабочий и с помощью glBufferSubData дописываю новый элемент. Но мне что-то подсказывает что это все можно решить куда проще)

Буду благодарен вашим советам.

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

Координаты текстуры меняются так (cell - передается из вершинного шейдера и представляет собой кординаты ячейки):

float PHI = 1.61803398874989484820459; // Golden Ratio vec2 uv = PHI*fragTexCoord + floor(cell)*PHI;

Хотелось бы в общих чертах понять почему эта линия на стыках вообще возникает и как от нее избавится.

Update: Отключение фильтрации в семплере не помогает.

Update2: Если ликвидировать функцию floor - линия на стыках исчезает, но мне нужна целая часть от числа и без floor я обойтись не могу.

Правка: 6:55

  • Перевод

Перед тем как мы начнём, скажу: я знаю об OpenGL гораздо больше чем о Direct3D. Я в жизни не написал ни одной строки кода для D3D, и я писал руководства по OpenGL. Так что то что я тут расскажу, не вопрос предвзятости. Теперь это просто история.

Зарождение конфликта

Однажды, в начале 90-х, Microsoft огляделась вокруг. Они увидели замечательные Super Nintendo и Sega Genesis, на которых было много отличных игр. И они увидели DOS. Разработчики писали для DOS так же как для консолей: прямо на железе. Но, в отличии от консолей, где разработчик точно знал каким железом располагает пользователь, разработчики для DOS вынуждены были писать в расчёте на множество различных конфигураций оборудования. А это гораздо сложнее, чем кажется на первый взгляд.

У Microsoft в то время была ещё большая проблема: Windows. Windows хотела единолично распоряжаться оборудованием, в отличие от DOS, позволявшей разработчику делать всё что ему заблагорассудится. Владение оборудованием было обязательно для того чтобы упорядочить взаимодействие между приложениями. Взаимодействие-то и не нравилось разработчикам игр, потому что забирало ценные ресурсы, которые они могли использовать для своих замечательных игр.

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

И так родился DirectX.

3D ускорители появились на свет несколько месяцев спустя. И перед Microsoft встало сразу несколько проблем. Видите ли, DirectDraw, графический компонент DirectX, работал только с 2D графикой: выделением графической памяти и побитовым копированием между разными выделенными секциями памяти.

И Microsoft купили некий промежуточный драйвер и сделали из него Direct3D версии 3. Его ругали все и повсюду . И не просто так; одного взгляда на код было достаточно чтобы отшатнуться в ужасе.

Старый Джон Кармак из Id Software взглянул на этот мусор, сказал: «К чёрту!», и решил писать под другой API - OpenGL.

Другая часть этого клубка проблем состояла в том что Microsoft были очень заняты совместной работой с SGI над реализацией OpenGL для Windows. Идея была проста - привлечь разработчиков типичных рабочих GL-приложений: систем автоматического проектирования, моделирования, всего такого. Игры были последним, о чём тогда думали в Microsoft. Это всё предназначалось для Windows NT, но Microsoft решили добавить эту реализацию и в Win95.

Чтобы привлечь внимание разработчиков профессионального софта к Windows, Microsoft попробовали подкупить их доступом к новым функциям ускорителей трехмерной графики. Microsoft сделали протокол Installable Client Driver: производитель графического ускорителя мог перегрузить программную реализацию OpenGL аппаратной. Код просто автоматически использовал аппаратную реализацию если она была доступна.

Восход OpenGL

Итак, расклад сил был определён: Direct3D против OpenGL. Это действительно интересная история, учитывая насколько ужасен был D3D v3.

Комитет Архитектурных Решений OpenGL («Architectural Review Board», ARB) был организацией, ответственной за поддержку стандарта OpenGL. Они выпустили много расширений, следили за репозиторием расширений и создавали новые версии API. В Комитет входили многие влиятельные игроки графической индустрии и разработчики ОС. Apple и Microsoft в своё время тоже входили в этот комитет.

Потом появился 3Dfx с Voodoo2. Это было первое устройство, способное выполнять мультитекстурирование, чего OpenGL делать раньше не мог. Хотя 3Dfx совершенно не вписывался в стандарт OpenGL, NVIDIA, разработчик последующих графических чипов с мультитекстурированием (TNT1), положили глаз на эту реализацию. ARB пришлось выпустить расширение: GL_ARB_multitexture, которое давало доступ к мультитекстурированию.

В это же время вышел Direct3D v5. Теперь D3D стал настоящим API , а не странным куском кошачьей рвоты. Проблема? Отсутствие мультитекстурирования.

По большому счёту, это было не так важно как должно было быть, потому что люди особо не использовали мультитекстурирование. По крайней мере напрямую. Мультитекстурирование сильно ухудшало быстродействие, и во многих случаях просто не было смысла использовать его вместо multi-passing. И, естественно, разработчики игр старались убедиться что их игры заработают на старом железе, в котором мультитекстурирования не было, и многие игры его просто не использовали.

D3D это сошло с рук.

Прошло какое то время и NVIDIA выпустили GeForce 256 (не GeForce GT-250; самый первый GeForce), по большому счёту прекратив гонку вооружений в графических ускорителях на следующие два года. Основная продающая фишка - возможность делать вертексные преобразования и освещение (T&L) аппаратно. Но это не всё: NVIDIA настолько полюбили OpenGL, что их движок T&L по сути и был OpenGL. Причём буквально: насколько я понимаю, некоторые регистры действительно напрямую принимали объекты OpenGL в качестве значений.

Выходит Direct3D v6. Наконец появилось мультитекстурирование, но… нет аппаратного T&L. У OpenGL всегда был конвеер T&L, даже до того как вышел 256 он был реализован программно. Так что для NVIDIA было не очень сложно преобразовать программную реализацию в аппаратную. В D3D аппаратный T&L появился только к седьмой версии.

Рассвет шейдеров, сумерки OpenGL

Потом вышел GeForce 3 и одновременно произошло много вещей.

Microsoft решили что теперь-то они уж точно не опоздают к празднику. И вместо того чтобы смотреть что делают NVIDIA и копировать это постфактум они пришли к NVIDIA и поговорили. Потом они полюбили друг друга и от этого союза появилась маленькая игровая приставка.

Потом был болезненный развод. Но это совсем другая история.

Для PC это значило что GeForce 3 вышел одновременно с D3D v8. И несложно увидеть насколько GeForce 3 повлиял на шейдеры в восьмерке. Пиксельные шейдеры в Shader Model 1 были очень сильно привязаны к железу NVIDIA. Аппаратной абстракции от NVIDIA не было вообше; SM 1.0 по сути был тем что делал GeForce 3.

Когда ATI вступили в гонку производительных графических карт со своим Radeon 8500, обнаружилась проблема. Пиксельный конвеер 8500 был мощнее чем у NVIDIA. И Microsoft выпустил Shader Model 1.1, который по сути был «тем что делал 8500».

Это может показаться провалом со стороны D3D. Но провал и успех это вопрос относительный. Настоящий провал происходил в стане OpenGL.

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

Видите ли, в D3D v8 вы по крайней мере можете запустить шейдеры SM 1.0 на железе ATI. Естественно, чтобы использовать вкусности 8500 Вам придётся написать новые шейдеры, но Ваш код по крайней мере работал .

Чтобы получить хоть какие то шейдеры на 8500 в OpenGL, ATI пришлось написать несколько расширений OpenGL. Проприетартных расширений, подходящих только для ATI. Итак, Вам приходилось писать два пути выполнения кода, для NVIDIA и для ATI, чтобы иметь хоть какие то шейдеры.

Сейчас Вы наверняка спросите: «А чем занимался OpenGL ARB, чьей работой было поддержание OpenGL в актуальном состоянии?». Да тем же, чем занимаются большинство комитетов: тупил.

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

И ARB стал выпускать расширение за расширением. Каждое расширение, в названии которого присутствовало «texture_env» было очередной попыткой залатать этот стареющий дизайн. проверьте реестр: между расширениями ARB и EXT таких вышло аж восемь штук. Многие вошли в основные версии OpenGL.

Microsoft в это время были участником ARB; они ушли примерно во время выхода D3D 9. Так что в принципе, вполне возможно что они каким то образом саботировали разработку OpenGL. Я лично сомневаюсь в этой версии по двум причинам. Во первых, им пришлось бы заручиться помощью других членов комитета, поскольку у одного участника только один голос. И, что более важно, во вторых, Комитету не нужен был Microsoft чтобы прийти к такому провалу. Чуть позже мы увидим что так и получилось.

Со временем ARB, скорее всего под натиском ATI и NVIDIA (очень активных участников) проснулся настолько чтобы принять шейдеры ассемблерного типа.

Хотите увидеть еще большую глупость?

Аппаратный T&L. Который в OpenGL появился раньше . Тут вот что интересно. Чтобы выжать максимальную производительность из аппаратного T&L, Вам необходимо хранить данные в GPU. В конце концов, именно в GPU они используются.

В D3D v7 Microsoft представил концепцию вертексных буферов. Это выделенные области памяти GPU для хранения данных о вертексах.

Хотите узнать когда в OpenGL появился свой аналог этого? О, NVIDIA, будучи фанатом всего что относится к OpenGL (до тех пор пока написанное остаётся проприетарным расширением NVIDIA), выпустили расширение для вертексных массивов еще при первом выпуске GeForce 256. Но когда ARB решил официально предоставить подобный функционал?

Два года спустя . Это случилось после того как они одобрили вертексные и фрагментные шейдеры (пиксели в языке D3D). Вот сколько времени заняла у ARB разработка кроссплатформенного решения для хранения данных в памяти GPU. Именно то, что нужно чтобы выжать максимум из аппаратного T&L.

Один язык чтобы всё разрушить

Итак, разработка OpenGL была раздроблена. Нет единых шейдеров, нет единого хранилища в GPU, когда пользователи D3D уже наслаждались и тем, и другим. Могло ли стать ещё хуже?

Ну… можно сказать и так. Встречайте: 3D Labs .

Кто они такие, спросите Вы? Это разорившаяся компания которую я считаю настоящими убийцами OpenGL. Естественно, общая вялось ARB сделала OpenGL уязвимым, когда он должен был бить D3D на всех фронтах. Но 3D Labs это возможно самая большая причина текущего рыночного положения OpenGL. Что они могли сделать такого?

Они разработали Язык Шейдеров OpenGL.

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

Так и получилось.

И, в попытке остаться на плаву в мире, которому не нужна была их продукция, 3D Labs появились на Game Developer Conference с презентацией того что они назвали «OpenGL 2.0». Это должно было стать полностью переписанным с нуля API OpenGL. И это имело смысл; в API OpenGL было немало шероховатостей (примечание: они есть и сейчас). Просто посмотрите на что похожа загрузка и привязка текстур; это просто какая то чёрная магия.

Частью их предложения был язык шейдеров. Вот так. Однако, в отличие от текущих кросс-платформенных расширений ARB, их язык шейдеров был «высокоуровневым» (C это высокоуровневый язык для шейдеров. Нет, правда).

Итак, Microsoft в это время работали над своим собственным высокоуровневым языком шейдеров. Назвали они его, по своей давней привычке, «Высокоуровневым Языком Шейдеров» (HLSL). Но подход к языку был в корне другим.

Самая большая проблема языка шейдеров от 3D Labs была в том что он был встроенным. Видите ли, HLSL был языком, определённым Microsoft. Они выпустили для него компилятор, который генерировал ассемблерный код для Shader Model 2.0 (и последующих версий), который Вы вставляли в D3D. В дни D3D v9, HLSL никогда не вызывался из D3D напрямую. Он был удобной абстракцией, но совершенно необязательной. У разработчика всегда оставалась возможность отложить компилятор и доработать код до максимальной производительности.

В языке 3D Labs ничего этого не было. Вы скармливали драйверу код на C-подобном языке, и он возвращал шейдер. Всё, конец истории. И не ассемблерный шейдер, не то что можно вставить куда нибудь. Настоящий объект OpenGL, представляющий шейдер.

Это означало что пользователи OpenGL были беззащитны перед ошибками разработчиков, которые только начали разбираться с компилируемыми ассемблеро-подобными языками. Баги компилятора в новом языке шейдеров OpenGL (GLSL) ходили просто табунами . Что еще хуже, если Вам удавалось правильно скомпилировать шейдер для нескольких платформ (само по себе непростая задача), Вам всё равно приходилось иметь дело с оптимизаторами того времени. Которые были не так оптимальны, как могли бы.

Хотя это было главной проблемой GLSL, но не единственной. Далеко не единственной.

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

В GLSL ничего такого не было. Вертексные и фрагментные шейдеры были собраны в единую абстракцию, которую 3D Labs назвали «программный объект». И если Вам хотелось использовать вместе вертексные и фрагментные программы, Вам приходилось строить несколько таких программных объектов. И это было причиной второй проблемы.

Видите ли, 3D Labs думали что поступают очень умно. Они основали модель компиляции в GLSL на C/C++. Вы берёте.c или.cpp и компилируете в объектный файл. Потом Вы берете один или несколько объектных файлов и линкуете их в программу. Вот так и происходит компиляция в GLSL: вы компилируете шейдер (вертексный или фрагментный) в объект шейдера. Потом Вы помещаете этот объект шейдера в программный объект и связываете их вместе чтобы получить программу.

Хотя это позвляло делать некоторые потенциально крутые вещи вроде «библиотек» шейдеров, содержащих дополнительный код, который совместно использовали основные шейдеры, на практике это означало что шейдеры компилировались дважды. Один раз на этапе компиляции, один раз на этапе линковки. Не создавалось никакого промежуточного объектного кода; шейдер просто компилировался, результат компиляции выбрасывался и компиляция повторялась во время линковки.

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

У GLSL были и другие проблемы. Возможно неправильно винить во всём 3D Labs, потому что ARB в конце концов одобрила и приняла этот язык (но ничего кроме него из их предложения «OpenGL 2.0» не прошло). Но идея была именно их.

А вот действительно печальная часть. 3D Labs по большому счёту были правы . GLSL это не векторный язык шейдеров, каким всегда был HLSL. Так случилось потому что железо 3D Labs было скалярным железом (так же как современные карты NVIDIA), но в целом они были правы по части направления развития ускорителей.

Они также были правы с «compile-online» моделью для «высокоуровневых» языков. D3D впоследствии тоже на неё перешёл.

Проблема была в том что 3D Labs оказались правы в неправильное время . И в попытке призвать будущее слишком рано, в попытке его предугадать, они отбросили настоящее. Это также как OpenGL всегда имел возможность делать T&L. Если не считать того что конвееры T&L в OpenGL были полезны ещё до выхода аппаратной реализации, а GLSL был просто обузой прежде чем мир оказался готов его принять.

Сейчас GLSL - хороший язык. Но для своего времени он был ужасен. И OpenGL пострадал за это.

Приближается апофеоз

Хотя я и утверждаю что 3D Labs нанесли смертельный удар, именно комитет ARB забил последний гвоздь в крышку гроба OpenGL.

Эту историю вы наверняка слышали. Во времена OpenGL 2.1, OpenGL встал перед проблемой. У него было много старых неровностей. API было сложно использовать. Для каждого действия существовало по пять способов и никто не знал, какой окажется самым быстрым. Можно было «изучить» OpenGL по простым руководствам, но никто не говорил Вам какое API даст Вам максимум производительности.

И ARB решил сделать ещё одну попытку изобрести OpenGL заново. Это было похоже на «OpenGL 2.0» от 3D Labs, но лучше, потому что за ней стоял ARB. Попытку назвали «Longs Peak».

Что было не так с попыткой исправить старые API? Плохо было то что Microsoft в то время были уязвимы. Это было время выхода Vista.

В Vista Microsoft решили ввести давно необходимые изменения в драйверах дисплея. Они заставили драйверы обращаться к ОС для виртуализации видеопамяти и многих других вещей.

Хотя можно сомневаться в том было ли это необходимо, но факт остаётся фактом: Microsoft решили что D3D 10 будет только для Vista (и последующих ОС). Даже если у Вас было железо, способное выполнять функции D3D 10, Вы не могли запускать D3D 10 приложения без запуска Vista.

Вы также наверное помните, что Vista… ну, скажем просто что она получилась не очень. Итак, у Вас была тормозная ОС, новое API, работающее только на этой ОС, и новое поколение ускорителей которым было нужно API и ОС чтобы превзойти предыдущее поколение ускорителей.

Однако, разработчики могли бы получить доступ к фунциям уровня D3D 10 через OpenGL. Ну, смогли бы, если бы ARB не были так заняты работой над Longs Peak.

По большому счёту, ARB потратил полтора-два года на то чтобы сделать API лучше. Когда вышел OpenGL 3.0, время Vista уже заканчивалось, на горизонте появилась Win7, и большинство разработчиков уже не интересовались фунцкиями D3D-10. В конце концов, железо для которого предназначался D3D 10 замечательно работало с D3D 9. А с расцветом портов с PC на приставки (или PC-разработчиков, занявшихся разработкой для приставок) функции класса D3D 10 оказались невостребованы.

Если бы у разработчиков был доступ к этим функциям раньше, через OpenGL на машинах с WinXP, разработка OpenGL получила бы так необходимый импульс. Но ARB упустил эту возможность. И знаете что самое плохое?

Несмотря на то что два ценных года были потрачены на разработку API с нуля… они всё равно провалились и вынуждены были вернуться к предыдущей версии.

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

Это и есть история борьбы OpenGL и Direct3D. История упущенных возможностей, огромной глупости, слепоты и просто безрассудства.

OpenGL (Open Graphics Library - открытая графическая библиотека) - спецификация, определяющая независимый от языка программирования кросс-платформенный программный интерфейс для написания приложений, использующих двумерную и трехмерную компьютерную графику.
Включает более 250-ти функций для рисования сложных трехмерных сцен из простых примитивов. Используется при создании видео-игр, САПР, виртуальной реальности, визуализации в научных исследованиях. Под Windows конкурирует с DirectX.
Спецификация
На базовом уровне, OpenGL - это просто спецификация, то есть документ, описывающий набор функций и их точное поведение. Производители оборудования на основе этой спецификации создают реализации - библиотеки функций, соответствующих набору функций спецификации. Реализация использует возможности оборудования, там где это возможно. Если аппаратура не позволяет реализовать какую-либо возможность, она должна быть эмулирована программно. Производители должны пройти специфические тесты (conformance tests - тесты на соответствие) прежде чем реализация будет классифицирована как OpenGL реализация. Таким образом, разработчикам программного обеспечения достаточно научиться использовать функции, описанные в спецификации, оставив эффективную реализацию последних разработчикам аппаратного обеспечения.
Эффективные реализации OpenGL существуют для Windows, Unix платформ, PlayStation 3 и Mac OS. Эти реализации обычно предоставляются изготовителями видеоадаптеров и активно используют возможности последних. Существуют также чисто программные реализации спецификации OpenGL, одной из которых является библиотека Mesa. Из лицензионных соображений Mesa является «неофициальной» реализацией OpenGL, хотя полностью с ней совместима на уровне кода.
Спецификация OpenGL пересматривается Консорциумом ARB (Architecture Review Board), который был сформирован в 1992 году. Консорциум состоит из компаний, заинтересованных в создании широко распространенного и доступного API. Согласно официальному сайту OpenGL, членами ARB с решающим голосом на ноябрь 2004 года являются производители профессиональных графических аппаратных средств SGI, 3Dlabs, Matrox и Evans & Sutherland (военные приложения), производители потребительских графических аппаратных средств ATI и NVIDIA, производитель процессоров Intel, и изготовители компьютеров и компьютерного оборудования IBM, Apple, Dell, Hewlett-Packard и Sun Microsystems, а также один из лидеров компьютерной игровой индустрии id Software. Microsoft, один из основоположников Консорциума, покинула его в марте 2003 года. Помимо постоянных членов, каждый год приглашается большое количество других компаний, становящихся частью OpenGL ARB в течение одного года. Такое большое число компаний, вовлеченных в разнообразный круг интересов, позволило OpenGL стать прикладным интерфейсом широкого назначения с большим количеством возможностей.
Курт Экелей (Kurt Akeley) и Марк Сегал (Mark Segal) являются авторами оригинальной спецификации OpenGL. Крис Фрэзиер (Chris Frazier) редактировал версию 1.1. Йон Лич (Jon Leech) редактировал версии с 1.2 по настоящую версию 2.0.
Архитектура
OpenGL ориентируется на следующие две задачи:
скрыть сложности адаптации различных 3D-ускорителей предоставляя разработчику единый API
скрыть различия в возможностях аппаратных платформ, требуя имплементации недостающей функциональности с помощью софтверной эмуляции
Основным принципом работы OpenGL является получение наборов векторных графических примитивов в виде точек, линий и многоугольников с последующей математической обработкой полученных данных и построением растровой картинки на экране и/или в памяти. Векторные транформации и растеризация выполняются графическим конвейером (graphics pipeline), который по сути представляет из себя дискретный автомат. Абсолютное большинство команд OpenGL попадают в одну из двух групп: либо они добавляют графические примитивы на вход в конвейер, либо конфигурируют конвейер на различное исполнение транформаций.
OpenGL является низкоуровневым, процедурным API, что вынуждает программиста диктовать точную последовательность шагов, чтобы построить результирующую растровую графику (императивный подход). Это является основным отличием от дескрипторных подходов, когда вся сцена передается в виде структуры данных (чаще всего дерева), которое обрабатывается и строится на экране. С одной стороны императивный подход требует от программиста глубокого знания законов трёхмерной графики и математических моделей, с другой стороны даёт свободу внедрения различных инноваций.
Расширения
Стандарт OpenGL, с появлением новых технологий, позволяет отдельным производителям добавлять в библиотеку функциональность через механизм расширений. Расширения распространяются с помощью двух составляющих: заголовочный файл, в котором находятся прототипы новых функций и констант, а также драйвер устройства, поставляемого разработчиком. Каждый производитель имеет аббревиатуру, которая используется при именовании его новых функций и констант. Например, компания NVIDIA имеет аббревиатуру NV, которая используется при именовании ее новых функций, как, например, glCombinerParameterfvNV(), а также констант, GL_NORMAL_MAP_NV. Может случиться так, что определенное расширение могут реализовать несколько производителей. В этом случае используется аббревиатура EXT, например, glDeleteRenderbuffersEXT. В случае же, когда расширение одобряется Консорциумом ARB, оно приобретает аббревиатуру ARB и становится стандартным расширением. Обычно, расширения, одобренные Консорциумом ARB включаются в одну из последующих спецификаций OpenGL.
Список зарегистрированных расширений можно найти в официальной базе расширений.
Дополнительные библиотеки
Существует ряд библиотек, созданных поверх или в дополнение к OpenGL. Например, библиотека GLU, являющаяся практически стандартным дополнением OpenGL и всегда её сопровождающая, построена поверх последней, то есть использует её функции для реализации своих возможностей. Другие библиотеки, как, например, GLUT и SDL, созданы для реализации возможностей, недоступных в OpenGL. К таким возможностям относятся создание интерфейса пользователя (окна, кнопки, меню и др.), настройка контекста рисования (область рисования, использующаяся OpenGL), обработка сообщений от устройств ввода/вывода (клавиатура, мышь и др.), а также работа с файлами. Обычно, каждый оконный менеджер имеет собственную библиотеку-расширение для реализации вышеописанных возможностей, например, WGL в Windows или GLX в X Window System, однако библиотеки GLUT и SDL являются кросс-платформенными, что облегчает перенос написанных приложений на другие платформы.
Библиотеки, как GLEW (>) и GLEE (>) созданы для облегчения работы с расширениями и различными версиями OpenGL. Это особенно актуально для программистов в Windows, так как, заголовочные и библиотечные файлы, поставляемые с Visual Studio, находятся на уровне версии OpenGL 1.1.
OpenGL имеет только набор геометрических примитивов (точки, линии, многоугольники) из которых создаются все трехмерные объекты. Порой подобный уровень детализации не всегда удобен при создании сцен. Поэтому поверх OpenGL были созданы более высокоуровневые библиотеки, такие как Open Inventor и VTK. Данные библиотеки позволяют оперировать более сложными трехмерными объектами, что облегчает и ускоряет создание трехмерной сцены.
Независимость от языка программирования
Для подтверждения независимости от языка программирования были разработаны различные варианты привязки (binding) функций OpenGL или полностью перенесены на другие языки. Одним из примеров может служить библиотека Java 3D, которая может использовать аппаратное ускорение OpenGL. Прямая привязка функций реализована в Lightweight Java Game Library, которая имеет прямую привязку OpenGL для Java. Sun также выпустила версию JOGL, которая предоставляет прямую привязку к C-функциям OpenGL, в отличие от Java 3D, которая не имеет столь низкоуровневой поддержки. Официальный сайт OpenGL имеет ссылки на привязки для языков Java, Fortran 90, Perl, Pike, Python, Ada и Visual Basic. Имеются также варианты привязки OpenGL для языков C++ и C#, смотрите.
История
Сегодня компьютерная графика нашла широкое распространение и применение в повседневной жизни. Ученые используют компьютерную графику для анализа результатов моделирования. Инженеры и архитекторы используют трехмерную графику для создания виртуальных моделей. Кинематографы создают удивительные спецэффекты или полностью анимированные фильмы (Shrek, Toy Story и др.). В последние годы широкое распространение получили также компьютерные игры, максимально использующие трехмерную графику для создания виртуальных миров.
Распространению компьютерной графики сопутствовали свои трудности. Лет 15 назад, разработка программного продукта, способного работать на большом количестве графического оборудования было сопряжено с большими временными и финансовыми затратами. Было необходимо отдельно создавать модули для каждого типа графических адаптеров, что порой приводило к большой дупликации исходного кода. Это сильно тормозило развитие и распространение компьютерной графики.
Silicon Graphics Incorporated специализировалась на создании высокотехнологического графического оборудования и программных средств. Являясь в то время лидером в трехмерной графике, SGI видела проблемы и барьеры в росте рынка. Поэтому было принято решение стандартизировать метод доступа к графической аппаратуре на уровне программного интерфейса.
Таким образом появился программный интерфейс OpenGL, который стандартизирует доступ к графической аппаратуре, путем смещения ответственности за создание аппаратного драйвера на производителя графического устройства. Это позволило разработчикам программного обеспечения использовать более высокий уровень абстракции от графического оборудования, что значительно ускорило создание новых программных продуктов и снизило на них затраты.
В 1992 году компания SGI возглавила OpenGL ARB - группу компаний по разработке спецификации OpenGL. OpenGL эволюционировал из 3D-интерфейса SGI - IRIS GL. Одним из ограничений IRIS GL было то, что он позволял использовать только возможности, поддерживаемые оборудованием; если возможность не была реализована аппаратно, приложение не могло её использовать. OpenGL преодолевает эту проблему за счёт программной реализации возможностей, не предоставляемых аппаратно, что позволяет приложениям использовать этот интерфейс на относительно маломощных системах…
Когда в 1995 году была выпущена библиотека Direct3D, Microsoft, SGI и Hewlett-Packard начали проект под названием Fahrenheit, который предусматривал создание более универсального программного интерфейса на основе Direct3D и OpenGL. Идея казалась достаточно обещающей, призванной навести порядок в области интерактивной трехмерной графики, однако в результате финансовых трудностей в SGI и отсутствии должной индустриальной поддержки проект был закрыт.
OpenGL 2.0
По сравнению с DirectX, говорили что главной проблемой OpenGL является Консорциум, в который входит большое количество компаний с различными интересами, что приводит к длительному периоду принятия новой версии спецификации. OpenGL версии 2.0 была представлена 3Dlabs в ответ на беспокойства относительно медленного развития и нечеткого направления OpenGL. 3Dlabs предложила ряд существенных дополнений к стандарту, наиболее значимым из которого был GLSL (OpenGL Shading Language). Это позволяет программисту заменить фиксированный конвейер OpenGL небольшими программами на специальном языке, GLSL, для создания таких эффектов, как «рельефные текстуры» («bump mapping»), волны и водная рябь. Финальная версия спецификации OpenGL 2.0 включает в себя поддержку GLSL. Однако, еще до введения в стандарт OpenGL языка GLSL существовала возможность разрабатывать спецэффекты на языках: assembler (расширения vertex_program, fragment_program) и Cg (NVidia C for Graphics). К сожалению многие предложенные возможности пока отсутствуют в версии OpenGL 2.0, хотя некоторые из них реализованы многими производителями в виде расширений.
Официальный сайт OpenGL
сайт 3Dlabs для разработчиков использующих OpenGL
сайт программирования для GPU
Neon Helium - Уроки по OpenGL
Теория 3D графики с примерами на OpenGL
Уроки с примерами для разработчиков игр.

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

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

Что из себя представляет куб? Ответ прост – 6 граней (квадратов) и 8 вершин 😉 Именно так мы и будем строить фигуру – построим по отдельности 6 его граней, а эта задача для нас уже не представляет никакой сложности.

Но прежде, чем приступать к рисованию, добавим в функцию initializeGL() следующее:

glShadeModel(GL_FLAT) ; glEnable(GL_CULL_FACE) ;

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

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

Собственно, с приготовлениями на этом заканчиваем, начинаем отрисовку. Нам понадобятся три массива:

GLfloat cubeVertexArray[ 8 ] [ 3 ] ; GLfloat cubeColorArray[ 8 ] [ 3 ] ; GLubyte cubeIndexArray[ 6 ] [ 4 ] ;

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

cubeVertexArray[ 0 ] [ 0 ] = 0.0 ; cubeVertexArray[ 0 ] [ 1 ] = 0.0 ; cubeVertexArray[ 0 ] [ 2 ] = 1.0 ; cubeVertexArray[ 1 ] [ 0 ] = 0.0 ; cubeVertexArray[ 1 ] [ 1 ] = 1.0 ; cubeVertexArray[ 1 ] [ 2 ] = 1.0 ; cubeVertexArray[ 2 ] [ 0 ] = 1.0 ; cubeVertexArray[ 2 ] [ 1 ] = 1.0 ; cubeVertexArray[ 2 ] [ 2 ] = 1.0 ; cubeVertexArray[ 3 ] [ 0 ] = 1.0 ; cubeVertexArray[ 3 ] [ 1 ] = 0.0 ; cubeVertexArray[ 3 ] [ 2 ] = 1.0 ; cubeVertexArray[ 4 ] [ 0 ] = 0.0 ; cubeVertexArray[ 4 ] [ 1 ] = 0.0 ; cubeVertexArray[ 4 ] [ 2 ] = 0.0 ; cubeVertexArray[ 5 ] [ 0 ] = 0.0 ; cubeVertexArray[ 5 ] [ 1 ] = 1.0 ; cubeVertexArray[ 5 ] [ 2 ] = 0.0 ; cubeVertexArray[ 6 ] [ 0 ] = 1.0 ; cubeVertexArray[ 6 ] [ 1 ] = 1.0 ; cubeVertexArray[ 6 ] [ 2 ] = 0.0 ; cubeVertexArray[ 7 ] [ 0 ] = 1.0 ; cubeVertexArray[ 7 ] [ 1 ] = 0.0 ; cubeVertexArray[ 7 ] [ 2 ] = 0.0 ; cubeColorArray[ 0 ] [ 0 ] = 0.0 ; cubeColorArray[ 0 ] [ 1 ] = 0.0 ; cubeColorArray[ 0 ] [ 2 ] = 1.0 ; cubeColorArray[ 1 ] [ 0 ] = 0.6 ; cubeColorArray[ 1 ] [ 1 ] = 0.98 ; cubeColorArray[ 1 ] [ 2 ] = 0.6 ; cubeColorArray[ 2 ] [ 0 ] = 1.0 ; cubeColorArray[ 2 ] [ 1 ] = 0.84 ; cubeColorArray[ 2 ] [ 2 ] = 0.8 ; cubeColorArray[ 3 ] [ 0 ] = 0.8 ; cubeColorArray[ 3 ] [ 1 ] = 0.36 ; cubeColorArray[ 3 ] [ 2 ] = 0.36 ; cubeColorArray[ 4 ] [ 0 ] = 1.0 ; cubeColorArray[ 4 ] [ 1 ] = 0.27 ; cubeColorArray[ 4 ] [ 2 ] = 0.0 ; cubeColorArray[ 5 ] [ 0 ] = 0.82 ; cubeColorArray[ 5 ] [ 1 ] = 0.13 ; cubeColorArray[ 5 ] [ 2 ] = 0.56 ; cubeColorArray[ 6 ] [ 0 ] = 0.54 ; cubeColorArray[ 6 ] [ 1 ] = 0.17 ; cubeColorArray[ 6 ] [ 2 ] = 0.89 ; cubeColorArray[ 7 ] [ 0 ] = 0.0 ; cubeColorArray[ 7 ] [ 1 ] = 1.0 ; cubeColorArray[ 7 ] [ 2 ] = 1.0 ;

Мы задали координаты и цвета вершин куба. Давайте для наглядности посмотрим на рисунок:

Номера вершин на рисунке полностью соответствуют тому, как мы их задали в нашей программе. Теперь, глядя на картинку можно с легкостью пройтись по всем 6 граням куба и заполнить массив индексов. Давайте смотреть:

  • Грань 1 – вершины 0, 3, 2, 1
  • Грань 2 – вершины 0, 1, 5, 4
  • Грань 3 – вершины 7, 4, 5, 6
  • Грань 4 – вершины 3, 7, 6, 2
  • Грань 5 – вершины 1, 2, 6, 5
  • Грань 6 – вершины 0, 4, 7, 3

Теперь тоже самое делаем в программе =)

cubeIndexArray[ 0 ] [ 0 ] = 0 ; cubeIndexArray[ 0 ] [ 1 ] = 3 ; cubeIndexArray[ 0 ] [ 2 ] = 2 ; cubeIndexArray[ 0 ] [ 3 ] = 1 ; cubeIndexArray[ 1 ] [ 0 ] = 0 ; cubeIndexArray[ 1 ] [ 1 ] = 1 ; cubeIndexArray[ 1 ] [ 2 ] = 5 ; cubeIndexArray[ 1 ] [ 3 ] = 4 ; cubeIndexArray[ 2 ] [ 0 ] = 7 ; cubeIndexArray[ 2 ] [ 1 ] = 4 ; cubeIndexArray[ 2 ] [ 2 ] = 5 ; cubeIndexArray[ 2 ] [ 3 ] = 6 ; cubeIndexArray[ 3 ] [ 0 ] = 3 ; cubeIndexArray[ 3 ] [ 1 ] = 7 ; cubeIndexArray[ 3 ] [ 2 ] = 6 ; cubeIndexArray[ 3 ] [ 3 ] = 2 ; cubeIndexArray[ 4 ] [ 0 ] = 1 ; cubeIndexArray[ 4 ] [ 1 ] = 2 ; cubeIndexArray[ 4 ] [ 2 ] = 6 ; cubeIndexArray[ 4 ] [ 3 ] = 5 ; cubeIndexArray[ 5 ] [ 0 ] = 0 ; cubeIndexArray[ 5 ] [ 1 ] = 4 ; cubeIndexArray[ 5 ] [ 2 ] = 7 ; cubeIndexArray[ 5 ] [ 3 ] = 3 ;

Теперь осталось только вызвать функцию рисования:

glVertexPointer(3 , GL_FLOAT, 0 , cubeVertexArray) ; glColorPointer(3 , GL_FLOAT, 0 , cubeColorArray) ; glDrawElements(GL_QUADS, 24 , GL_UNSIGNED_BYTE, cubeIndexArray) ;

Запускаем программу и видим наш куб!

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

Сделаем так – при перемещении указателя мыши от левого края до правого будем поворачивать куб на 180 градусов. Осталось вычислить, на сколько градусов нужно повернуть фигуру при произвольном перемещении курсора. С этим все просто, получаем формулу:

yAngle = 180 * d / w;

Здесь d – это расстояние, на которое мы переместили курсор, w – ширина нашего окна. Обратите внимание, что при движении мыши вдоль оси x поворот будет осуществляться вокруг оси y , и наоборот, перемещение вдоль y – поворот вокруг x .

Итак, сейчас нам нужно получить в программе данные о перемещениях мыши. Для этого переопределим методы:

void MainScene:: mousePressEvent (QMouseEvent * event) { pressPosition = event-> pos() ; } void MainScene:: mouseMoveEvent (QMouseEvent * event) { xAxisRotation += (180 * ((GLfloat) event-> y() - (GLfloat) pressPosition.y () ) ) / (currentHeight) ; yAxisRotation += (180 * ((GLfloat) event-> x() - (GLfloat) pressPosition.x () ) ) / (currentWidth) ; pressPosition = event-> pos() ; updateGL() ; }

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

Функцию updateGL() то мы вызвали, но само собой ничего, естественно, не повернется)

Для поворота фигуры предлагает нам следующую функцию:

То фигура повернется вокруг оси x на 45 градусов.

С этим все понятно, осталось только добавить вызов этой функций к нам в программу, а точнее в функцию paintGL() :

Как видите, все оказалось довольно-таки просто)

Теперь нам предстоит собрать проект и проверить результат:

На этом заканчиваем сегодняшнюю статью! А уже скоро, а именно в следующем уроке, мы разберемся с текстурами в – создадим собственные текстуры и нанесем их на 3D объекты, так что до скорого, не пропустите новую статью 😉

Эта статья не о самом OpenGL , а скорее о том, каким образом использовать совместно OpenGL и SFML .

Как вы знаете, одна из самых важных сторон OpenGL - переносимость на другие платформы. Но одного OpenGL недостаточно для создания завершенных приложений: вам потребуется окно, контекст рендеринга, пользовательский ввод, и так далее. У вас нету другого выбора, кроме как писать платформо-зависимый код для управления этими вещами. В этом месте модуль sfml-window выходит из тени. Давайте посмотрим, как он позволяет вам использовать OpenGL .

Включаем и линкуем OpenGL к вашему приложению

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

#include

Этот заголовочный файл включает функции OpenGL и GLU , ничего более. Люди иногда думаю, что SFML автоматически подключает GLEW (библиотеку, которая дает управление над расширениями OpenGL ), потому что SFML использует GLEW внутри, но это технические детали. Со стороны пользователя, GLEW должен быть подключен также, как остальные библиотеки расширений.

Затем нам потребуется прилинковать к вашему приложению библиотеку OpenGL . К несчастью, такого же простого пути,как с заголовочными файлами, нету, и SFML не может предоставить общий принцип подключения OpenGL . Таким образом, вам нужно знать, какая библиотека прилинкована, в зависимости от используемой операционной системы ("opengl32 " в Windows , "GL" в Linux , и так далее). То же самое касается GLU , если вы хотите его использовать ("glu32 " в Windows , "GLU " в Linux , и так далее).

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

Создаем окно OpenGL

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

Sf::Window window(sf::VideoMode(800, 600), "OpenGL"); // это работает без каких-либо дополнительных действий glEnable(GL_TEXTURE_2D); ...

В случае, если вы думаете, что это слишком автоматизировано, конструктор sf::Window имеет дополнительный аргумент, который позволяет изменить настройки основного контекста OpenGL . Этот аргумент - это экземпляр структуры sf::ContextSettings , который обеспечивает доступ к следующим настройкам:
depthBits - это количество битов на пиксель для использования в буфере глубины (0 , чтобы отключить его)
stencilBits - это количество бит на пиксель, чтобы использовать для буфера трафарета (0 , чтобы отключить его)
antialiasingLevel - это уровень мультисэмплинга
MajorVersion и MinorVersion содержат запрашиваемую версию OpenGL

Sf::ContextSettings settings; settings.depthBits = 24; settings.stencilBits = 8; settings.antialiasingLevel = 4; settings.majorVersion = 3; settings.minorVersion = 0; sf::Window window(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, settings);

Если какие-либо из этих настроек не поддерживаются графической картой, SFML попытается найти ближайшее схожее значение. Например, если 4х-антиалиасинг не поддерживается, SFML попытается использовать 2х, а если и это не поможет, то такая функция будет попросту отключена.
В любом случае, вы можете проверить, какие настройки SFML использует в данный момент с помощью функции getSettings :

Sf::ContextSettings settings = window.getSettings(); std::cout << "depth bits:" << settings.depthBits << std::endl; std::cout << "stencil bits:" << settings.stencilBits << std::endl; std::cout << "antialiasing level:" << settings.antialiasingLevel << std::endl; std::cout << "version:" << settings.majorVersion << "." << settings.minorVersion << std::endl;

OpenGL версии 3.0 и выше поддерживается в SFML (если поддерживается в драйвере видеокарты), но вы не сможете установить флаги на данный момент. Это означает, что вы не можете создать отладочный или совместимый контекст. На самом деле SFML автоматически создает контекст с "совместимыми" флагами, потому что он использует внутренние устаревшие функции. Это будет исправлено в ближайшее время и флаги будут выставлены также и в общественном API .

На OS X SFML поддерживается создание контекста OpenGL 3.2 используя профиль ядра. Помните, что эти контексты не совместимы с графическим модулем SFML. Если вы хотите использовать графический модуль, вам нужно использовать стандартный контекст с версией 2.1.

Типичное OpenGL-SFML приложение

Тут полный код минимального SFML приложения, использующего OpenGL :

#include #include int main() { // создаем окно sf::Window window(sf::VideoMode(800, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(32)); window.setVerticalSyncEnabled(true); // загружаем ресурсы, инициализируем состояния OpenGL // запускаем главный цикл bool running = true; while (running) { // обрабатываем события sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { // пора закрывать приложение running = false; } else if (event.type == sf::Event::Resized) { // применяем область просмотра, когда изменены размеры окна glViewport(0, 0, event.size.width, event.size.height); } } // очищаем буферы glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // рисуем // конец текущего кадра (меняем местами передний и задний буферы) window.display(); } // освобождаем ресурсы return 0; }

Здесь мы не используем window.isOpen() как условие в главном цикле, потому что нам нужно, чтобы окно оставалось открытым, пока приложение не закончит свою работу, поэтому у нас все еще будет к этому моменту валидный контекст OpenGL для последней итерации цикла и очистки ресурсов.

Не стесняйтесь посмотреть на примеры "OpenGL " и "Window " в SFML SDK , там вы увидите более развернутые объяснения, которые могут содержать ответы на ваши вопросы.

Управляем несколькими OpenGL окнами

Управление многими окнами OpenGL не намного сложнее, чем управление одним, здесь только стоит запомнить несколько важных вещей.

Вызовы OpenGL делаются в активном контексте (в активном окне). Таким образом, если вы хотите отрисовать два разных окна в одном приложении, вам нужно выбрать, какое окно будет активным, перед тем, как что-либо нарисовать. Для этого нужно использовать функцию setActive:

// активируем первое окно window1.setActive(true); // рисуем первое окно // активируем второе окно window2.setActive(true); // рисуем второе окно

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

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

OpenGL без окна

Иногда требуется вызвать функции OpenGL без активного окна и, соответственно, без контекста. Например, когда вы загружаете текстуры из разных потоков или перед тем, как первое из окон будет создано. SFML позволяет создавать полу-контекстные окна с помощью класса sf::Context . Все, что вам нужно сделать, это создать его экземпляр, чтобы получить правильный контекст.

Int main() { sf::Context context; // загружаем ресурсы OpenGL sf::Window window(sf::VideoMode(800, 600), "OpenGL"); ... return 0; }

Рисование в потоках

Типичное использование многопоточных приложений - это управление окном и его событиями в одном потоке (главном), и рисование в другом. Если вы так делаете, вам нужно кое-что запомнить: вы не можете активировать контекст (окно), если оно активировано в другом потоке. Это значит, что вам нужно деактивировать ваше окно перед запуском потока отрисовки.

Void renderingThread(sf::Window* window) { // активируем контекст окна window->setActive(true); // цикл отрисовки while (window->isOpen()) { // рисуем // конец текущего кадра - здесь функция отрисовки(требует контекст) window->display(); } } int main() { // создаем окно sf::Window window(sf::VideoMode(800, 600), "OpenGL"); // деактивируем его контексты OpenGL window.setActive(false); // запускаем поток отрисовки sf::Thread thread(&renderingThread, &window); thread.Launch(); // цикл событий\логики\чего-либо еще while (window.isOpen()) { ... } return 0; }

Использование OpenGL вместе с графическим модулем SFML

В этой статье описывается совместное использование OpenGL с модулем sfml-window , это очень легко, так как это единственная цель этого модуля. Использование совместно с графическим модулем немного сложнее: sfml-graphics тоже использует OpenGL , поэтому требуется усиленное внимание, чтобы состояния, заданные пользователем и внутри SFML не конфликтовали между собой.

Если вы еще не изучили графический модуль, то вам следует знать, что класс sf::Window заменен классом sf::RenderWindow , который содержит все его функции и добавляет функционал для отрисовки специальных сущностей SFML .

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

Draw with OpenGL - save OpenGL states - draw with SFML - restore OpenGL states - draw with OpenGL ...

Простое решение - это позволить SFML делать это за вас,с помощью функций pushGLStates/popGLStates :

GlDraw... window.pushGLStates(); window.draw(...); window.popGLStates(); glDraw...

Так как SFML не знает всё о вашем коде OpenGL , он не может оптимизировать все шаги и в результате сохраняет\загружает все доступные состояния и матрицы OpenGL . Это может быть приемлемо для небольших проектов, но может существенно замедлять работу больших приложений, которые требуют максимальной производительности. В этом случае, вы можете управлять сохранением и загрузкой состояний OpenGL вручную, с помощью glPushAttrib/glPopAttrib , glPushMatrix/glPopMatrix , и так далее.
Если вы будете это делать, вам не нужно восстанавливать состояния SFML перед отрисовкой. Это будет сделано с помощью функции resetGLStates .

GlDraw... glPush... window.resetGLStates(); window.draw(...); glPop... glDraw...

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