您的位置:首页 > 运维架构

OpenGL (1) 入门

2009-04-01 15:46 120 查看
前几天跟着师兄做东西,“被迫”学习了一下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是一个工具包,是Nate
Robin开发的,他的主页上有非常详细的教程。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 ];

//
// Camera
Properties
//
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;

//
// Project
a 2D mouse coordinates to a 3D sphere
//
int ProjectSphere( GLint x, GLint y, GLint r, GLfloat v[ 3 ]
)
{
GLfloat
z;
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)
{
return
0;
}
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 window
size
//
wndHeight
= h;
wndWidth
= w;
}

GLboolean JudgeSelect( GLint x, GLint y )
{
GLuint
selectBUf[ SELECT_BUFF_SIZE
];
GLint hitNum;
GLint viewPort[ 4 ];
//
// Judge if
select the cube
//
glGetIntegerv(
GL_VIEWPORT, viewPort
);
glSelectBuffer(
SELECT_BUFF_SIZE ,selectBUf
);
glRenderMode(
GL_SELECT );

glInitNames();
glPushName(
0 );

glMatrixMode(
GL_PROJECTION );
glPushMatrix();

glLoadIdentity();

//
// Set a small
area 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 ) {
return
FALSE;
} else {
return
TRUE;
}

}
这一部分是用来实现选择的,即如果用户没有选择立方体,那么拖动鼠标就不会旋转,选择的机制这里先略过,因为我还没有彻底搞懂。。。,从结果上看,就是hitNum来记录点击的次数。

void Mouse( GLint button, GLint state, GLint x, GLint y)
{

//
// Record mouse
state
//
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 )
{
GLfloat
d, 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 ];
//
// If
mouse 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开头的数据类型,这个是跨平台的问题了。。。
最后呢,运行的效果图像这样吧:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: