终于开始走上正轨了。
下午本来是想去图书馆看《程序员》的,可是去早了,还没有开门,干脆就回寝室上网。一下午也没有干什么正经事,胡乱找了些enya和cranberries的mp3,很没意思。
现在学一下openGL,晚上把虚拟机的思路理一下,,,
在开始OpenGL之前有必要复习一下windows编程:
1、 程序前加上#define WIN32_LEANAND_MEAN,表示不适用mfc的代码;当然还需要
#include <windows.h>
#include <windowsx.h>
2、 winmain()入口函数
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
3、 windows程序框架:msgproc()消息回调;msgloop()消息循环;
4、 一下就不罗嗦的,直接进入正题吧
用的是nehe的网上教程,用金山词霸读英文网站真的很爽,呵呵
1、头文件,这些就不需要再解释了
#include <windows.h> // Header File For Windows
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
#include <gl\glaux.h> // Header File For The Glaux Library
先从WinMain()开始看,nehe从具体函数开始的方法没有大局观,看起来不爽
int WINAPI WinMain(
HINSTANCE hInstance, // Instance 句柄
HINSTANCE hPrevInstance, // Previous Instance无用,老哪个版本的windows
LPSTR lpCmdLine, // Command Line Parameters命令行参数,无用
int nCmdShow) // Window Show State窗口的初始状态(SW_开头)
{
MSG msg; // Windows Message Structure
注:MSG的定义
typedef struct tagMSG { // msg HWND hwnd; Handle to the window whose window procedure receives the message.
UINT message; Specifies the message number.
WPARAM wParam;
Specifies additional information about the message. The exact meaning depends on the value of the message member.
LPARAM lParam; Specifies additional information about the message. The exact meaning depends on the value of the message member.
DWORD time; Specifies the time at which the message was posted.
POINT pt; Specifies the cursor position, in screen coordinates, when the message was posted.
} MSG; BOOL done=FALSE; // Bool Variable To Exit Loop
// Ask The User Which Screen Mode They Prefer
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
备注:
int MessageBox(HWNDhWnd,// handle of owner window
LPCTSTRlpText,// address of text in message box
LPCTSTRlpCaption,// address of title of message box
UINTuType// style of message box
);
Return Values
The return value is zero if there is not enough memory to create the message box.
If the function succeeds, the return value is one of the following menu-item values returned by the dialog box:
| Value | Meaning |
| IDABORT | Abort button was selected. |
| IDCANCEL | Cancel button was selected. |
| IDIGNORE | Ignore button was selected. |
| IDNO | No button was selected. 本程序是当选择NO时将全屏参数设置成false |
| IDOK | OK button was selected. |
| IDRETRY | Retry button was selected. |
| IDYES | Yes button was selected. |
{
fullscreen=FALSE; // Windowed Mode
}
// Create Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
下面开始处理消息
while(!done) //Loop That Runs While done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
备注:
BOOL PeekMessage(LPMSGlpMsg,// pointer to structure for message
HWNDhWnd,// handle to window
UINTwMsgFilterMin,// first message
UINTwMsgFilterMax,// last message
UINTwRemoveMsg// removal flags
);
wRemoveMsg
Specifies how messages are handled. This parameter can be one of the following values:
| Value | Meaning |
| PM_NOREMOVE | Messages are not removed from the queue after processing by PeekMessage. |
| PM_REMOVE | Messages are removed from the queue after processing by PeekMessage. |
By default, all message types are processed. To specify that only certain message should be processed, specify one of more of the following values:
| Value | Meaning |
| PM_QS_INPUT | Windows NT 5.0 and Windows 98: Process mouse and keyboard messages. |
| PM_QS_PAINT | Windows NT 5.0 and Windows 98: Process paint messages. |
| PM_QS_POSTMESSAGE | Windows NT 5.0 and Windows 98: Process all posted messages, including timers and hotkeys. |
| PM_QS_SENDMESSAGE | Windows NT 5.0 and Windows 98: Process all sent messages. |
{
if (msg.message==WM_QUIT) // Have We Received A Quit Message?
{
done=TRUE; // If So done=TRUE
}
else // If Not, Deal With Window Messages
{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
以上是当消息队列中还有消息时的处理
当再没有消息处理时:
else // If There Are No Messages
{
// Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
if (active) // Program Active?
{
if (keys[VK_ESCAPE]) // Was ESC Pressed?
{
done=TRUE; // ESC Signalled A Quit
}
else // Not Time To Quit, Update Screen
{
DrawGLScene(); // Draw The Scene
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
The SwapBuffers function exchanges the front and back buffers if the current pixel format for the window referenced by the specified device context includes a back buffer.
这一块没有看懂是干什么用的……BOOL SwapBuffers(HDChdc// device context whose buffers get swapped
);
}
}
if (keys[VK_F1]) // Is F1 Being Pressed?
{
keys[VK_F1]=FALSE; // If So Make Key FALSE
KillGLWindow(); // Kill Our Current Window
fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
// Recreate Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
}
}
}
// Shutdown
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}
2、在程序前定义了几个设备相关变量,nehe对于这的解释是The RC connects OpenGL to the DC.
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
3、然后设置了一个数组来监控键盘消息,设置数组的方法可以在一次按键中处理多个键值。Active变量是用来告诉程序我们的窗口是否被最小化到任务栏上。
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
5、 对wndproc的申明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
6、 在winmain函数首先出现的自定义函数是:createGLwindow,那么就先从它下手:好像挺长的哦!!
现看一下刚才在winmain中的调用实例
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
按照下面的定义,第一个参数是标题,第二个和第三个是定义窗口的大小,这里是一个640*480的窗口,(原来的老游戏都是这个分辨率),最后一个参数是用来设置全屏属性的。初始定义中fullscreen=TRUE。
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Holds The Results After Searching For A Match
WNDCLASS wc; // Windows Class Structure
备注:将WNDCLASS的结构定义先放在这里,以后再具体讨论
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
以上两行的具体作用暂时还没有看出来
下面定义了一个窗口的大小
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
WindowRect.left=(long)0; // Set Left Value To 0
WindowRect.right=(long)width; // Set Right Value To Requested Width
WindowRect.top=(long)0; // Set Top Value To 0
WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height
这里给前面定义的全局变量赋值,在这次调用中fullscreen是TRUE(因为fullscreen直接作为参数传了过来)
fullscreen=fullscreenflag; // Set The Global Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
Return Values
If the function succeeds, the return value is a handle to the specified module.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
这里只是简单了给hInstace申请了一个值
下面定义了窗口类的具体值
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC(注:这里是给窗口创建一个私有DC);// Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = "OpenGL"; // Set The Class Name
注册窗口类
if (!RegisterClass(&wc)) // Attempt To Register The Window Class
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (fullscreen) // Attempt Fullscreen Mode?
{
DEVMODE dmScreenSettings; // Device Mode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
memset函数是用来开辟一段字符空间用的memset,它的格式如下
void *memset( void *dest, int c, size_t count );
因此这里是开一个dmScreenSettings大小的空间用字符0填充
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
这一片又很复杂了,,DEVMODE是一个很长的结构体,是DEVMODEA的别名,我先帖下来
typedef struct _devicemodeA {
BYTE dmDeviceName[CCHDEVICENAME];
WORD dmSpecVersion;
WORD dmDriverVersion;
WORD dmSize;
WORD dmDriverExtra;
DWORD dmFields;
union {
struct {
short dmOrientation;
short dmPaperSize;
short dmPaperLength;
short dmPaperWidth;
};
POINTL dmPosition;
};
short dmScale;
short dmCopies;
short dmDefaultSource;
short dmPrintQuality;
short dmColor;
short dmDuplex;
short dmYResolution;
short dmTTOption;
short dmCollate;
BYTE dmFormName[CCHFORMNAME];
WORD dmLogPixels;
DWORD dmBitsPerPel;
DWORD dmPelsWidth;
DWORD dmPelsHeight;
DWORD dmDisplayFlags;
&nb