`
jgsj
  • 浏览: 963808 次
文章分类
社区版块
存档分类
最新评论

基于glut的OpenGL框架(一)

 
阅读更多
<style type="text/css"> <!-- @page {margin:2cm} pre.cjk {font-family:"DejaVu Sans Condensed",monospace} p {margin-bottom:0.21cm} --> </style>

基于glutOpenGL框架(一)

——面向对象框架的搭建

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

首先是我的main.cpp文件:

// main.cpp
// 110852 最后编辑
#include "GLWidget.h"
// 宽屏的程序要求纵横比16:9,我们指定高,宽就出来了。
#define _WINDOW_HEIGHT_     360
#define _WINDOW_WIDTH_      _WINDOW_HEIGHT_ * 16 / 9
static 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;
}

我的目标是制作一个宽高比为169的程序,暂时不考虑全屏。为什么是169呢?因为大多数计算机显示屏都是169,宽屏的显示器越来越多,方屏的显示器越来越少,我们学校里面今年来也都换成了宽屏的显示屏,而且我工作的游戏公司在制作游戏时的标准也是按照169制作的。适配方屏屏幕时上下空余部分自动变成黑色。所以我们在定义窗口大小时,只需指定高,宽可以让预编译器自动计算出来。这里我默认了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()函数保存了传入的宽和高,并且规定了清除屏幕时使用的颜色是不透明的黑色(R0G0B0A255)。这里介绍Reshape()函数。假设用户对窗口改变了形状,这是传入的widthheight就是新的窗口宽和高,我们用aspectRatio来保存宽高比,

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


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

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

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


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

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

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

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

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


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


分享到:
评论

相关推荐

    基于 C++ OpenGL框架实现五角星绘制【100010595】

    在Windows系统中,配置GLUT库:解压并打开文件夹glut.zip,取出glut.h,glut32.lib,glut32.dll。之后有两种配置方式,一是将以上3个文件分别放在系统盘的相应目录下;二是针对具体项目(本次实验给定项目Ex1)进行...

    glut实现连续按键处理

    前面一篇文章我向大家介绍了我自己制作的基于glut的OpenGL框架。接下来我们要加入交互的内容,说到交互,其实就是键盘的控制了。我们会在这一次体会到面向对象机制给我们带来的巨大便利以及glut给我们带来的诸多方便...

    OpenGL 4.0 GLSL 框架

    内容包含了glm glut 等库,用vs2008 基于OpenGL 4.0 ,可以直接运行,包含所有源码,里面含有很多使用性技巧

    opengl实现的坦克世界

    基于glut框架的opengl实现的坦克世界,支持坦克在高度场内漫游,子弹根据坦克姿势计算发射角度,基于billboard绘制了树,坦克击中地面有粒子效果,子弹击中树树会倒下,包含坦克与树、天空盒的碰撞检验。

    扫描线填充算法的OpenGL实现

    基于AEL(活化边表)的扫描线填充算法的OpenGL实现。该算法包含一个基于GLUT的事件捕获框架用于绘制多边形。

    基于MFC的OpenGL绘图.doc

    单击"Link"标签,选择"General"目录,在Object/Library Modules的编辑框中输入"opengl32.lib glu32.lib glut.lib glaux.lib"(注意,输入双引号中的内容,各个库用空格分开;否则会出现链接错误),选择"OK"结束。...

    c语言跨平台图形渲染库GBOX.zip

    GBOX是一个用c语言实现的多平台图形渲染库 ... 目前基于opengl的渲染设备,绘制tiger.svg可达到60fps 基于bitmap的纯算法渲染设备,绘制tiger.svg可达到30-40fps(pc上测试,数据仅供参考) 标签:GBOX

    基于QT(C++)实现画图工具(计算机图形学课程设计)【100013282】

    用途:此程序为《计算机图形学》课程设计,作用和画图类似。...代码框架:代码主要分为MainWindow和OpenGLWindow两个大类,前者是UI界面及交互相关,后者为画板图形处理相关。 开发工具:Qt5,OpenGL-glut

    delfem2:用C ++编写的物理模拟研究原型框架

    DelFEM2是用于几何处理和FEM仿真的端到端框架,涵盖了范围广泛的组件,包括形状编辑,网格划分,FEM仿真,线性求解器,变体网格变形器和可视化。 DelFEM2旨在成为一种交互式数字工程和创作工具。 在以下位置有...

    Air Guitar Framework-开源

    用于基于空气吉他的应用程序的框架。 代码设置...-语言:C ++-使用的IDE:Microsoft Visual Studio 2005-库:FMOD,OpenGL + GLUT和DsVideoLib

Global site tag (gtag.js) - Google Analytics