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

基于glut的OpenGL框架(二)

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

基于glutOpenGL框架(二)

——加入键盘控制

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

组件化glut按键处理功能,这是一个好主意。在这个想法下,我将glut按键处理的功能写到一个类,叫作KeyEvent。下面是我们这个类的定义:

#ifndef KEYEVENT_H
#define KEYEVENT_H
#define _MAX_KEY_NUM_   256
class KeyEvent
{
public:
    KeyEvent( void );
    void KeyDown( int key );
    void KeyUp( int key );
protected:
    char m_KeyState[_MAX_KEY_NUM_];
};
#endif // KEYEVENT_H

这个类非常简单,包含了一个构造函数、按键按下时的函数以及按键弹上时的函数。私有成员为256个按键。定义_MAX_KEY_NUM_256是因为考虑键盘一般有108个按键,而一个字节最多可以保存28256中状态。所以就定义了256。以后为了适应不同的情况,可以将这个宏的值进行改变。

接下来我要向大家展示一个程序,并且希望大家能够编译运行再测试一下这个程序,看它的按键响应效果。(来自《OpenGL超级宝典》)

// Points.c
// OpenGL SuperBible
// Demonstrates OpenGL Primative GL_POINTS
// Program by Richard S. Wright Jr.
#include <GL/glut.h>
#include <math.h>
// Define a constant for the value of PI
#define GL_PI 3.1415f
// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
// Called to draw scene
void RenderScene(void)
{
    GLfloat x,y,z,angle; // Storeage for coordinates and angles
    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT);
    // Save matrix state and do the rotation
    glPushMatrix();
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    // Call only once for all remaining points
    glBegin(GL_POINTS);
    z = -50.0f;
    for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f)
    {
        x = 50.0f*sin(angle);
        y = 50.0f*cos(angle);
        // Specify the point and move the Z value up a little
        glVertex3f(x, y, z);
        z += 0.5f;
    }
    // Done drawing points
    glEnd();
    // Restore transformations
    glPopMatrix();
    // Flush drawing commands
    glutSwapBuffers();
}
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
    // Black background
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
    // Set drawing color to green
    glColor3f(0.0f, 1.0f, 0.0f);
}
void SpecialKeys(int key, int x, int y)
{
    if(key == GLUT_KEY_UP)
        xRot-= 5.0f;
    if(key == GLUT_KEY_DOWN)
        xRot += 5.0f;
    if(key == GLUT_KEY_LEFT)
        yRot -= 5.0f;
    if(key == GLUT_KEY_RIGHT)
        yRot += 5.0f;
    if(key > 356.0f)
        xRot = 0.0f;
    if(key < -1.0f)
        xRot = 355.0f;
    if(key > 356.0f)
        yRot = 0.0f;
    if(key < -1.0f)
        yRot = 355.0f;
    // Refresh the Window
    glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
    GLfloat nRange = 100.0f;
    // Prevent a divide by zero
    if(h == 0) h = 1;
    // Set Viewport to window dimensions
    glViewport(0, 0, w, h);
    // Reset projection matrix stack
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    // Establish clipping volume (left, right, bottom, top, near, far)
    if (w <= h)
        glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
    else
        glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
    // Reset Model view matrix stack
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutCreateWindow("Points Example");
    glutReshapeFunc(ChangeSize);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);
    SetupRC();
    glutMainLoop();
    return 0;
}

运行一下,你会看到是一个螺线形(圆形),截图如下:


按上下左右可以移动螺线形,但是的细心的同学可能发现了,如果你按紧左键,它会连续地运动,但是其中有一个小小的停顿。这是由于这种机制下的按键是按照按下按键的ASCII码来响应的。不信大家可以打开gedit或者notepad,按紧a,你会发现第一个a和第二个a出现中间有一段停顿,而后面的a则较连贯地显示出来。这样的效果是不符合大多数游戏的交互体验的,而正是这样,我才开始了新的摸索,好在glut还提供了这样一个函数:glutKeyboardUpFunc,它可以对按键弹上进行回调。glutKeyboardUpFunc()函数的声明是这样的:

FGAPI void FGAPIENTRY glutKeyboardUpFunc( void (* callback)( unsigned char, int, int ) );


有了这个函数,我们就可以知道玩家按下一个按键有多久,可以实现一些游戏当中才能出现的按键效果,比如组合键、蓄气、二倍速跑、二段跳等。了解了这么多,我开始介绍KeyEvent类的实现了。

