Nachdem ich einen guten Ruf für eine erfolglose Prämie ausgegeben hatte, um Hilfe in diesem Bereich zu erhalten, wurde mir endlich klar, wie komplex das Problem war, an dem ich interessiert war.
Die wenigen Personen, die diese Aufgabe erfüllt haben, teilen nicht viel . Während meiner Recherche habe ich verschiedene Wege gefunden, um das zu erreichen, wonach ich gesucht habe. Eines der interessantesten ist AeroGL , und es zeigt Code - Schnipsel mit einer Technik , die bisher nicht erwähnt wurde, die die Grafiken auf eine rendert geräteunabhängige Bitmap (DIB).
Um diesen Thread dauerhaft zu schließen, implementiert der folgende Quellcode diese Technik. Der Code selbst ist eine geringfügige Modifikation einer hier vorgestellten Anwendung (ein großes Dankeschön an Andrei Sapronov Y. ).
Das Endergebnis ist im Bild unten zu sehen:
Der Code wurde unter Windows XP (32 Bit) und Windows 8.1 (32 Bit) getestet.
Genießen!
#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);
}
Da alle bisher gegebenen Antworten nur auf Windows abzielen, dies aber sicherlich auch auf X11 mit einem zusammengesetzten Fenstermanager erforderlich ist, poste ich meinen Beispielcode hier als Referenz (ebenfalls unter https://github.com/datenwolf) /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
Der Haupttrick besteht darin, die richtige FBConfig zu erhalten. Sie müssen nach einem Alpha-Kanal fragen und den zugehörigen
XRenderPictFormat
auf das Vorhandensein einer Alpha-Maske testen .quelle
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Dies könnte ein Community-Wiki werden, wenn wir heutzutage noch so etwas machen.Ich weiß, dass dies mit Windows 7 möglich ist, nicht sicher über frühere Versionen.
Um den Fensterrand zu entfernen, müssen Sie den
WS_OVERLAPPEDWINDOW
Stil aus dem Fenster entfernen und den Stil hinzufügenWS_POPUP
:Um den Hintergrund des OpenGL-Fensters transparent zu machen, müssen Sie die folgende
DwmEnableBlurBehindWindow
Funktion verwenden:Sie müssen beim Aufrufen auch 0 für den Alpha-Wert angeben
glClearColor
.Stellen Sie außerdem beim Erstellen Ihres OpenGL-Kontexts sicher, dass Sie einen Alphakanal zuweisen.
Jetzt sollte Ihr Hintergrund vollständig transparent sein. Wenn Sie die Fensterdekorationen beibehalten, wird im Hintergrund der Aero-Unschärfe-Look verwendet, und Sie können den Transparenzgrad mithilfe des Alpha-Werts in anpassen
glClearColor
.quelle
bb.hRgnBlur
Parameter aufCreateRectRgn(0, 0, 1, 1);
undbb.dwFlags
aufDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Dadurch wird genau ein Pixel unscharf und der Rest des Fensters (wo mit glClear gelöscht) als vollständig transparent angezeigt.identifier "DWM_BLURBEHIND" is undefined
. Gibt es eine Bibliothek, die ich aufnehmen muss?Dies ist eine alte Frage, aber da neuere Windows-Versionen Compositing und Unterstützung für Opengl haben, wie Datenwolf andeutet, können wir einige dieser speziellen Saucen verwenden, um dies zu erreichen. Obwohl es auch mit DirectX trivial ist (siehe Abbildung ...), hat Microsoft OpenGL-Kontexten Compositing-Hinweise hinzugefügt. Yay kartellrechtliche Ängste!
Anstelle einer ineffizienten Aktion zum Kopieren in den physischen Speicher kann die Compositing-Engine also nur verstehen, wie der OpenGL-Kontext verwendet wird.
Sie müssen also einen OpenGL-Kontext mit einem Pixelformat erstellen, das einen Alphakanal angibt und dessen Komposition verwendet werden soll (Zeile 82). Anschließend verwenden Sie die Routinen DwmApi.h, um ein unscharfes Fenster (Zeile 179) mit einem vollständig ungültigen Bereich zu aktivieren, das nichts verwischt und das Fenster transparent macht. (Sie müssen einen schwarzen + transparenten Pinsel für die Fensterklasse angeben! Seltsamerweise!) Dann verwenden Sie einfach nur opengl, wie Sie es gewohnt sind. In der Ereignisschleife können Sie bei jeder Gelegenheit einfach Puffer zeichnen und austauschen (Zeile 201) und daran denken, GL_BLEND zu aktivieren! :) :)
Bitte überprüfen / gabeln Sie https://gist.github.com/3644466 oder sehen Sie sich stattdessen das folgende Code-Snippet an, das auf der Antwort des OP mit dieser Technik basiert (Sie können dies einfach in ein leeres Projekt einfügen):
quelle
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
umgehen, indem ich das Fenster 1 Pixel größer als der Bildschirm machte, dh als an CreateWindowEx übergebene Werte verwendete und dannglViewport(0, 0, screen_width, screen_height)
wie gewohnt aufrief .Dies wäre sehr einfach, wenn OpenGL-Fenster geschichtet werden könnten. Aber das sind sie nicht, also musst du dich für etwas anderes entscheiden.
Sie können ein Ebenenfenster (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em, wenn Sie sie nicht kennen) erstellen, um die Transparenz zu verwalten, und ein verstecktes OpenGL-Fenster für das Rendern. Rendern Sie die OpenGL-Szene in einen Off-Screen-Puffer, lesen Sie sie zurück und teilen Sie sie mit dem Ebenenfenster. Anschließend können Sie sie mit Bitblt (GDI-Funktion) in das Ebenenfenster einfügen.
Dies mag für sehr komplexe Dinge zu langsam sein, gibt Ihnen jedoch den gewünschten Effekt und funktioniert unter Windows 2000 und höher.
BEARBEITEN: Wenn es darum geht, den eigentlichen Off-Screen-Puffer zu erstellen, sind Framebuffer-Objekte (FBOs) wahrscheinlich die beste Wahl. Sie könnten einfach auf das versteckte OpenGL-Fenster zurückgreifen, obwohl ich mich an jemanden erinnere, der über Probleme damit berichtet, weil Pixelbesitz sind - FBOs werden empfohlen. Sie können auch Pixelpuffer (pbuffer) verwenden, diese sind jedoch veraltet (gestempeltes "Legacy"), und FBOs gelten als die moderne Methode, dies zu tun. FBOs sollten Ihnen Hardwarebeschleunigung geben (falls unterstützt) und Sie selbst nicht auf eine bestimmte OpenGL-Version beschränken. Sie benötigen einen OpenGL-Kontext, um ihn verwenden zu können. Daher müssen Sie dieses versteckte OpenGL-Fenster erstellen und von dort aus den FBO einrichten.
Hier sind einige Ressourcen auf FBOs:
Wikipedia
FBO
GameDev Artikel
Führer (für Mac, aber könnte hilfreich sein)
quelle
Tolle Demos mit Quellen, die Sie Schritt für Schritt durch den Prozess führen:
http://www.dhpoware.com/demos/index.html
quelle
Ich weiß, dass dies alt ist, aber ich habe versucht, die Xlib-Lösung auf Gtk + zu portieren. Nach vielem Lernen habe ich es endlich geschafft, also möchte ich es hier wirklich für alle teilen, die es brauchen.
Zusammengestellt mit
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Getestet unter Ubuntu 18.04 (zusätzlich zu GTK müssen Sie installierenlibgtkglext1-dev
).BEARBEITEN
Ich habe den Rendering-Code von einfach a
glClear
in ein Rechteck geändert .Der Code ist eine modifizierte Version dieser Frage und auch dieser Frage .
quelle
Sie können die 3D-Szene in einen Puffer rendern und mit einem Farbschlüssel auf den Bildschirm blenden.
quelle
Das ClanLib-Spiel SDK macht das.
Wenn Sie nur einen statischen transparenten Rand benötigen, verwenden Sie die folgende Technik:
Erstellt 5 Fenster
AAAAA
B ### C.
B ### C.
DDDDD
A, B, C, D sind geschichtete Fenster
"#" ist das Hauptfenster.
Siehe die Bilder unten unter - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
quelle