OpenGL (1) 入门
来源:互联网 发布:逐鹿seo军刀 编辑:程序博客网 时间:2024/04/30 09:21
前几天跟着师兄做东西,“被迫”学习了一下OpenGl,这几天又要开始忙着作业啊,考试啊,什么的,赶紧抓个空把学到的东西记录下来,不然一个不小心就“丢”了。
首先讨论一下开发环境配置问题,需要一些DLL、头文件、LIB文件,这些东西网上应该都有:
1) GLU.DLL、 GLUT.DLL、GLUT32.DLL
2) GL.H、GLAUX.H、GLEXT.H、GLU.H、GLUT.H、WGLEXT.H
3) GLAUX.LIB、GLU32.LIB、GLUT32.LIB、OPENGL32.LIB
由于OpenGL是跨平台的,因此上面的是Win平台下需要的东西。其实GLUT是一个工具包,是NateRobin开发的,他的主页上有非常详细的教程。GLUT对Win的窗口、事件相关的API进行了封装,至少对于初学的我来说用起来十分舒服,而且它也被大家广泛使用的。
当然,我更喜欢原滋原味的东西,因此还是要看看Nehe的教程的。
不说废话了,就以我目前做的一个小例子来说说OpenGL的基本框架吧,当然这里面没有用到光照、雾、纹理等高级的东西,不过有涉及到拾取。
在VS2008新建一个控制台程序,源代码如下:
#include<Windows.h>
#include<gl/glut.h>
#include<math.h>
#include<memory.h>
注意这里只需要有GL/GLUT.H就够了,因为其内部包含了对OPENGL的头文件的正确引用。
#define BLACK_F 0.0, 0.0, 0.0
#define RED_F 1.0, 0.0, 0.0
#define GREEN_F 0.0, 1.0, 0.0
#define YELLOW_F 1.0, 1.0, 0.0
#define BLUE_F 0.0, 0.0, 1.0
#define WHITE_F 1.0, 1.0, 1.0
定义了浮点数形式的RGB数值,只是为了编程方便~ “*_F”这种表示方法是模仿OpenGL的,因为OpenGL里的很多函数是根据参数的数据类型来命名最后一个的譬如glColor3f(),glColor3d(),分别表示参数为GLfloat和GLdouble类型,但是其功能是一模一样的。
#define CUBE_NAME 1
#define PI 3.1415926
#define CUBE_SIZE 2.0
#define HEIGHT 600
#define WIDTH 800
#define SELECT_BUFF_SIZE 512
GLint wndWidth, wndHeight;
全局的变量用来记录当前的窗口宽和高
GLfloat angle = 0.0;
//
// Rotate Axis
//
GLfloat axis[ 3 ] = { 0.0, 0.0,0.0 };
//
// Mouse location
//
GLfloat lastPos[ 3 ], currentPos[ 3 ];
//
// CameraProperties
//
GLfloat eye[3] = { 0.0, 0.0, 4.0};
GLfloat at[3] = { 0.0, 0.0, 0.0};
GLfloat up[3] = { 0.0, 1.0, 0.0};
GLfloat lastMatrix[ 16 ] =
{1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0};
GLfloat selectCopy[ 16 ] =
{1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0};
unsigned char btnState;
GLboolean isSelected;
//
// Projecta 2D mouse coordinates to a 3D sphere
//
int ProjectSphere( GLint x, GLint y, GLint r, GLfloat v[ 3 ])
{
GLfloatz;
v[0] =( GLfloat ) x * 2.0 - ( GLfloat) r;
v[1] =( GLfloat ) r - ( GLfloat) y * 2.0;
z = r * r - v[0] * v[0] - v[1] * v[1];
if (z < 0)
{
return0;
}
v[2] =sqrt( z);
//
// Normalise
//
v[0]/= ( GLfloat ) r;
v[1]/= ( GLfloat ) r;
v[2]/= ( GLfloat ) r;
return!0;
}
这个函数是将二维的鼠标坐标映射到一个单位球面上,通过它可以实现鼠标控制摄像机。
void Init( void )
{
glClearColor(WHITE_F, 0.0 );
glShadeModel(GL_FLAT );
//glEnable(GL_DEPTH_TEST );
}
做一些初始化的操作glClearColor()设置了清除颜色缓冲区时所试用的颜色,glShadeModel()则指明了着色方案,你只有两种选择GL_FLAT 或者是GL_SMOOTH,默认是后者
void Display( void )
{
glClear(GL_COLOR_BUFFER_BIT );
glClear(GL_DEPTH_BUFFER_BIT );
清除颜色缓冲区和深度缓冲区
glColor3f(RED_F );
设置了我们画东西需要用什么颜色来画
glMatrixMode(GL_MODELVIEW );
调整模型视图矩阵,这里需要特殊说明一下:
OpenGL在完成绘制前一般有几个步骤:
1) 调整投影矩阵, 简洁来说就是把照相机放好,放到哪个位置,朝哪个方向。
2) 调整模型视图矩阵,对物体进行的一系列变换,譬如我们要让它旋转、平移等。
3) 调整视口, 摄像机看到的东西要映射到屏幕上,需要调整映射到哪里的问题。
4) 绘制
glLoadIdentity();
//
// Apply the new transform with the old one
//
glRotatef(angle, axis[0 ], axis[1 ], axis[2 ] );
glMultMatrixf(lastMatrix );
首先进行了旋转,然后乘上了之前的变换矩阵,然而由于矩阵乘法顺序的问题,实际上我们等于先做了lastMatrix的变换,然后才有旋转的效果。
//
// Save as last Matrix
//
glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);
glutSolidCube(CUBE_SIZE );
绘制函数,这里我们只是调用了一个GLUT的一个工具函数,画了一个实心的立方体,其实很多时候要绘制什么需要我们自己定义的,调用glDraw***一类的函数,然后放到glBegin()和glEnd()之间就可以了。。。
glutSwapBuffers();
为了实现旋转效果的平滑过渡,我们用了双缓冲,在后面的初始化里有这样的设置,当然你也可以设置成单缓冲,那么这里一定要有glFlush()
}
以上就是画好我们想要的东西,然后把这个函数设置为回调函数就可以了。那么对于用户输入的响应或者一些变换需要在下面实现:
void Reshape( GLsizei w, GLsizei h )
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION );
glLoadIdentity();
gluPerspective(60.0, ( GLfloat ) w/ ( GLfloat ) h,0.1, 8.0 );
gluLookAt(eye[0], eye[1],eye[2], at[0],at[1], at[2],up[0], up[1],up[2] );
对投影矩阵进行变换,之所以没有把它放到Display函数里,是因为当我们改变窗口大小的时候,会导致投影矩阵做相应的变换,同样视口也一样
//
// New windowsize
//
wndHeight= h;
wndWidth= w;
}
GLboolean JudgeSelect( GLint x, GLint y )
{
GLuintselectBUf[ SELECT_BUFF_SIZE];
GLint hitNum;
GLint viewPort[ 4 ];
//
// Judge ifselect the cube
//
glGetIntegerv(GL_VIEWPORT, viewPort);
glSelectBuffer(SELECT_BUFF_SIZE ,selectBUf);
glRenderMode(GL_SELECT );
glInitNames();
glPushName(0 );
glMatrixMode(GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
//
// Set a smallarea to be hitted
//
gluPickMatrix(( GLdouble ) x,( GLdouble ) ( viewPort[3 ] - y ), 5.0, 5.0, viewPort );
gluPerspective(60.0, ( GLfloat ) wndWidth/ ( GLfloat ) wndHeight,0.1, 8.0 );
gluLookAt(eye[0], eye[1],eye[2], at[0],at[1], at[2],up[0], up[1],up[2] );
glMatrixMode(GL_MODELVIEW );
glPushMatrix();
memcpy(selectCopy, lastMatrix,16 * sizeof( lastMatrix[0 ] ) );
glLoadIdentity();
//
// Apply the new transform with the old one
//
glRotatef(angle, axis[0 ], axis[1 ], axis[2 ] );
glMultMatrixf(lastMatrix );
//
// Save as last Matrix
//
glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);
glLoadName(CUBE_NAME );
glutSolidCube(CUBE_SIZE );
glPopMatrix();
memcpy(lastMatrix, selectCopy,16 * sizeof( lastMatrix[0 ] ) );
glMatrixMode(GL_PROJECTION );
glPopMatrix();
glFlush();
hitNum= glRenderMode( GL_RENDER);
if( hitNum <= 0 ) {
returnFALSE;
} else {
returnTRUE;
}
}
这一部分是用来实现选择的,即如果用户没有选择立方体,那么拖动鼠标就不会旋转,选择的机制这里先略过,因为我还没有彻底搞懂。。。,从结果上看,就是hitNum来记录点击的次数。
void Mouse( GLint button, GLint state, GLint x, GLint y)
{
//
// Record mousestate
//
btnState= (unsigned char) button;
btnState<<= 4;
btnState|= (unsigned char)state;
switch( button)
{
case GLUT_LEFT_BUTTON:
//
//Initial the lastPos
//
ProjectSphere(x, y, wndHeight, lastPos );
isSelected= JudgeSelect( x,y );
break;
}
}
这个函数也是一个回调函数,用来处理鼠标事件,当点击鼠标之后我们就要准备进行旋转了,但是也不一定,isSelected会告诉我们是否点到了立方体上,是否需要旋转。
void Motion( GLint x, GLint y )
{
GLfloatd, dx, dy, dz;
if( ! isSelected ) {
return;
}
if ( ( ( btnState >> 4 ) & 0x0F) == GLUT_LEFT_BUTTON )
{
if(! ProjectSphere( x, y, wndHeight, currentPos) )
{
return;
}
dx= currentPos[ 0 ] - lastPos[0 ];
dy= currentPos[ 1 ] - lastPos[1 ];
dz= currentPos[ 2 ] - lastPos[2 ];
//
// Ifmouse has moved
//
if(dx || dy|| dz)
{
//
//Moving angle
//
d= sqrt( dx* dx + dy* dy + dz* dz );
angle=d * 180.0;
//
//The nomal vector of the move plane
//
axis[0]= lastPos[1] * currentPos[2]- lastPos[2] * currentPos[1];
axis[1]= lastPos[2] * currentPos[0]- lastPos[0] * currentPos[2];
axis[2]= lastPos[0] * currentPos[1]- lastPos[1] * currentPos[0];
//
//Record the old coordinates
//
lastPos[0]= currentPos[0];
lastPos[1]= currentPos[1];
lastPos[2]= currentPos[2];
}
glutPostRedisplay();
}
}
鼠标移动的回调函数,这里我们实现了旋转需要的数学运算,最关键的是记得最后要有glutPostRedisplay()这个函数,它告诉窗体要重绘。
GLint main( GLint argc, char** argv )
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );/* | GLUT_DEPTH);*/
glutInitWindowSize(WIDTH, HEIGHT);
glutInitWindowPosition(100, 100 );
glutCreateWindow("BLIZZARD" );
同样是一些初始化的工作,可以都一起扔到Init()函数里面。。。
Init();
glutDisplayFunc(Display );
glutReshapeFunc(Reshape );
glutMouseFunc(Mouse );
glutMotionFunc(Motion );
glutMainLoop();
这里就是对上面所有的回调函数进行绑定。GlutMainLoop则进入消息循环,里面会有一些默认的处理机制,譬如点窗口的X,就会关掉窗口等。。。
return 0;
}
使用glut工具包,确实非常方便,简化了和系统相关的编程部分。。。另外尽量使用GL开头的数据类型,这个是跨平台的问题了。。。
最后呢,运行的效果图像这样吧:
- OpenGL入门【1 快速入门】
- OpenGL (1) 入门
- OpenGL入门(1)
- opengl入门1
- opengl入门6。1
- Opengl ES入门1
- OpenGL 入门1
- opengl入门系列1- OpenGL纹理环绕
- OpenGL入门
- OpenGL 入门
- Opengl入门
- OpenGL 入门
- OpenGL 入门
- opengl入门
- OpenGL入门
- opengl入门
- OpenGL入门
- openGL入门
- Android模拟器入门
- 编程工具
- AIX5.3上安装gcc(gcc++)for aix简明步骤
- oracle忘记用户名密码怎样恢复
- uclinux 下PPPoE 拨号
- OpenGL (1) 入门
- FLEX例子之高级数据表(AdvancedDataGrid)
- 深层揭密extern "C"
- const char*, char const* and char *const
- sql server 2000更改数据库名称 、更改逻辑文件名
- Eclipse的hibernate plugin(downmoon)
- Solaris常用软件怎么安装?
- 一点编程感受
- jsp的乱码问题