首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

基于glut的OpenGL框架(1)

2012-11-07 
基于glut的OpenGL框架(一)基于glut的OpenGL框架(一)——面向对象框架的搭建我是一名OpenGL的初学者,在看完《O

基于glut的OpenGL框架(一)

基于glut的OpenGL框架(一)

——面向对象框架的搭建

我是一名OpenGL的初学者,在看完《OpenGL超级宝典》的前几章后,开始迫不及待地动手验证一下书上程序的代码了。我发现书上的例子程序是用C语言描述的,虽然简单,但却无法在此基础上进行扩展,即使扩展了也仅仅是添加了几个函数并且调用之,没有什么层次性。所以我开始利用C++和已经形成了标准的glut框架编写自己的OpenGL框架。

首先是我的main.cpp文件:

// main.cpp// 11时08分52秒 最后编辑#include "GLWidget.h"// 宽屏的程序要求纵横比16:9,我们指定高,宽就出来了。#define _WINDOW_HEIGHT_     360#define _WINDOW_WIDTH_      _WINDOW_HEIGHT_ * 16 / 9static GLWidget* pWidget = 0;void Reshape( int x, int y ){    assert( pWidget != 0 );    pWidget->Reshape( x, y );}void Render( void ){    glClear( GL_COLOR_BUFFER_BIT );           // 用黑色清屏    glColor3ub( 255, 255, 255 );    glRecti( 0, 0, _WINDOW_WIDTH_, _WINDOW_HEIGHT_ );// 绘制白色的矩形背景    // 执行widget里的绘图函数    assert( pWidget != 0 );    pWidget->Render( );    // 交换缓存    glutSwapBuffers( );}void Idle( void )       // 空转时候运行的函数{    assert( pWidget != 0 );    // 如果有必要的话,让其更新    glutPostRedisplay( );}int main( int argc, char** argv ){    // 初始化控件类    pWidget = new GLWidget;    glutInit( &argc, argv );    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );    glutInitWindowSize( _WINDOW_WIDTH_, _WINDOW_HEIGHT_ );    glutCreateWindow( "Simple Object" );    glutDisplayFunc( Render );              // 渲染函数    glutReshapeFunc( Reshape );             // 重改变形状函数    glutIdleFunc( Idle );                   // 空转时运行的函数    pWidget->Init( _WINDOW_WIDTH_, _WINDOW_HEIGHT_ );    glutMainLoop( );    return 0;}

我的目标是制作一个宽高比为16:9的程序,暂时不考虑全屏。为什么是16:9呢?因为大多数计算机显示屏都是16:9,宽屏的显示器越来越多,方屏的显示器越来越少,我们学校里面今年来也都换成了宽屏的显示屏,而且我工作的游戏公司在制作游戏时的标准也是按照16:9制作的。适配方屏屏幕时上下空余部分自动变成黑色。所以我们在定义窗口大小时,只需指定高,宽可以让预编译器自动计算出来。这里我默认了640×480的分辨率。

用面向对象的方法编写OpenGL程序自然要用到类。这里我使用了自定义的GLWidget类,稍后我会对这个类进行介绍。这里我创建了一个全局静态指针(静态是因为它仅在main.cpp文件中使用)。

接下来则是一些回调函数的定义了,Reshape()函数作用是对窗口大小更改是我们该怎样应对,我们将在GLWidget::Reshape()里面介绍。Render()函数作用是进行渲染,这里我们用不透明的黑色清除了屏幕,并且使用一个白色的矩形填充了以后需要渲染的区域。随后的绘制就在GLWidget::Render()里面完成了。Idle()函数我认为是一个非常重要的函数了。记得我在编写DirectX相关程序时,自己就要写消息循环体,其中就有如果窗口没有任何消息时,我们该做什么。我和很多书上的做法是一样的,强制让程序渲染。这里也是一样的,用glPostRedisplay()函数告诉OpenGL,下一个循环一定要调用Render()函数进行渲染。

为了防止错误地使用了空指针,我们使用assert()函数来假定不会访问出错。assert.h被包含在GLWidget.h中。

在main.cpp函数中我们用new创建了一个对象。我们有一些步骤可以放在构造函数里面做(比如说验证版本的一致啊,更新啦……扯远了),随后是初始化glut的相关组件并且注册回调函数,在初始化GLWidget控件之后glutMainLoop()表示开始了游戏的循环。

循环之后就是return0;,随后程序结束。但是我想告诉大家,这里有一个隐含的内存泄漏,因为glutMainLoop()函数没有办法跳出。起初我认为是有办法退出glut的循环的,但是在我看了这篇文章:

OpenGL内存泄漏之主循环函数glutMainLoop()

之后,我发现没法跳出循环了!由于各个glut的实现不一样,在试了原作者的方法后,我发现没有找到相关的函数。所以我索性后面不写对pWidget空间的释放了,在需要退出程序的时候,我们这样调用:deletepWidget; exit( 0 );。这样也能不造成内存泄漏。

