2009年5月4日星期一
不是不想写而是实在没啥可写的了
近期任务:
1) 魔方
2) Java词法分析器
3) 专业外语的Presentation
4) 准备亚信杯
5) VHDL实验环境熟悉
6) 计算机网络看书!!!!!!!
7) 复习编译原理!!!!!!!!
8) 看胡事民老师的论文!!!!!
估计在这里面的东西完成几个之前是不会有什么突破了!学习啊是无止境的~
2009年4月13日星期一
2009年4月10日星期五
OpenGL (1) 入门
首先讨论一下开发环境配置问题,需要一些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
#include
#include
#include
注意这里只需要有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 <>
{
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开头的数据类型,这个是跨平台的问题了。。。
正则表达式 读书笔记 (一)
1) 匹配纯文本
2) . 字符可以匹配任何一个单个的字符(包括字符、字母、数字甚至是 . 字符本身)
3) 匹配特殊字符,需要使用转义字符 \
4) 匹配多个字符中的某一个,使用字符集合, 例如 [0-9]
5) 取非匹配,例如 [^0-9]
6) \f 换页符
\n 换行符
\r 回车符
\t 制表符
\v 垂直制表符
\d 任何一个数字字符
\D 任何一个非数字字符
\w 任何一个字母数字字符或者是下划线字符
\W 任何一个非字符数字且非下划线字符
\s 任何一个空白字符
\S 任何一个非空白字符
7) 匹配一个字符一次或多次重复,只需要加上后缀 + 例子:\w+
(未完)
创建一个窗口 -- From GameTutorials, LLC
#include
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
// LRESULT 是一个32位的数值,对于一个回调函数其返回类型是LRESULT。
// 其定义为:
// typedef LONG_PTR LRESULT;
// 关于回调函数(CALLBACK),它可以和一个窗口类相关联。
//MSDN中的解释是:CALLBACK Calling convention for callback functions.
//
//This type is declared in WinDef.h as follows:
//
//
//#define CALLBACK __stdcall
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow )
// 同CALLBACK一样 WINAPI 也是 Calling convention for the Win32 API,
// 个人目前无法理解。。。
// WinMain各个参数的解释:
// HINSTANCE hInstance -- 应用程序的实例信息,一个程序可以有两个实例同时运行(开两个QQ)
// HINSTANCE hPrevInstance -- 一般情况下为NULL,没有实际用处。
// PSTR szCmdLine -- 传给应用程序的参数,PSTR就是字符串。
// int iCmdShow -- 保存关于窗口的信息,诸如最大化、最小化等等。
{
HWND hwnd; // HWND -- 窗口句柄,用它来追踪窗口,应用程序可以有多个窗口。
MSG msg; // MSG -- 消息,传递给窗口的。
WNDCLASSEX wndclass; // WNDCLASSEX -- 窗口类,存有窗口的常用信息如图标、标题、大小等。
// 设置窗口属性。
wndclass.cbSize = sizeof( wndclass );
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc; // 指定窗口过程,告知窗口去调用哪个回调函数来处理窗口消息。
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0; // 通常都赋值为0
wndclass.hInstance = hInstance; // ?不解啊
// 设置图标和箭头,空值为HINSTANCE类型。
wndclass.hIcon = LoadIcon( NULL, IDI_WINLOGO );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = ( HBRUSH ) GetStockObject( WHITE_BRUSH );
// The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes.
// 返回值是VOID* 因此需要类型转换。
wndclass.lpszMenuName = NULL;
// 为窗口类起的名字,在创建窗口时将会用到。
wndclass.lpszClassName = TEXT( "Window Class" );
wndclass.hIconSm = LoadIcon( NULL, IDI_WINLOGO );
RegisterClassEx( &wndclass ); // 注册窗口类。
hwnd = CreateWindow( TEXT( "Window Class" ),
TEXT( "BLIZZARD Window" ), // 窗口标题。
WS_OVERLAPPEDWINDOW, // 窗口类型。
CW_USEDEFAULT, // 初始X位置。
CW_USEDEFAULT, // 初始Y位置。
CW_USEDEFAULT, // 初始X大小。
CW_USEDEFAULT, // 初始Y大小。
NULL, // 父窗口句柄。
NULL, // 菜单句柄。
hInstance, // 实例句柄。
NULL // 可以利用最后一个参数给WndProc传递变量,当然也可以用全局变量的方式。
);
ShowWindow( hwnd, iCmdShow ); // MSDN中的注释:The ShowWindow function sets the specified window's show state.
// 因此不能从翻译上理解ShowWindow函数调用后会显示窗口。
UpdateWindow( hwnd ); // 绘制窗口,会向窗口发送一个WM_PAINT消息。
// 消息循环。
// 如果收到WM_QUIT消息将返回0。
while( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg ); // 将消息发送给窗口过程。
}
UnregisterClass( TEXT( "Window Class" ), hInstance );
return msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{
switch( iMsg ) {
case WM_CREATE : // 创建窗口。
break;
case WM_SIZE : // 改变窗口大小。
break;
case WM_PAINT : // 窗口重绘。
break;
case WM_DESTROY :
PostQuitMessage( 0 ); // 必须调用PostQuitMessage。
break;
}
return DefWindowProc( hwnd, iMsg, wParam, lParam );
// 调用DefWindowProc采用默认方式处理未处理消息。
}
学习WIN 32离不开MSDN,这点深有体会,许多东西必须查!
WIN 32 编程的一般步骤是:
1) 初始化窗口类。
2) 注册窗口类。
3) 创建窗口。
4) 从窗口过程获取消息。
5) 处理消息。