关于opengl在缓存DC上的绘制问题

一直想把opengl用来渲染界面,我知道这本身是没什么不可以,但只有实践来测试其可行性

既然做界面必然离不开windows的编程机制,如果opengl与gdi能很好的结合,那这就能解开我心中的疑惑。

那首先的一个问题就是opengl能否在缓存DC上进行绘制呢,如果能那就可以很好的融入directui界面库的机制,我可以选择性的使用各种不同的渲染引擎,对于不同的需求灵活性的使用不同的方案。

opengl在内存上绘制有这几个步奏

1.创建窗口

2.初始化opengl

3.初始化视口与观察矩阵

4.创建兼容DC并选入位图

5.为兼容DC选择像素格式

6.创建和激活HGLRC,这必须使用我们的兼容DC

7.响应WM_PANIT进行opengl的绘制,绘制完成后,使用BitBlt\StrechBlt或者AlphaBlend到窗口DC上

8.消息循环

上面是我的测试程序的基本流程

能否成功其实只在于4,5,6这三步,其他基本上没什么大的问题,经过各种测试也就是这三步会出现各种问题

首先选入位图不要直接用CreateCompatibleBitmap创建的兼容位图,而是需要手动CreateDIBSection创建32位位图,因为之后设置像素格式可以更加精确的匹配

然后设置的像素格式的dwFlag使用PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL,而不要选择双缓冲或者必须支持窗口,否则会在激活HGLRC失败。

所有一切顺利完成以后,下面是使用bitblt把缓存DC的数据拷贝到窗口DC上的效果。

一个蓝色半透明矩形与一条直线。

AlphaBlend?基本上都很难看见蓝色了,黑色部分就完全透明了。这就悲剧了!

之后我遍历了兼容DC的位图数据,发现opengl绘制后的位图Alpha通道为0。无论你在绘制时选择什么颜色绘制的,始终是全透明,导致渲染窗口截取了底层窗口的背景。

然后我换一个绘制方式使用glDrawPixels绘制,然而还是这种情况。所以,如果我需要在已有背景的DC上绘图或者需要制作半透明窗口的效果,都会遇到这个阻碍,难道要手动修改位图的Alpha通道?但这个效率太低基本不会选择。

先把我的测试代码贴上,如果有大神能帮我解开这个疑惑,感激不尽。

#define _WIN32_WINNT 0x0500
#include "stdafx.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
const float PI = 3.1415926154;
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
//#pragma comment (lib, "glaux.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;
HWND hMainWnd = NULL;
LPBYTE bmp_cnt = NULL;
int cxDIB(0);
int cyDIB(0);
//BITMAPINFOHEADER BIH;
BITMAPINFOHEADER BIH;
void PaintClient();
LPBYTE bgImageBuffer = NULL;


void polarCoorCircle(float radius, float circlePointX, float circlePointY)
{
    float i, n = 360.0;
    float t = 2 * PI / n, x, y;
    glBegin(GL_LINE_LOOP);
    for (i = 0.0; i < n; i += 0.2)
    {
        x = radius*sinf(t*i);
        y = radius*cosf(t*i);
        glVertex2f(circlePointX + x, circlePointY + y);
    }
    glEnd();
    glBegin(GL_QUADS);                            //  绘制正方形
    glVertex3f(-1.0f, 1.0f, 0.0f);                    // 左上
    glVertex3f(1.0f, 1.0f, 0.0f);                    // 右上
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 左下
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();
}

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

//     if (h == 0)                                // 防止被零除
//     {
//         h = 1;                            // 将Height设为1
//     }
//     glViewport(0, 0, w, h);                    // 重置当前的视口
//     glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
//     glLoadIdentity();                            // 重置投影矩阵
//     // 设置视口的大小
//     gluPerspective(45.0f, (GLfloat)w / (GLfloat)h, 0.1f, 100.0f);
//     glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
//     glLoadIdentity();                            // 重置模型观察矩阵
//     glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
//     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);                    // 黑色背景
//     glClearDepth(1.0f);                            // 设置深度缓存
//     glEnable(GL_BLEND);
//     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//     glEnable(GL_DEPTH_TEST);                        // 启用深度测试
//     glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
//     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正

    return 0;
}

void resizeSC(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, width, 0.0, height);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

BOOL renderSC()
{
    
    glLineWidth(10);
    glBegin(GL_LINES);
    glVertex2f(0, 0);
    glVertex2f(120, 120);
    glEnd();

    LPBYTE pData = new BYTE[100*100*4];
    memset(pData, 0, 40000);
    for(int i=0; i<10000;++i)
    {
        pData[i*4] = 255;
        pData[i*4+1] = 0;
        pData[i*4+2] = 0;
        pData[i*4+3] = 128;
    }
    glDrawPixels(100, 100,
        GL_BGRA_EXT, GL_UNSIGNED_BYTE, pData);
    delete []pData;
    glFlush();
    return 0;
}

