Depois de gastar um pouco de reputação em uma recompensa malsucedida para conseguir ajuda nesse assunto, finalmente percebi o quão complexo era o problema no qual estava interessado.
Os poucos indivíduos que realizaram essa tarefa não compartilham muito . Durante minha pesquisa, encontrei diferentes maneiras de alcançar o que procurava. Um dos mais interessantes é o AeroGL , que mostra trechos de código usando uma técnica que não foi mencionada até agora, que é renderizar os gráficos em um bitmap independente de dispositivo (DIB).
Para fechar este segmento permanentemente, o código-fonte abaixo implementa essa técnica. O código em si é uma pequena modificação de um aplicativo apresentado aqui (muito obrigado a Andrei Sapronov Y. ).
O resultado final pode ser visto na imagem abaixo:
O código foi testado no Windows XP (32 bits) e no Windows 8.1 (32 bits).
Aproveitar!
#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);
}
Uma vez que todas as respostas fornecidas até agora são direcionadas apenas ao Windows, mas certamente também há uma demanda para fazer isso no X11 com um gerenciador de janelas compostas, para referência eu posto meu código de exemplo aqui (também para ser encontrado em https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
O principal truque é obter o FBConfig certo. Você precisa solicitar um canal alfa e testar o associado
XRenderPictFormat
quanto à presença de uma máscara alfa.fonte
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Isso poderia se tornar um wiki da comunidade, se ainda estivermos fazendo esse tipo de coisa hoje em dia.Eu sei que isso é possível com o Windows 7, não tenho certeza sobre as versões anteriores.
Para se livrar da borda da janela, você precisa remover o
WS_OVERLAPPEDWINDOW
estilo da janela e adicionar oWS_POPUP
estilo:Para tornar o plano de fundo da janela OpenGL transparente, você precisará usar a
DwmEnableBlurBehindWindow
função:Você também precisará especificar 0 para o valor alfa ao chamar
glClearColor
.Além disso, ao criar seu contexto OpenGL, certifique-se de alocar um canal alfa.
Agora seu fundo deve ser totalmente transparente. Se você mantiver as decorações da janela, o plano de fundo usará a aparência de desfoque Aero e você poderá ajustar o nível de transparência usando o valor alfa em
glClearColor
.fonte
bb.hRgnBlur
parâmetro comoCreateRectRgn(0, 0, 1, 1);
ebb.dwFlags
comoDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Isso borrará exatamente um pixel e mostrará o resto da janela (onde foi apagado com glClear) como completamente transparente.identifier "DWM_BLURBEHIND" is undefined
. Preciso incluir uma biblioteca?Essa é uma questão antiga, mas como as versões mais recentes do Windows têm composição e suporte, como dicas do datenwolf, para opengl, podemos usar um pouco desse molho especial para fazer isso. Embora também seja trivial com DirectX (veja a figura ...), a Microsoft adicionou dicas de composição para contextos opengl. Medos anti-trust!
Portanto, em vez de uma ação ineficiente de copiar para a memória física, podemos fazer com que o mecanismo de composição apenas entenda como fazer uso do contexto opengl.
Portanto, você deve criar um contexto opengl com um formato de pixel que especifica um canal alfa e que deve usar composição (linha 82). Então, você usa as rotinas DwmApi.h para habilitar uma janela borrada (linha 179) com uma região completamente inválida especificada, o que não borrará nada e deixará a janela transparente. (Você precisa especificar um pincel preto + transparente na classe de janela! Estranhamente!) Então, você apenas usa opengl como está acostumado a usá-lo. No loop de evento, a cada chance que você tiver, você pode simplesmente desenhar e trocar buffers (linha 201) e lembre-se de habilitar GL_BLEND! :)
Reveja / fork https://gist.github.com/3644466 ou apenas visualize o seguinte fragmento de código com base na própria resposta do OP com esta técnica (você pode simplesmente colocá-lo em um projeto vazio):
fonte
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
como os valores passados para CreateWindowEx e chameglViewport(0, 0, screen_width, screen_height)
normalmente.Isso seria muito fácil se as janelas OpenGL pudessem ser colocadas em camadas. Mas eles não são, então você terá que ir para outra coisa.
O que você poderia fazer é criar uma janela em camadas (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em se você não os conhece) para lidar com a transparência e uma janela OpenGL oculta para a renderização. Renderize a cena OpenGL em um buffer fora da tela, leia-a de volta e compartilhe-a com a janela em camadas e, em seguida, bitblt (função GDI) na janela em camadas.
Isso pode ser muito lento para coisas muito complexas, mas fornecerá o efeito que você está pedindo e funcionará no Windows 2000 e superior.
EDITAR: Quando se trata de criar o buffer real fora da tela, objetos framebuffer (FBOs) são provavelmente sua melhor aposta. Você poderia simplesmente desenhar na janela oculta do OpenGL, embora eu acho que me lembro de alguém postando sobre ter problemas com isso, por causa da propriedade de pixel - FBOs são recomendados. Você também pode usar buffers de pixel (pbuffers), mas eles estão meio desatualizados (marcado como "legado") e os FBOs são considerados a maneira moderna de fazer isso. Os FBOs devem fornecer aceleração de hardware (se houver suporte) e não o limitarão a uma versão específica do OpenGL. Você precisará de um contexto OpenGL para usá-lo, então você terá que criar aquela janela OpenGL oculta e configurar o FBO a partir dela.
Aqui estão alguns recursos sobre FBOs: Guia de artigos do
Wikipedia
FBO
Gamedev (para mac, mas pode ser útil)
fonte
grande conjunto de demonstrações com o código-fonte, guiando você passo a passo:
http://www.dhpoware.com/demos/index.html
fonte
Eu sei que isso é antigo, mas eu estava tentando portar a solução Xlib para Gtk +. Depois de muito estudo, finalmente consegui, então realmente quero compartilhar aqui para todos que precisam.
Compilado com
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Testado no Ubuntu 18.04 (além do gtk, você precisará instalarlibgtkglext1-dev
).EDITAR
Mudei o código de renderização de simplesmente um
glClear
para um retângulo.O código é uma versão modificada desta questão e também desta questão .
fonte
Você pode renderizar a cena 3D em um pbuffer e colocá-la na tela usando uma chave de cor.
fonte
O SDK do jogo ClanLib faz isso.
Se você precisar apenas de uma borda transparente estática, use a seguinte técnica:
Cria 5 janelas
AAAAA
B ### C
B ### C
DDDDD
A, B, C, D são janelas em camadas
"#" é a janela principal.
Veja as imagens na parte inferior de - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
fonte