重拾OpenGL,起步艰难。我发现自己也同胡老师一样了,学得快(当初花了一个国庆假就把基础都看完了)也忘得快。废话少说,进入正题。
新建一个win32控制台程序。选择“空项目“以及在上面的“应用程序类型”里选择“Windows应用程序”。建立项目后再工程属性里设置“常规”下设置字符集为“多字节……”。然后再新建一个cpp文件,把以下代码拷贝进去运行就OK了。
至于代码里的Windows窗口注册、消息循环机制等等,以及OpenGL里的窗口设置、初始化代码就自己理解吧。呵呵,欢迎一起交流,哎,自己忘得太快了。这次再次接触恐怕是30岁前都会记得了。 写下这篇文章的时候想着明天弟弟就要高考。只希望一切都好,考完那晚别太疯狂了。也别学他这没用的哥哥就是了。呵呵。
#include <windows.h> //视窗头文件 #include <GL/gl.h> #include <GL/glu.h> #include <gl/glaux.h>
//定义程序中计划使用的所有变量 HWND hWnd=NULL; //得到窗口的句柄 HDC hDC=NULL; //私有的GDI设备环境 HGLRC hRC=NULL; //定义渲染环境 HINSTANCE hInstance; //建立一个程序的例子Instance(事件)
//设置窗口大小,斜率及全屏标志变量
RECT rect; int sw = 640; int sh = 480; bool fullscreen = 1; GLfloat aspect;
#pragma comment( lib, "opengl32.lib" ) #pragma comment( lib, "glu32.lib" ) #pragma comment( lib, "glaux.lib" )
//OpenGL初始化开始 void SceneInit(int w,int h) { glShadeModel(GL_SMOOTH); //允许平滑着色 glClearColor( 1.0, 1.0, 1.0, 0.5 ); //设置屏幕清屏的颜色 glClearDepth(1.0f); //设置深度缓冲区 glEnable(GL_DEPTH_TEST); //允许深度测试 glDepthFunc(GL_LEQUAL); //深度测试的类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //透视校正,会使透视效果显得更好一些 }
//窗口尺寸改变时OpenGL场景的尺寸 void SceneResizeViewport(GLsizei w, GLsizei h) { if(h==0) { h=1; } aspect = (GLfloat)w/(GLfloat)h;
//设置透视场景,透视的作用是景物距离越远其尺寸越小 glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); //选择投影矩阵 glLoadIdentity();
gluPerspective( 45.0f, aspect, 0.1f, 100.0f ); //基于窗口的高和宽并以45度视角进行计算。0.1f和 //100f是绘制在屏幕上相对于屏幕距离的开始点和结束点 glMatrixMode(GL_MODELVIEW); //确定任何新的变换都会影响模型视点矩阵ModelView glLoadIdentity(); //把被选择的矩阵还原到初始状态 }
void SceneShow(GLvoid) //这里进行所有的绘图工作 { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //清屏和清除深度缓冲区 glLoadIdentity(); //重置当前Modelview矩阵 //此处添加绘图命令 }
//激活创建OpenGL窗口 void EnableOpenGL() { PIXELFORMATDESCRIPTOR pfd; int iFormat;
hDC = GetDC( hWnd );
ZeroMemory( &pfd, sizeof( pfd ) ); pfd.nSize = sizeof( pfd ); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 16; pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE;
iFormat = ChoosePixelFormat( hDC, &pfd );
SetPixelFormat( hDC, iFormat, &pfd );
hRC = wglCreateContext( hDC ); wglMakeCurrent( hDC, hRC ); }
// 取消 OpenGL ,在程序结束前调用,释放渲染环境,设备环境以及最终窗口句柄。 void DisableOpenGL() { wglMakeCurrent( NULL, NULL ); wglDeleteContext( hRC ); ReleaseDC( hWnd, hDC ); }
//改变屏幕的分辨率 bool ChangeResolution(int w, int h, int bitdepth) { DEVMODE devMode; int modeSwitch; int closeMode = 0;
EnumDisplaySettings(NULL, closeMode, &devMode);
devMode.dmBitsPerPel = bitdepth; devMode.dmPelsWidth = w; devMode.dmPelsHeight = h; devMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
modeSwitch = ChangeDisplaySettings(&devMode, CDS_FULLSCREEN);
if(modeSwitch == DISP_CHANGE_SUCCESSFUL) { return true; } else { ChangeDisplaySettings(NULL, 0); return false; } }
//处理窗口信息 LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch ( message ) //将message设置为所有事件声明比较的 //值,message将得到要处理的消息的名字 { case WM_CREATE: //监视窗口激活的信息 GetWindowRect(hWnd, &rect); sw = rect.right - rect.left; sh = rect.bottom - rect.top; SceneResizeViewport(sw, sh); return 0;
case WM_SIZE: //改变OpenGL窗口的尺寸。
if(!fullscreen) { GetWindowRect(hWnd, &rect); sw = rect.right - rect.left; sh = rect.bottom - rect.top; if(sw>0 && sh>0) SceneResizeViewport(sw, sh); } else { SceneResizeViewport(GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN )); }
return 0;
case WM_CLOSE: //是否收到一个关闭信息? ShowWindow (hWnd, SW_HIDE); PostQuitMessage( 0 ); return 0;
case WM_DESTROY: return 0;
case WM_KEYDOWN: //是否有一个键被按下? switch( wParam ) { case VK_ESCAPE: PostMessage(hWnd, WM_CLOSE, 0, 0); break; } return 0; default: return DefWindowProc( hWnd,message, wParam, lParam ); //一些无须在意的信息视窗自己处理 } }
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { WNDCLASS wc; //窗口类 MSG msg; //检测是否有信息在等待处理 bool bQuit = false; //bQuit为FLASE,程序当前在运行;为TRUE程序将退出
//询问选择那种屏幕模式 if (MessageBox(NULL,"是否选择全屏显示模式?", "全屏模式运行?",MB_YESNO|MB_ICONQUESTION)==IDNO) { fullscreen=0; //窗口模式 } //创建OpenGL窗口,设置标题、宽高及颜色数 wc.style = CS_OWNDC; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); wc.lpszMenuName = NULL; wc.lpszClassName = "Name"; RegisterClass( &wc );
if(fullscreen) { ChangeResolution(640, 480, 16); hWnd = CreateWindow( "Name", "Sink", WS_POPUP | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), NULL, NULL, hInstance, NULL ); } else { hWnd = CreateWindow( "Name", "Sink", WS_TILEDWINDOW | WS_VISIBLE, GetSystemMetrics( SM_CXSCREEN )/2-sw/2, GetSystemMetrics( SM_CYSCREEN )/2-sh/2, sw, sh, NULL, NULL, hInstance, NULL ); ChangeDisplaySettings(NULL, 0); } ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd);
//初始化 EnableOpenGL(); SceneInit(sw, sh);
if(!fullscreen) { GetWindowRect(hWnd, &rect); sw = rect.right - rect.left; sh = rect.bottom - rect.top; if(sw>0 && sh>0) SceneResizeViewport(sw, sh); } else { SceneResizeViewport(GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN )); }
//下面开始循环,如果bQuit的值一直是FLASE,循环将一直重复下去 while ( !bQuit ) { if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) //通过PeekMessage(),检测是否有消息 //在等待,又不会使程序挂起 if ( msg.message == WM_QUIT ) //检测是否有退出消息发出 bQuit = true; else { TranslateMessage( &msg );//如果消息不是退出,使得WndProc()或窗口能处理它 DispatchMessage( &msg ); } else {
// OpenGL 动画,如果程序被激活并且ESC没有被按下,可以渲染场景及交换缓冲区(通过使用 //双缓冲区可获得平滑运动的动画) //通过使用双缓存,先在不可见的隐藏屏幕上绘制,当交换缓冲区时,隐藏屏幕变为可见屏幕, //原先可见的屏幕变为隐藏,这样 //就看不到屏幕被绘制的过程,画面会马上出现,这样看上去会更真实。
SceneShow(); SwapBuffers(hDC);
} }
//关闭,退出程序。清除OpenGL窗口,以便释放所有资源,然后退出程序。 DisableOpenGL();
ShowWindow (hWnd, SW_HIDE); DestroyWindow( hWnd );
ChangeDisplaySettings(NULL, 0);
return msg.wParam;
return 0; }