OpenGL4.x + QT 不错的选择

来源:互联网 发布:软件支持多核 编辑:程序博客网 时间:2024/05/16 21:13

http://blog.csdn.net/gamesdev/article/details/18986265

新的一年到了,在这里祝愿大家马到成功!

       去年学习了一些GLSL的知识,主要是OpenGL(ES)2.0相关的,并且写了几篇文章:

探究OpenGL光照模型的着色器实现

Qt的Graphics-View框架和OpenGL结合详解

QtOpenGLGLSL以及QtGraphics-View框架结合

基于QtGLSL的着色器例子

编写基于QtGLSL程序需要注意的几点

但是OpenGL发展快,知识点很多,我自认为学了这一点知识并不够,于是花了一段时间读了几本OpenGL编程书籍,包括了《OpenGL ES2.0 Programming Guide》以及《OpenGL超级宝典(第六版)》。但是看的书总归还要实践才行,于是过了年后我尝试着写一些OpenGL的程序。首先我开始研究可编程渲染管线和OpenGL4.x。

       从OpenGL3.2开始,固定渲染的功能逐渐地被废弃,可编程渲染成为了主角,为此我们也必须跟上来,学习OpenGL可编程渲染部分。不过虽然说固定渲染功能被废弃了,有些内容还真的必须是固定渲染流程,没法走可编程渲染。比如说栅格化(Rasterization)和帧缓存(一说帧缓冲区)的操作(Framebuffer operations),在现阶段无法做到可编程。直到OpenGL4.4,着色器的种类增加到了6个。它们分别是顶点着色器、分格化控制着色器(Tessellation control shader)、分格化估值着色器(暂译)(Tessellation evaluation shader)、几何着色器(Geometry shader)、片断着色器以及计算着色器(Compute Shader)。除了顶点着色器和片断着色器这两个我们所熟知的外,另外四个是在OpenGL3.2版本后依次被引入的。下面是一个小的表格说明了这一点:

着色器

OpenGL版本

顶点着色器(Vertex shader)

>=2.0

片断着色器(Fragment shader)

>=2.0

几何着色器(Geometry shader)

>=3.2

分格化控制着色器(Tessellation control shader)

>=4.0

分格化估值着色器(Tessellation evaluation shader)

>=4.0

计算着色器(Compute Shader)

>=4.3

       乍一看学这些着色器的知识点肯定很多,实际上这些着色器是用的是统一的语法,它就是GLSL。OpenGL3.2以后GLSL的一些语法格式和OpenGL(ES)2.0的语法稍微有些不一样,这点需要注意。

       大多数人应该会使用Visual Studio开发OpenGL应用程序。于是如何开发OpenGL4.x应用就成了一个问题,因为微软的OpenGL兼容驱动只解析OpenGL1.1规范的函数。幸运的是sourceforge上的项目glew可以帮助我们解析以后版本的OpenGL函数。而glut是老牌的OpenGL开发库,因此glut+glew成为了大多数人的选择,另外可能有些人选择MFC+ glew+wgl,这也是可以的。

       不过跨平台开发库Qt给了我们另外的选择。Qt自从5.0以来,对OpenGL的支持越来越强,开发基于OpenGL的应用越来越容易,这其中KDAB功不可没。事实上KDAB也写了若干有关Qt+OpenGL开发的博客,大家可以访问下面网站看看:

http://www.kdab.com/

       对于自己,由于有Qt开发经验,因此使用Qt会是我的优先选择。以后的文章我会用Qt搭建一个简易的OpenGL4.x框架,在这个框架下探究OpenGL4.x开发技术。


OpenGL是一套充满活力的跨平台的渲染API。在2004年前,其不过是刚迈入了2.0版本,那时候Direct3D成为游戏开发者们最青睐的开发框架,HLSL已经被一些游戏所使用,而OpenGL才刚刚支持顶点着色器和片断着色器,人们都为此而感到担心——究竟OpenGL还有没有希望?OpenGL的出路到底在哪里?幸运的是随后的几年,OpenGL在khronos组织和其它巨头的推动下取得了长足的发展,版本很快迈向了3.0甚至是4.0。2012年OpenGL4.3发布,NVIDIA等厂商随即发布了支持它的驱动程序,当时我认为4.3应该是OpenGL的一个巅峰,不会频繁地更新了吧。就在去年,也就是2013年,OpenGL4.4发布,让我们知道了OpenGL仍然富有活力,不断前进。相比OpenGL的蓬勃迈进,Direct3D的步伐明显慢了许多。从Direct3D11随着Windows7的发布已经有了近5年,它的继任者却迟迟难产。即使Direct3D9有着大量的拥趸,对于一个刚入门的开发者来说,是跟还是不跟?我想大家心里都有自己的一杆秤。

