Après avoir dépensé une certaine réputation sur une prime infructueuse pour obtenir de l' aide sur ce problème, j'ai finalement réalisé à quel point le problème qui m'intéressait était complexe.
Les quelques personnes qui ont accompli cette tâche ne partagent pas grand-chose . Au cours de mes recherches, j'ai trouvé différentes façons de réaliser ce que je recherchais. L'un des plus intéressants est AeroGL , et il montre des extraits de code en utilisant une technique qui n'a pas été mentionnée jusqu'à présent, qui consiste à rendre les graphiques dans un bitmap indépendant du périphérique (DIB).
Pour fermer définitivement ce thread, le code source ci-dessous implémente cette technique. Le code lui-même est une légère modification d'une application présentée ici (un grand merci à Andrei Sapronov Y. ).
Le résultat final peut être vu dans l'image ci-dessous:
Le code a été testé sur Windows XP (32 bits) et Windows 8.1 (32 bits).
Prendre plaisir!
#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);
}
Étant donné que toutes les réponses données jusqu'à présent ne visent que Windows, mais qu'il y a sûrement aussi une demande de le faire sur X11 avec un gestionnaire de fenêtres composites, pour référence, je poste mon exemple de code ici (également disponible sur https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
L'astuce principale est d'obtenir le bon FBConfig. Vous devez demander un canal alpha et tester l'associé
XRenderPictFormat
pour la présence d'un masque alpha.la source
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Cela pourrait devenir un wiki communautaire, si nous faisons toujours ce genre de chose ces jours-ci.Je sais que cela est possible avec Windows 7, pas sûr des versions antérieures.
Pour vous débarrasser de la bordure de la fenêtre, vous devez supprimer le
WS_OVERLAPPEDWINDOW
style de la fenêtre et ajouter leWS_POPUP
style:Pour rendre l'arrière-plan de la fenêtre OpenGL transparent, vous devrez utiliser la
DwmEnableBlurBehindWindow
fonction:Vous devrez également spécifier 0 pour la valeur alpha lors de l'appel
glClearColor
.De plus, lors de la création de votre contexte OpenGL, assurez-vous d'allouer un canal alpha.
Maintenant, votre arrière-plan doit être entièrement transparent. Si vous conservez les décorations de fenêtre, l'arrière-plan utilisera l'aspect flou Aero et vous pourrez ajuster le niveau de transparence à l'aide de la valeur alpha dans
glClearColor
.la source
bb.hRgnBlur
paramètre surCreateRectRgn(0, 0, 1, 1);
etbb.dwFlags
surDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Cela rendra flou exactement un pixel et affichera le reste de la fenêtre (là où effacé avec glClear) comme complètement transparent.identifier "DWM_BLURBEHIND" is undefined
. Dois-je inclure une bibliothèque?C'est une vieille question, mais comme les nouvelles versions de Windows ont la composition et la prise en charge, comme l'indique datenwolf, pour opengl, nous pouvons utiliser une partie de cette sauce spéciale pour accomplir cela. Bien que ce soit également trivial avec DirectX (allez comprendre ...), Microsoft a ajouté des conseils de composition aux contextes opengl. Yay craintes anti-trust!
Ainsi, au lieu d'une action de copie dans la mémoire physique inefficace, nous pouvons demander au moteur de composition de comprendre simplement comment utiliser le contexte opengl.
Il faut donc créer un contexte opengl avec un format pixel qui spécifie un canal alpha et qu'il doit utiliser la composition (ligne 82). Ensuite, vous utilisez les routines DwmApi.h pour activer une fenêtre floue (ligne 179) avec une région complètement invalide spécifiée, ce qui ne brouillera rien et laissera la fenêtre transparente. (Vous devez spécifier un pinceau noir + transparent sur la classe de fenêtre! Curieusement!) Ensuite, vous utilisez simplement opengl comme vous êtes habitué à l'utiliser. Dans la boucle d'événements, chaque fois que vous en avez l'occasion, vous pouvez simplement dessiner et échanger des tampons (ligne 201) et n'oubliez pas d'activer GL_BLEND! :)
Veuillez consulter / fork https://gist.github.com/3644466 ou simplement afficher l'extrait de code suivant basé sur la propre réponse de l'OP avec cette technique à la place (vous pouvez simplement placer ceci dans un projet vide):
la source
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
problème en augmentant la hauteur de la fenêtre de 1 pixel par rapport à l'écran, c'est-à-dire utiliser comme valeurs transmises à CreateWindowEx, puis appelerglViewport(0, 0, screen_width, screen_height)
comme d'habitude.Ce serait très facile si les fenêtres OpenGL pouvaient être superposées. Mais ils ne le sont pas, vous devrez donc opter pour autre chose.
Ce que vous pouvez faire est de créer une fenêtre en couches (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em si vous ne les connaissez pas) pour gérer la transparence, et une fenêtre OpenGL cachée pour le rendu. Rendez la scène OpenGL dans un tampon hors écran, lisez-la et partagez-la avec la fenêtre en couches, puis bitblt (fonction GDI) dans la fenêtre en couches.
Cela peut être trop lent pour des choses très complexes, mais vous donnera l'effet que vous demandez et fonctionnera sous Windows 2000 et supérieur.
EDIT: Quand il s'agit de créer le tampon hors écran réel, les objets framebuffer (FBO) sont probablement votre meilleur pari. Vous pouvez simplement dessiner sur la fenêtre OpenGL cachée, même si je pense que je me souviens que quelqu'un a posté des problèmes avec cela, à cause de la propriété des pixels - les FBO sont recommandés. Vous pouvez également utiliser des tampons de pixels (pbuffers), mais ceux-ci sont un peu obsolètes (estampillés "legacy"), et les FBO sont considérés comme la manière moderne de le faire. Les FBO devraient vous donner une accélération matérielle (si prise en charge) et ne vous limiteront pas à une version OpenGL spécifique. Vous aurez besoin d'un contexte OpenGL pour l'utiliser, vous devrez donc créer cette fenêtre OpenGL cachée et configurer le FBO à partir de là.
Voici quelques ressources sur les FBO:
Wikipedia
FBO
Gamedev article
Guide (pour mac, mais pourrait être utile)
la source
grand ensemble de démos avec la source qui vous guide étape par étape:
http://www.dhpoware.com/demos/index.html
la source
Je sais que c'est vieux, mais j'essayais de porter la solution Xlib sur Gtk +. Après de nombreuses études, je l'ai finalement fait, donc je veux vraiment le partager ici pour toute personne dans le besoin.
Compilé avec
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Testé sur Ubuntu 18.04 (en plus de gtk, vous devrez installerlibgtkglext1-dev
).ÉDITER
J'ai changé le code de rendu de simplement un
glClear
à un rectangle.Le code est une version modifiée de cette question et aussi de cette question .
la source
Vous pouvez rendre la scène 3D sur un tampon et la rendre à l'écran à l'aide d'une touche de couleur.
la source
Le SDK du jeu ClanLib fait cela.
Si vous n'avez besoin que d'une bordure transparente statique, utilisez la technique suivante:
Crée 5 fenêtres
AAAAA
AVANT JC
AVANT JC
DDDDD
A, B, C, D sont des fenêtres en couches
"#" est la fenêtre principale.
Voir les images au bas de - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
la source