OpenGL教程2.docx
《OpenGL教程2.docx》由会员分享,可在线阅读,更多相关《OpenGL教程2.docx(15页珍藏版)》请在冰豆网上搜索。
OpenGL教程2
#include
#include//HeaderFileForWindows
#include//HeaderFileForStandardInput/Output
#include//HeaderFileForTheOpenGL32Library
#include//HeaderFileForTheGLu32Library
#include//HeaderFileForTheGlauxLibrary
HGLRChRC=NULL;//窗口着色描述表句柄
HDChDC=NULL;//OpenGL渲染描述表句柄
HWNDhWnd=NULL;//保存我们的窗口句柄
HINSTANCEhInstance;//保存程序的实例
boolkeys[256];//保存键盘按键的数组
boolactive=TRUE;//窗口的活动标志,缺省为TRUE
boolfullscreen=TRUE;//全屏标志缺省,缺省设定成全屏模式
GLfloatxrot;//X转变量
GLfloatyrot;//Y转变量
GLfloatzrot;//Z转变量
GLuinttexture[1];//存储一个纹理
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);//WndProc的定义
AUX_RGBImageRec*LoadBMP(char*Filename){//载入位图图象
FILE*File=NULL;//File句柄
if(!
Filename){//确定文件
returnNULL;
}
File=fopen(Filename,"r");//打开文件
if(File){//存在?
fclose(File);//关闭文件
returnauxDIBImageLoad(Filename);//载入位图,返回指针
}
returnNULL;//载入失败
}
intLoadGLTextures(){//载入位图并转换成纹理
intStatus=FALSE;//状态指示器
/*然后设置一个叫做Status的变量。
我们使用它来跟踪是否能够载入位图以及能否创建纹理。
Status缺省设为FALSE(表示没有载入或创建任何东东)。
*/
AUX_RGBImageRec*TextureImage[1];//创建纹理的存储空间
memset(TextureImage,0,sizeof(void*)*1);//将指针设为NULL,
//清除图像记录,确保其内容为空
if(TextureImage[0]=LoadBMP("Data/NeHe.bmp")){
//载入位图,检查有无错误,如果位图没找到则退出
Status=TRUE;
glGenTextures(1,&texture[0]);//创建纹理
/*使用来自位图数据生成的典型纹理。
现在使用中TextureImage[0]的数据创建纹理。
第一行glGenTextures(1,&texture[0])告诉OpenGL我们想生成一个纹理名字(如果您想载入多个纹理,加大数字)。
值得注意的是,开始我们使用GLuinttexture[1]来创建一个纹理的存储空间,您也许会认为第一个纹理就是存放在&texture[1]中的,但这是错的。
正确的地址应该是&texture[0]。
同样如果使用GLuinttexture[2]的话,第二个纹理存放在texture[1]中。
『译者注:
学C的,在这里应该没有障碍,数组就是从零开始的嘛。
』第二行glBindTexture(GL_TEXTURE_2D,texture[0])告诉OpenGL将纹理名字texture[0]绑定到纹理目标上。
2D纹理只有高度(在Y轴上)和宽度(在X轴上)。
主函数将纹理名字指派给纹理数据。
本例中我们告知OpenGL,&texture[0]处的内存已经可用。
我们创建的纹理将存储在&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);
//*生成纹理下来我们创建真正的纹理。
下面一行告诉OpenGL此纹理是一个2D纹理(GL_TEXTURE_2D)。
参数“0”代表图像的详细程度,通常就由它为零去了。
参数三是数据的成分数。
因为图像是由红色数据,绿色数据,蓝色数据三种组分组成。
TextureImage[0]->sizeX是纹理的宽度。
如果您知道宽度,您可以在这里填入,但计算机可以很容易的为您指出此值。
TextureImage[0]->sizey是纹理的高度。
参数零是边框的值,一般就是“0”。
GL_RGB告诉OpenGL图像数据由红、绿、蓝三色数据组成。
GL_UNSIGNED_BYTE意味着组成图像的数据是无符号字节类型的。
最后...TextureImage[0]->data告诉OpenGL纹理数据的来源。
此例中指向存放在TextureImage[0]记录中的数据。
*/
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
/*线形滤波下面的两行告诉OpenGL在显示图像时,当它比放大得原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小得比原始得纹理小(GL_TEXTURE_MIN_FILTER)时OpenGL采用的滤波方式。
通常这两种情况下我都采用GL_LINEAR。
这使得纹理从很远处到离屏幕很近时都平滑显示。
使用GL_LINEAR需要CPU和显卡做更多的运算。
如果您的机器很慢,您也许应该采用GL_NEAREST。
过滤的纹理在放大的时候,看起来斑驳的很『译者注:
马赛克啦』。
您也可以结合这两种滤波方式。
在近处时使用GL_LINEAR,远处时GL_NEAREST*/
}
if(TextureImage[0]){//纹理存在?
if(TextureImage[0]->data){//纹理图像存在?
free(TextureImage[0]->data);//释放内存
}
free(TextureImage[0]);//释放图像结构
}
/*现在我们释放前面用来存放位图数据的内存。
我们先查看位图数据是否存放在处。
如果是的话,再查看数据是否已经存储。
如果已经存储的话,删了它。
接着再释放TextureImage[0]图像结构以保证所有的内存都能释放。
*/
returnStatus;
}
GLvoidReSizeGLScene(GLsizeiwidth,GLsizeiheight){
if(height==0){
height=1;
}
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
intInitGL(GLvoid){
if(!
LoadGLTextures()){//调用纹理载入子例程
returnFALSE;
}
glEnable(GL_TEXTURE_2D);//启用纹理映射
glShadeModel(GL_SMOOTH);
glClearColor(0.0f,0.0f,0.0f,0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
returnTRUE;
}
intDrawGLScene(GLvoid){
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
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]);
/*选择我们使用的纹理。
如果您在您的场景中使用多个纹理,您应该使用来glBindTexture(GL_TEXTURE_2D,texture[所使用纹理对应的数字])选择要绑定的纹理。
当您想改变纹理时,应该绑定新的纹理。
有一点值得指出的是,您不能在glBegin()和glEnd()之间绑定纹理,必须在glBegin()之前或glEnd()之后绑定。
注意我们在后面是如何使用glBindTexture来指定和绑定纹理的。
*/
glBegin(GL_QUADS);
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);
//BackFace
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);
//TopFace
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);
//BottomFace
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);
//Rightface
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);
//LeftFace
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();
/*为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。
如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。
glTexCoord2f的第一个参数是X坐标。
0.0f是纹理的左侧。
0.5f是纹理的中点,1.0f是纹理的右侧。
glTexCoord2f的第二个参数是Y坐标。
0.0f是纹理的底部。
0.5f是纹理的中点,1.0f是纹理的顶部。
所以纹理的左上坐标是X:
0.0f,Y:
1.0f,四边形的左上顶点是X:
-1.0f,Y:
1.0f。
其余三点依此类推。
试着玩玩glTexCoord2f的X,Y坐标参数。
把1.0f改为0.5f将只显示纹理的左半部分,把0.0f改为0.5f将只显示纹理的右半部分。
*/
xrot+=0.3f;
yrot+=0.2f;
zrot+=0.4f;
returnTRUE;
}
GLvoidKillGLWindow(GLvoid){//正常销毁窗口
if(fullscreen){//我们处于全屏模式吗?
ChangeDisplaySettings(NULL,0);//是的话,切换回桌面
ShowCursor(TRUE);//显示鼠标指针
}
if(hRC){//我们拥有OpenGL渲染描述表吗?
if(!
wglMakeCurrent(NULL,NULL)){//我们能否释放DC和RC描述表?
MessageBox(NULL,"释放DC或RC失败。
","关闭错误",MB_OK|MB_ICONINFORMATION);
}
if(!
wglDeleteContext(hRC)){//我们能否删除RC?
MessageBox(NULL,"释放RC失败。
","关闭错误",MB_OK|MB_ICONINFORMATION);
}
hRC=NULL;//将RC设为NULL
}
if(hDC&&!
ReleaseDC(hWnd,hDC)){//我们能否释放DC?
MessageBox(NULL,"释放DC失败。
","关闭错误",MB_OK|MB_ICONINFORMATION);
hDC=NULL;//将DC设为NULL
}
if(hWnd&&!
DestroyWindow(hWnd)){//能否销毁窗口?
MessageBox(NULL,"释放窗口句柄失败。
","关闭错误",MB_OK|MB_ICONINFORMATION);
hWnd=NULL;//将hWnd设为NULL
}
if(!
UnregisterClass("OpenG",hInstance)){//能否注销类?
MessageBox(NULL,"不能注销窗口类。
","关闭错误",MB_OK|MB_ICONINFORMATION);
hInstance=NULL;//将hInstance设为NULL
}
}
BOOLCreateGLWindow(char*title,intwidth,intheight,intbits,boolfullscreenflag){
GLuintPixelFormat;//保存查找匹配的结果
WNDCLASSwc;//窗口类结构
DWORDdwExStyle;//扩展窗口风格
DWORDdwStyle;//窗口风格
RECTWindowRect;//取得矩形的左上角和右下角的坐标值
WindowRect.left=(long)0;//将Left设为0
WindowRect.right=(long)width;//将Right设为要求的宽度
WindowRect.top=(long)0;//将Top设为0
WindowRect.bottom=(long)height;//将Bottom设为要求的高度
fullscreen=fullscreenflag;//设置全局全屏标志
hInstance=GetModuleHandle(NULL);//取得我们窗口的实例
wc.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;//移动时重画,并为窗口取得DC
wc.lpfnWndProc=(WNDPROC)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;//GL不需要背景
wc.lpszMenuName=NULL;//不需要菜单
wc.lpszClassName="OpenG";//设定类名字
if(!
RegisterClass(&wc)){//尝试注册窗口类
MessageBox(NULL,"注册窗口失败","错误",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//退出并返回FALSE
}
if(fullscreen){//要尝试全屏模式吗?
DEVMODEdmScreenSettings;//设备模式
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));//确保内存清空为零
dmScreenSettings.dmSize=sizeof(dmScreenSettings);//Devmode结构的大小
dmScreenSettings.dmPelsWidth=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,"全屏模式在当前显卡上设置失败!
\n使用窗口模式?
","NeHeG",MB_YESNO|MB_ICONEXCLAMATION)==IDYES){
fullscreen=FALSE;//选择窗口模式(Fullscreen=FALSE)
}else{
MessageBox(NULL,"程序将被关闭","错误",MB_OK|MB_ICONSTOP);
returnFALSE;//退出并返回FALSE
}
}
}
if(fullscreen){//仍处于全屏模式吗?
/*如果我们仍处于全屏模式,设置扩展窗体风格为WS_EX_APPWINDOW,这将强制我们的窗体可见时处于最前面。
再将窗体的风格设为WS_POPUP。
这个类型的窗体没有边框,使我们的全屏模式得以完美显示。
最后我们禁用鼠标指针。
当您的程序不是交互式的时候,在全屏模式下禁用鼠标指针通常是个好主意。
如果我们使用窗口而不是全屏模式,我们在扩展窗体风格中增加了WS_EX_WINDOWEDGE,增强窗体的3D感观。
窗体风格改用WS_OVERLAPPEDWINDOW,创建一个带标题栏、可变大小的边框、菜单和最大化/最小化按钮的窗体。
*/
dwExStyle=WS_EX_APPWINDOW;//扩展窗体风格
dwStyle=WS_PO