原创文章,反对未声明的引用。原博客地址:http://blog.csdn.net/gamesdev/article/details/18995825

       我想OpenGL和同样是优秀的跨平台图形API——Qt的结合应该是一件非常有趣的事儿吧。有关Qt的介绍我已有大量的篇幅描述,有兴趣的搜搜我以前写的日志吧。OpenGL和Qt结合使用,简单快捷见效快。如果一定要说有什么缺点,我想应该是Qt的个头稍大并且可能对纯C的开发者并不友好,毕竟Qt是C++的开发框架。题外说一句,如果实在不喜欢Qt+OpenGL这一组合,那么使用wxWidgets+OpenGL也是可以的,我最近发现博客专栏里多出了介绍wxWidgets的专栏,有兴趣的话可以搜搜看。

       一个简单的OpenGL4.x框架,从我写的QWindow的子类:GLWindow开始说起。下面是类的声明:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class GLWindow: public QWindow,  
  2.         protected QOpenGLFunctions_4_3_Core  
  3. {  
  4.     Q_OBJECT  
  5. public:  
  6.     GLWindow( QWindow* pParent = Q_NULLPTR );  
  7. protected:  
  8.     void exposeEvent( QExposeEvent* pEvent );  
  9.     void InitGL( void );                    // 额外OpenGL的初始化  
  10.     void RenderGL( void );                  // OpenGL渲染相关  
  11.     void ResizeGL( int width, int height ); // 窗口大小改变时该如何应对  
  12. private:  
  13.     QOpenGLContext* m_pContext;  
  14. };  

Qt5.1以后,为了方便,添加了QOpenGLFunctions_x_y_z这样形式的类。这样好处是可以保证编译器这些函数的原型都能被找到而不至于出现编译错误。要是能够运行,还是的找能够支持该规范的显卡吧。这里重载了exposeEvent()函数以便进行初始化OpenGL渲染上下文m_pContext和进行实际的渲染。我们关注的则是InitGL()、RenderGL()和ResizeGL()这三个函数。以后部分我主要在这三个函数中做文章,以后可能还会添加ReleaseGL()这样释放内存的函数。

下面是GLWindow成员函数实现的前半部分:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /*---------------------------------------------------------------------------*/  
  2. GLWindow::GLWindow( QWindow* pParent ):  
  3.     QWindow( pParent ),  
  4.     m_pContext( Q_NULLPTR )  
  5. {  
  6.     setSurfaceType( OpenGLSurface );  
  7.     resize( 640, 360 );  
  8. }  
  9. /*---------------------------------------------------------------------------*/  
  10. void GLWindow::exposeEvent( QExposeEvent* pEvent )  
  11. {  
  12.     Q_UNUSED( pEvent );  
  13.   
  14.     if ( m_pContext == Q_NULLPTR )  
  15.     {  
  16.         QSurfaceFormat format;  
  17.         format.setVersion( 4, 3 );      // 需要OpenGL4.3版本  
  18.         format.setSamples( 4 );         // 四倍采样  
  19.   
  20.         m_pContext = new QOpenGLContext( this );  
  21.         m_pContext->setFormat( format );  
  22.         m_pContext->create( );  
  23.         m_pContext->makeCurrent( this );  
  24.   
  25.         initializeOpenGLFunctions( );  
  26.         InitGL( );  
  27.     }  
  28.     ResizeGL( width( ), height( ) );  
  29.   
  30.     RenderGL( );  
  31.     m_pContext->swapBuffers( this );  
  32. }  

