Setelah menghabiskan beberapa reputasi pada hadiah yang gagal untuk mendapatkan bantuan tentang masalah ini, saya akhirnya menyadari betapa rumitnya masalah yang saya minati.
Beberapa individu yang telah menyelesaikan tugas ini tidak banyak berbagi . Selama penelitian saya, saya menemukan berbagai cara untuk mencapai apa yang saya cari. Salah satu yang paling menarik adalah AeroGL , dan ini menunjukkan potongan kode menggunakan teknik yang sejauh ini tidak disebutkan, yaitu merender grafik ke bitmap (DIB) yang tidak tergantung perangkat .
Untuk menutup utas ini secara permanen, kode sumber di bawah ini menerapkan teknik itu. Kode itu sendiri adalah sedikit modifikasi dari aplikasi yang disajikan di sini (terima kasih banyak kepada Andrei Sapronov Y. ).
Hasil akhirnya bisa dilihat pada gambar di bawah ini:
Kode telah diuji pada Windows XP (32-bit) dan Windows 8.1 (32-bit).
Nikmati!
#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);
}
Karena semua jawaban yang diberikan sejauh ini hanya menargetkan Windows, tetapi pasti ada juga permintaan untuk melakukan ini di X11 dengan pengelola jendela gabungan, untuk referensi saya memposting kode contoh saya di sini (juga dapat ditemukan di https://github.com/datenwolf /codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
Trik utamanya adalah mendapatkan FBConfig yang benar. Anda perlu meminta saluran alfa dan menguji terkait
XRenderPictFormat
keberadaan topeng alfa.sumber
g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender
. Ini bisa menjadi wiki komunitas, jika kita masih melakukan hal semacam itu sekarang.Saya tahu ini mungkin dengan Windows 7, tidak yakin tentang versi sebelumnya.
Untuk menghilangkan batas jendela Anda perlu menghapus
WS_OVERLAPPEDWINDOW
gaya dari jendela dan menambahkanWS_POPUP
gaya:Untuk membuat latar belakang jendela OpenGL transparan, Anda perlu menggunakan
DwmEnableBlurBehindWindow
fungsi:Anda juga perlu menentukan 0 untuk nilai alfa saat memanggil
glClearColor
.Selain itu, saat membuat konteks OpenGL Anda, pastikan Anda mengalokasikan saluran alfa.
Sekarang latar belakang Anda harus sepenuhnya transparan. Jika Anda menyimpan dekorasi jendela, maka latar belakang akan menggunakan tampilan Aero blur dan Anda dapat menyesuaikan tingkat transparansi menggunakan nilai alpha in
glClearColor
.sumber
bb.hRgnBlur
parameter keCreateRectRgn(0, 0, 1, 1);
danbb.dwFlags
keDWM_BB_ENABLE | DWM_BB_BLURREGION;
. Ini akan memburamkan tepat satu piksel dan menampilkan sisa jendela (yang dibersihkan dengan glClear) sebagai sepenuhnya transparan.identifier "DWM_BLURBEHIND" is undefined
. Apakah ada perpustakaan yang perlu saya sertakan?Ini adalah pertanyaan lama, tetapi karena versi Windows yang lebih baru memiliki pengomposisian dan dukungan, seperti petunjuk datenwolf, untuk opengl, kita dapat menggunakan beberapa saus khusus untuk mencapai ini. Meskipun juga sepele dengan DirectX (go figure ...) Microsoft menambahkan petunjuk pengomposisian ke konteks terbuka. Yay anti-trust fears!
Jadi, alih-alih tindakan salin-ke-memori fisik yang tidak efisien, kita dapat membuat mesin pengomposisian memahami cara menggunakan konteks terbuka.
Jadi, Anda harus membuat konteks terbuka dengan format piksel yang menentukan saluran alfa dan harus menggunakan komposisi (baris 82). Kemudian, Anda menggunakan rutinitas DwmApi.h untuk mengaktifkan jendela buram (baris 179) dengan wilayah yang benar-benar tidak valid, yang tidak akan mengaburkan apa pun dan membiarkan jendela transparan. (Anda perlu menentukan kuas hitam + transparan pada kelas jendela! Anehnya!) Kemudian, Anda sebenarnya hanya menggunakan opengl seperti yang biasa Anda gunakan. Dalam loop acara, setiap ada kesempatan, Anda bisa menggambar dan menukar buffer (baris 201) dan ingat untuk mengaktifkan GL_BLEND! :)
Harap tinjau / garpu https://gist.github.com/3644466 atau cukup lihat cuplikan kode berikut berdasarkan jawaban OP sendiri dengan teknik ini (Anda dapat mencelupkannya di proyek kosong):
sumber
window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1
sebagai nilai yang diteruskan ke CreateWindowEx, lalu panggilglViewport(0, 0, screen_width, screen_height)
seperti biasa.Ini akan sangat mudah jika jendela OpenGL diizinkan untuk dibuat berlapis. Tapi ternyata tidak, jadi Anda harus mencari yang lain.
Apa yang dapat Anda lakukan adalah membuat jendela berlapis (WS_EX_LAYERED + SetLayeredWindowAttributes () - Google 'em jika Anda tidak mengetahuinya) untuk menangani transparansi, dan jendela OpenGL tersembunyi untuk rendering. Render adegan OpenGL ke buffer di luar layar, baca kembali dan bagikan dengan jendela berlapis, lalu bitblt (fungsi GDI) ke jendela berlapis.
Ini mungkin terlalu lambat untuk hal-hal yang sangat kompleks, tetapi akan memberikan efek yang Anda minta, dan berfungsi pada Windows 2000 dan yang lebih baru.
EDIT: Saat membuat buffer luar layar yang sebenarnya, objek framebuffer (FBO) mungkin adalah pilihan terbaik Anda. Anda bisa menggambar di jendela OpenGL yang tersembunyi, meskipun saya pikir saya ingat seseorang memposting tentang mengalami masalah dengan itu, karena kepemilikan piksel - FBO disarankan. Anda juga dapat menggunakan buffer piksel (pbuffers), tetapi ini agak ketinggalan jaman (dicap "warisan"), dan FBO dianggap sebagai cara modern untuk melakukan ini. FBO seharusnya memberi Anda akselerasi perangkat keras (jika didukung), dan tidak akan membatasi Anda pada versi OpenGL tertentu. Anda memerlukan konteks OpenGL untuk menggunakannya, jadi Anda harus membuat jendela OpenGL yang tersembunyi dan mengatur FBO dari sana.
Berikut beberapa sumber daya di FBO: Panduan artikel Gamedev
Wikipedia
FBO (untuk mac, tapi mungkin bisa membantu)
sumber
kumpulan demo hebat dengan sumber yang membawa Anda melalui langkah demi langkah:
http://www.dhpoware.com/demos/index.html
sumber
Saya tahu ini sudah lama, tetapi saya mencoba mem-port solusi Xlib ke Gtk +. Setelah banyak belajar akhirnya saya berhasil jadi saya sangat ingin membagikannya disini untuk siapapun yang membutuhkan.
Disusun dengan
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0`
. Diuji di Ubuntu 18.04 (selain gtk, Anda harus menginstallibgtkglext1-dev
).EDIT
Saya mengubah kode rendering dari hanya
glClear
menjadi persegi panjang.Kode adalah versi modifikasi dari pertanyaan ini dan juga pertanyaan ini .
sumber
Anda dapat membuat adegan 3d menjadi pbuffer dan memadukannya ke layar menggunakan tombol warna.
sumber
SDK game ClanLib melakukan ini.
Jika Anda hanya memerlukan batas transparan statis, gunakan teknik berikut:
Membuat 5 jendela
AAAAA
B ### C
B ### C
DDDDD
A, B, C, D adalah jendela berlapis
"#" adalah jendela utama.
Lihat gambar di bagian bawah - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes
sumber