1、前言
在我学习C++的过程中,研究了一下OpenGL编程,打开了3D世界的编程世界,3D世界的效果还是相当不错。而且OpenGL能够支持跨平台兼容,是不错的学习方向,于是就自己学习了网上的很多教程,并将所有学到的知识运用到自己编程中去,现在正好有时间,就将自己编程中的一些点点滴滴汇总出来,以供大家参考。
2、设计的目标
那么我们还是从游戏的角度出发,去了解一下游戏中的功能都是怎么实现的。这一切还是要从自己玩游戏开始说起,此前就玩过一下3D游戏,当时就被游戏里的一些画面深深的吸引了,同时,游戏里头还有很多,很有趣的设定,比如,玩家的视角是怎么移动的?崎岖不平的地图是怎样制作的?人物和物体、地面的碰撞是怎样检测的?鼠标是怎样选中眼前的物体的?魔法技能是怎样释放的?不用加载进度条的无缝世界地图是怎么实现的?带着这些疑问,我们走进了一个OpenGL世界的3D世界。
3、程序设计的思路
为了实现以上游戏丰富多彩的内容,我们需要对相应的游戏内容实现进行探索,从一开始的准备工作,游戏库文件的准备,第一个游戏窗口的创建,运用OpenGL画出基本的3D物体,文字的显示,视角的变化,贴图纹理的使用,点在三维世界的位置转换,三维世界顶点在二维屏幕的投影位置,鼠标怎样选中物体,贝塞尔曲线和曲面的应用等等一系列的问题进行研究,逐步模仿实现游戏世界的内容。虽然有很多内容网上都只言片语的提到过,但很少有系统来说的,这里我就我自己编程中遇到的问题和解决的办法汇总出来,仅供参考。
4、准备工作
我们这里还是选取VC6.0来创建工程,毕竟他对系统的要求不高,实现一些功能来说,简单容易上手,源码文件体积小,也更好分享一些。这里我们首先要准备一下OpenGL的运行库,除了VC6.0自带的运行库外,我们需要在网上下载一个很重要的OpenGL支持运行库,这里我已经准备好放在附件里了,大家可以直接下载使用。
后期我们在建立名称为World的工程文件夹后,需要对以上运行库进行以下处理,以便后期使用,具体步骤如下:
第一、把以上的运行库的GL文件夹直接放在工程的World目录下;
第二、把所有DLL后缀的文件直接放在工程的World目录下;
第三、把所有LIB后缀的文件拷贝到VC的安装路径(通常为Microsoft Visual Studio\VC98\Lib)下,如果提示文件重复是否需要覆盖,全部选择覆盖即可。
那么我们现在急需一个工程,并创建第一个窗口。
5、创建一个窗口
我们想制作一个游戏,首先必须得创建一个WINDOWS的窗口,我们先用VC创建一个最简单的窗口。就好比在学各种编程语言的时候都会创建一个基本的hello world示例程序一样,这样可以给我们的编程从感觉上带来简单容易上手的良好效果。
6、创建窗口的代码
这里我们没有使用网上众多采用的DOS窗口建立的运行环境,主要是那样的窗口给游戏设计带来的体验感较差,这里使用WINDOWS窗口更符合设计的需求。我们先建立一个World.cpp的源文件,将以下程序保存到文件,再用VC编译运行后就能产生一个简单的程序窗口。
这里窗口代码运行有错误提示(unresolved external symbol _main)的,请参照消灭星星游戏程序设计【连载一】——游戏窗口的创建中第6部分的解决办法。
//加载系统头文件
#include "Windows.h"
#include "Stdio.h"
#include "Imm.h"
#include "Math.h"
#include "Time.h"
#include "Winuser.h"
#include "FStream.h"
#include "IOStream.h"
#include "MMSystem.h"
//加载OPENGL头文件
#include "gl/glu.h"
#include "gl/glut.h"
#include "gl/glaux.h"
//加载链接库
#pragma comment(lib,"glut.lib")
#pragma comment(lib,"glaux.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glut32.lib")
#pragma comment(lib,"opengl32.lib")
//加载链接库
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"msimg32.lib")
#pragma comment(lib,"imm32.lib")
//加载自定义头文件
#include "Library\Library.h"
//#include "Game\Game.h"
//#include "Interface\Interface.h"
//加载自定义头文件
#include "Library\Library.cpp"
//#include "Game\Game.cpp"
//#include "Interface\Interface.cpp"
//全局句柄
HWND hWnd=NULL;
HINSTANCE hInstance=NULL;
//消息处理模块
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
switch (message)
{
case WM_CREATE:
return 0;
case WM_PAINT:
PAINTSTRUCT PS;
hDC=BeginPaint(hWnd,&PS);
ReleaseDC(hWnd,hDC);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
//主函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
MSG message;
HWND hWnd;
CHAR szAppName[]="World";
//设置程序的样式
WNDCLASS WC;
WC.style = CS_HREDRAW|CS_VREDRAW;
WC.lpfnWndProc = WndProc;
WC.cbClsExtra = 0;
WC.cbWndExtra = 0;
WC.hInstance = hInstance;
WC.hIcon = LoadIcon(hInstance,IDI_APPLICATION);
WC.hCursor = LoadCursor(hInstance,IDC_ARROW);
WC.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
WC.lpszMenuName = NULL;
WC.lpszClassName = szAppName;
if(!RegisterClass(&WC)){return 0;}
//创建窗口
hWnd=CreateWindow(szAppName,szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
//赋值全局变量
::hWnd=hWnd;
::hInstance=hInstance;
//显示更新窗口
ShowWindow(hWnd,iCmdShow);
UpdateWindow(hWnd);
//消息循环
while(GetMessage(&message,NULL,0,0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return message.wParam;
}
7、3D屏幕的初始化
我们看到刚刚窗口跟平时建立的窗口没有什么区别,但是这样的窗口,目前现在还不能显示3d的物体,现在还只能像我们平时的应用程序显示文字和图片,但这并不是我们需要的,我们需要的是一个能够显示3d效果的窗口。我们需要给现实设备HDC进行初始化设置,我们通过一个SetPixelFormat(HDC hDC)函数进行设置操作,以便告诉系统我们要进行OpenGL的3d显示,在以上初始化完屏幕后,我们就可以显示3d物体了。
这里要注意:在以下添加代码前,必须完成本节第4部分的准备工作,将所有的库文件准备完毕,否则会提示出错。
//初始化OPENGL设置
bool SetPixelFormat(HDC hDC)
{
HGLRC hRC;
GLuint pixelformat;
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
16, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if(!(pixelformat=ChoosePixelFormat(hDC,&pfd)))
{
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!SetPixelFormat(hDC,pixelformat,&pfd))
{
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!(hRC=wglCreateContext(hDC)))
{
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!(wglMakeCurrent(hDC,hRC)))
{
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
//初始化OPENGL窗口
if(true)
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
}
return true;
}
随后,我们只需要在程序初始化消息WndProc函数中对屏幕进行初始化,既可以完成3d窗口的设置,开启我们的3d编程之旅。
case WM_CREATE:
hDC=GetDC(hWnd);
SetPixelFormat(hDC);
ReleaseDC(hWnd,hDC);
return 0;
8、显示第一个3D物体
设置好以上显示模式后,我们就行可以开始显示3D物体了。我们需要在消息处理函数WndProc中对WM_PAINT的处理添加我们的显示内容。
case WM_PAINT:
PAINTSTRUCT PS;
hDC=BeginPaint(hWnd,&PS);
......
//在此处添加3D显示设置及内容
......
ReleaseDC(hWnd,hDC);
return 0;
接下来,我们在以上待添加3D显示设置及内容的地方添加显示代码如下:
//显示3d世界内容
if(true)
{
//获取窗口大小
RECT tempClientRect;
GetClientRect(hWnd,&tempClientRect);
//初始化3D视角
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45,(float)tempClientRect.right/(float)tempClientRect.bottom,0.01f,1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//设置用户眼睛视角,展示壮观的三维世界从这里开始
gluLookAt(10.0f,10.0f,10.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
//进行初始化设置,清除屏幕
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//开始用户自定义绘图
if(true)
{
//设定颜色
glColor3f(1.0f,0.0f,0.0f);
//显示立方体
glutSolidCube(3.0f);
}
//结束用户自定义绘图并显示到屏幕上
glFlush();
SwapBuffers(hDC);
}
小结
通过以上操作,我们已经生成了第一个Windows窗口下的OpenGL程序,在窗口的正中央显示了一个正方体,当然这个正方体是从45度视角向下观察的结果,我们后期还会给正方体各个面添加不同的颜色,这样立方体才能更加逼真。但作为OpengGL编程的第一节内容,能够实现一个3D物体,已经达到了我们的目的。随着后期内容的添加,我们将探索更多、更神奇的功能效果。
立方体的显示效果如下:
运行源码见附近。