// KeyEvent.cpp 键盘事件的实现
// 193501 最后编辑
#include <cstring>
#include "KeyEvent.h"
KeyEvent::KeyEvent( void )
{
    // 初始化这些按键
    using namespace std;
    memset( m_KeyState, 0, _MAX_KEY_NUM_ );
}
void KeyEvent::KeyDown( int key )
{
    m_KeyState[key] = 1;
}
void KeyEvent::KeyUp( int key )
{
    m_KeyState[key] = 0;
}

在实现中,我们对108个按键的状态进行标识,以便使用的时候读取。KeyDown()函数对按下的按键置1KeyUp()函数对弹出的按键置0

现在将我们的按键功能集成到GLWidget类中吧。当然是使用继承啦。

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

以上是GLWidget类的定义,我们看到,该类继承自KeyEvent类,表示我们108个按键的状态都可以在GLWidget类的内部使用。此外定义了一个成员函数ProcessKey(),它专门用来处理按键的响应。

以下是GLWidget.cpp的实现:

// GLWidget.cpp 包含了控件的使用
// 11:14:27 最后编辑
#include "GLWidget.h"
GLWidget::GLWidget( void )
{
    // 构造函数的代码在这里
    m_Width     = 0.0;
    m_Height    = 0.0;
    m_X2 = 160.0f;
}
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 )
{
    // 渲染代码
    glColor3ub( 255, 0, 0 );
    glRectf( 0.0f, 0.0f, m_X2, 180.0f );
    glRectf( 160.0f, 260.0f, 180.0f, 280.0f );
}
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( );
}
void GLWidget::ProcessKey( void )
{
    // 注意:这里大写的B和小写的b是不一样的。如果你开启了caps lock键,那么按下b键就会有效,否则无效。
    if ( m_KeyState['b'] )
    {
        m_X2 += 0.5f;
    }
    if ( m_KeyState['x'] )
    {
        delete this;
        exit( 0 );
    }
}

前面的函数我在第一节和大家都介绍了,下面我介绍一下ProcessKey()函数。ProcessKey()函数体内包含了两条if语句,表示对按键bx的处理。这里注意,Bb的意义不一样,要响应B键,要在CapsLock键按下才有效。此外,当我们按下x键后,将会退出程序。不必担心deletethis语句,因为在这条语句后,我们不会访问this下的任何数据成员,我们会直接调用exit(0 )退出。

最后,让我们再看看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 );
    // 处理按键
    pWidget->ProcessKey( );
    // 如果有必要的话,让其更新
    glutPostRedisplay( );
}
void KeyDown( unsigned char key, int, int )
{
    assert( pWidget != 0 );
    pWidget->KeyDown( key );
}
void KeyUp( unsigned char key, int, int )
{
    assert( pWidget != 0 );
    pWidget->KeyUp( key );
}
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 );             // 重改变形状函数
    //glutSpecialFunc( Special );             // 特殊函数
    glutKeyboardFunc( KeyDown );            // 键盘按下函数
    glutKeyboardUpFunc( KeyUp );            // 键盘按上函数
    glutIdleFunc( Idle );                   // 空转时运行的函数
    pWidget->Init( _WINDOW_WIDTH_, _WINDOW_HEIGHT_ );
    glutMainLoop( );
    return 0;
}


实现连续响应按键的关键一步,就是将响应按键的内容放入Idle()函数中,而Idle()函数在程序空转的时候执行按键的响应和画面的渲染。此外,在按键按下的时候,我们通过调用GLWidget::KeyDown()GLWidget::KeyUp()函数记录下按键的状态,从而保证按键状态是最新的。

好了,激动人心的一刻到来了,我们要运行我们的源程序,看看它对于响应按键的强大吧。


我们按下b键,可以看到下面红色矩形逐渐变宽,这种变化是实时的。而按下x键则退出程序。

<style type="text/css"> <!-- @page {margin:2cm} p {margin-bottom:0.21cm} --> </style>

(程序源代码下载地址在这里

分享到:
评论

相关推荐

    基于 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

    二、MFC中的OpenGL基本框架  1、首先创建工程  用AppWizard产生一个MFC EXE项目,其他默认即可。  2、将此工程所需的OpenGL文件和库加入到工程中  在工程菜单中,选择"Build"下的"Settings"项。单击"Link"标签...

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

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

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

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

    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