【问题标题】:How to make an OpenGL rendering context with transparent background?如何制作具有透明背景的 OpenGL 渲染上下文?
【发布时间】:2011-05-02 11:06:42
【问题描述】:

渲染上下文的背景通常是纯色(黑色或其他,见下图):

我想知道是否可以设置一个没有装饰且具有透明背景的窗口,同时允许我在其上渲染 OpenGL 内容。

这会产生三角形漂浮在屏幕上的错觉。透明背景应该能让您看到桌面或可能位于其后面的其他应用程序。

能否用源代码举例说明?

平台:Windows(仅限 win32)

【问题讨论】:

    标签: c winapi opengl transparency


    【解决方案1】:

    我知道这在 Windows 7 上是可能的,但不确定早期版本。

    要摆脱窗口边框,您需要从窗口中删除 WS_OVERLAPPEDWINDOW 样式并添加 WS_POPUP 样式:

    DWORD style = ::GetWindowLong(hWnd, GWL_STYLE);
    style &= ~WS_OVERLAPPEDWINDOW;
    style |= WS_POPUP;
    ::SetWindowLong(hWnd, GWL_STYLE, style);
    

    要使 OpenGL 窗口的背景透明,您需要使用 DwmEnableBlurBehindWindow 函数:

    DWM_BLURBEHIND bb = {0};
    bb.dwFlags = DWM_BB_ENABLE;
    bb.fEnable = true;
    bb.hRgnBlur = NULL;
    DwmEnableBlurBehindWindow(hWnd, &bb);
    

    您还需要在调用glClearColor 时将 alpha 值指定为 0。

    glClearColor(0.0f,0.0f,0.0f,0.0f);
    

    此外,在创建 OpenGL 上下文时,请确保分配 Alpha 通道。

    现在您的背景应该是完全透明的。如果保留窗口装饰,则背景将使用 Aero 模糊外观,您可以使用 glClearColor 中的 alpha 值调整透明度。

    【讨论】:

    • 谢谢,但 DwmEnableBlurBehindWindow() 是 DWM 的一部分,不属于 Win32 API。此解决方案适用于 Windows Vista 及更高版本。
    • @karlphilip:Vista 之前的 Windows:es 没有桌面合成,所以我不确定你能不能很好地做到这一点。
    • @karlphilip。抱歉,我个人还没有获得与 XP 一起使用的透明 OpenGL 窗口,但我记得在 OpenGL 论坛上看到过有关它的帖子。尝试通过以下搜索结果查看更多信息:google.com/…
    • 这真的很有帮助,但我想添加以下内容:对我来说,上面的方法使用默认的 Win7 模糊和假光反射渲染了所有背景。为了消除模糊并获得一个完全透明的窗口,我将bb.hRgnBlur 参数设置为CreateRectRgn(0, 0, 1, 1);,将bb.dwFlags 设置为DWM_BB_ENABLE | DWM_BB_BLURREGION;。这将模糊一个像素,并将窗口的其余部分(用 glClear 清除)显示为完全透明。
    • 当我尝试这个时,我得到identifier "DWM_BLURBEHIND" is undefined。我需要包含一个库吗?
    【解决方案2】:

    如果允许 OpenGL 窗口分层,这将非常容易。但他们不是,所以你必须去做别的事情。

    您可以做的是创建一个分层窗口(WS_EX_LAYERED + SetLayeredWindowAttributes() - 如果您不知道它们,请用谷歌搜索)来处理透明度,以及一个用于渲染的隐藏 OpenGL 窗口。将 OpenGL 场景渲染到屏幕外缓冲区,将其读回并与分层窗口共享,然后将其 bitblt(GDI 函数)到分层窗口。

    这对于非常复杂的东西来说可能太慢了,但会给你想要的效果,并且可以在 Windows 2000 及更高版本上工作。

    编辑:在创建实际的屏幕外缓冲区时,帧缓冲区对象 (FBO) 可能是您最好的选择。您可以只在隐藏的 OpenGL 窗口上绘图,但我想我记得有人发布过关于遇到麻烦的帖子,因为像素所有权 - 建议使用 FBO。您也可以使用像素缓冲区 (pbuffers),但这些已经过时(标记为“旧版”),并且 FBO 被认为是实现此目的的现代方式。 FBO 应该为您提供硬件加速(如果支持),并且本身不会限制您使用特定的 OpenGL 版本。您需要一个 OpenGL 上下文才能使用它,因此您必须创建隐藏的 OpenGL 窗口并从那里设置 FBO。

    这里有一些关于 FBO 的资源:
    Wikipedia
    FBO
    Gamedev article
    Guide(对于 mac,但可能会有所帮助)

    【讨论】:

    • 这与我的建议类似。将 OpenGL 场景渲染到内存(FBO 或类似),然后使用 glReadPixels 存储到位图对象中。然后你可以选择你的透明颜色并使用 GDI 将其 bitblt 到屏幕上。
    【解决方案3】:

    您可以将 3d 场景渲染到 pbuffer 并使用颜色键将其 blit 到屏幕上。

    【讨论】:

      【解决方案4】:

      在未成功的赏金上花费了一些声誉以在此问题上获得一些帮助后,我终于意识到我感兴趣的问题有多复杂。

      完成此任务的少数人don't share much。在我的研究中,我发现了不同的方法来实现我正在寻找的东西。其中一个最有趣的是 AeroGL,它使用一种目前尚未提及的技术来显示 snippets of code,即将图形渲染到 device-independent bitmap (DIB)。

      要永久关闭此线程,下面的源代码实现了该技术。代码本身是对here 提供的应用程序的轻微修改(非常感谢Andrei Sapronov Y.)。

      最终结果如下图所示:

      该代码已在 Windows XP(32 位)和 Windows 8.1(32 位)上进行了测试。 享受吧!

      #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); 
      }
      

      【讨论】:

      • 值得一提的是,这种技术对于复杂的渲染和图形动画并不实用。为每一帧将 GPU 数据复制到 RAM 对 CPU 的要求非常高,导致 FPS 较低。检查这个答案的 cmets:stackoverflow.com/questions/4780756/…
      • 请注意,在 Windows Vista 和 Windows 7 中,您可以实现与下面的 x11argb 演示完全相同的效果。 PIXELFORMATDESCRIPTOR 支持启用合成的新标志。不过,我还没有为此创建一个演示。不过,要到四月才会发生,那时我有时间再做一次。
      • 我在 2020 年 64 位中有这些 pragma cmets #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "User32.lib") #pragma comment(lib, "Gdi32.lib")
      【解决方案5】:

      ClanLib 游戏 SDK 执行此操作。

      如果您只需要静态透明边框,请使用以下技术:

      创建 5 个窗口

      AAAAA

      B###C

      B###C

      DDDDD

      A,B,C,D 是分层窗口

      “#”是主窗口。

      查看底部的图片 - http://clanlib.org/wiki/ClanLib_2.2.9_Release_Notes

      【讨论】:

      • 谢谢,但这个问题专门询问如何使用 Win32 API 来实现此效果。
      【解决方案6】:

      大量带有源代码的演示集带您逐步完成:

      http://www.dhpoware.com/demos/index.html

      【讨论】:

      • “OpenGL Layered Windows”演示基本上是最好的方法,pbuffer比直接渲染到DIB要快得多。DIB通常使用加速pbuffer的软件渲染器
      【解决方案7】:

      由于到目前为止给出的所有答案都只针对 Windows,但肯定也需要在 X11 上使用复合窗口管理器执行此操作,作为参考,我在这里发布了我的示例代码(也可以在 https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c 找到)

      /*------------------------------------------------------------------------
       * A demonstration of OpenGL in a  ARGB window 
       *    => support for composited window transparency
       *
       * (c) 2011 by Wolfgang 'datenwolf' Draxinger
       *     See me at comp.graphics.api.opengl and StackOverflow.com
        
       * License agreement: This source code is provided "as is". You
       * can use this source code however you want for your own personal
       * use. If you give this source code to anybody else then you must
       * leave this message in it.
       *
       * This program is based on the simplest possible 
       * Linux OpenGL program by FTB (see info below)
       
        The simplest possible Linux OpenGL program? Maybe...
      
        (c) 2002 by FTB. See me in comp.graphics.api.opengl
      
        --
        <\___/>
        / O O \
        \_____/  FTB.
      
      ------------------------------------------------------------------------*/
      
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <math.h>
      
      #include <GL/gl.h>
      #include <GL/glx.h>
      #include <GL/glxext.h>
      #include <X11/Xatom.h>
      #include <X11/extensions/Xrender.h>
      #include <X11/Xutil.h>
      
      #define USE_CHOOSE_FBCONFIG
      
      static void fatalError(const char *why)
      {
          fprintf(stderr, "%s", why);
          exit(0x666);
      }
      
      static int Xscreen;
      static Atom del_atom;
      static Colormap cmap;
      static Display *Xdisplay;
      static XVisualInfo *visual;
      static XRenderPictFormat *pict_format;
      static GLXFBConfig *fbconfigs, fbconfig;
      static int numfbconfigs;
      static GLXContext render_context;
      static Window Xroot, window_handle;
      static GLXWindow glX_window_handle;
      static int width, height;
      
      static int VisData[] = {
      GLX_RENDER_TYPE, GLX_RGBA_BIT,
      GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
      GLX_DOUBLEBUFFER, True,
      GLX_RED_SIZE, 8,
      GLX_GREEN_SIZE, 8,
      GLX_BLUE_SIZE, 8,
      GLX_ALPHA_SIZE, 8,
      GLX_DEPTH_SIZE, 16,
      None
      };
      
      static int isExtensionSupported(const char *extList, const char *extension)
      {
       
        const char *start;
        const char *where, *terminator;
       
        /* Extension names should not have spaces. */
        where = strchr(extension, ' ');
        if ( where || *extension == '\0' )
          return 0;
       
        /* It takes a bit of care to be fool-proof about parsing the
           OpenGL extensions string. Don't be fooled by sub-strings,
           etc. */
        for ( start = extList; ; ) {
          where = strstr( start, extension );
       
          if ( !where )
            break;
       
          terminator = where + strlen( extension );
       
          if ( where == start || *(where - 1) == ' ' )
            if ( *terminator == ' ' || *terminator == '\0' )
              return 1;
       
          start = terminator;
        }
        return 0;
      }
      
      static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg)
      {    
          return d && e && arg && (e->type == MapNotify) && (e->xmap.window == *(Window*)arg);
      }
      
      static void describe_fbconfig(GLXFBConfig fbconfig)
      {
          int doublebuffer;
          int red_bits, green_bits, blue_bits, alpha_bits, depth_bits;
      
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DOUBLEBUFFER, &doublebuffer);
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_RED_SIZE, &red_bits);
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_GREEN_SIZE, &green_bits);
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_BLUE_SIZE, &blue_bits);
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_ALPHA_SIZE, &alpha_bits);
          glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DEPTH_SIZE, &depth_bits);
      
          fprintf(stderr, "FBConfig selected:\n"
              "Doublebuffer: %s\n"
              "Red Bits: %d, Green Bits: %d, Blue Bits: %d, Alpha Bits: %d, Depth Bits: %d\n",
              doublebuffer == True ? "Yes" : "No", 
              red_bits, green_bits, blue_bits, alpha_bits, depth_bits);
      }
      
      static void createTheWindow()
      {
          XEvent event;
          int x,y, attr_mask;
          XSizeHints hints;
          XWMHints *startup_state;
          XTextProperty textprop;
          XSetWindowAttributes attr = {0,};
          static char *title = "FTB's little OpenGL example - ARGB extension by WXD";
      
          Xdisplay = XOpenDisplay(NULL);
          if (!Xdisplay) {
              fatalError("Couldn't connect to X server\n");
          }
          Xscreen = DefaultScreen(Xdisplay);
          Xroot = RootWindow(Xdisplay, Xscreen);
      
          fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, VisData, &numfbconfigs);
          fbconfig = 0;
          for(int i = 0; i<numfbconfigs; i++) {
              visual = (XVisualInfo*) glXGetVisualFromFBConfig(Xdisplay, fbconfigs[i]);
              if(!visual)
                  continue;
      
              pict_format = XRenderFindVisualFormat(Xdisplay, visual->visual);
              XFree(visual);
              if(!pict_format)
                  continue;
      
              fbconfig = fbconfigs[i];
              if(pict_format->direct.alphaMask > 0) {
                  break;
              }
          }
      
          if(!fbconfig) {
              fatalError("No matching FB config found");
          }
      
          describe_fbconfig(fbconfig);
      
          /* Create a colormap - only needed on some X clients, eg. IRIX */
          cmap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone);
      
          attr.colormap = cmap;
          attr.background_pixmap = None;
          attr.border_pixmap = None;
          attr.border_pixel = 0;
          attr.event_mask =
              StructureNotifyMask |
              EnterWindowMask |
              LeaveWindowMask |
              ExposureMask |
              ButtonPressMask |
              ButtonReleaseMask |
              OwnerGrabButtonMask |
              KeyPressMask |
              KeyReleaseMask;
      
          attr_mask = 
              CWBackPixmap|
              CWColormap|
              CWBorderPixel|
              CWEventMask;
      
          width = DisplayWidth(Xdisplay, DefaultScreen(Xdisplay))/2;
          height = DisplayHeight(Xdisplay, DefaultScreen(Xdisplay))/2;
          x=width/2, y=height/2;
      
          window_handle = XCreateWindow(  Xdisplay,
                          Xroot,
                          x, y, width, height,
                          0,
                          visual->depth,
                          InputOutput,
                          visual->visual,
                          attr_mask, &attr);
      
          if( !window_handle ) {
              fatalError("Couldn't create the window\n");
          }
      
      #if USE_GLX_CREATE_WINDOW
          int glXattr[] = { None };
          glX_window_handle = glXCreateWindow(Xdisplay, fbconfig, window_handle, glXattr);
          if( !glX_window_handle ) {
              fatalError("Couldn't create the GLX window\n");
          }
      #else
          glX_window_handle = window_handle;
      #endif
      
          textprop.value = (unsigned char*)title;
          textprop.encoding = XA_STRING;
          textprop.format = 8;
          textprop.nitems = strlen(title);
      
          hints.x = x;
          hints.y = y;
          hints.width = width;
          hints.height = height;
          hints.flags = USPosition|USSize;
      
          startup_state = XAllocWMHints();
          startup_state->initial_state = NormalState;
          startup_state->flags = StateHint;
      
          XSetWMProperties(Xdisplay, window_handle,&textprop, &textprop,
                  NULL, 0,
                  &hints,
                  startup_state,
                  NULL);
      
          XFree(startup_state);
      
          XMapWindow(Xdisplay, window_handle);
          XIfEvent(Xdisplay, &event, WaitForMapNotify, (char*)&window_handle);
      
          if ((del_atom = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", 0)) != None) {
              XSetWMProtocols(Xdisplay, window_handle, &del_atom, 1);
          }
      }
      
      static int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
      {
          fputs("Error at context creation", stderr);
          return 0;
      }
      
      static void createTheRenderContext()
      {
          int dummy;
          if (!glXQueryExtension(Xdisplay, &dummy, &dummy)) {
              fatalError("OpenGL not supported by X server\n");
          }
      
      #if USE_GLX_CREATE_CONTEXT_ATTRIB
          #define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
          #define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
          render_context = NULL;
          if( isExtensionSupported( glXQueryExtensionsString(Xdisplay, DefaultScreen(Xdisplay)), "GLX_ARB_create_context" ) ) {
              typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
              glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
              if( glXCreateContextAttribsARB ) {
                  int context_attribs[] =
                  {
                      GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
                      GLX_CONTEXT_MINOR_VERSION_ARB, 0,
                      //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                      None
                  };
      
                  int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
                  
                  render_context = glXCreateContextAttribsARB( Xdisplay, fbconfig, 0, True, context_attribs );
      
                  XSync( Xdisplay, False );
                  XSetErrorHandler( oldHandler );
      
                  fputs("glXCreateContextAttribsARB failed", stderr);
              } else {
                  fputs("glXCreateContextAttribsARB could not be retrieved", stderr);
              }
          } else {
                  fputs("glXCreateContextAttribsARB not supported", stderr);
          }
      
          if(!render_context)
          {
      #else
          {
      #endif
              render_context = glXCreateNewContext(Xdisplay, fbconfig, GLX_RGBA_TYPE, 0, True);
              if (!render_context) {
                  fatalError("Failed to create a GL context\n");
              }
          }
      
          if (!glXMakeContextCurrent(Xdisplay, glX_window_handle, glX_window_handle, render_context)) {
              fatalError("glXMakeCurrent failed for window\n");
          }
      }
      
      static int updateTheMessageQueue()
      {
          XEvent event;
          XConfigureEvent *xc;
      
          while (XPending(Xdisplay))
          {
              XNextEvent(Xdisplay, &event);
              switch (event.type)
              {
              case ClientMessage:
                  if (event.xclient.data.l[0] == del_atom)
                  {
                      return 0;
                  }
              break;
      
              case ConfigureNotify:
                  xc = &(event.xconfigure);
                  width = xc->width;
                  height = xc->height;
                  break;
              }
          }
          return 1;
      }
      
      /*  6----7
         /|   /|
        3----2 |
        | 5--|-4
        |/   |/
        0----1
      
      */
      
      GLfloat cube_vertices[][8] =  {
          /*  X     Y     Z   Nx   Ny   Nz    S    T */
          {-1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 0.0}, // 0
          { 1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 0.0}, // 1
          { 1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 1.0, 1.0}, // 2
          {-1.0,  1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 1.0}, // 3
      
          { 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0}, // 4
          {-1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0}, // 5
          {-1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0}, // 6
          { 1.0,  1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0}, // 7
      
          {-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0}, // 5
          {-1.0, -1.0,  1.0, -1.0, 0.0, 0.0, 1.0, 0.0}, // 0
          {-1.0,  1.0,  1.0, -1.0, 0.0, 0.0, 1.0, 1.0}, // 3
          {-1.0,  1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0}, // 6
          
          { 1.0, -1.0,  1.0,  1.0, 0.0, 0.0, 0.0, 0.0}, // 1
          { 1.0, -1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 0.0}, // 4
          { 1.0,  1.0, -1.0,  1.0, 0.0, 0.0, 1.0, 1.0}, // 7
          { 1.0,  1.0,  1.0,  1.0, 0.0, 0.0, 0.0, 1.0}, // 2
          
          {-1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 0.0, 0.0}, // 5
          { 1.0, -1.0, -1.0,  0.0, -1.0, 0.0, 1.0, 0.0}, // 4
          { 1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 1.0, 1.0}, // 1
          {-1.0, -1.0,  1.0,  0.0, -1.0, 0.0, 0.0, 1.0}, // 0
      
          {-1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 0.0, 0.0}, // 3
          { 1.0, 1.0,  1.0,  0.0,  1.0, 0.0, 1.0, 0.0}, // 2
          { 1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 1.0, 1.0}, // 7
          {-1.0, 1.0, -1.0,  0.0,  1.0, 0.0, 0.0, 1.0}, // 6
      };
      
      static void draw_cube(void)
      {
          glEnableClientState(GL_VERTEX_ARRAY);
          glEnableClientState(GL_NORMAL_ARRAY);
          glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      
          glVertexPointer(3, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][0]);
          glNormalPointer(GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][3]);
          glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][6]);
      
          glDrawArrays(GL_QUADS, 0, 24);
      }
      
      float const light0_dir[]={0,1,0,0};
      float const light0_color[]={78./255., 80./255., 184./255.,1};
      
      float const light1_dir[]={-1,1,1,0};
      float const light1_color[]={255./255., 220./255., 97./255.,1};
      
      float const light2_dir[]={0,-1,0,0};
      float const light2_color[]={31./255., 75./255., 16./255.,1};
      
      static void redrawTheWindow()
      {
          float const aspect = (float)width / (float)height;
      
          static float a=0;
          static float b=0;
          static float c=0;
      
          glDrawBuffer(GL_BACK);
      
          glViewport(0, 0, width, height);
      
          // Clear with alpha = 0.0, i.e. full transparency
              glClearColor(0.0, 0.0, 0.0, 0.0);
          glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
          
          glMatrixMode(GL_PROJECTION);
          glLoadIdentity();
          glFrustum(-aspect, aspect, -1, 1, 2.5, 10);
      
          glMatrixMode(GL_MODELVIEW);
          glLoadIdentity();
      
          glEnable(GL_DEPTH_TEST);
          glEnable(GL_CULL_FACE);
      
          glEnable(GL_BLEND);
          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      
          glLightfv(GL_LIGHT0, GL_POSITION, light0_dir);
          glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
      
          glLightfv(GL_LIGHT1, GL_POSITION, light1_dir);
          glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color);
      
          glLightfv(GL_LIGHT2, GL_POSITION, light2_dir);
          glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_color);
      
          glTranslatef(0., 0., -5.);
      
          glRotatef(a, 1, 0, 0);
          glRotatef(b, 0, 1, 0);
          glRotatef(c, 0, 0, 1);
      
          glEnable(GL_LIGHT0);
          glEnable(GL_LIGHT1);
          glEnable(GL_LIGHTING);
      
          glEnable(GL_COLOR_MATERIAL);
          glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
      
          glColor4f(1., 1., 1., 0.5);
      
          glCullFace(GL_FRONT);
          draw_cube();
          glCullFace(GL_BACK);
          draw_cube();
      
          a = fmod(a+0.1, 360.);
          b = fmod(b+0.5, 360.);
          c = fmod(c+0.25, 360.);
      
          glXSwapBuffers(Xdisplay, glX_window_handle);
      }
      
      int main(int argc, char *argv[])
      {
          createTheWindow();
          createTheRenderContext();
      
          while (updateTheMessageQueue()) {
              redrawTheWindow();
          }
      
          return 0;
      }
      

      主要技巧是获得正确的 FBConfig。您需要请求 Alpha 通道测试关联的 XRenderPictFormat 是否存在 Alpha 掩码。

      【讨论】:

      • 呜呜呜!极好的!我用:g++ gl_transparent.cpp -o gl_transparent -lGL -lX11 -lXext -lXrender 编译它。如果我们这些天还在做这种事情,这可能会成为一个社区 wiki。
      • @karlphillip:在我的 github 存储库中,您会找到一个 makefile。还有这个演示的变体,它使用片段着色器来添加波浪效果——只是为了吸引眼球。不幸的是,不可能将“背景”合并到着色器效果中,因为整个背景只有在所有窗口都绘制完它们的内容后才由合成器创建。
      • 我明白了。感谢您的提醒。
      • @datenwolf 我最初是在寻求帮助,但我成功地将您的代码转换为现代 OpenGL。我将在这里发布我的解决方案以供将来参考。
      • 非常感谢您的教程。我也想要它没有边界。我想使用opengl制作一个橡皮筋(在屏幕上绘制矩形),但我没有找到它的例子。
      【解决方案8】:

      这是一个老问题,但由于较新版本的 Windows 具有合成和支持,正如 datenwolf 提示的那样,对于 opengl,我们可以使用一些特殊的调味料来完成此任务。尽管 DirectX 也很简单(看图...),但微软确实在 opengl 上下文中添加了合成提示。是的反垄断恐惧!

      因此,我们可以让合成引擎了解如何利用 opengl 上下文,而不是低效的复制到物理内存操作。

      因此,您必须使用像素格式创建一个 opengl 上下文,该格式指定一个 Alpha 通道并且它应该使用合成(第 82 行)。然后,您使用 DwmApi.h 例程启用一个模糊窗口(第 179 行),其中指定了一个完全无效的区域,这将不会模糊任何内容并使窗口保持透明。 (您需要在窗口类上指定一个黑色+透明笔刷!奇怪!)然后,您实际上只是像习惯使用它一样使用 opengl。在事件循环中,每有机会,您就可以绘制和交换缓冲区(第 201 行)并记住启用 GL_BLEND! :)

      请查看/fork https://gist.github.com/3644466 或仅查看基于 OP 自己的答案的以下代码 sn-p(您可以将其放入一个空项目中):

      #define _WIN32_WINNT 0x0500
      
      #include <windows.h>
      #include <windowsx.h>
      #include <GL/gl.h>
      #include <GL/glu.h>
      
      #include <dwmapi.h>
      
      #pragma comment (lib, "opengl32.lib")
      #pragma comment (lib, "glu32.lib")
      
      #pragma comment (lib, "dwmapi.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("TransparentGL");
      
      HDC hDC;            
      HGLRC m_hrc;        
      int w = 240;
      int h = 240;
      
      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;
      }
      
      BOOL CreateHGLRC(HWND hWnd) {
          PIXELFORMATDESCRIPTOR pfd = {
            sizeof(PIXELFORMATDESCRIPTOR),
            1,                                // Version Number
            PFD_DRAW_TO_WINDOW      |         // Format Must Support Window
            PFD_SUPPORT_OPENGL      |         // Format Must Support OpenGL
            PFD_SUPPORT_COMPOSITION |         // Format Must Support Composition
            PFD_DOUBLEBUFFER,                 // Must Support Double Buffering
            PFD_TYPE_RGBA,                    // Request An RGBA Format
            32,                               // Select Our Color Depth
            0, 0, 0, 0, 0, 0,                 // Color Bits Ignored
            8,                                // An Alpha Buffer
            0,                                // Shift Bit Ignored
            0,                                // No Accumulation Buffer
            0, 0, 0, 0,                       // Accumulation Bits Ignored
            24,                               // 16Bit Z-Buffer (Depth Buffer)
            8,                                // Some Stencil Buffer
            0,                                // No Auxiliary Buffer
            PFD_MAIN_PLANE,                   // Main Drawing Layer
            0,                                // Reserved
            0, 0, 0                           // Layer Masks Ignored
         };     
      
         HDC hdc = GetDC(hWnd);
         int PixelFormat = ChoosePixelFormat(hdc, &pfd);
         if (PixelFormat == 0) {
            assert(0);
            return FALSE ;
         }
      
         BOOL bResult = SetPixelFormat(hdc, PixelFormat, &pfd);
         if (bResult==FALSE) {
            assert(0);
            return FALSE ;
         }
      
         m_hrc = wglCreateContext(hdc);
         if (!m_hrc){
            assert(0);
            return FALSE;
         }
      
         ReleaseDC(hWnd, hdc);
      
         return TRUE;
      }
      
      LRESULT CALLBACK WindowFunc(HWND hWnd,UINT msg, WPARAM wParam, LPARAM lParam) {
          PAINTSTRUCT ps;
      
          switch(msg) {
              case WM_CREATE:
              break;
      
              case WM_DESTROY:
                  if(m_hrc) {
                      wglMakeCurrent(NULL, NULL);
                      wglDeleteContext(m_hrc) ;
                  }
                  PostQuitMessage(0) ;
              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)CreateSolidBrush(0x00000000);
          wc.lpszClassName = szAppName;
      
          if(!RegisterClassEx(&wc)) {
              MessageBox(NULL, _T("RegisterClassEx - failed"), _T("Error"), MB_OK | MB_ICONERROR);
              return FALSE;
          }
      
          HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, 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;
          }
      
          DWM_BLURBEHIND bb = {0};
          HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
          bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
          bb.hRgnBlur = hRgn;
          bb.fEnable = TRUE;
          DwmEnableBlurBehindWindow(hWnd, &bb);
      
          CreateHGLRC(hWnd);
      
          HDC hdc = GetDC(hWnd);
          wglMakeCurrent(hdc, m_hrc);
          initSC();
          resizeSC(w, h);
          ReleaseDC(hWnd, hdc);
      
          MSG msg;  
          while(1) {
              if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                  TranslateMessage(&msg);
                  DispatchMessage(&msg);
              }
              else {
                  HDC hdc = GetDC(hWnd);
                  wglMakeCurrent(hdc, m_hrc);
      
                  renderSC();
      
                  SwapBuffers(hdc);
                  ReleaseDC(hWnd, hdc);
              }
          } 
      
          return (FALSE); 
      }
      

      【讨论】:

      • 在 Win 7 上运行良好)。我可以通过使窗口比屏幕高 1 个像素来解决此问题,即使用 window_x = 0, window_y = -1, window_width = screen_width, window_height = screen_height + 1 作为传递给 CreateWindowEx 的值,然后像往常一样调用 glViewport(0, 0, screen_width, screen_height)
      • 绝妙的答案!正是我需要的。所有其他答案都显示了绘制到屏幕外缓冲区然后将 BitBlt() 绘制到可见的绘图上下文的方法,这非常慢并且不适用于任何严肃的应用程序。
      【解决方案9】:

      我知道这是旧的,但我试图将 Xlib 解决方案移植到 Gtk+。经过大量的研究,我终于做到了,所以我真的很想在这里分享给有需要的人。

      #include <gtk/gtk.h>
      #include <gdk/gdkscreen.h>
      #include <gdk/gdkkeysyms.h>
      #include <gtk/gtkgl.h>
      #include <GL/gl.h>
      #include <GL/glu.h>
      
      static gboolean supports_alpha = FALSE;
      
      /***
       *** Configure the OpenGL framebuffer.
      ***/
      static GdkGLConfig* configure_gl(void)
      {
          GdkGLConfig* glconfig;
      
          /* Try double-buffered visual */
          glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGBA |
              GDK_GL_MODE_ALPHA |
              GDK_GL_MODE_DEPTH |
              GDK_GL_MODE_DOUBLE);
          if (glconfig == NULL)
          {
              printf("Cannot find the double-buffered visual.\n");
              printf("No appropriate OpenGL-capable visual found.\n");
              exit(1);
          }
          printf("Find GLConfig with alpha channel.\n");
          return glconfig;
      }
      
      static void screen_changed(GtkWidget* widget, GdkScreen* old_screen, gpointer userdata)
      {
          /* To check if the display supports alpha channels, get the colormap */
          GdkScreen* screen = gtk_widget_get_screen(widget);
          GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
      
          if (!colormap)
          {
              printf("Your screen does not support alpha channels!\n");
              colormap = gdk_screen_get_rgb_colormap(screen);
              supports_alpha = FALSE;
          }
          else
          {
              printf("Your screen supports alpha channels!\n");
              supports_alpha = TRUE;
          }
      
          gtk_widget_set_colormap(widget, colormap);
      }
      
      static gboolean expose(GtkWidget* widget, GdkEventExpose* event, gpointer userdata)
      {
          GdkGLContext* glcontext = gtk_widget_get_gl_context(widget);
          GdkGLDrawable* gldrawable = gtk_widget_get_gl_drawable(widget);
      
          if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
          {
              return FALSE;
          }
      
          glDrawBuffer(GL_BACK);
      
          glClearColor(0.0, 0.0, 0.0, 0.0);
          glClear(GL_COLOR_BUFFER_BIT);
      
          glBegin(GL_QUADS);
          glColor4f(1.0f, 0.0f, 0.0f, 0.3f);
          glVertex3f(-0.5f, -0.5f, 0);
          glVertex3f(+0.5f, -0.5f, 0);
          glVertex3f(+0.5f, +0.5f, 0);
          glVertex3f(-0.5f, +0.5f, 0);
          glEnd();
      
          gdk_gl_drawable_swap_buffers(gldrawable);
      
          gdk_gl_drawable_gl_end(gldrawable);
          return TRUE;
      }
      
      int main(int argc, char** argv)
      {
          gtk_init(&argc, &argv);
      
          GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      
          /* Added to config GLConfig */
          GdkGLConfig* glconfig = configure_gl();
          gtk_widget_set_gl_capability(window,
              glconfig,
              NULL,
              TRUE,
              GDK_GL_RGBA_TYPE);
      
          gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
          gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
          gtk_window_set_title(GTK_WINDOW(window), "Alpha Demo");
          g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);
      
          gtk_widget_set_app_paintable(window, TRUE);
      
          g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(expose), NULL);
          g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL);
      
          screen_changed(window, NULL, NULL);
      
          gtk_widget_show_all(window);
          gtk_main();
      
          return 0;
      }
      

      使用gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 gtkglext-1.0` 编译。在 Ubuntu 18.04 上测试(除了 gtk,您还需要安装 libgtkglext1-dev)。

      编辑

      我将渲染代码从简单的glClear 更改为矩形。

      代码是this questionthis question的修改版本。

      【讨论】:

      • 有趣,您分享的其中一个链接也来自我的另一个答案。确保对所有内容进行投票。如果您分享该程序运行的屏幕截图,以便我们知道它的外观,我会投票赞成这个答案。
      • 抱歉,虽然我经常使用 stackoverflow(来自 google),但我丢失了密码并且很久没有登录。我将来会这样做。也感谢其他答案。
      • 感谢您的建议。我会尝试添加一个屏幕截图来改进,但是否支持它仍然是您自己的选择。 @karlphillip
      • 那张截图会给你的答案增加很多价值。
      猜你喜欢
      • 2017-09-28
      • 2013-11-11
      • 2019-10-09
      • 2012-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多