一直想把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
}