<style type="text/css">
<!--
@page
{margin:2cm}
pre.cjk
{font-family:"DejaVu Sans Condensed",monospace}
p
{margin-bottom:0.21cm}
-->
</style>
基于glut的OpenGL框架(二)
——加入键盘控制
前面一篇文章我向大家介绍了我自己制作的基于glut的OpenGL框架。接下来我们要加入交互的内容,说到交互,其实就是键盘的控制了。我们会在这一次体会到面向对象机制给我们带来的巨大便利以及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个按键,而一个字节最多可以保存28即256中状态。所以就定义了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 键盘事件的实现
// 19时35分01秒 最后编辑
#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()函数对按下的按键置1,KeyUp()函数对弹出的按键置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语句,表示对按键b和x的处理。这里注意,B和b的意义不一样,要响应B键,要在CapsLock键按下才有效。此外,当我们按下x键后,将会退出程序。不必担心deletethis语句,因为在这条语句后,我们不会访问this下的任何数据成员,我们会直接调用exit(0
)退出。
最后,让我们再看看main.cpp是什么样子吧。
// main.cpp
// 11时08分52秒 最后编辑
#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>
(程序源代码下载地址在这里)
分享到:
相关推荐
在Windows系统中,配置GLUT库:解压并打开文件夹glut.zip,取出glut.h,glut32.lib,glut32.dll。之后有两种配置方式,一是将以上3个文件分别放在系统盘的相应目录下;二是针对具体项目(本次实验给定项目Ex1)进行...
前面一篇文章我向大家介绍了我自己制作的基于glut的OpenGL框架。接下来我们要加入交互的内容,说到交互,其实就是键盘的控制了。我们会在这一次体会到面向对象机制给我们带来的巨大便利以及glut给我们带来的诸多方便...
内容包含了glm glut 等库,用vs2008 基于OpenGL 4.0 ,可以直接运行,包含所有源码,里面含有很多使用性技巧
基于glut框架的opengl实现的坦克世界,支持坦克在高度场内漫游,子弹根据坦克姿势计算发射角度,基于billboard绘制了树,坦克击中地面有粒子效果,子弹击中树树会倒下,包含坦克与树、天空盒的碰撞检验。
基于AEL(活化边表)的扫描线填充算法的OpenGL实现。该算法包含一个基于GLUT的事件捕获框架用于绘制多边形。
二、MFC中的OpenGL基本框架 1、首先创建工程 用AppWizard产生一个MFC EXE项目,其他默认即可。 2、将此工程所需的OpenGL文件和库加入到工程中 在工程菜单中,选择"Build"下的"Settings"项。单击"Link"标签...
用途:此程序为《计算机图形学》课程设计,作用和画图类似。...代码框架:代码主要分为MainWindow和OpenGLWindow两个大类,前者是UI界面及交互相关,后者为画板图形处理相关。 开发工具:Qt5,OpenGL-glut
GBOX是一个用c语言实现的多平台图形渲染库 ... 目前基于opengl的渲染设备,绘制tiger.svg可达到60fps 基于bitmap的纯算法渲染设备,绘制tiger.svg可达到30-40fps(pc上测试,数据仅供参考) 标签:GBOX
DelFEM2是用于几何处理和FEM仿真的端到端框架,涵盖了范围广泛的组件,包括形状编辑,网格划分,FEM仿真,线性求解器,变体网格变形器和可视化。 DelFEM2旨在成为一种交互式数字工程和创作工具。 在以下位置有...
用于基于空气吉他的应用程序的框架。 代码设置...-语言:C ++-使用的IDE:Microsoft Visual Studio 2005-库:FMOD,OpenGL + GLUT和DsVideoLib