Visual C++单文档OpenGL框架详见:https://blog.csdn.net/qq_34911636/article/details/86666112
一般而言OpenGL绘制图像的函数大多数在单文档程序的OnDraw函数内部实现,但是在绘制复杂图像或者像素点比较多
时会出现严重的卡顿现象,为了解决绘制复杂图像卡顿问题,本文以在三维空间中显示点为例子,利用多线程-结合VBO
编程的方式改善性能。
1、多线程框架的搭建,OpenGL初始化代码如下:
if (!SetupPixelFormat(pdc))//检测安装OpenGL
{
MessageBox(NULL, _T("SetPixelFormat failed"), _T("Error"), MB_OK);
return FALSE;
}
/*GetPixelFormat函数是获取当前DC的像素索引值,函数SetupPixelFormat,设置了一个像素格式,它就有了一个自己的索引值通过这个函数GetPixelFormat,可以获得它的索引值并赋给n*/
n = ::GetPixelFormat(pdc->GetSafeHdc());
/*DescribePixelFormat函数是描述像素格式,获得索引之后要向设备描述你的像素格式,这样设备才会按照你的像素格式绘图*/
::DescribePixelFormat(pdc->GetSafeHdc(), n, sizeof(pfd), &pfd);
/*WglCreateContext函数创建一个新的OpenGL渲染描述表,此描述表必须适用于绘制到由hdc返回的设备.这个渲染描述表将有和设备上下文(dc)一样的像素格式*/
m_hRC = wglCreateContext(pdc->GetSafeHdc()); //m_hRC是设备的主渲染描述符
m_SharehRC = wglCreateContext(pdc->GetSafeHdc()); //m_SharehRC为共享渲染描述符,共享主渲染描述符m_hRC
//这边多线程显示,Opengl每一时刻只允许一个线程渲染,不允许多个线程共同渲染,所以这边只能通过共享的方式共享主渲染表
if (m_hRC == NULL)
{
return FALSE;
}
/*这边多线程显示,Opengl每一时刻只允许一个线程渲染,不允许多个线程共同渲染,所以这边只能通过共享的方式共享主渲染表wglShareLists函数设置共享渲染表,wglShareLists(HGLRC hglrc1, HGLRC hglrc2)其中参数hglrc1提供共享资源的render context参数hglrc2共享参数hglrc1提供的资源*/
wglShareLists(m_hRC, m_SharehRC);
上述代码中红色字体为OpenGL初始化代码,而蓝色字体为注解
上述的代码核心是创建两个设备的渲染描述符分别为m_hRC、m_SharehRC,其中m_hRC为主描述符,而m_SharehRC是分享
主描述符m_hRC的描述符。
2、创建两个线程
在单文档初始化程序设置线程
m_ProcessingThread1 = AfxBeginThread(CallbackProcessing1, m_ClientDC, 0, 0, CREATE_SUSPENDED); //创建线程1
m_ProcessingThread2 = AfxBeginThread(CallbackProcessing2, m_ClientDC, 0, 0, CREATE_SUSPENDED); //创建线程2
m_ProcessingThread1->ResumeThread(); //启动线程
m_ProcessingThread2->ResumeThread(); //启动线程
其中UINT CallbackProcessing1(LPVOID pParam)为线程回调函数1
UINT CallbackLidarProcessing(LPVOID pParam)
{
while(true)
{
/*wglMakeCurrent函数设定OpenGL当前线程的渲染环境,使一个指定的OpenGL渲染上下文调用线程的当前呈现上下文
OpenGL只允许当前一个线程拥有渲染环境,若wglMakeCurrent函数返回FALSE则说明其他线程已占有渲染环境,如果
返回TRUE则成功的设置当前的线程为渲染环境*/
if (wglMakeCurrent(Pdc->GetSafeHdc(), m_OpenGL->m_hRC) == TRUE)
{
glewInit();//初始化VBO缓冲区,前提必须启动函数glewInit初始化,否则无法初始化
GLuint vboid;
glGenBuffers(1, &vboid); //创建顶点缓冲区对象
glBindBuffer(GL_ARRAY_BUFFER, vboid); //将顶点缓冲区对象设置为当前数组缓冲区对象
glBufferData(GL_ARRAY_BUFFER, sizeof(m_Pointdata), m_Pointdata, GL_DYNAMIC_DRAW); /*为顶点缓冲区对 象申请内存空间,并进行初始化,其中m_Pointdata要显示的顶点数组*/
m_OpenGL->vboid = vboid //把VBO对象标识vboid存储在全局变量m_OpenGL->vboid中供后续使用
m_ThreadProcessing2++;
/*当该线程创建VBO结束或者当前绘图结束时记得要释放当前渲染环境,若没有释放渲染环境则在其他线程就没办法获取
渲染环境操作*/
wglMakeCurrent(NULL, NULL);//释放渲染环境
}
}
}
注解:在上面CallbackLidarProcessing例程只是一个示例,里面VBO的初始化只要一次就行,但这部的代码会处于不断循环,真正运行此段代码需要修改,若想实时的改变数组m_Pointdata的显示内容可以采用VBO的glMapBuffer函数修改内存数据,从而改变显示内容。
///
其中UINT CallbackProcessing2(LPVOID pParam)为线程回调函数2
UINT CallbackProcessing2(LPVOID pParam)
{
CClientDC *Pdc = (CClientDC *)(pParam); //创建线程函数AfxBeginThread传递CClientDC指针
while (true)
{
/*wglMakeCurrent函数设定OpenGL当前线程的渲染环境,使一个指定的OpenGL渲染上下文调用线程的当前呈现上下文
OpenGL只允许当前一个线程拥有渲染环境,若wglMakeCurrent函数返回FALSE则说明其他线程已占有渲染环境,如果
返回TRUE则成功的设置当前的线程为渲染环境*/
if (wglMakeCurrent(Pdc->GetSafeHdc(), m_OpenGL->m_SharehRC) == TRUE)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //设置显示区域的背景色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除原有的画面
m_OpenGL->DisplayModeInit(); //显示方式初始化
m_OpenGL->ViewAngle(); //设置视角
m_OpenGL->DisplayGrid(); //画水平网格
m_OpenGL->DisplayCoordinate(); //画坐标系
m_OpenGL->DirectionLight(); //光照函数
if (m_OpenGL->vboid != 0) //判断VBO对象是否有效
{
glBindBuffer(GL_ARRAY_BUFFER, m_OpenGL->vboid); //关联已有的VBO对象
glEnableClientState(GL_VERTEX_ARRAY); //打开顶点绘图
glVertexPointer(3, GL_FLOAT, 0, 0); //设置顶点的格式
glDrawArrays(GL_POINTS, 0, 3); //绘图
glDisableClientState(GL_VERTEX_ARRAY); //关闭绘图
glBindBuffer(GL_ARRAY_BUFFER, 0); //解除关联对象
}
m_OpenGL->Point();
glFlush(); //强制刷新绘图缓冲区
SwapBuffers(wglGetCurrentDC()); //交换缓冲区,把已绘制完成的缓冲区的数据显示出来
m_ThreadProcessing2++;
wglMakeCurrent(NULL, NULL); //释放渲染环境
}
}
return 0;
}
上述代码通过主程序初始化OpenGL代码,并在主程序中创建两个线程,通过共享渲染描述符的方式两个线程分时
的获取绘图句柄,在线程1中负责数据的导入或修改等(通过VBO),在线程2中负责数据的显示,这种可以增强
系统渲染的实时性。