Потратив некоторую репутацию на неудачное вознаграждение, чтобы получить некоторую помощь по этому вопросу, я наконец понял, насколько сложной была проблема, которая меня интересовала.
Те немногие люди, которые выполнили эту задачу , мало что разделяют . Во время своего исследования я нашел разные способы достичь того, что искал. Одним из наиболее интересных является AeroGL , который показывает фрагменты кода с использованием техники, о которой до сих пор не упоминалось, а именно рендеринга графики в независимое от устройства растровое изображение (DIB).
Чтобы закрыть этот поток навсегда, исходный код ниже реализует эту технику. Сам код представляет собой небольшую модификацию представленного здесь приложения (большое спасибо Андрею Сапронову Ю. ).
Конечный результат можно увидеть на изображении ниже:
Код был протестирован в Windows XP (32-бит) и Windows 8.1 (32-бит).
Наслаждайтесь!
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <windowsx.h>
#include <GL/gl.h>
#include <GL/glu.h>
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
#include <assert.h>
#include <tchar.h>
#ifdef assert
#define verify(expr) if(!expr) assert(0)
#else verify(expr) expr
#endif
const TCHAR szAppName[]=_T("TransparentGL");
const TCHAR wcWndName[]=_T("WS_EX_LAYERED OpenGL");
HDC hDC;
HGLRC m_hrc;
int w(240);
int h(240);
HDC pdcDIB;
HBITMAP hbmpDIB;
void *bmp_cnt(NULL);
int cxDIB(0);
int cyDIB(0);
BITMAPINFOHEADER BIH;
BOOL initSC()
{
glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 0);
return 0;
}
void resizeSC(int width,int height)
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW );
glLoadIdentity();
}
BOOL renderSC()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glPushMatrix();
glColor3f(0, 1, 1);
glBegin(GL_TRIANGLES); // Drawing Using Triangles
glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red
glVertex3f( 0.0f, 1.0f, 0.0f); // Top
glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green
glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left
glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue
glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right
glEnd();
glPopMatrix();
glFlush();
return 0;
}
// DIB -> hDC
void draw(HDC pdcDest)
{
assert(pdcDIB);
verify(BitBlt(pdcDest, 0, 0, w, h, pdcDIB, 0, 0, SRCCOPY));
}
void CreateDIB(int cx, int cy)
{
assert(cx > 0);
assert(cy > 0);
cxDIB = cx ;
cyDIB = cy ;
int iSize = sizeof(BITMAPINFOHEADER);
memset(&BIH, 0, iSize);
BIH.biSize = iSize;
BIH.biWidth = cx;
BIH.biHeight = cy;
BIH.biPlanes = 1;
BIH.biBitCount = 24;
BIH.biCompression = BI_RGB;
if(pdcDIB)
verify(DeleteDC(pdcDIB));
pdcDIB = CreateCompatibleDC(NULL);
assert(pdcDIB);
if(hbmpDIB)
verify(DeleteObject(hbmpDIB));
hbmpDIB = CreateDIBSection(
pdcDIB,
(BITMAPINFO*)&BIH,
DIB_RGB_COLORS,
&bmp_cnt,
NULL,
0);
assert(hbmpDIB);
assert(bmp_cnt);
if(hbmpDIB)
SelectObject(pdcDIB, hbmpDIB);
}
BOOL CreateHGLRC()
{
DWORD dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_BITMAP;
PIXELFORMATDESCRIPTOR pfd ;
memset(&pfd,0, sizeof(PIXELFORMATDESCRIPTOR)) ;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = dwFlags ;
pfd.iPixelType = PFD_TYPE_RGBA ;
pfd.cColorBits = 24 ;
pfd.cDepthBits = 32 ;
pfd.iLayerType = PFD_MAIN_PLANE ;
int PixelFormat = ChoosePixelFormat(pdcDIB, &pfd);
if (PixelFormat == 0){
assert(0);
return FALSE ;
}
BOOL bResult = SetPixelFormat(pdcDIB, PixelFormat, &pfd);
if (bResult==FALSE){
assert(0);
return FALSE ;
}
m_hrc = wglCreateContext(pdcDIB);
if (!m_hrc){
assert(0);
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WindowFunc(HWND hWnd,UINT msg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
switch(msg)
{
case WM_ERASEBKGND:
return 0;
break;
case WM_CREATE:
break;
case WM_DESTROY:
if(m_hrc)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(m_hrc) ;
}
PostQuitMessage(0) ;
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
renderSC(); // OpenGL -> DIB
draw(hDC); // DIB -> hDC
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
w = LOWORD(lParam); h = HIWORD(lParam);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(m_hrc);
CreateDIB(w, h);
CreateHGLRC();
verify(wglMakeCurrent(pdcDIB, m_hrc));
initSC();
resizeSC(w, h);
renderSC();
break;
default:
return DefWindowProc(hWnd,msg,wParam,lParam);
}
return 0;
}
int WINAPI _tWinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR str,int nWinMode)
{
WNDCLASSEX wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowFunc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hThisInst;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW);
wc.lpszClassName = szAppName;
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, _T("RegisterClassEx - failed"), _T("Error"), MB_OK | MB_ICONERROR);
return FALSE;
}
HWND hWnd = CreateWindowEx(WS_EX_LAYERED, szAppName, wcWndName,
WS_VISIBLE | WS_POPUP, 200, 150, w, h,
NULL, NULL, hThisInst, NULL);
if(!hWnd){
MessageBox(NULL, _T("CreateWindowEx - failed"), _T("Error"), MB_OK | MB_ICONERROR);
return FALSE;
}
verify(SetLayeredWindowAttributes(hWnd, 0x0, 0, LWA_COLORKEY));
MSG msg;
while(1)
{
while (PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else return 0;
}
}
return (FALSE);
}
Поскольку все ответы, приведенные до сих пор, нацелены только на Windows, но, безусловно, также есть требование сделать это на X11 с помощью составного оконного менеджера, для справки я публикую здесь свой пример кода (также его можно найти на https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
Главный трюк - получить правильный FBConfig. Вам нужно запросить альфа-канал и проверить связанный
XRenderPictFormat
на наличие альфа-маски.источник
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Это могло бы стать вики-сообществом, если мы все еще делаем что-то подобное в наши дни.Я знаю, что это возможно с Windows 7, но не уверен в более ранних версиях.
Чтобы избавиться от границы окна, вам нужно удалить
WS_OVERLAPPEDWINDOW
стиль из окна и добавитьWS_POPUP
стиль:Чтобы сделать фон окна OpenGL прозрачным, вам нужно будет использовать
DwmEnableBlurBehindWindow
функцию:Вам также нужно будет указать 0 для альфа-значения при вызове
glClearColor
.Кроме того, при создании контекста OpenGL убедитесь, что вы выделяете альфа-канал.
Теперь ваш фон должен быть полностью прозрачным. Если вы сохраните оформление окон, тогда для фона будет использоваться эффект размытия Aero, и вы можете настроить уровень прозрачности, используя значение альфа в
glClearColor
.источник
bb.hRgnBlur
параметр вCreateRectRgn(0, 0, 1, 1);
иbb.dwFlags
кDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Это приведет к размытию ровно одного пикселя и покажет остальную часть окна (очищенную с помощью glClear) как полностью прозрачную.identifier "DWM_BLURBEHIND" is undefined
. Есть ли библиотека, которую мне нужно включить?Это старый вопрос, но поскольку более новые версии Windows имеют компоновку и поддержку, как подсказывает datenwolf, для opengl, мы можем использовать этот специальный соус для достижения этой цели. Хотя с DirectX это тоже тривиально (см. Рисунок ...), Microsoft добавила подсказки по композитингу в контексты opengl. Ура антимонопольные опасения!
Таким образом, вместо неэффективного копирования в физическую память мы можем заставить движок композитинга просто понимать, как использовать контекст opengl.
Итак, вам нужно создать контекст opengl с форматом пикселей, который указывает альфа-канал и должен использовать композицию (строка 82). Затем вы используете подпрограммы DwmApi.h, чтобы включить размытое окно (строка 179) с указанной полностью недопустимой областью, что ничего не размывает и оставляет окно прозрачным. (Вам нужно указать черную + прозрачную кисть в классе окна! Странно!) Тогда вы фактически просто используете opengl, как вы привыкли его использовать. В цикле событий, при любой возможности, вы можете просто нарисовать и поменять местами буферы (строка 201) и не забудьте включить GL_BLEND! :)
Просмотрите / разветвите https://gist.github.com/3644466 или просто просмотрите следующий фрагмент кода, основанный на собственном ответе OP с помощью этой техники (вы можете просто вставить это в пустой проект):
источник
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
в качестве значений, переданных в CreateWindowEx, а затем вызватьglViewport(0, 0, screen_width, screen_height)
как обычно.Это было бы очень просто, если бы окна OpenGL могли быть многоуровневыми. Но это не так, поэтому вам придется пойти на что-то другое.
Что вы можете сделать, так это создать многоуровневое окно (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google, если вы их не знаете) для обработки прозрачности и скрытое окно OpenGL для рендеринга. Визуализируйте сцену OpenGL во внеэкранном буфере, прочтите ее и поделитесь ею с многоуровневым окном, затем bitblt (функция GDI) в многоуровневом окне.
Это может быть слишком медленно для очень сложных вещей, но даст вам желаемый эффект и будет работать в Windows 2000 и выше.
РЕДАКТИРОВАТЬ: Когда дело доходит до создания фактического внеэкранного буфера, объекты фреймбуфера (FBO), вероятно, являются вашим лучшим выбором. Вы можете просто рисовать в скрытом окне OpenGL, хотя я думаю, что я помню, как кто-то писал о проблемах с этим из-за владения пикселями - рекомендуются FBO. Вы также можете использовать пиксельные буферы (pbuffers), но они уже устарели (помечены как «устаревшие»), и FBO считаются современным способом сделать это. FBO должны обеспечивать аппаратное ускорение (если поддерживается) и сами по себе не ограничивают вас конкретной версией OpenGL. Для его использования вам понадобится контекст OpenGL, поэтому вам нужно будет создать это скрытое окно OpenGL и настроить оттуда FBO.
Вот некоторые ресурсы по FBO: Руководство по статьям
Wikipedia
FBO
Gamedev (для Mac, но может быть полезно)
источник
отличный набор демонстраций с исходным кодом, которые помогут вам шаг за шагом:
http://www.dhpoware.com/demos/index.html
источник
Я знаю, что это устарело, но я пытался перенести решение Xlib на Gtk +. После долгого изучения я, наконец, сделал это, поэтому я действительно хочу поделиться им здесь для всех, кто в этом нуждается.
Скомпилировано с помощью
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Протестировано на Ubuntu 18.04 (помимо gtk необходимо установитьlibgtkglext1-dev
).РЕДАКТИРОВАТЬ
Я изменил код рендеринга с простого
glClear
на прямоугольник.Код является измененной версией этого вопроса, а также этого вопроса .
источник
Вы можете визуализировать трехмерную сцену в pbuffer и перенести ее на экран с помощью цветового ключа.
источник
Это делает игровой SDK ClanLib.
Если вам требуется только статическая прозрачная граница, используйте следующую технику:
Создает 5 окон
AAAAA
ДО НАШЕЙ ЭРЫ
ДО НАШЕЙ ЭРЫ
DDDDD
A, B, C, D - многослойные окна
«#» - главное окно.
Смотрите изображения внизу - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
источник