大二的时候开始想着做游戏,因为学校的课程实在是无聊就想着做些有意义的事情。毕竟学了编程这一行就得做些实事,于是就在网上搜了一下图形编程,偶然的了解到了Opengl (同时还有Windows上的Direct3D 但由于Opengl的跨平台等原因入了坑)。虽然到现在也没写出什么小游戏出来,毕竟一个用一个那么底层的图像接口做一个有玩法有乐趣的游戏工作量还是很大的。然后因为最近刚开了博客想起这段学习经历,于是这几天就再看了一下我之前写过的代码,把我的理解写
下来希望对别人有帮助同时也写给未来的自己。
首先我是在Windows上编写OpenGL的,但网上大多数都叫你去下一个glut库来创建Windows窗口,因为这样简单方便不用再去学Windows的API。这些教程也就不需要再给你介绍这些东西,但我个人觉得应该用Windows的API来创建窗口;这因为Windows提供很多API,通过这些API我们可以实现很多功能,而创建窗口的这些API是很基础的;学习这些API你可以更好的了解Windows,而且使用API可以更加灵活的定制你自己想要的窗口和实现甘glut库没有提供的功能。对于Linux由于我学识有限还不知道怎么使用Opengl大家可以自行百度一下。
// Win32Project.cpp : 定义应用程序的入口点。
// #include "stdafx.h" //Windows消息循环 处理各种信息
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{ switch (message)
{
case WM_CREATE:
return ;
case WM_CLOSE:
PostQuitMessage();
return ; case WM_DESTROY:
return ; case WM_KEYDOWN: //键盘按下消息
return ; default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
} //Windows程序入口函数
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int iCmdShow)
{ HDC hDC;
HGLRC hRC;
MSG msg;
BOOL bQuit = FALSE; WNDCLASS wc; wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = ;
wc.cbWndExtra = ;
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 = TEXT("GLSample");
RegisterClass(&wc); //下面这个函数中间的4个数字前两个是屏幕的x,y后面是高宽
HWND hWnd = CreateWindowA("GLSample", "OpenGL Sample", WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE, , , , , NULL, NULL, hInstance, NULL);
if (hWnd == NULL) return -; hDC = GetDC(hWnd); PIXELFORMATDESCRIPTOR pfd; ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = ;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = ;
pfd.cDepthBits = ;
pfd.iLayerType = PFD_MAIN_PLANE; int iFormat = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC, iFormat, &pfd); hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC); while (!bQuit)
{ if (PeekMessage(&msg, NULL, , , PM_REMOVE))
{ if (msg.message == WM_QUIT)
{
bQuit = TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{ glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*
在这里添加Opengl代码
*/
SwapBuffers(hDC); Sleep();
}
} wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd); return msg.wParam;
}
开头CallBack标志的函数是消息回调函数,Windows是一个由窗口组成的操作系统,每一个窗口都由唯一的一个窗口句柄(四个字节的数字标识),窗口之间有父子关系,操作系统会给主窗口分配一个消息队列,当用户操作时比如移动鼠标(WM_MOUSEMOVE)、按下键盘(WM_KEYDOWN)都会有一个对应的消息被送到消息队列中,而这里的CallBack函数就是默认的处理消息的函数,四个参数也是Windows要求的默认格式,其中重要的参数是UINT message,每次有消息发送到窗口,处理函数被调用消息通过UINT message传进来,我们添加一个判断语句switch就可以对对应消息处理,比如对按下键盘进行处理switch(message) case WM_KEYDOWN: return 0;在return 0之前可以加任何你想得到操作。return 0;表示的是消息已经被你处理,返回告诉系统不需要进行处理,对于你没有处理的消息通过return DefWindowProc(hWnd, message, wParam, lParam)来交给系统处理。
WinMain函数是程序的入口点有固定的格式,Win32程序开始执行时从这里进入并开始执行程序,相当与控制台程序的main函数,他的四个参数:第一个是窗口的实例句柄,第二个是之前的实例句柄,第三个是通过控制台来启动程序是传入的字符串指针,第四个是窗口的显示方式列如SW_NORMAL,WM_HIDE。接下来是窗口创建过程,首先你要了解Windows的窗口是包含很多数据的,比如他的位置大小、窗口名称和样式以及图标信息等等,是由很多数据组成的。而你要创建窗口就必须要给出这些信息,Windowsi给开发者提供了一个创建窗口的Api ---- CreateWindowA() (A表示的是Ascll码 字符串是Ascll格式)CreateWindow有很多参数,不过都比较好记忆。前面说了创建窗口需要很多信息所以在调用CreateWindow之前需要填写窗口信息,Windows提供了一个WNDCLASS的结构体调用CreateWindow之前必须声明一个WNDCLASS变量并填写信息,填写好了后再通过RegisterClass这个API把这个结构体提交给系统。CreateWIndow就可以创建一个窗口了。PIXELFORMATDESCRIPTOR结构体是为了使窗口能显示OpenGL渲染后的图片在窗口上填写的信息,然后 int iFormat = ChoosePixelFormat(hDC, &pfd); SetPixelFormat(hDC, iFormat, &pfd); 这两段代码分别是把结构体PIXELFORMATDESCRIPTOR与设备环境句柄HDC绑定在一起,这个设备环境句柄是在Windows窗口上渲染图片或画图之类的十分重要的句柄(句柄在Windows里是一个很重要的概念,你可以把它理解为一个四字节的标识符,Windows有很多窗口要管理,窗口又有子窗口,每个窗口都有设备环境所以要通过一个值来区分开他们)。
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
第一句代码是通过HDC生成OpenGL资源环境句柄hRC,而第二句则是把设备环境句柄和OpenGL环境句柄绑定在一起。之后就可以调用OpenGL的基本语句了(列如:glColor3f、glVertex3f、glBegin、glEnd。。。)不过最后要记得 调用
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
来解绑和释放OpenGL资源环境句柄、设备环境句柄。
上面这个代码你要用Visual Studio或Visual C++创建一个Win32工程来使用,因为我用的是Visual Studio 2015所以以下的图片都是在VS2015截的,VS和VC的向导界面是不同的,但大概意思的选项还是能找到的,因为博主比较懒所以就只在关键步骤发图了。
打开VS或VC → 新建 → 项目
弹出创建向导,找到Visual C++下的Win32,里面有个Win32控制台和Win32项目,选择Win32项目写一个喜欢的项目名字点确定。
之后会再弹出一个向导,直接点下一步确定不,过要注意以下选项。
如果你是初学者,你应该在创建向导界面不要勾选空项目这个选项,他自己生成的代码可以帮助你更好的分析和入门
接下来VS会帮你创建好很多文件,这些文件你可能不知道有什么作用。
如下图:
但关键的文件就4个stdafx.h , stdafx.cpp ,你取的工程名加.h 和工程名.cpp(PS:这里我取得名字为Win32Project3)。
然后再简单讲解一下4个文件的作用
stdafx.h:你把VS自带的头文件例如<iostream>、<Windows.h>都包含在这个文件中。在你第一次编译时生成一个.pch文件之后编译编译器会利用这个文件加快你的编译速度
stdafx.cpp:包含stdafx.h头文件具体为什么要有这个文件我就不太清楚了知道的大佬可以留言给我
Win32Project.h:头文件可以写你自己定义的结构体、类和变量
Win32Project.cpp:这次我们的代码就写在这里WinMain主要的文件
现在创建好了一个工程你点调试→开始调试运行就能弹出一个Windows窗口
接下来双击打开Win32Project.cpp删除原来的代码把文章开头的代码粘贴过来
开始调试发现报错因为没有引入头文件
在stdafx.h文件中加入 #include <gl/GL.h>当然你也可以直接加在Win32Project.cpp文件里
再次调试运行 一般情况下都会出现如下结果
这是因为没有引入OpenGL的Lib库文件可以在stdafx.h 或 Win32Project.cpp加入#pragma comment(lib, "opengl32.lib")来引用OpenGL库文件
最后调试运行
如果你的窗口变黄了就代表你成功了
下篇Blog我打算写OpenGL的glew库的使用,个人觉得glew对OpenGL来说是十分重要的。