При работе с OpenGL в Delphi часто возникает потребность в рендеринге прямо на битмап для последующей обработки или отображения. В этой статье мы рассмотрим, как настроить оффскрин рендеринг OpenGL в Delphi и получить результат в виде битмапа.
Проблема
Разработчик создал приложение с множеством окон и контролов (2D рендеринг) и хочет рендерить каждое окно и управление на свой битмап. Пример кода, который у него есть:
uses
dglOpenGL;
var
BMP: TBitmap;
DC, RC: HDC;
function TMainForm.Init: Boolean;
begin
Result := InitOpenGL;
if Result = True then
begin
BMP := TBitmap.Create;
BMP.PixelFormat := pf24bit;
BMP.Width := 1280;
BMP.Height := 1024;
DC := BMP.Canvas.Handle;
RC := CreateRenderingContext(DC,
[opGDI, opDoubleBuffered],
24, 24, 0, 0, 0, 0);
ActivateRenderingContext(DC, RC);
glClearColor(0.27, 0.4, 0.7, 0.0);
glViewport(0, 0, 1280, 1024);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
glOrtho(0, 1280, 0, 1024, -1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
end;
end;
procedure TMainForm.Render;
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
// Красный квадрат
glColor3f(1, 0, 0);
glBegin(GL_QUADS);
glVertex2f(100, 100);
glVertex2f(1280-100, 100);
glVertex2f(1280-100, 1024-100);
glVertex2f(100, 1024-100);
glEnd;
// Обмен буферами
SwapBuffers(DC);
end;
При запуске приложения нет никакого вывода, но если использовать MainForm.Canvas.Draw(0, 0, BMP);, то появится белый прямоугольник. Разработчик хочет настроить приложение для оффскрин рендеринга, чтобы иметь возможность выполнять другие операции с битмапами, такие как рисование других изображений, текст или размытие.
PFD_DRAW_TO_WINDOW: Буфер может рисовать на окно или поверхность устройства.
PFD_DRAW_TO_BITMAP: Буфер может рисовать на память битмап.
Однако не спешите создавать контекст рендеринга для вашего DIB DC, так как OpenGL контексты рендеринга на разделе DIB будут использовать программный растеризатор, поддерживающий только OpenGL-1.1, работающий на процессоре, что будет очень медленно.
Вместо этого создайте объект кадра (Framebuffer Object), прикрепите к нему цветной буфер рендеринга и, когда закончите, выполните glReadPixels в ваш DIBSection. Это намного проще и быстрее.
Пример кода
Вот пример функции, которая читает пиксели OpenGL в HBITMAP, который затем можно использовать в вашем приложении:
// Функция сбрасывает очередь ошибок OpenGL и подсчитывает общее количество ошибок
function FlushGLErrors: Integer;
begin
Result := 0;
while (glGetError() <> GL_NO_ERROR) do
Inc(Result);
end;
// Возвращает HBITMAP или NULL. HBITMAP должен быть освобожден с помощью DeleteObject
function ReadPixelsToHBITMAP(x, y, width, height: Integer): HBITMAP;
var
pdata: Pointer;
bmih: TBitmapInfoHeader;
begin
Result := NULL;
// Заполните заголовок BITMAPINFOHEADER
bmih.biSize := SizeOf(TBitmapInfoHeader);
bmih.biWidth := width;
bmih.biHeight := -height; // -height, так как OpenGL использует координаты (0,0) в левом верхнем углу
bmih.biPlanes := 1;
bmih.biBitCount := 24;
bmih.biCompression := BI_RGB;
bmih.biSizeImage := 0;
bmih.biXPelsPerMeter := 2835; // 72 DPI
bmih.biYPelsPerMeter := 2835; // 72 DPI
bmih.biClrUsed := 0;
bmih.biClrImportant := 0;
// Создайте DIBSection
if CreateDIBSection(hdc, @bmih, DIB_RGB_COLORS, pdata, nil, 0) then
begin
// Сбросьте очередь ошибок OpenGL
if FlushGLErrors = 0 then
begin
// Настройте параметры хранения пикселей
glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_PACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Проверьте наличие ошибок OpenGL
if FlushGLErrors = 0 then
begin
// Прочитайте пиксели в буфер
glReadPixels(x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, pdata);
// Проверьте наличие ошибок OpenGL
if FlushGLErrors = 0 then
// Создайте HBITMAP из буфера
Result := CreateBitmapFromDIBSection(pdata, width, height);
end;
end;
end;
end;
После выполнения рендеринга вы можете вызвать функцию ReadPixelsToHBITMAP для получения HBITMAP, который затем можно использовать в вашем приложении. Не забудьте освободить HBITMAP, когда он больше не нужен, используя DeleteObject.
Вывод
может быть сложной задачей, но с правильным подходом и пониманием того, как работают OpenGL контексты, можно добиться отличных результатов. Используя Framebuffer Object и glReadPixels, вы можете легко получить результат рендеринга в виде битмапа, который можно использовать в вашем приложении.
Настройка оффскрин рендеринга OpenGL в Delphi для получения результата в виде битмапа.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.