[C++, GTK+] Gtk, OpenGL и все-все-все
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Не так давно мне пришлось столкнуться с использованием OpenGL в Gtkmm версии 3. В данной статье я постараюсь изложить детали имплементации стандартного виджета для OpenGL-графики.
Gtk::GLArea
Быстрый поиск по фразе Gtkmm+OpenGL выдает кучу примеров для Gtk::GLArea — стандартного виджета для OpenGL. Он имеет довольно гибкий интерфейс, а так же следующие возможности:
- опциональная поддержка прозрачности самого виджета,
- настройка буфера трафарета и глубины,
- сигналы для инициализации и освобождения контекста OpenGL.
В обычном случае, все команды рисования располагаются в обработчике сигнала signal_draw(), но есть возможность создать сцену в процессе инициализации, и она будет отображаться даже при перерисовке окна.
Копаем глубже
Gtk::GLArea содержит некоторое количество опциональных возможностей, которые
будут осложнять изложение, поэтому ссылаться я буду на созданную мной
упрощенную версию — OpenGLWidget.
В целом, жизненный цикл виджета состоит из нескольких этапов:
- Инициализация
- Повторяющийся вызов функции отрисовки
- Утилизация
Рассмотрим более подробно первые два пункта.
Инициализация
За процесс инициализации отвечает метод OpenGLWidget::on_realize, а схема показана на рисунке ниже:
Для перехватывания событий необходимо создать Gdk::Window. Так же наличие
у виджета своего экземпляра окна позволяет рисовать на его поверхности.
Код, который создает Gdk::Window:
GdkWindowAttr attributes;
memset(&attributes, 0, sizeof(attributes));
Gtk::Allocation allocation = get_allocation();
//Set initial position and size of the Gdk::Window:
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.event_mask = get_events ();
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
m_refGdkWindow = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
set_window(m_refGdkWindow);
register_window(m_refGdkWindow);
Здесь задаются атрибуты, такие как тип, класс, позиция, ширина и высота.
Так же важно вызвать Gtk::Widget::set_window, чтобы передать виджету информацию о сконструированном Gdk::Window.
Метод Gtk::Widget::register_window делает возможным получение событий виджетом от указанного окна.
Gdk::GLContext является оберткой над контекстом OpenGL. Его создание иллюстрирует следующий код:
m_Context = m_refGdkWindow->create_gl_context();
if(!m_Context->realize())
{
std::cerr << "Context realize failed" << std::endl;
m_Context.reset();
return;
}
Перед выполнением любых команд OpenGL необходимо вызвать Gdk::GLContext::make_current, как это сделано перед инициализацией Framebuffer и Renderbuffer:
m_Context->make_current();
glGenFramebuffersEXT (1, &frame_buffer);
glGenRenderbuffersEXT (1, &render_buffer);
Вывод на экран
Здесь необходимо сделать небольшое отступление. Дело в том, что виджет, как таковой, не
имеет непосредственного доступа к внутреннему буферу окна операционной системы и не может в него рисовать.
Поэтому Gtk::GLArea создает связку Framebuffer и Renderbuffer или текстуры.
Все команды OpenGL выполняются над текстурой или Renderbuffer, формируя итоговое
изображение. Далее, вызов функции gdk_cairo_draw_from_gl с необходимыми параметрами
сохраняет его в буфер окна ОС, после оно будет показано пользователю. В OpenGLWidget
используется связка Framebuffer и Renderbuffer.
Последовательность шагов для вывода на экран показана на диаграмме:
Все операции с графикой выполняются в имплементации Gtk::Widget::on_draw — OpenGLWidget::on_draw.
Вот код, иллюстрирующий как привязываются буферы:
m_Context->make_current();
auto scale = get_scale_factor ();
m_BufferDimensions = {
scale,
get_allocated_width () * scale,
get_allocated_height () * scale
};
glBindRenderbuffer (GL_RENDERBUFFER, render_buffer);
glRenderbufferStorage (GL_RENDERBUFFER, GL_RGB8, m_BufferDimensions->width, m_BufferDimensions->height);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, frame_buffer);
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, render_buffer);
glViewport (0, 0, m_BufferDimensions->width, m_BufferDimensions->height );
Здесь необходимо отметить сохранение параметров области вывода в переменную m_BufferDimensions, это нужно для дальнейшего вызова gdk_cairo_draw_from_gl.
Перед вызовом пользовательского колбэка и gdk_cairo_draw_from_gl необходимо проверить статус Framebuffer, и если он не готов, то пропустить эти инструкции. Их выполнение над таким Framebuffer будет ошибочным.
Вызов пользовательского клолбэка осуществляется с помощью механизма сигналов библиотеки sigc:
m_DrawSignal.emit();
Для вывода сформированного изображения из Renderbuffer необходимо выполнить инструкцию:
gdk_cairo_draw_from_gl (cr->cobj(),
m_refGdkWindow->gobj(),
render_buffer,
GL_RENDERBUFFER,
m_BufferDimensions->scale, 0, 0,
m_BufferDimensions->width,
m_BufferDimensions->height);
Заключительный этап — отвязываем буферы:
m_Context->make_current();
glBindRenderbuffer (GL_RENDERBUFFER, 0);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
m_Context->clear_current();
m_Context->make_current() здесь важен, потому что gdk_cairo_draw_from_gl может поменять текущий контекст.
Handmade Cairo::Context
В заключении рассмотрим вопрос явного, ручного создания Cairo::Context.
Его получает в качестве параметра обработчик сигнала signal_draw() или
метод Gtk::Widget::on_draw. Они вызываются, если есть потребность перерисовать контент виджета.
Создать такой контекст можно функцией Gdk::Window::begin_draw_frame, а в завершении
вызвать Gdk::Window::end_draw_frame.
Пример, как работает эта связка:
void OpenGLWidget::draw_content()
{
if(m_refGdkWindow && m_refGdkWindow->is_visible())
{
auto region = m_refGdkWindow->get_visible_region(); /*1*/
auto context = m_refGdkWindow->begin_draw_frame(region); /*2*/
draw(context->get_cairo_context()); /*3*/
m_refGdkWindow->end_draw_frame(context); /*4*/
}
}
Здесь m_refGdkWindow — это Gdk::Window связанный с виджетом. Перед созданием контекста необходимо получить параметры области для рисования (выражение 1). Далее, вызывается Gdk::Window::begin_draw_frame для конструирования Cairo::Context для указанной области (выражение 2). Для формирования контента вызываем Gtk::Widget::draw (выражение 3), которая передаст управление либо имплементации Gtk::Widget::on_draw, либо вызовет обработчик сигнала signal_draw(). Сообщаем о завершении процедуры рисования с помощью Gdk::Window::end_draw_frame
(выражение 4).
===========
Источник:
habr.com
===========
Похожие новости:
- [C++, C] На собеседовании: Почему не пишут ядро ОС на C++? Немного про Fuchsia и Zircon
- [C++, Визуализация данных, Разработка под Windows] Ещё один модуль рисования графиков
- [Информационная безопасность, C++] XSEC: как изучить Windows Access Control за два часа
- [C++, Серверная оптимизация] Многопоточный HTTP-сервер с ThreadPool’ом и конечным автоматом
- [C++] Breadth/Depth First Search
- [Программирование, C++] Мета-программирование атрибутов для сериализации
- [C++, Разработка игр, Unreal Engine, Дизайн игр] Making Grenades in Unreal Engine, Part 2: Attributes, Gameplay Effects, Replication
- [C++, Unreal Engine, DevOps] IncrediBuild: Как ускорить сборку и анализ вашего проекта
- [C++, Unreal Engine, DevOps] IncrediBuild: How to Speed up Your Project's Build and Analysis
- [Java, C++, Разработка под Android, C] Производительность Android Runtime vs NDK
Теги для поиска: #_c++, #_gtk+, #_gtkmm, #_c++, #_opengl, #_c++, #_gtk+
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 21-Ноя 21:39
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Не так давно мне пришлось столкнуться с использованием OpenGL в Gtkmm версии 3. В данной статье я постараюсь изложить детали имплементации стандартного виджета для OpenGL-графики. Gtk::GLArea Быстрый поиск по фразе Gtkmm+OpenGL выдает кучу примеров для Gtk::GLArea — стандартного виджета для OpenGL. Он имеет довольно гибкий интерфейс, а так же следующие возможности:
В обычном случае, все команды рисования располагаются в обработчике сигнала signal_draw(), но есть возможность создать сцену в процессе инициализации, и она будет отображаться даже при перерисовке окна. Копаем глубже Gtk::GLArea содержит некоторое количество опциональных возможностей, которые будут осложнять изложение, поэтому ссылаться я буду на созданную мной упрощенную версию — OpenGLWidget. В целом, жизненный цикл виджета состоит из нескольких этапов:
Рассмотрим более подробно первые два пункта. Инициализация За процесс инициализации отвечает метод OpenGLWidget::on_realize, а схема показана на рисунке ниже: Для перехватывания событий необходимо создать Gdk::Window. Так же наличие у виджета своего экземпляра окна позволяет рисовать на его поверхности. Код, который создает Gdk::Window: GdkWindowAttr attributes;
memset(&attributes, 0, sizeof(attributes)); Gtk::Allocation allocation = get_allocation(); //Set initial position and size of the Gdk::Window: attributes.x = allocation.get_x(); attributes.y = allocation.get_y(); attributes.width = allocation.get_width(); attributes.height = allocation.get_height(); attributes.event_mask = get_events (); attributes.window_type = GDK_WINDOW_CHILD; attributes.wclass = GDK_INPUT_OUTPUT; m_refGdkWindow = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y); set_window(m_refGdkWindow); register_window(m_refGdkWindow); Здесь задаются атрибуты, такие как тип, класс, позиция, ширина и высота. Так же важно вызвать Gtk::Widget::set_window, чтобы передать виджету информацию о сконструированном Gdk::Window. Метод Gtk::Widget::register_window делает возможным получение событий виджетом от указанного окна. Gdk::GLContext является оберткой над контекстом OpenGL. Его создание иллюстрирует следующий код: m_Context = m_refGdkWindow->create_gl_context();
if(!m_Context->realize()) { std::cerr << "Context realize failed" << std::endl; m_Context.reset(); return; } Перед выполнением любых команд OpenGL необходимо вызвать Gdk::GLContext::make_current, как это сделано перед инициализацией Framebuffer и Renderbuffer: m_Context->make_current();
glGenFramebuffersEXT (1, &frame_buffer); glGenRenderbuffersEXT (1, &render_buffer); Вывод на экран Здесь необходимо сделать небольшое отступление. Дело в том, что виджет, как таковой, не имеет непосредственного доступа к внутреннему буферу окна операционной системы и не может в него рисовать. Поэтому Gtk::GLArea создает связку Framebuffer и Renderbuffer или текстуры. Все команды OpenGL выполняются над текстурой или Renderbuffer, формируя итоговое изображение. Далее, вызов функции gdk_cairo_draw_from_gl с необходимыми параметрами сохраняет его в буфер окна ОС, после оно будет показано пользователю. В OpenGLWidget используется связка Framebuffer и Renderbuffer. Последовательность шагов для вывода на экран показана на диаграмме: Все операции с графикой выполняются в имплементации Gtk::Widget::on_draw — OpenGLWidget::on_draw. Вот код, иллюстрирующий как привязываются буферы: m_Context->make_current();
auto scale = get_scale_factor (); m_BufferDimensions = { scale, get_allocated_width () * scale, get_allocated_height () * scale }; glBindRenderbuffer (GL_RENDERBUFFER, render_buffer); glRenderbufferStorage (GL_RENDERBUFFER, GL_RGB8, m_BufferDimensions->width, m_BufferDimensions->height); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, frame_buffer); glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, render_buffer); glViewport (0, 0, m_BufferDimensions->width, m_BufferDimensions->height ); Здесь необходимо отметить сохранение параметров области вывода в переменную m_BufferDimensions, это нужно для дальнейшего вызова gdk_cairo_draw_from_gl. Перед вызовом пользовательского колбэка и gdk_cairo_draw_from_gl необходимо проверить статус Framebuffer, и если он не готов, то пропустить эти инструкции. Их выполнение над таким Framebuffer будет ошибочным. Вызов пользовательского клолбэка осуществляется с помощью механизма сигналов библиотеки sigc: m_DrawSignal.emit();
Для вывода сформированного изображения из Renderbuffer необходимо выполнить инструкцию: gdk_cairo_draw_from_gl (cr->cobj(),
m_refGdkWindow->gobj(), render_buffer, GL_RENDERBUFFER, m_BufferDimensions->scale, 0, 0, m_BufferDimensions->width, m_BufferDimensions->height); Заключительный этап — отвязываем буферы: m_Context->make_current();
glBindRenderbuffer (GL_RENDERBUFFER, 0); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); m_Context->clear_current(); m_Context->make_current() здесь важен, потому что gdk_cairo_draw_from_gl может поменять текущий контекст. Handmade Cairo::Context В заключении рассмотрим вопрос явного, ручного создания Cairo::Context. Его получает в качестве параметра обработчик сигнала signal_draw() или метод Gtk::Widget::on_draw. Они вызываются, если есть потребность перерисовать контент виджета. Создать такой контекст можно функцией Gdk::Window::begin_draw_frame, а в завершении вызвать Gdk::Window::end_draw_frame. Пример, как работает эта связка: void OpenGLWidget::draw_content()
{ if(m_refGdkWindow && m_refGdkWindow->is_visible()) { auto region = m_refGdkWindow->get_visible_region(); /*1*/ auto context = m_refGdkWindow->begin_draw_frame(region); /*2*/ draw(context->get_cairo_context()); /*3*/ m_refGdkWindow->end_draw_frame(context); /*4*/ } } Здесь m_refGdkWindow — это Gdk::Window связанный с виджетом. Перед созданием контекста необходимо получить параметры области для рисования (выражение 1). Далее, вызывается Gdk::Window::begin_draw_frame для конструирования Cairo::Context для указанной области (выражение 2). Для формирования контента вызываем Gtk::Widget::draw (выражение 3), которая передаст управление либо имплементации Gtk::Widget::on_draw, либо вызовет обработчик сигнала signal_draw(). Сообщаем о завершении процедуры рисования с помощью Gdk::Window::end_draw_frame (выражение 4). =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 21-Ноя 21:39
Часовой пояс: UTC + 5