这里我们看到,通过QSurfaceFormat、QOpenGLContext等类,我们初始化好了OpenGL的环境。相关的函数参见Qt文档,这里为了能正确地解析OpenGL4.4的函数指针,需要调用initializeOpenGLFunctions()这个函数。

       下面就是GLWindow实现的剩余部分:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /*---------------------------------------------------------------------------*/  
  2. void GLWindow::InitGL( void )  
  3. {  
  4.   
  5. }  
  6. /*---------------------------------------------------------------------------*/  
  7. void GLWindow::ResizeGL( int width, int height )  
  8. {  
  9.     Q_UNUSED( width );  
  10.     Q_UNUSED( height );  
  11. }  
  12. /*---------------------------------------------------------------------------*/  
  13. void GLWindow::RenderGL( void )  
  14. {  
  15.     static const GLfloat red[] = { 1.0f, 0.0f, 0.0f, 1.0f };  
  16.     glClearBufferfv(GL_COLOR, 0, red );  
  17. }  
  18. /*---------------------------------------------------------------------------*/  

       第一个程序实现非常简单,我们在RenderGL()这个函数中调用glClearBufferfv()函数,清除了颜色缓存(缓冲区),填充的颜色是100%不透明的红色,其余我们什么也没做。

       程序能够顺利地编译。在我的开发环境(Windows8+Qt5.2+QtCreator3.0)中没有出现问题。运行的时候可能有些不一样。如果没有安装好最新的显卡驱动程序,那么可能无法运行该程序,好在我们使用的唯一的gl类函数glClearBufferfv它是自OpenGL3.0引入的,并不是来自最高的OpenGL4.4,有些计算机虽然不能支持OpenGL4.4,但应该也能运行。这里有两个程序可以判定当前计算机最高支持多少OpenGL规范:一个是qglinfo,它是Qt3D库的实用工具;另外一个是glewinfo,它来自于glew。下面是程序的运行截图:

       源代码下载地址:这里


OpenGL4.x是OpenGL系列最新的规范,它为此制定了很多的概念,包括6种着色器、以及诸多内存对象,让简单的三维显示变得不简单。我也是刚刚了解这些新的特性,所以有的地方阐述不正确还请高手们指出来。

原创文章,反对未声明的引用。原博客地址:http://blog.csdn.net/gamesdev/article/details/19505817

       程序源代码下载地址:这里

这次我将沿用上篇文章的框架,在此基础上添加着色器的载入和使用、创建和绑定VAO以及绘制最简单的图元——点。我使用的框架是Qt,而从Qt5.2起,对OpenGL的封装算是很全面了,因此程序可以使用Qt封装的C++风格的类进行实现。不过我打算使用裸的OpenGL的API,因为这更容易被那些使用其他框架开发的同行们理解。

       下面是例子程序的核心代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /*---------------------------------------------------------------------------*/  
  2. // 这里存放了一些全局变量  
  3. GLuint g_VertexShader = 0;  
  4. GLuint g_FragmentShader = 0;  
  5. GLuint g_Program = 0;  
  6. GLuint g_VertexArray = 0;  
  7. /*---------------------------------------------------------------------------*/  
  8. inline QByteArray ShaderData( const QString& fileName )  
  9. {  
  10.     QByteArray result;  
  11.     QFile shaderFile( fileName );  
  12.     if ( shaderFile.open( QIODevice::ReadOnly ) )  
  13.     {  
  14.         result = shaderFile.readAll( );  
  15.         shaderFile.close( );  
  16.     }  
  17.     return result;  
  18. }  
  19. /*---------------------------------------------------------------------------*/  
  20. void GLWindow::InitGL( void )  
  21. {  
  22.     // 初始化顶点着色器  
  23.     QByteArray vsData = ShaderData( ":/Shader.vert" );  
  24.     const char* vsPointer = vsData.constData( );  
  25.     g_VertexShader = glCreateShader( GL_VERTEX_SHADER );  
  26.     glShaderSource( g_VertexShader,                             // 着色器标识  
  27.                     1,                                          // 着色器文本数目  
  28.                     &vsPointer,                                 // 着色器字符串指针  
  29.                     Q_NULLPTR );                                // 字符串长度  
  30.     glCompileShader( g_VertexShader );  
  31.   
  32.     // 初始化片断着色器  
  33.     QByteArray fsData = ShaderData( ":/Shader.frag" );  
  34.     const char* fsPointer = fsData.constData( );  
  35.     g_FragmentShader = glCreateShader( GL_FRAGMENT_SHADER );  
  36.     glShaderSource( g_FragmentShader,                           // 着色器标识  
  37.                     1,                                          // 着色器文本数目  
  38.                     &fsPointer,                                 // 着色器字符串  
  39.                     Q_NULLPTR );                                // 字符串长度  
  40.     glCompileShader( g_FragmentShader );  
  41.   
  42.     // 初始化着色器程序  
  43.     g_Program = glCreateProgram( );  
  44.     glAttachShader( g_Program, g_VertexShader );  
  45.     glAttachShader( g_Program, g_FragmentShader );  
  46.     glLinkProgram( g_Program );  
  47.   
  48.     // 删除着色器  
  49.     glDeleteShader( g_VertexShader );  
  50.     glDeleteShader( g_FragmentShader );  
  51.   
  52.     // 初始化顶点数组对象(VAO)  
  53.     glGenVertexArrays( 1, &g_VertexArray );  
  54.     glBindVertexArray( g_VertexArray );  
  55.     glPointSize( 10.0f );  
  56. }  
  57. /*---------------------------------------------------------------------------*/  
  58. void GLWindow::ResizeGL( int width, int height )  
  59. {  
  60.     glViewport( 0, 0, width, height );  
  61. }  
  62. /*---------------------------------------------------------------------------*/  
  63. void GLWindow::RenderGL( void )  
  64. {  
  65.     static const GLfloat black[] = { 0.0f, 0.0f, 0.0f, 1.0f };  
  66.     glClearBufferfv( GL_COLOR, 0, black );  
  67.   
  68.     glUseProgram( g_Program );  
  69.     glDrawArrays( GL_POINTS, 0, 1 );  
  70. }  
  71. /*---------------------------------------------------------------------------*/  
  72. void GLWindow::ReleaseGL( void )  
  73. {  
  74.     glDeleteVertexArrays( 1, &g_VertexArray );  
  75.     glDeleteProgram( g_Program );  
  76. }  

