我们知道,现在的3D引擎有OPENGL(Openning Graphics Language,开放性图形语言,opengl是状态机)和微软公司的D3D(Directed 3 Dimentional),D3D虽然是后起之秀,而且在windows操作系统上具有良好的工作效率,即使如此,我个人认为D3D永远无法取代opengl在业界的地位,毕竟opengl是3D的标准,因为它是跨平台的(不管是windows操作系统,或者Linux,UINX,Android....都能很好的运行,),高效率的,虽然D3D在某些方面做得比opengl要好,但我个人认为,没有opengl,就没有D3D,微软是实实在在的剽窃者(包括微软的MS SQL SERVER都是剽窃来的),尽管现在opengl一直世风日下,DX的出现更使其应用量大大减少,但是opengl毕竟是3D引擎的标准,是永远不会被替代的重要角色,在某些方面还是要用到opengl的....
好了,现在我们以C+win32 sdk+Opengl开发库的方式一起写opengl程序,虽然我自己也是初学者,但是这全当做备份好了,如果读者对所贴出的东东有任何意见,可以与我联系。
(注:我学习opengl参考的书籍是《opengl开发宝典》和《NeHe的OPENGL教程》,不过如果想真正成为一个游戏开发或者虚拟开发人员,深入了解OPENGL,有关计算机图形学的知识必须精通(我自己是自学的),高等数学必须好,线性代数必须精通),我们知道,写opengl程序有多种开发方式(基于c,基于win32...),不过,为了与前面我写的WIN32 sdk相关联,我用的是基于WIN32 的方式,因为这样会更加良好的工作在Windows操作系统之上(opengl是跨平台的,如果你想让你的opengl程序在其他操作系统上跑起来,可以采取其他方式编写,当然库还是opengl库),下面就开始写opengl程序。
《opengl开发宝典》采取的是C方式,《NeHe的OPENGL教程》采取的是WIN32方式,两本书都很好,不过,我发现Nehe的教程简直是太经典了,所以一下代码均采用他的方案。
opengl第一天:完善opengl程序框架(openglFrame):
一个程序的框架的搭建是程序友好性的体现,Nehe的程序框架是我见过的算是最好的了,下面就列出Nehe框架源码(没有任何更改,只有注释是我自己加上的):
//#include "stdafx.h"
//头文件
#include<gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.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程序内所有消息都在这里响应
GLvoid ReSizeGLScreen(GLsizei width,GLsizei height)//当窗口大小改变时进行窗口窗调
{
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();//重置模型观察矩阵
}
//下面对opengl程序进行初始化
int InitGL(GLvoid)
{
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;
}
//下面的函数用来实现你想要绘制的任何东东,不过这个程序只是opengl的框架程序,没有绘制其他东西
int DrawGLScreen(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象
glLoadIdentity();//重置模型观察矩阵,
return TRUE;
}
//下面的函数处理窗口销毁,释放一些资源,如果不处理,就会泄露,导致CPU工作异常
GLvoid KillGLWindow(GLvoid)
{
if (fullscreen)
{
ChangeDisplaySettings(NULL,0);//Nehe教程是这样解释的:
//我们使用ChangeDisplaySettings(NULL,0)回到原始桌面。将NULL作为第一个参数,0作为第二个参数传递强制Windows使用当前存放在注册表中的值(缺省的分辨率、色彩深度、刷新频率,等等)来有效的恢复我们的原始桌面。切换回桌面后,我们还要使得鼠标指针重新可见。
ShowCursor(TRUE);//显示光标
}
if (hRC)//判断是否拥有渲染描述表,这里解释下什么是DC,RC,我们知道,opengl工作强烈依赖于计算机的显卡(显示器),显卡是一种设备,外部程序访问设备,然后让CPU去处理,就必须找到设备的接口,这里的DC(着色描述表)和RC(渲染描述表)其实是与显示器相关的一个内存字段,我们就是通过他们来访问显卡设备,然后对其进行操作的(个人的见解,可能有错误)
{
if (!wglMakeCurrent(NULL,NULL))//释放DC和RC
{
MessageBox(NULL,TEXT("释放DC和RC失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC))//删除RC
{
MessageBox(NULL,TEXT("释放渲染上下文失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
}
hRC=NULL;
}
if (hDC && !ReleaseDC(hWnd,hDC))//删除DC
{
MessageBox(NULL,TEXT("释放设备上下文失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
hDC=NULL;
}
if (hWnd && !DestroyWindow(hWnd))//销毁窗口
{
MessageBox(NULL,TEXT("销毁窗口失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
hWnd=NULL;
}
if (!UnregisterClass(TEXT("OpenGL"),hInstance))//销毁注册的窗口类
{
MessageBox(NULL,TEXT("销毁注册类失败"),TEXT("失败"),MB_OK | MB_ICONINFORMATION);
hInstance=NULL;
}
}
//下面的函数创建一个安全的opengl窗口,包括标题,窗口大小,显示模式,像素....
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;//没有菜单,当然你也可以设置菜单,具体方案可参考我前面的win32SDK篇的菜单篇
wc.lpszClassName=TEXT("OpenGL");//窗口类名
if (!RegisterClass(&wc))//尝试注册窗口类
{
MessageBox(NULL,TEXT("注册窗口类失败"),TEXT("失败"),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;//设置设备相关量,包括宽度,色彩深度,高度。
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)//尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
{
//如果失败,让用户区处理选择
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"),
title,
dwStyle|
WS_CLIPSIBLINGS|
WS_CLIPCHILDREN,
0,
0,
WindowRect.right-WindowRect.left,
WindowRect.bottom-WindowRect.top,
NULL,
NULL,
hInstance,
NULL)))
{
KillGLWindow();
MessageBox(NULL,TEXT("创建窗口失败"),TEXT("失败"),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("创建设备上下文失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION );
return FALSE;
}
//尝试寻找响应的像素模式
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
{
KillGLWindow();
MessageBox(NULL,TEXT("没有发现可使用的像素模式"),TEXT("失败"),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("创建渲染上下文失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
//激活渲染上下文
if (!wglMakeCurrent(hDC,hRC))
{
KillGLWindow();
MessageBox(NULL,TEXT("激活渲染上下文失败"),TEXT("失败"),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("初始化opengl环境失败"),TEXT("失败"),MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
//一切成功,顺利返回
return TRUE;
}
//下面的东东我就不解释了,和WIN32sdk没多大区别,
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 (MessageBox(NULL,TEXT("是否在全屏模式下运行"),TEXT("提示"),MB_YESNO |MB_ICONQUESTION )==IDNO)
{
fullscreen=FALSE;
}
if (!CreateGLWindow(TEXT("天策的opengl程序框架"),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)
{
if (keys[VK_ESCAPE])
{
done=TRUE;
}
else
{
DrawGLScreen();
SwapBuffers(hDC);
}
}
if (keys[VK_F1])
{
keys[VK_F1]=FALSE;
KillGLWindow();
fullscreen=!fullscreen;
if (!CreateGLWindow(TEXT("天策的opengl程序框架"),640,480,16,fullscreen))
{
return 0;
}
}
}
}
KillGLWindow();
return (msg.wParam);
}
OpenGL第二天:在窗口内绘制简单图形
上面的框架实现了一个友好的OpenGL程序窗口,下面我们来在窗口内绘制一点简单的东西(以绘制三角形和正方形为例):
框架不需要修改,只要在 DrawGLScreen(GLvoid)里添加即可:
int DrawGLScreen(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象
glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
//顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
glVertex3f(-1.0f,-1.0f,0.0f);//左
glVertex3f(1.0f,-1.0f,0.0f);//右
glEnd();//绘制结束
glTranslatef(4.0f,0.0f,0.0f); // 右移3单位
glBegin(GL_QUADS); // 绘制正方形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd(); // 正方形绘制结束
return TRUE;
}
OpenGL第三天:为所绘制的图形添加色彩
上面画出的图形默认为白色,实在太单调,下面让它看起来更加美观一点,只要在绘制每个点时指定颜色即可,每个区域的颜色取决于离他最近的你所设定的颜色:
int DrawGLScreen(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象
glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
//顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
glColor3f(1.0f,0.0f,0.0f); // 设置当前色为红色
glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
glColor3f(0.0f,1.0f,0.0f); // 设置当前色为绿色
glVertex3f(-1.0f,-1.0f,0.0f);//左
glColor3f(0.0f,0.0f,1.0f); // 设置当前色为蓝色
glVertex3f(1.0f,-1.0f,0.0f);//右
glEnd();//绘制结束
glTranslatef(4.0f,0.0f,0.0f); // 右移3单位
glBegin(GL_QUADS); // 绘制正方形
glColor3f(0.5f,0.0f,1.0f); //这里的设置将影响到整个图形
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd(); // 正方形绘制结束
return TRUE;
}
OpenGL第四天:实现动态图形
下面在实现对图形的动态展示:
首先说明一下原理:
就好像我们挥手一样,我们看到的是一条弧线,我们挥手的每个时刻,手的位置都是不同的,由于挥手的速度很快,加上视觉暂留,所以形成弧线,如果放慢速度,我们看到的就不是弧线了,利用这个原理,计算机动画就形成了,上面说过,显示器并不是静态的,而是以一定的频率在刷新(大概几十毫秒),这个频率足以让我们看不见闪烁,OPENGL利用每次刷屏的时候,改变图形的在显示屏的位置,就形成了动画。如果你查看DrawGLScreen(GLvoid)在程序里面的位置(不是申明或定义),就会发现我说的是完全正确的。
//先增加两个全局变量,分别表示两个图形的旋转度数
GLfloat rtri; // 用于三角形的角度 GLfloat rquad; // 用于四边形的角度
//然后在DrawGLScreen(GLvoid)里添加
int DrawGLScreen(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象
glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0)
glTranslatef(-2.0f,0.0f,-6.0f);//实现坐标移动,X坐标向左移动2个单位,Y坐标不变,Z坐标向内移动6个单位,为用户能看到效果做准备
//顺便说明一下,opengl程序的坐标空间是三维的(X,Y,Z),从左到右,X坐标渐增,从下到上,Y坐标渐增,从内到外,Z坐标渐增。z坐标的作用是它越小,物体离用户越远,物体看起来越小,所以上面要进行Z轴的移动是必要的,否则你将发现看不到图形,因为当Z轴为0时,图形大得让屏幕无法容纳
glRotatef(rtri,0.0f,1.0f,0.0f); // 绕Y轴旋转三角形
glBegin(GL_TRIANGLES);//利用glBegin和glEnd组合实现绘图,这里是绘制矩形GL_TRIANGLES
glColor3f(1.0f,0.0f,0.0f); // 设置当前色为红色
glVertex3f(0.0f,1.0f,0.0f);//分别绘制三角形的三个点,这里是上
glColor3f(0.0f,1.0f,0.0f); // 设置当前色为红色
glVertex3f(-1.0f,-1.0f,0.0f);//左
glColor3f(0.0f,0.0f,1.0f); // 设置当前色为红色
glVertex3f(1.0f,-1.0f,0.0f);//右
glEnd();//绘制结束
glLoadIdentity(); // 重置模型观察矩阵
glTranslatef(1.5f,0.0f,-6.0f); // 右移3单位
glRotatef(rquad,1.0f,0.0f,0.0f); // 绕X轴旋转四边形
glBegin(GL_QUADS); // 绘制正方形
glColor3f(0.5f,0.0f,1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f); // 左上
glVertex3f( 1.0f, 1.0f, 0.0f); // 右上
glVertex3f( 1.0f,-1.0f, 0.0f); // 左下
glVertex3f(-1.0f,-1.0f, 0.0f); // 右下
glEnd(); // 正方形绘制结束
rtri+=0.2f; // 增加三角形的旋转变量
rquad-=0.15f; // 减少四边形的旋转变量
return TRUE;
}
OpenGL第五天:进入3D空间
OpenGL作为3D引擎,如果我们学习OPENGL不写3D程序的话....白学了(其他任何API均能实现二位空间)
其实3D程序也是很简单的,绘制3D图形,只要按照现实中的逻辑,我们学习数学时绘图一样,先画出三维坐标系,然后分别在X,Y,Z轴上画出顶点,连线即可。
下面参照NeHe的程序,列出代码:
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-1.5f,0.0f,-6.0f);
glRotatef(rtri,0.0f,1.0f,0.0f);
//绘制棱锥
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f( 1.0f,-1.0f, -1.0f);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f( 1.0f,-1.0f, -1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(-1.0f,-1.0f, -1.0f);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glEnd();
glLoadIdentity();
glTranslatef(1.5f,0.0f,-7.0f);
glRotatef(rquad,1.0f,1.0f,1.0f);
//绘制立方体
glBegin(GL_QUADS);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f( 1.0f, 1.0f,-1.0f);
glVertex3f(-1.0f, 1.0f,-1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glColor3f(1.0f,0.5f,0.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(1.0f,1.0f,0.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f, 1.0f,-1.0f);
glVertex3f( 1.0f, 1.0f,-1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(1.0f,0.0f,1.0f);
glVertex3f( 1.0f, 1.0f,-1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
glEnd();
rtri+=0.2f;
rquad-=0.15f;
return TRUE;
}
OpenGL第六天:3D贴图
opengl的3D贴图称为纹理,CF里花哨的图景我想就是利用了纹理(当然CF里烟、雾、雪花...opengl均能实现),下面实现简单纹理:
首先增加几个全局变量:
GLfloat xrot; // x轴的旋转步进
GLfloat yrot; // y轴的旋转步进
GLfloat zrot; // z轴的旋转步进
GLuint texture[1]; // 存储一个纹理
下面是核心部分,为程序载入纹理:
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 LoadGLTextures() { int Status=FALSE; //状态标记
AUX_RGBImageRec *TextureImage[1]; //创建一个纹理存储空间存储纹理信息
memset(TextureImage,0,sizeof(void *)*1); // 清空存储空间
if (TextureImage[0]=LoadBMP(L"zjw.bmp"))//载入图片纹理,图片放在可执行文件的同一目录下,调试状态下放在debug同一目录下,“zjw.bmp”为文件名
{ 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);//线性滤波处理 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//线性滤波处理 } //纹理生成成功后要释放资源 if (TextureImage[0]) { if (TextureImage[0]->data) { free(TextureImage[0]->data);
}
free(TextureImage[0]);
}
return Status;
}
//然后在处理初始化opengl环境时要载入纹理
int InitGL(GLvoid) {
if (!LoadGLTextures()) { return FALSE; } glEnable(GL_TEXTURE_2D); //启用纹理映射 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; }
//修改 DrawGLScreen(GLvoid)内容,绘制具有纹理的立方体
int DrawGLScreen(GLvoid) { glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);//清楚屏幕和深度缓存,为什么要这样做呢,因为我们要利用显示器来绘制东东,就要明白其原理,我们在显示屏上看到的东西并不是一成不变的,而是时刻以某种频率在刷新(这里就是刷新,同时清除缓存残留),由于人眼有视觉暂留,导致我们感觉不到闪屏现象
glLoadIdentity();//重置模型观察矩阵,使屏幕中心点位于三维坐标原点(0,0,0) glTranslatef(0.0f,0.0f,-5.0f);
glRotatef(xrot,1.0f,0.0f,0.0f); glRotatef(yrot,0.0f,1.0f,0.0f); glRotatef(zrot,0.0f,0.0f,1.0f);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS); // 立方体的正前方
解释下glTexCoord2f(int x,int y)函数,这个函数的作用是每当我们绘制一个点时,就将纹理(位图)的响应的点对应上,
glTexCoord2f 的第一个参数是X坐标。 0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。 glTexCoord2f 的第二个参数是Y坐标。 0.0f 是纹理的底部。 0.5f 是纹理的中点, 1.0f 是纹理的顶部。比如下面第一行,我们是(逆时针)绘制立方体的正前方的左下角,“左”对应X=0,“下”对应Y=0。
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 后方 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 上方 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 下方 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 右方 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 左方 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd();
//刷新位置,旋转立方体 xrot+=0.6f; yrot+=0.4f; zrot+=0.3f;
return TRUE; }
//效果如下
OpenGL第七天:光照和键盘控制
连载中......................