c# - 透明Windows窗体,内部有OpenGL绘图

标签 c# c winforms opengl

我希望我的 OpenGL 绘图位于透明窗口窗体内。

我的计算机上安装了 Windows 7,并且正在使用 .NET。

这是我设法编写的代码,但它不起作用 - 无论我做什么,我仍然可以返回绘图背后的背景。

请告诉我我做错了什么?

public partial class MainForm : Form
{
    private Graphics m_graphics;
    private IntPtr m_hDC;
    private IntPtr m_RC;

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= (int)User32.WS_EX_LAYERED;
        return cp;
    }
}

public MainForm()
{
    InitializeComponent();

    this.m_graphics = null;
    this.m_hDC = IntPtr.Zero;
    this.m_RC = IntPtr.Zero;
}

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);

    User32.SetLayeredWindowAttributes(Handle, 0, 0, User32.LWA_COLORKEY);

    m_graphics = Graphics.FromHwnd(Handle);
    m_hDC = m_graphics.GetHdc();

    GosNIIAS.Import.PIXELFORMATDESCRIPTOR pfd = new GosNIIAS.Import.PIXELFORMATDESCRIPTOR();
    pfd.nSize = (ushort)Marshal.SizeOf(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = OpenGL32.PFD_SUPPORT_OPENGL | OpenGL32.PFD_DRAW_TO_WINDOW | OpenGL32.PFD_DOUBLEBUFFER;
    pfd.iPixelType = OpenGL32.PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 16;
    pfd.cStencilBits = 8;

    int PixelFormat = GDI32.ChoosePixelFormat(m_hDC, ref pfd);
    if (PixelFormat != 0)
    {
        bool bResult = GDI32.SetPixelFormat(m_hDC, PixelFormat, ref pfd);
        if (bResult != false)
        {
            m_RC = OpenGL32.wglCreateContext(m_hDC);
            bResult = OpenGL32.wglMakeCurrent(m_hDC, m_RC);
        }
    }
}

protected override void OnFormClosing(FormClosingEventArgs e)
{
    base.OnFormClosing(e);

    if (m_RC != IntPtr.Zero)
    {
        OpenGL32.wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
        OpenGL32.wglDeleteContext(m_RC);
        m_RC = IntPtr.Zero;
    }

    if (m_graphics != null)
    {
        m_graphics.ReleaseHdc();
        m_graphics.Dispose();
        m_graphics = null;
        m_hDC = IntPtr.Zero;
    }
}

private void tTimer_Tick(object sender, EventArgs e)
{
    OpenGL32.wglMakeCurrent(m_hDC, m_RC);

    OpenGL32.glClear(OpenGL32.GL_COLOR_BUFFER_BIT | OpenGL32.GL_DEPTH_BUFFER_BIT);

    OpenGL32.glEnable(OpenGL32.GL_BLEND);
    OpenGL32.glBlendFunc(OpenGL32.GL_SRC_ALPHA, OpenGL32.GL_ONE_MINUS_SRC_ALPHA);
    OpenGL32.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    OpenGL32.glColor3f(0, 1, 1);
    OpenGL32.glBegin(OpenGL32.GL_TRIANGLES);
    OpenGL32.glColor3f(1.0f, 0.0f, 0.0f); 
    OpenGL32.glVertex3f(0.0f, 1.0f, 0.0f);
    OpenGL32.glColor3f(0.0f, 1.0f, 0.0f);
    OpenGL32.glVertex3f(-1.0f, -1.0f, 0.0f);
    OpenGL32.glColor3f(0.0f, 0.0f, 1.0f);
    OpenGL32.glVertex3f(1.0f, -1.0f, 0.0f);
    OpenGL32.glEnd();

    GDI32.SwapBuffers(m_hDC);
}

}

当我使用纯 C 代码时,结果相同。

#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")

#define vW 660
#define vH 563

const int cX = vW / 2;
const int cY = vH / 2 - 90;

BOOL CreateHGLRC( HDC hDC, HGLRC* m_hrc )
{
    DWORD dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;

    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(hDC, &pfd);

   BOOL bResult = SetPixelFormat(hDC, PixelFormat, &pfd);

   *m_hrc = wglCreateContext(hDC);

   return TRUE;
}