程序在InitGL()函数中创建了顶点着色器和片断着色器对象并且用着色器程序将其链接起来。最后初始化了顶点数组对象(Vertex Array Object,VAO),并且绑定之,以及设定了点的大小。而顶点着色器和片断着色器在本例中也非常的简单:

Shader.vert

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #version 430 core  
  2.   
  3. void main( void )  
  4. {  
  5.     gl_Position = vec4( 0.0, 0.0, 0.0, 1.0 );  
  6. }  
Shader.frag
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #version 430 core  
  2.   
  3. out vec4 color;  
  4.   
  5. void main( void )  
  6. {  
  7.     color = vec4( 1.0, 1.0, 1.0, 1.0 );  
  8. }  

       顶点着色器中指明了绘制图元的位置在三维点( 0, 0, 0 )上,片断着色器中表示绘制的颜色是不透明的白色。与OpenGL(ES) 2.x不同,着色器不再用varying来表示从某一着色器到另外一着色器传递数据,而是使用in表示传入数据,out表示数据传入下一阶段。以下是程序的演示效果:


OpenGL很多人都有所了解,也有很多人使用OpenGL开发过工业级的应用和游戏应用,不过它的最新版本OpenGL4.x了解的人就不是太多了。目前OpenGL的最新版本是4.4,并且NVIDIA和AMD厂商发布了支持OpenGL最新版本的驱动程序,我想基于OpenGL4.x讲解的书籍也会逐渐增多。

原创文章,反对未声明的引用。原博客地址:http://blog.csdn.net/gamesdev/article/details/19506553

       程序源代码下载地址:这里

       这回我将上一次的代码稍微作了一下修改,绘制出了一个三角形。其实代码也非常简单,在Client端将GL_POINTS改为GL_TRIANGLES就可以了,片断着色器也不需要做更多的修改,主要是顶点着色器。

下面是顶点着色器的内容:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #version 430 core  
  2.   
  3. void main( void )  
  4. {  
  5.     // 根据传入的ID使用不同gl_Position  
  6.     const vec4 vertices[3] = vec4[3](  
  7.                 vec4( 0.25, -0.25, 0.5, 1.0 ),  
  8.                 vec4( -0.25, -0.25, 0.5, 1.0 ),  
  9.                 vec4( 0.25, 0.25, 0.5, 1.0 ) );  
  10.     gl_Position = vertices[gl_VertexID];  
  11. }  

       这里注意的是gl_VertexID是一个在OpenGL中是一个内置的变量,每个顶点运行它时它们的gl_VertexID都不一样。我们利用这样的特点,将此作一个索引,得到不同的gl_Position,用以传入下一阶段——栅格化(Rasterize),这样一个三角形就得以显示出来了。

下面是演示程序的截图:



from :http://blog.csdn.net/fu851523125/article/details/51585641


0 0