Po wydaniu trochę reputacji na nieudaną nagrodę, aby uzyskać pomoc w tej sprawie, w końcu zdałem sobie sprawę, jak złożony był problem, który mnie interesował.
Kilka osób, które wykonały to zadanie , nie ma wiele wspólnego . Podczas moich badań znalazłem różne sposoby na osiągnięcie tego, czego szukałem. Jedną z najciekawszych jest AeroGL , która pokazuje fragmenty kodu przy użyciu techniki, o której do tej pory nie wspomniano, czyli renderowania grafiki do niezależnej od urządzenia mapy bitowej (DIB).
Aby trwale zamknąć ten wątek, poniższy kod źródłowy implementuje tę technikę. Sam kod jest drobną modyfikacją przedstawionej tutaj aplikacji (wielkie podziękowania dla Andrieja Sapronowa Y. ).
Efekt końcowy można zobaczyć na poniższym obrazku:
Kod został przetestowany na Windows XP (32-bity) i Windows 8.1 (32-bity).
Cieszyć się!
#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);
}
Ponieważ wszystkie odpowiedzi udzielone do tej pory dotyczą tylko systemu Windows, ale z pewnością istnieje również zapotrzebowanie na zrobienie tego na X11 z połączonym menedżerem okien, w celach informacyjnych zamieszczam tutaj mój przykładowy kod (można go również znaleźć na https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
Główną sztuczką jest uzyskanie odpowiedniego FBConfig. Musisz poprosić o kanał alfa i przetestować skojarzony pod
XRenderPictFormat
kątem obecności maski alfa.źródło
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Może to stać się wiki społeczności, jeśli nadal będziemy to robić w dzisiejszych czasach.Wiem, że jest to możliwe w systemie Windows 7, nie jestem pewien co do wcześniejszych wersji.
Aby pozbyć się obramowania okna, musisz usunąć
WS_OVERLAPPEDWINDOW
styl z okna i dodaćWS_POPUP
styl:Aby tło okna OpenGL było przezroczyste, musisz użyć
DwmEnableBlurBehindWindow
funkcji:Podczas dzwonienia będziesz musiał również podać 0 dla wartości alfa
glClearColor
.Ponadto, tworząc kontekst OpenGL, upewnij się, że przydzielasz kanał alfa.
Teraz twoje tło powinno być w pełni przezroczyste. Jeśli zachowasz dekoracje okna, tło będzie miało wygląd rozmycia Aero i możesz dostosować poziom przezroczystości za pomocą wartości alfa w
glClearColor
.źródło
bb.hRgnBlur
parametr naCreateRectRgn(0, 0, 1, 1);
ibb.dwFlags
naDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Spowoduje to rozmycie dokładnie jednego piksela i wyświetlenie reszty okna (jeśli wyczyszczono je za pomocą funkcji glClear) jako całkowicie przezroczystej.identifier "DWM_BLURBEHIND" is undefined
. Czy jest biblioteka, którą muszę dołączyć?To stare pytanie, ale ponieważ nowsze wersje systemu Windows mają kompozycję i obsługę, jak wskazuje datenwolf, dla opengl, możemy użyć trochę tego specjalnego sosu, aby to osiągnąć. Chociaż jest to również trywialne w przypadku DirectX (idź rysunek ...) Microsoft dodał wskazówki dotyczące kompozycji do kontekstów OpenGL. Hej, lęki antymonopolowe!
Więc zamiast nieefektywnej operacji kopiowania do pamięci fizycznej, możemy sprawić, by silnik kompozycji po prostu zrozumiał, jak wykorzystać kontekst OpenGL.
Musisz więc stworzyć kontekst OpenGL z formatem pikseli, który określa kanał alfa i powinien używać kompozycji (wiersz 82). Następnie używasz procedur DwmApi.h, aby włączyć rozmyte okno (wiersz 179) z określonym całkowicie nieprawidłowym regionem, który nic nie rozmyje i pozostawi okno przezroczyste. (Musisz określić czarny + przezroczysty pędzel w klasie okna! Dziwne!) Następnie po prostu używasz opengl, tak jak zwykłeś go używać. W pętli wydarzeń, przy każdej okazji, możesz po prostu narysować i zamienić bufory (linia 201) i pamiętaj, aby włączyć GL_BLEND! :)
Proszę przejrzeć / rozwidlić https://gist.github.com/3644466 lub po prostu wyświetlić następujący fragment kodu oparty na własnej odpowiedzi OP za pomocą tej techniki (możesz po prostu umieścić to w pustym projekcie):
źródło
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
jako wartości przekazanych do CreateWindowEx, a następnie zadzwońglViewport(0, 0, screen_width, screen_height)
jak zwykle.Byłoby to bardzo łatwe, gdyby pozwolono na tworzenie warstw okien OpenGL. Ale tak nie jest, więc będziesz musiał pójść na coś innego.
To, co możesz zrobić, to utworzyć warstwowe okno (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em, jeśli ich nie znasz) do obsługi przezroczystości i ukryte okno OpenGL do renderowania. Renderuj scenę OpenGL do bufora pozaekranowego, odczytaj ją z powrotem i udostępnij w oknie warstwowym, a następnie bitblt (funkcja GDI) w oknie warstwowym.
Może to być zbyt wolne w przypadku bardzo złożonych rzeczy, ale da efekt, o który prosisz, i będzie działać w systemie Windows 2000 i nowszych.
EDYCJA: Jeśli chodzi o tworzenie rzeczywistego bufora pozaekranowego, prawdopodobnie najlepszym rozwiązaniem są obiekty bufora ramki (FBO). Możesz po prostu narysować na ukrytym oknie OpenGL, chociaż myślę, że przypominam sobie, że ktoś pisał o kłopotach z tym z powodu własności pikseli - zaleca się FBO. Możesz także użyć buforów pikseli (buforów pb), ale są one nieco przestarzałe (oznaczone jako „legacy”), a FBO są uważane za nowoczesny sposób na zrobienie tego. FBO powinny zapewniać przyspieszenie sprzętowe (jeśli jest obsługiwane) i same nie ograniczają Cię do określonej wersji OpenGL. Będziesz potrzebował kontekstu OpenGL, aby go używać, więc będziesz musiał utworzyć to ukryte okno OpenGL i stamtąd skonfigurować FBO.
Oto kilka zasobów na temat FBO: Przewodnik po artykule FBO na
Wikipedii o grze Gamedev (dla komputerów Mac, ale może być pomocny)
źródło
świetny zestaw demonstracji ze źródłami przeprowadzającymi Cię krok po kroku:
http://www.dhpoware.com/demos/index.html
źródło
Wiem, że to jest stare, ale próbowałem przenieść rozwiązanie Xlib na Gtk +. Po wielu badaniach w końcu udało mi się to, więc naprawdę chcę się nim tutaj podzielić dla wszystkich potrzebujących.
Skompilowane z
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Testowane na Ubuntu 18.04 (oprócz GTK, musisz zainstalowaćlibgtkglext1-dev
).EDYTOWAĆ
Zmieniłem kod renderujący z prostego a
glClear
na prostokąt.Kod jest zmodyfikowaną wersją tego pytania, a także tego pytania .
źródło
Możesz wyrenderować scenę 3D do bufora i przefiltrować ją na ekran używając klawisza koloru.
źródło
Robi to pakiet SDK gry ClanLib.
Jeśli potrzebujesz tylko statycznej przezroczystej ramki, użyj następującej techniki:
Tworzy 5 okien
AAAAA
PNE
PNE
DDDDD
A, B, C, D to okna warstwowe
„#” to główne okno.
Zobacz zdjęcia na dole - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
źródło