//新建一个win32应用程序项目,选择空项目,然后在项目资源中的source添加一个爆炸.cpp
//========================================
爆炸.cpp
#include <iostream>
#define GLUT_DISABLE_ATEXIT_HACK
#include<Windows.h>
#include<stdio.h>
#include<GL/GLUT.H>
#include<GLAUX.H>
#include<math.h>
#include<tchar.h>
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glaux.lib")
HDC hDC = NULL;//设备上下文
HGLRC hRC = NULL;//着色描述表
HWND hWnd = NULL;//窗口句柄
HINSTANCE hInstance;//程序实例句柄
bool keys[256];//按键状态
bool active = TRUE;//窗口激活状态
bool fullscreen = TRUE;//全屏模式标记
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//整口过程,opengl程序内所有消息都在这里响应
//以上是我们实现了一个OpenGL窗口,
//以下是我们在这个窗口中做点儿事情。
//做出爆炸的效果。
#define MAX_PARTICLES 1500 //粒子数
bool rainbow = true, sp, rp;
float slowdown = 2.0f, xspeed, yspeed, zoom = -40.0f;
GLuint k, col, delay, texture[1];
typedef struct //定义结构体类型,对粒子特性进行详细描述。
{
bool active;
float life;
float fade;
float r;
float g;
float b;
float x;
float y;
float z;
float xi;
float yi;
float zi;
float xg;
float yg;
float zg;
}particles; //结构体变量名
particles particle[MAX_PARTICLES]; //结构数组,装载粒子
static GLfloat colors[12][3]=
{
{ 1.0f, 0.5f, 0.5f }, { 1.0f, 0.75f, 0.5f }, { 1.0f, 1.0f, 0.5f }, { 0.75f, 1.0f, 0.5f },
{ 0.5f, 1.0f, 0.5f }, { 0.5f, 1.0f, 0.75f }, { 0.5f, 1.0f, 1.0f }, { 0.5f, 0.75f, 1.0f },
{ 0.5f, 0.5f, 1.0f }, { 0.75f, 0.5f, 1.0f }, { 1.0f, 0.5f, 1.0f }, { 1.0f, 0.5f, 0.75f }
};
//以下格式非常重要
AUX_RGBImageRec *LoadBMP(WCHAR *Filename) // 载入位图资源,读取纹理图像函数。
{
HANDLE File; //文件句柄
if (!Filename) // 检查文件名是否正确
{
return NULL; //不存在的话返回失败
}
WCHAR *strFilePathTemp;//存储文件路径
int FILEPATHSIZE=2048;//文件路径长度
strFilePathTemp=(WCHAR *)calloc(FILEPATHSIZE,sizeof(WCHAR));//初始化文件路径
File=CreateFile(strFilePathTemp, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开位图文件,读取位图信息
if (File) //检查文件是否存在
{
CloseHandle(File); // 信息已经获取,关闭文件设备句柄
return auxDIBImageLoadW(Filename); // 成功返回文件信息
}
return NULL; // 紧接着上面的判断,失败的话返回NULL
}
int LoadGLTexture() //读取图像
{
int Status = FALSE; //读取状态标记
AUX_RGBImageRec *TextureImage[1]; //创建一个纹理空间存储纹理信息
memset(TextureImage, 0, sizeof(void *)* 1); //首先清空存储空间
if (TextureImage[0] = LoadBMP(L"Image/par.bmp")) //载入图片纹理,图片放在可执行文件的同一目录下,调试状态下放在debug同一目录
{
Status = TRUE; // 如果载入成功设置状态位为真
glGenTextures(1, &texture[0]); //创建纹理
glBindTexture(GL_TEXTURE_2D, texture[0]); //使用位图资源生成纹理
//生成纹理
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//线性滤波方式
}
//纹理生成成功后要释放资源,这个非常重要。
if (TextureImage[0])
{
if (TextureImage[0]->data)
{
free(TextureImage[0]->data);
}
free(TextureImage[0]);
}
return Status;
}
GLvoid ReSizeGLScreen(GLsizei width, GLsizei height) //当窗口大小改变时进行窗口窗调
{
//glRotatef(60, 0.0f, 1.0f, 0.0f); //“粒子”旋转。
if (height == 0)
{
height = 1; //防止被零除,因为在调整窗口大小时,为不改变窗口内容的显示效果
//(原来在左上角的东东调整后还在左上角。。。),
//利用的是窗口宽度与高度的比例进行调整的(width/height)
}
glViewport(0, 0, width, height); //重新调整当前视口
glMatrixMode(GL_PROJECTION); //选择投影矩阵
glLoadIdentity(); //重置投影矩阵
gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f);//选择模型观察矩阵
glMatrixMode(GL_MODELVIEW);// 选择模型观察矩阵
glLoadIdentity();//重置模型观察矩阵
}
int InitGL(GLvoid)
{
if (!LoadGLTexture())
{
return FALSE;
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
//如果把以上两句改成注释,那么程序运行后会出现球状“粒子”向四处飞去的情形。
glEnable(GL_TEXTURE_2D); //启用纹理映射
for (k = 0; k < MAX_PARTICLES; k++)
{
particle[k].life = 1.0f;
particle[k].fade = float(rand() % 100) / 1000.0f + 0.003f;
particle[k].r = colors[k*(12 / MAX_PARTICLES)][0];
particle[k].g = colors[k*(12 / MAX_PARTICLES)][0];
particle[k].b = colors[k*(12 / MAX_PARTICLES)][0];
particle[k].xi = float((rand() % 50) - 26.0f)*10.0f;
particle[k].yi = float((rand() % 50) - 25.0f)*10.0f;
particle[k].zi = float((rand() % 50) - 25.0f)*10.0f;
particle[k].xg = 0.0f;
particle[k].yg = -0.8f;
particle[k].zg = 0.0f;
}
glShadeModel(GL_SMOOTH);//启用阴影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);//设置清屏颜色(类似于win32c程序的背景色)
glClearDepth(1.0f);//设置深度缓存
glEnable(GL_DEPTH_TEST);//启用深度测试
glDepthFunc(GL_LEQUAL);//选择深度测试类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//修正透视(如果有必要的话)
return TRUE;
}
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色缓存和深度缓存,防止交叉重复。
glLoadIdentity(); // 重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0, 0,0)
for (k = 0; k < MAX_PARTICLES; k++)
{
float x = particle[k].x;
float y = particle[k].y;
float z = particle[k].z + zoom;
glRotatef(90.0, 0.0f, 0.0f, 1.0f);
glRotatef(10, 0.0f, 1.0f, 0.0f);
//以上两句旋转函数,控制会出的图元旋转,以实现有趣的效果。
glColor4f(particle[k].r, particle[k].g, particle[k].b, particle[k].life);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2d(1, 1); glVertex3f(x + 0.5f, y + 0.5f, z);
glTexCoord2d(0, 1); glVertex3f(x - 0.5f, y + 0.5f, z);
glTexCoord2d(1, 0); glVertex3f(x + 0.5f, y - 0.5f, z);
glTexCoord2d(0, 0); glVertex3f(x - 0.5f, y - 0.5f, z);
glEnd();
particle[k].x += particle[k].xi / (slowdown * 1000);
particle[k].y += particle[k].yi / (slowdown * 1000);
particle[k].z += particle[k].zi / (slowdown * 1000);
particle[k].xi += particle[k].xg;
particle[k].yi += particle[k].yg;
particle[k].zi += particle[k].zg;
//particle[k].life -= particle[k].fade;
/*把语句particle[k].life-=particle[k].fade修改为particle[k].life-=0.2*particle[k].fade;
那么粒子的存活时间增加,爆炸残片停留时间变长,爆炸片较多,像烟花。*/
particle[k].life -= 0.2*particle[k].fade;
//如果把上一句该为注释,那么所有的粒子都在飞行,直到远离。
}
return TRUE;
}
//下面的函数处理窗口销毁,释放一些资源,如果不处理,就会泄露,导致CPU工作异常
GLvoid KillGLWindow(GLvoid)
{
if (fullscreen)
{
ChangeDisplaySettings(NULL, 0);//Nehe教程是这样解释的:
/*我们使用ChangeDisplaySettings(NULL,0)回到原始桌面。将NULL作为第一个参数,
0作为第二个参数传递强制Windows使用当前存放在注册表中的值(缺省的分辨率、色彩深度、刷新频率,等等)
来有效的恢复我们的原始桌面。切换回桌面后,我们还要使得鼠标指针重新可见。*/
ShowCursor(TRUE);//显示光标
}
/*判断是否拥有渲染描述表,这里解释下什么是DC,RC,我们知道,opengl工作强烈依赖于计算机的显卡(显示器),
显卡是一种设备,外部程序访问设备,然后让CPU去处理,就必须找到设备的接口,
这里的DC(着色描述表)和RC(渲染描述表)其实是与显示器相关的一个内存字段,
我们就是通过他们来访问显卡设备,然后对其进行操作的(个人的见解,可能有错误)*/
if (hRC)
{
if (!wglMakeCurrent(NULL, NULL))
{
MessageBox(NULL, TEXT("Release Of DC And RC Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC))
{
MessageBox(NULL, TEXT("Release Rendering Context Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
}
hRC = NULL;
}
if (hDC&&!ReleaseDC(hWnd, hDC))
{
MessageBox(NULL, TEXT("Release Device Context Failed."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
hDC = NULL;
}
if (hWnd&&!DestroyWindow(hWnd))
{
MessageBox(NULL, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
hWnd = NULL;
}
if (!UnregisterClass(TEXT("OpenGL"), hInstance))
{
MessageBox(NULL, TEXT("Could Not Unregister Class."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
hInstance = NULL;
}
}
BOOL CreateGLWindow(TCHAR * title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; //像素模式
WNDCLASS wc; //窗口类
DWORD dwExStyle; //扩展的窗口模式
DWORD dwStyle; //窗口模式
RECT WindowRect; //窗口大小
WindowRect.left = (long)0;
WindowRect.right = (long)width;
WindowRect.top = (long)0;
WindowRect.bottom = (long)height;
fullscreen = fullscreenflag; //显示模式标志
hInstance = GetModuleHandle(NULL); //获取主调进程的实例句柄
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; //设置窗口类型
wc.lpfnWndProc = (WNDPROC)WndProc; //关联窗口过程
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //设置图标
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //设置光标
wc.hbrBackground = NULL; //没有背景,因为上面已经设置了窗口背
wc.lpszMenuName = NULL; //没有菜单,当然你也可以设置菜单
wc.lpszClassName = TEXT("OpenGL"); //窗口类名,此时为OpenGL
if (!RegisterClass(&wc))
{
MessageBox(NULL, TEXT("Failed To Register The Window Class."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if (fullscreen) //判断是否处于全屏模式,是的话,进行一下处理
{
DEVMODE dmScreenSettings; //设备模式
//清零,当用户多次选择全屏时,都会执行这里的代码,但我们并不知道我们所申请的内存是否可用,所以每次进来都要清零
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
//初始化
dmScreenSettings.dmSize = sizeof(dmScreenSettings); //dmScreenSettings结构大小
dmScreenSettings.dmPaperWidth = width; //屏幕宽度
dmScreenSettings.dmPelsHeight = height; //屏幕高度
dmScreenSettings.dmBitsPerPel = bits; //每位像素的色彩深度
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;//设置设备相关量,包括宽度,色彩深度,高度。
//尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
//如果失败,让用户区处理选择
if (MessageBox(NULL, TEXT("您当前PC不支持全屏模式,确定是否使用窗口模式!"), TEXT("提示"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
{
fullscreen = FALSE;
}
else
{
MessageBox(NULL, TEXT("应用程序即将推出"), TEXT("失败"), MB_OK | MB_ICONSTOP);
return FALSE;
}
}
}
//这里是紧接着上面的设置显示模式,如果成功的话(fullscreen为真),设置窗口显示模式
if (fullscreen)
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP;
ShowCursor(FALSE);
}
//如果失败的话,也要进行相应的处理
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW;
}
//用户已经改变了窗口(大小或方位),要进行调整
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
//创建窗口,如果失败,销毁它(包括一切相关资源)
if (!(hWnd = CreateWindowEx(dwExStyle, TEXT("OpenGL"), (LPCWSTR)title, dwStyle | WS_CLIPCHILDREN, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, NULL, NULL, hInstance, NULL)))
{
KillGLWindow();
MessageBox(NULL, TEXT("Window Creation Error."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
//下面利用一个结构体来设置像素模式
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),//上述格式描述符的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | //格式支持窗口
PFD_SUPPORT_OPENGL | //格式支持opengl
PFD_DOUBLEBUFFER, //支持双缓冲
PFD_TYPE_RGBA, //支持RGBA(Red,Green,Blue,Alpha,即三原色和透明度,任何色彩都有这组成)
bits, //色彩深度
0, 0, 0, 0, 0, 0, //忽略的某些色彩位
0, //没有ALPHA缓冲
0, //忽略Shift Bit
0, //无累加缓存
0, 0, 0, 0, // 忽略聚集位
16, // 16位 Z-缓存 (深度缓存)
0, //无模板缓存
0, //无辅助缓存
PFD_MAIN_PLANE, //主会绘图层
0, //保留位
0, 0, 0 //忽略层遮罩
};
//尝试获取设备上下文
if (!(hDC = GetDC(hWnd)))
{
KillGLWindow();
MessageBox(NULL, TEXT("Can't Create A GL Device Context."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
{
KillGLWindow();
MessageBox(NULL, TEXT("Can't Find A Suitable PixelFormat."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
//找到后设置它
if (!SetPixelFormat(hDC, PixelFormat, &pfd))
{
KillGLWindow();
MessageBox(NULL, L"设置像素模式失败.", L"失败", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
//创建渲染上下文
if (!(hRC = wglCreateContext(hDC)))
{
KillGLWindow();
MessageBox(NULL, TEXT("Can't Create A GL Rendering Context."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
//激活渲染上下文
if (!wglMakeCurrent(hDC, hRC))
{
KillGLWindow();
MessageBox(NULL, TEXT("Can't Activate The GL Rendering Context"), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
ShowWindow(hWnd, SW_SHOW); //如果一切顺利就显示窗口
SetForegroundWindow(hWnd); //将窗口暂时挂在Z轴最前端
SetFocus(hWnd); //将焦点调整到窗口
ReSizeGLScreen(width, height); //重新设置窗口大小
//初始化OpenGL环境
if (!InitGL())
{
KillGLWindow();
MessageBox(NULL, TEXT("Initialization Failed."), TEXT("ERROR"), MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_ACTIVATE: //监视窗口激活消息
{
if (!HIWORD(wParam))
{
active = TRUE;
}
else
{
active = FALSE;
}
return 0;
}
case WM_SYSCOMMAND: //监视系统中断,阻止一切中断
{
switch (wParam)
{
case SC_SCREENSAVE: //屏保中断
case SC_MONITORPOWER: //显示器节电中断
return 0;
}
break;
}
case WM_CLOSE: //退出
{
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN: //按键
{
keys[wParam] = TRUE;
return 0;
}
case WM_KEYUP: //释放按键
{
keys[wParam] = FALSE;
return 0;
}
case WM_SIZE: //窗口大小改变
{
ReSizeGLScreen(LOWORD(lParam), HIWORD(lParam));
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam); //缺省处理
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
BOOL done = FALSE;
if (!CreateGLWindow(TEXT("Waving Picture"), 640, 480, 16, fullscreen))
{
return 0;
}
while (!done)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
done = TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
if ((active&&!DrawGLScene()) || keys[VK_ESCAPE])
{
done = TRUE;
}
else
{
SwapBuffers(hDC);
}
if (keys[VK_F1])
{
keys[VK_F1] = FALSE;
KillGLWindow();
fullscreen = !fullscreen;
if (!CreateGLWindow(TEXT("Waving Picture"), 640, 480, 16, fullscreen))
{
return 0;
}
}
}
}
KillGLWindow();
return (msg.wParam);
}