LRESULT CALLBACK WndProc(   HWND    hWnd,
                            UINT    message,
                            WPARAM  wParam,
                            LPARAM  lParam)
{
    static HDC hDC = NULL;          /* Private GDI Device context */
    static HGLRC hRC = NULL;

    switch (message)
    {
        /* Window creation, setup for OpenGL */
        case WM_CREATE:
      {
            /* Store the device context */
            hDC = GetDC(hWnd);      

            CreateHGLRC( hDC, &hRC );

            wglMakeCurrent(hDC, hRC);

            /* Create a timer that fires 100 times a second */
            SetTimer(hWnd,10,100,NULL);
      }
        break;

        /* Window is being destroyed, cleanup */
        case WM_DESTROY:
      {
            /* Kill the timer that we created */
            KillTimer(hWnd,101);

            /* Tell the application to terminate after the window */
            /* is gone. */
            PostQuitMessage(0);
      }
        break;

        /* Timer, moves and bounces the rectangle, simply calls */
        /* our previous OnIdle function, then invalivaluees the */
        /* window so it will be redrawn. */
        case WM_TIMER:

            {
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

                glPushMatrix();

                        glEnable(GL_BLEND);
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

                        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                        glColor3f(0, 1, 1);
                        glBegin(GL_TRIANGLES);
                        glColor3f(1.0f, 0.0f, 0.0f);
                        glVertex3f(0.0f, 1.0f, 0.0f);
                        glColor3f(0.0f, 1.0f, 0.0f);
                        glVertex3f(-1.0f, -1.0f, 0.0f);
                        glColor3f(0.0f, 0.0f, 1.0f);
                        glVertex3f(1.0f, -1.0f, 0.0f);
                        glEnd();

                glPopMatrix();
                glFlush();
            }

        break;

        case WM_ERASEBKGND:
            return 0;
        break;

      default:   /* Passes it on if unproccessed */
      {
         return (DefWindowProc(hWnd, message, wParam, lParam));
      }
   }

    return (0L);
}

int APIENTRY WinMain(HINSTANCE  hInstance,
                     HINSTANCE  hPrevInstance,
                     LPSTR      lpCmdLine,
                     int        nCmdShow)
{
    char *lpszAppName = "TransparentWindow";

    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)WndProc;
    wc.cbClsExtra  = 0;
    wc.cbWndExtra  = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW);
    wc.lpszClassName = lpszAppName;

    int return_value;

    RegisterClassEx(&wc);

    HWND hWnd = CreateWindowEx(WS_EX_LAYERED, lpszAppName, lpszAppName,
                    WS_VISIBLE | WS_POPUP, 200, 150, vW, vH,
                    NULL, NULL, hInstance, NULL);

    SetLayeredWindowAttributes(hWnd, 0x000000, 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); 
}

最佳答案

要获得透明窗口,必须使用扩展帧缓冲区配置而不是普通像素格式。像这样:

int attribs[] = {
    WGL_DRAW_TO_WINDOW_ARB, TRUE,
    WGL_DOUBLE_BUFFER_ARB, TRUE,
    WGL_SUPPORT_OPENGL_ARB, TRUE, 
    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
    WGL_TRANSPARENT_ARB, TRUE,
    WGL_COLOR_BITS_ARB, 32,
    WGL_RED_BITS_ARB, 8,
    WGL_GREEN_BITS_ARB, 8,
    WGL_BLUE_BITS_ARB, 8,
    WGL_ALPHA_BITS_ARB, 8,
    WGL_DEPTH_BITS_ARB, 24,
    WGL_STENCIL_BITS_ARB, 8,
    0, 0
};

INT iPF;
UINT num_formats_choosen;
if( !wglChoosePixelFormatARB(
        hDC, 
        attribs, 
        NULL,
        1,
        &iPF,
        &num_formats_choosen) ) {
    fprintf(stderr, "error choosing proper pixel format\n");
    return NULL;
}
if( !num_formats_choosen ) {
    return NULL;
}

PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
/* now this is a kludge; we need to pass something in the PIXELFORMATDESCRIPTOR 
 * to SetPixelFormat; it will be ignored, mostly. OTOH we want to send something
 * sane, we're nice people after all - it doesn't hurt if this fails. */
DescribePixelFormat(hDC, iPF, sizeof(pfd), &pfd);

if( !SetPixelFormat(hDC, iPF, &pfd) ) {
    fprintf(stderr, "error setting proper pixel format\n");
    ReleaseDC(hWnd, hDC);
    DestroyWindow(hWnd);

    return NULL;
}

此外,您还必须启用 DwmBlurBehindWindow 或使用 WS_POPUP,而不是带有框架窗口的顶级 WS_OVERLAPPED。

WS_EX_LAYERED 不会创建“alpha 透明度”窗口! WS_EX_LAYERED 的作用是,它允许您只需很少的控制即可实现全局窗口不透明度(应用于整个窗口)。

作为引用,您可以查看我的 wglarb 测试程序之一:https://github.com/datenwolf/wglarb/blob/master/test/layered.c

关于c# - 透明Windows窗体,内部有OpenGL绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45011914/

相关文章:

c# - 单击一次相同的发布版本但程序集版本不同,更新不会发生

c - 当Str1不包含Str2时,strcspn()的返回值是多少?

c# - 从 dll 调用 opencv Mat 到 Windows 窗体,图像出现故障

c# - Switch 语句性能 C#

C# WebBrowser 控制 System.AccessViolationException

c - 如何使用 sendfile 接收文件?

c# - 在面板上动态创建按钮

c# - app.config 从其他节点读取关键连接字符串的值

c# - 如何在 C# 中的文本框(实际上是任何字符串)中显示无限字符/符号?

c - 无效函数调用作为表达式