void draw()
{
    HDC pdcDest = GetDC(hMainWnd);
    assert(pdcDIB);
    


    //verify(BitBlt(pdcDest, 0, 0, w, h, pdcDIB, 0, 0, SRCCOPY));
    RECT rc;
    GetClientRect(hMainWnd, &rc);
    BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
    POINT destPT = {0,0};
    POINT srcPT = {0,0};
    SIZE destSize = {rc.right-rc.left, rc.bottom-rc.top};
    //UpdateLayeredWindow(hMainWnd, pdcDest, &destPT, &destSize, pdcDIB, &srcPT, 0, &bf, ULW_ALPHA);
//     for(int i=0;i<w*h;++i)
//     {
//          bmp_cnt[4*i+3] = 128;
//
//         //bmp_cnt[4*i+3]=0;
//     }
    bool bret = AlphaBlend(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, pdcDIB, 0, 0, rc.right - rc.left, rc.bottom - rc.top, bf);
    //BitBlt(hDC, 0,0,rc.right - rc.left, rc.bottom - rc.top, pdcDIB,0,0,SRCCOPY);
}

void CreateDIB(int cx, int cy)
{
    assert(cx > 0);
    assert(cy > 0);
    cxDIB = cx;
    cyDIB = cy;
//     if(bmp_cnt == NULL)
//     {
//         bmp_cnt = new BYTE[cy*cx*4];
//         memset(bmp_cnt, 0, cx*cy);
//         
//     }
    int iSize = sizeof(BITMAPINFOHEADER);
    memset(&BIH, 0, iSize);
    BIH.biSize = iSize;
    BIH.biWidth = cx;
    BIH.biHeight = cy;
    BIH.biPlanes = 1;
    BIH.biBitCount = 32;
    BIH.biCompression = BI_RGB;
    if (pdcDIB)
        verify(DeleteDC(pdcDIB));
    pdcDIB = CreateCompatibleDC(hDC);
    assert(pdcDIB);

    if (hbmpDIB)
        verify(DeleteObject(hbmpDIB));
    hbmpDIB = CreateDIBSection(
        pdcDIB,
        (BITMAPINFO*)&BIH,
        DIB_RGB_COLORS,
        (void**)&bmp_cnt,
        NULL,
        0);
    assert(hbmpDIB);
    assert(bmp_cnt);
//    hbmpDIB = CreateCompatibleBitmap(pdcDIB, cx, cy);
    if (hbmpDIB)
        SelectObject(pdcDIB, hbmpDIB);
}

BOOL CreateHGLRC()
{
    DWORD dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_BITMAP;
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof (PIXELFORMATDESCRIPTOR), // struct size
        1, // Version number
        PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, // use OpenGL drawing to BM
        PFD_TYPE_RGBA, // RGBA pixel values
        32, // color bits
        0, 0, 0, // RGB bits shift sizes...
        0, 0, 0, // Don't care about them
        8, 8, // No alpha buffer info
        0, 0, 0, 0, 0, // No accumulation buffer
        32, // depth buffer bits
        0, // No stencil buffer
        0, // No auxiliary buffers
        PFD_MAIN_PLANE, // Layer type
        0, // Reserved (must be 0)
        0, // No layer mask
        0, // No visible mask
        0 // No damage mask
    };
    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;
    char path[MAX_PATH] = {0};
    switch (msg)
    {
    case WM_ERASEBKGND:
        return 0;
        break;
    case WM_CREATE:
        hMainWnd = hWnd;
        RECT rc;
        GetClientRect(hWnd, &rc);
        w = rc.right - rc.left; h = rc.bottom-rc.top;
        wglMakeCurrent(NULL, NULL);
        wglDeleteContext(m_hrc);
        CreateDIB(w, h);
        CreateHGLRC();
        verify(wglMakeCurrent(pdcDIB, m_hrc));
        initSC();
        resizeSC(w, h);
        //renderSC();
//         InvalidateRect(hWnd, NULL, FALSE);
//         PostMessage(hWnd, WM_PAINT, 0, 0);
        break;
    case WM_DESTROY:
        if (m_hrc)
        {
            wglMakeCurrent(NULL, NULL);
            wglDeleteContext(m_hrc);
        }
        PostQuitMessage(0);
        break;
    case WM_PAINT:
        hDC = BeginPaint(hWnd, &ps);
        PaintClient();
        EndPaint(hWnd, &ps);
        break;
    case WM_SIZE:

        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(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_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;
    }
    //verify(SetLayeredWindowAttributes(hWnd, 0x0, 0, LWA_COLORKEY));
    MSG msg;
    while (1)
    {
        while (true){
            if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
            {
                if (GetMessage(&msg, NULL, 0, 0))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            else
            {
                //PaintClient();
                Sleep(1);
            }
        }
    }
    return (FALSE);
}

void PaintClient()
{
    renderSC(); // OpenGL -> DIB
    draw();  // DIB -> hDC
}

发布了13 篇原创文章 · 获赞 0 · 访问量 4715

猜你喜欢

转载自blog.csdn.net/zhuhan1047026029/article/details/52667682