标签:
OpenGL的绘图机制是
OpenGL的绘图方式与Windows一般的绘图方式是不同的,主要区别如下:
(1)Windows采用的是GDI(Graphy Device Interface 图形设备接口)在设备描述表DC上进行绘图。
(2)OpenGL采用的是OpenGL相关的函数(OGL的命令)在渲染描述表RC上进行绘图。
(3)OpenGL使用的是特殊的像素格式。
在Windows中使用GDI绘图时必须指定在哪个设备环境DC中绘制,同同样的在使用OpenGL函数时也必须指定一个所谓的渲染环境。正如DC要存储GDI的绘制环境信息如笔,刷和字体等,RC也必须保存OpenGL所需的渲染信息如像素格式等。
先用OpenGL的绘图上下文Rendering Contex(渲染上下文),用GL的命令把图绘制好。在把所绘制的结果通过WinOS的SwapBuffer()函数传给WinOS的设备上下文DeviceContex,DC(绘图设备上下文)。
一般情况下,程序运行过程中,可能有多个DC,但是OpenGL只有一个RC。所以当一个DC完成画图操作后,要立刻释放RC,这样其他的设备上下文DC也可以进行绘制。
OpenGL渲染环境RC(Render Contex)的创建相关的函数:
渲染环境主要由以下六个wgl函数来管理:
(1)HGLRC wglCreateContext( HDC hdc ),这个创建的过程比较耗时,一般放在初始化预言创建的过程中
该函数用来创建一个OpenGL可用的渲染环境,并没有指定此RC就是当前线程的现行的RC。hdc必须是一个合法的支持至少16位色的屏幕设备描述表DC或内存设备描述表的句柄。该函数在调用前,设备描述表必须设置好适当的像素格式。成功创建渲染描述表RC之后,hdc可以释放或删除(此时并没有把hdc与RC关联起来,需要使用其他函数)。函数返回NULL值表示失败,否则返回值为渲染上下文的句柄。
(2)BOOL wglDeleteContex( HGLRC hglrc )
该函数删除一个RC,一般应用程序在删除RC之前,应使它成为非现行RC,不过,删除一个现行RC也是可以得。此时,OpenGL系统冲掉等待的绘图命令并使之成为非现行RC,后删除之。注意:删除一个属于别的线程的RC时会导致失败,所以只能管理本线程的RC。
(3)HGLRC wglGetCurrentcontext( void )
该函数返回线程的现行RC,如果线程无现行RC则返回NULL
(4)HDC wglGetCurrentDC( void )
该函数返回与线程现行RC关联的DC,如果线程无现行RC则返回NULL。
(5)BOOL wglMakeCurrent( HDC hdc, HGLRC hglrc )
该函数把hdc和hglrc关联起来,并使hglrc成为调用线程的现行RC,如果传给hglrc的值为NULL,则函数解除关联,并置线程的现行RC为非现行RC,测试忽略hdc参数。注意:传给该函数的hdc可以不是调用wglCreateContext时使用的值,但是,他们所关联的设备必须相同并且拥有相同的像素格式,因为创建RC是指借助于DC的基本的配置创建RC,并没有在创建的时候已经指定关联DC。
注意:
对于线程来说,一个线程可以创建(拥有)多个RC,一个RC可以由多个线程共享,但是只有一个RC可以成为“现行RC”,“现行RC”必然与一个DC相关联,否则RC不能成为现行RC。
一个线程在拥有现行RC进行绘图时,别的现场将无法同时绘图,因为当前的现行RC被占用。在使用RC时,不应该释放或者删除与之关联的DC。如果应用程序在整个生命期内保持一个现行RC,则应用程序也一直占有一个DC资源。Windows系统只有有限的DC资源。
MFC管理RC与DC的方式:
(1)在WM_CREATE消息响应时创建RC(因为创建RC非常耗时,所以在创建创建的时候创建RC),创建后立即释放DC(解除此RC对DC的独占,造成其他绘图不能使用DC);当WM_PAINT消息到来时,程序再获取DC句柄,并与RC关联起来(只有窗口重绘的时候,关联DC与RC,即在使用的时候占有DC,是合理的),绘图完成后,立即解除RC与DC得关联并释放DC(同上上);当WM_DESTROY消息到来时,程序只需简单的删除RC(如果在窗口销毁的过程中,还有OGL命令执行,则需要再次关联,然后删除RC,以及消除此线程创建的DC,释放资源)。
(2)RC在程序开始时创建并使之成为现行RC。它将保持为现行RC直至程序结束。相应的,GETDC在程序开始调用,RELEASEDC在程序结束时才调用。此中方法的好处是在相应WM_PAINT消息时,无需调用十分耗时的wglMakeCurrent函数。一般它要消耗几千个时钟周期。但是为了避免造成“RC”独占DC现象,以及多线程有多个RC绘图的影响,一般采用:创建,释放关联; 绘图:关联,释放;销毁:释放
具体的MFC管理RC与DC的程序下文有展示:
www.cmblog.com/icmzn
OpenGL与具体系统相关的几个函数:
glFlush: 将GL命令队列中的命令发送给“显卡”,并清空命令队列。发送完毕后,立刻返回。不能保证之前的显卡对这些命令队列的命令是否执行完毕,(可能显卡还没有图形绘制完毕)。glFlush就是强制刷新,要知道OpenGL是使用一条渲染管线线性处理命令的,一般情况下,我们提交给OpenGL的指令并不是马上送到显卡驱动程序里被GPU执行的,而是放到一个缓冲区里面,等这个缓冲区满了再一次过发到驱动程序里执行;很多时候只有几条指令是填充不满那个缓冲区的,这就是说这些指令根本没有被发送到驱动里,所以我们要调用glFlush来强制把这些指令送到驱动里进行处理。
glFinish: 将GL命令队列中的命令发送给“显卡”, 并清空命令队列。GL需要等显卡执行完命令完全执行完毕后(显卡完成绘图),再返回。
OpenGL,建议一般在绘图命令GL比较冗长的情况下,可以分段调用glFlush,清空命令队列并发送给显卡先执行这些命令,最后调用glFinish进行同步处理。如,在某些的场景绘制的过程中,渲染器如果有多个,渲染执行从shader0,一直渲染到shaderN,如果采用所有的额命令绘制完毕后在调用glFinish,则会造成CPU等待GPU进行绘图,造成CPU没有充分利用,反映到效果上就是图形卡卡的,或者就是对CPU的其他有影响,如影响程序的时钟系统,因为程序在主线程停在了等待GPU的反馈信号,而对其他模块产生影响。understand!
可以将上述情形优化为:每个shader完成后,调用一次glFlush,强制GPU开始执行命令队列中的GL绘图命令,这样在最后的shader渲染后,调用glFinish,完成当前帧内容的绘制并进行同步,因为GPU已经将之前的GL命令执行的差不多了,所以此方式对于GL冗长的情况执行效率比较高。
使用OpenGL渲染时,提高CPU效率的方式:
(1)使用多线程。
将更新和渲染作为线程A,辅助计算入AI(智能控制)策略、地图生成作为线程B。则当线程A因为glFinish阻塞时,开启线程B预先进行CPU计算。直到线程B完成后,根据B的结果做出相应的操作。
(2)使用CPU时间预算方法。先给出一帧所需要的时间预算,然后在调用glFinish之前判断是否有更多的时间需要等待,如果有,则调用glFlush后,直接进行“辅助计算”,完成或者超过预算时间后,在调用glFinish。(此时GPU的渲染帧的所有命令已经绘制完毕,只需要一条glFinish进行同步处理)。
对于SwapBuffer的理解:
SwapBuffer的命令指示把前台和后台的“缓冲区”指针交换了一下,即把前台的内容编程后台的内容,把后台的内容变换到了前台。但是,but,这个函数并不执行变换后的buffer清理工作。
所以,绘制当前帧的OpenGL模式方法是:(1)每一帧的绘制之前都需要先调用一次glClear函数,然后在绘制当前帧的内容。(2)用GL命令绘制当前帧的内容,在这里已经考虑glFlush,GlFinish的各种优化,当前帧内容的GL命令的最后,需要执行glFinish以完成GL命令与当前帧绘制的所有同步工作,只须等待前后台的缓存交换执行显示。(3)在GL命令绘制后,最后调用一次SwapBuffer,进行前后台的显示。
对wglMakeCurrent的理解
wglMakeCurrent 函数设定OpenGL当前线程的渲染环境。以后这个线程所有的OpenGL调用都是在这个hdc标识的设备上绘制。你也可以使用wglMakeCurrent 函数来改变调用线程的当前渲染环境,使之不再是当前的渲染环境。But,但是,正如上文所述,一般由多个DC,竞争使用一个RC,则在使用此函数获取一个DC后,本DC执行完毕后,需要在最后释放RC,不能独占RC。
函数原型如下:
BOOL wglMakeCurrent( HDC hdc, // device context of device that OpenGL calls are // to be drawn on,设备环境(绘图上下文)的句柄。调用这个函数的线程接下来的所有OpenGL调用都将在这个hdc所标识的设备上绘制。指定当前线程。 HGLRC hglrc // OpenGL rendering context to be made the calling // thread‘s current rendering context 函数设定的OpenGL渲染环境的句柄,作为当前线程的渲染环境。仅限定当前线程。 );
注意:如果hglrc为NULL,函数将使调用线程的当前渲染环境不再作为当前的渲染环境,并释放这个渲染环境所使用的设备环境。这种情况下hdc参数将被忽略。
// link the Win Device Context with the OGL Rendering Context 根据绘图设备上下文生成OGL的RC
m_hRC = wglCreateContext(m_pCDC->GetSafeHdc()); // specify the target DeviceContext (window) of the subsequent OGL calls 设置当前OGL的渲染环境RC对应的目标DC
wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); // free the target DeviceContext (window),释放DC,供其他DC使用本RC 释放DC,从而其他的DC可以使用本RC
wglMakeCurrent(NULL,NULL);
注意:(1)hdc参数的绘制表面必须是支持OpenGL的。
它不需是wglCreateContext 创建hglrc时所传进来的hdc,但它必须是相同的设备并具有相同的像素格式。GDI转换的hdc将不被渲染环境所支持。当前的渲染环境将一直使用hdc设备环境直到它不再是当前渲染环境。
(2)在切换到新的渲染环境之前,OpenGL将冲刷掉当前调用线程之前的所有渲染环境。
(3)一个线程可以有一个渲染环境(RC)。所以一个多线程的进程可以有多个渲染环境。一个线程在调用任何OpenGL函数之前必须设置一个当前渲染环境(RC)。否则所有OpenGL调用都将忽略,因为当前的RC没有可以执行GL命令的DC,必然被忽略。
(4)在某一时刻渲染环境(RC)只能属于一个线程,你不能使一个渲染环境同时属于多个线程,因为RC只有一个。
(5)一个应用程序可以通过不同线程中的不同的当前渲染环境(RC)产生多个绘图,它支持各个线程都拥有自己的渲染环境的设备环境(DC)。
(6)如果有错误发生,wglMakeCurrent 函数在返回之前将使渲染环境不作为线程当前的渲染环境。避免异常情况的独自占有情况。
在视图View创建之前,需要对设备环境,与当前线程的渲染环境进行设置:此外,也需要对OGL的全局设置进行处理,如开启深度缓存,设置背景颜色清除深度缓存信息。
1 int CGLEnabledView::OnCreate(LPCREATESTRUCT lpCreateStruct) 2 { 3 if (CView::OnCreate(lpCreateStruct) == -1) return -1; 4 // OpenGL rendering context creation 5 PIXELFORMATDESCRIPTOR pfd; 6 int n; 7 // initialize the private member 8 m_pCDC= new CClientDC(this);//当前线程窗口对应的设备绘制上下文即DC 9 // choose the requested video mode 10 if (!bSetupPixelFormat()) return 0; 11 // ask the system if the video mode is supported 12 n=::GetPixelFormat(m_pCDC->GetSafeHdc()); 13 ::DescribePixelFormat(m_pCDC->GetSafeHdc(),n,sizeof(pfd),&pfd); 14 // create a palette if the requested video mode has 256 colors (indexed mode) 15 CreateRGBPalette(); 16 // link the Win Device Context with the OGL Rendering Context 17 m_hRC = wglCreateContext(m_pCDC->GetSafeHdc()); 创建RC 18 // specify the target DeviceContext (window) of the subsequent OGL calls 19 wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 设置当前RC对应的DC 20 // performs default setting of rendering mode,etc.. 21 OnCreateGL();//OGL需要提前设置的配置属性。 22 // free the target DeviceContext (window) 23 wglMakeCurrent(NULL,NULL); 释放DC 24 return 0; 25 }
1 void CGLEnabledView::OnCreateGL()//执行的是对RC的全局设置 2 { 3 // perform hidden line/surface removal (enabling Z-Buffer) 4 glEnable(GL_DEPTH_TEST); 5 // set background color to black 6 glClearColor(0.f,0.f,0.f,1.0f ); 7 // set clear Z-Buffer value 8 glClearDepth(1.0f); 9 }
在View的OnDraw中,每次都要进行绘制处理,每次的处理流程都一样,这样用户只需要单独执行OnDrawGL();,就可以了,预设值以及善后就已经设置完毕。
void CGLEnabledView::OnDraw(CDC* pDC) { // prepare a semaphore 标志位 static BOOL bBusy = FALSE; // use the semaphore to enter this critic section if(bBusy) return; bBusy = TRUE; // specify the target DeviceContext of the subsequent OGL calls wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC);//只需执行的是切换功能,不需要执行耗时的创建过程 // clear background 清空缓存 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // call the virtual drawing procedure (to be overridden by user) //模板方法模式 OnDrawGL(); // execute OGL commands (flush the OGL graphical pipeline) //所有的绘制命令后,必须执行此GL的glFInish同步命令 glFinish(); // if double buffering is used it‘s time to swap the buffers,必须使用双缓存 if (m_bDoubleBuffer) SwapBuffers(m_pCDC->GetSafeHdc()); // turn the semaphore "green" bBusy = FALSE; // free the target DeviceContext (window),释放DC,供其他DC使用本RC wglMakeCurrent(NULL,NULL); }
View的onDestroy窗口销毁过程的动作:
void CGLEnabledView::OnDestroy() { OnDestroyGL(); // specify the target DeviceContext (window) of the subsequent OGL calls wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); // remove all display lists for (int c=0;c<MAX_LISTS;c++) if(m_DispListVector[c]) glDeleteLists(m_DispListVector[c],1); // release definitely OGL Rendering Context if (m_hRC!=NULL) ::wglDeleteContext(m_hRC); // Select our palette out of the dc CPalette palDefault; palDefault.CreateStockObject(DEFAULT_PALETTE); m_pCDC->SelectPalette(&palDefault, FALSE); // destroy Win Device Context if(m_pCDC) delete m_pCDC; // finally call the base function CView::OnDestroy(); }
end:
openGL中的原理理解1---一个视图需要支持OGL需要配置,GLenbalView的理解
标签:
原文地址:http://www.cnblogs.com/icmzn/p/5735196.html