随后介绍的就是GLWidget类,它维持着我们需要处理OpenGL图形的相关代码。接下来就是GLWidget类的定义:

#ifndef GLWIDGET_H#define GLWIDGET_H#include <assert.h>#include <GL/glut.h>class GLWidget{public:    GLWidget( void );    ~GLWidget( void );    void Init( int width, int height );    void Release( void );    void Render( void );    void Reshape( int width, int height );              // 重新改变窗口大小private:    GLdouble m_Width, m_Height;    GLdouble m_AspectRatio;};#endif // GLWIDGET_H

这里定义了构造函数、析构函数、初始化、释放空间、渲染和重新改变大小的函数。私有变量有初始化时候的宽和高,以及保存的宽高比。接下来我们看GLWidget.cpp文件。

// GLWidget.cpp 包含了控件的使用// 11:14:27 最后编辑#include "GLWidget.h"GLWidget::GLWidget( void ){    // 构造函数的代码在这里    m_Width     = 0.0;    m_Height    = 0.0;}GLWidget::~GLWidget( void ){    Release( );}void GLWidget::Init( int width, int height ){    // 保存初始化时窗口的宽和高    m_Width = GLdouble( width );    m_Height = GLdouble( height );    m_AspectRatio = m_Width / m_Height;    // 初始化代码    glClearColor( 0.0, 0.0, 0.0, 1.0 );}void GLWidget::Render( void ){    // 渲染代码}void GLWidget::Release( void ){    // 释放空间代码}void GLWidget::Reshape( int width, int height ){    // 改变大小时程序如何应对?    GLdouble aspectRatio = GLdouble( width ) / GLdouble( height );    // 设置视口    if ( aspectRatio < m_AspectRatio )    {        GLint smallHeight = GLint( GLdouble( width ) / m_AspectRatio );        GLint heightBlank = ( GLint( height ) - smallHeight ) / 2;        glViewport( 0, heightBlank, GLint( width ), smallHeight );    }    else    {        GLint smallWidth = GLint( GLdouble( height ) * m_AspectRatio );        GLint widthBlank = ( GLint( width ) - smallWidth ) / 2;        glViewport( widthBlank, 0, smallWidth, GLint( height ) );    }    glMatrixMode( GL_PROJECTION );    glLoadIdentity( );    // 设置裁剪区域(左右下上近远)    glOrtho( 0.0, m_Width, 0.0, m_Height, -10.0, 10.0 );    // 为模型视图载入标准矩阵    glMatrixMode( GL_MODELVIEW );    glLoadIdentity( );}

构造函数和析构函数就介绍了。Init()函数保存了传入的宽和高,并且规定了清除屏幕时使用的颜色是不透明的黑色(R:0G:0B:0A:255)。这里介绍Reshape()函数。假设用户对窗口改变了形状,这是传入的width和height就是新的窗口宽和高,我们用aspectRatio来保存宽高比,

随后我们就要进行判断了。如果当前的宽高比比我们期望的宽高比小,比如说这种情况:


基于glut的OpenGL框架(1)

我们这个时候让中间白色场景的高为smallHeight,两边黑框的高为heightBlank,计算之,并且设置视口(viewport,即我们能绘制的区域)。GlViewport函数的原型为:

glViewport( GLint x, GLint y, GLsizei width, GLsizei height );

第一、二个参数是屏幕的起始坐标,后面的两个参数是从这个坐标开始延伸的宽和高。注意这里遵循屏幕坐标系。所以我们填入了glViewport(0, heightBlank, GLint( width ), smallHeight );。如果前的宽高比比我们期望的宽高比大,如这样:

基于glut的OpenGL框架(1)

同理分析,这里就不介绍了。

然后设置投影矩阵为单位矩阵(重置投影矩阵),并且使用glOrtho()函数设置正交投影空间,glOrtho()函数的原型是这样的:

glOrtho( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val );

这里设定了左、右、下、上、近、远六个参数,就像一个长方体一样。如果要显得有三维的感觉,使用gluPerspective()函数设置透视投影空间,它看起来很像金字塔削去了尖头剩下的部分。这里不详细介绍gluPerspective()函数了,由于我们主要处理二维图形,因此near_val和far_val就简单地设为-10.0和10.0了。最后让模型矩阵为单位矩阵。

编译成功,链接,在g++下需要加上-lglut这个选项。如果你能顺利地运行,那么太好了,我的小程序能在你的机子上运行了。运行的时候是不是这个结果啊?下面是我在Ubuntu下的截图:

基于glut的OpenGL框架(1)

是一个空白,宽和高分别是640和360。在随后的介绍中我们将会在这里绘制一些简单的图形。相信你一定觉得这个例子很简单!


热点排行