Después de gastar algo de reputación en una recompensa fallida para obtener ayuda en este tema, finalmente me di cuenta de lo complejo que era el problema que me interesaba.
Las pocas personas que han logrado esta tarea no comparten mucho . Durante mi investigación encontré diferentes formas de lograr lo que buscaba. Uno de los más interesantes es AeroGL , y muestra fragmentos de código utilizando una técnica que no se mencionó hasta ahora, que es renderizar los gráficos en un mapa de bits independiente del dispositivo (DIB).
Para cerrar este hilo de forma permanente, el código fuente a continuación implementa esa técnica. El código en sí es una pequeña modificación de una aplicación presentada aquí ( muchas gracias a Andrei Sapronov Y. ).
El resultado final se puede ver en la siguiente imagen:
El código ha sido probado en Windows XP (32 bits) y Windows 8.1 (32 bits).
¡Disfrutar!
#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);
}
Dado que todas las respuestas dadas hasta ahora se dirigen solo a Windows, pero seguramente también hay una demanda para hacer esto en X11 con un administrador de ventanas compuesto, como referencia publico mi código de ejemplo aquí (también se puede encontrar en https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
El truco principal es conseguir el FBConfig correcto. Debe solicitar un canal alfa y probar el asociado
XRenderPictFormat
para detectar la presencia de una máscara alfa.fuente
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Esto podría convertirse en una wiki comunitaria, si todavía estamos haciendo ese tipo de cosas en estos días.Sé que esto es posible con Windows 7, no estoy seguro de las versiones anteriores.
Para deshacerse del borde de la ventana, debe eliminar el
WS_OVERLAPPEDWINDOW
estilo de la ventana y agregar elWS_POPUP
estilo:Para hacer que el fondo de la ventana OpenGL sea transparente, necesitará usar la
DwmEnableBlurBehindWindow
función:También necesitará especificar 0 para el valor alfa al llamar
glClearColor
.Además, al crear su contexto OpenGL, asegúrese de asignar un canal alfa.
Ahora tu fondo debería ser completamente transparente. Si mantiene las decoraciones de la ventana, entonces el fondo usará el aspecto de desenfoque Aero y podrá ajustar el nivel de transparencia usando el valor alfa en
glClearColor
.fuente
bb.hRgnBlur
parámetro enCreateRectRgn(0, 0, 1, 1);
ybb.dwFlags
enDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Esto desenfocará exactamente un píxel y mostrará el resto de la ventana (donde se borró con glClear) como completamente transparente.identifier "DWM_BLURBEHIND" is undefined
. ¿Hay alguna biblioteca que deba incluir?Esta es una vieja pregunta, pero dado que las versiones más nuevas de Windows tienen composición y soporte, como insinúa datenwolf, para opengl, podemos usar algo de esa salsa especial para lograr esto. Aunque también es trivial con DirectX (imagínese ...) Microsoft agregó sugerencias de composición a los contextos de opengl. ¡Yay miedos antimonopolio!
Entonces, en lugar de una acción ineficiente de copiar a memoria física, podemos hacer que el motor de composición simplemente comprenda cómo hacer uso del contexto opengl.
Entonces, debe crear un contexto opengl con un formato de píxel que especifique un canal alfa y que debe usar composición (línea 82). Luego, usa las rutinas DwmApi.h para habilitar una ventana borrosa (línea 179) con una región completamente inválida especificada, que no borrará nada y dejará la ventana transparente. (¡Necesita especificar un pincel negro + transparente en la clase de ventana! ¡Curiosamente!) Entonces, en realidad solo usa opengl como está acostumbrado a usarlo. En el bucle de eventos, en cada oportunidad que tenga, puede simplemente dibujar e intercambiar búferes (línea 201) y recuerde habilitar GL_BLEND. :)
Revise / fork https://gist.github.com/3644466 o simplemente vea el siguiente fragmento de código basado en la propia respuesta del OP con esta técnica en su lugar (puede colocar esto en un proyecto vacío):
fuente
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
como los valores pasados a CreateWindowEx, luego llameglViewport(0, 0, screen_width, screen_height)
como de costumbre.Esto sería muy fácil si se permitiera que las ventanas OpenGL estuvieran en capas. Pero no lo son, así que tendrás que optar por otra cosa.
Lo que podría hacer es crear una ventana en capas (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em si no los conoce) para manejar la transparencia, y una ventana OpenGL oculta para la representación. Renderice la escena OpenGL en un búfer fuera de la pantalla, léala y compártala con la ventana en capas, luego bitblt (función GDI) en la ventana en capas.
Esto puede ser demasiado lento para cosas muy complejas, pero le dará el efecto que está pidiendo y funcionará en Windows 2000 y superior.
EDITAR: Cuando se trata de crear el búfer fuera de la pantalla real, los objetos framebuffer (FBO) son probablemente su mejor opción. Podría simplemente dibujar en la ventana oculta de OpenGL, aunque creo que recuerdo que alguien publicó sobre tener problemas con eso, debido a la propiedad de píxeles: se recomiendan los FBO. También puede usar búferes de píxeles (pbuffers), pero estos están un poco desactualizados (con el sello "legado"), y los FBO se consideran la forma moderna de hacer esto. Los FBO deben brindarle aceleración de hardware (si es compatible) y no lo limitarán a una versión específica de OpenGL. Necesitará un contexto OpenGL para usarlo, por lo que tendrá que crear esa ventana OpenGL oculta y configurar el FBO desde allí.
Aquí hay algunos recursos sobre FBO:
Wikipedia
FBO
Gamedev article
Guide (para mac, pero puede ser útil)
fuente
gran conjunto de demostraciones con la fuente que lo guía paso a paso:
http://www.dhpoware.com/demos/index.html
fuente
Sé que esto es antiguo, pero estaba intentando portar la solución Xlib a Gtk +. Después de mucho estudio, finalmente lo logré, así que realmente quiero compartirlo aquí para quienes lo necesiten.
Compilado con
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Probado en Ubuntu 18.04 (además de gtk, deberá instalarlolibgtkglext1-dev
).EDITAR
Cambié el código de renderizado de simplemente a
glClear
a un rectángulo.El código es una versión modificada de esta pregunta y también de esta pregunta .
fuente
Puede convertir la escena 3D en un pbuffer y mostrarla en la pantalla usando una clave de color.
fuente
El SDK del juego ClanLib hace esto.
Si solo necesita un borde transparente estático, utilice la siguiente técnica:
Crea 5 ventanas
AAAAA
ANTES DE CRISTO
ANTES DE CRISTO
DDDDD
A, B, C, D son ventanas en capas
"#" es la ventana principal.
Vea las imágenes en la parte inferior de - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
fuente