Программирование: Использование OpenGL в SFML

Читай на DevTribe.ru!

» Раздел: C++

Оригинал статьи - официальный туториал
Автор перевода - Nerevar

Использование OpenGL в SFML

Введение

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

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

Заголовочные файлы OpenGL не одинаковы на разных операционных системах. Поэтому, SFML предоставляет "абстрактный" заголовочный файл, который позаботится о правильном подключении за вас.
#include <SFML/OpenGL.hpp>
Этот заголовочный файл включает функции 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 <SFML/Window.hpp>
#include <SFML/OpenGL.hpp>

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 собственноручно, вы можете использовать только те, которые вам реально необходимы, для снижения лишних вызовов драйвера.

Просмотров: 4 697

alexprey #1 - 3 года назад 2
Помните об это, когда будете
Затем нам потребуется прилинковать к вашему приложению библиотеку OpenGL. К несчастью, такого же простого пути,как с заголовочными файлами, нету, и SFML не может предоставить общий принцип подключения OpenGL. Таким образом, вам нужно знать, какая библиотека прилинкована, в зависимости от используемой операционной системы ("opengl32" в Windows, "GL" в Linux, и так далее). То же самое касается GLU, если вы хотите его использовать ("glu32" в Windows, "GLU" в Linux, и так далее).
Немного не понятна эта часть.
ScorpioT1000 #2 - 3 года назад 2
Картинок бы
alexprey #3 - 3 года назад 2
ScorpioT1000, пустых окон?
ScorpioT1000 #4 - 3 года назад 2
примеров мешей итп