opengl 曲面贴图
opengl 2011-02-12 15:31:16 阅读5 评论1 字号:大中小 订阅
OpenGL曲面纹理贴图技术--波浪的模拟
学过OpenGL的人都很容易的把图片贴到四边形和三角行上,但将纹理贴到一般的曲面上认为很困难,其实通过本文的简单分析,其实很简单。本文以波浪模拟为例,来介绍一般纹理贴图技术,大家很容易举一反三来模拟其他的现象。代码的蓝本主要来自NeHe。
1.简单的数学知识介绍
向量的乘积(这里指叉乘)。
用程序写出来如下。
//三维点定义
struct cvPoint
{
float x,y,z; //点的坐标
};
//矢量相乘C=A*B (方向符合右手定则)
void vect_mult(struct cvPoint *A, struct cvPoint *B, struct cvPoint *C)
{
C->x=A->y * B->z -A ->z * B->y;
C->y=A->z * B->x -A ->x * B->z;
C->z=A->x * B->y -A ->y * B->x;
}
四边形的法向选取
四边形的法向选取采用四边形两条对角线向量相乘。问什么不采用四边形的边相乘,因为四边形四顶点点并不一定共平面,采用四边形两条对角线向量相乘,显然要更精确一些。
波浪方程(其实就是正弦或余弦函数绕z轴旋转的)
double t=0.0;//相位
double sf(double x,double y)
{
return cos(sqrt(x*x+y*y)+t);
}
2.创建纹理(NeHe)
不清楚的可参考NeHe的教程,清楚地可跳过本节。
GLuint texture[3];
AUX_RGBImageRec *LoadBMP(char *Filename) // 载入位图图象
{
FILE *File=NULL; // 文件句柄
if(!Filename) // 确保文件名已提供
{
return NULL; // 如果没提供,返回 NULL
}
File=fopen(Filename,"r"); // 尝试打开文件
if(File) // 文件存在么?
{
fclose(File); // 关闭句柄
return auxDIBImageLoad(Filename); // 载入位图并返回指针
}
return NULL; // 如果载入失败,返回 NULL
}
int LoadGLTextures() // 载入位图(调用上面的代码)并转换成纹理
{
int Status=FALSE; // 状态指示器
AUX_RGBImageRec *TextureImage[1]; // 创建纹理的存储空间
memset(TextureImage,0,sizeof(void *)*1); // 将指针设为 NULL
// 载入位图,检查有无错误,如果位图没找到则退出
if(TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
{
Status=TRUE; // 将 Status 设为 TRUE
glGenTextures(3, &texture[0]); // 创建纹理
// 创建 Nearest 滤波贴图
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
// 创建线性滤波纹理
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
// 创建 MipMapped 纹理
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
if (TextureImage[0]) // 纹理是否存在
{
if (TextureImage[0]->data) // 纹理图像是否存在
{
free(TextureImage[0]->data); // 释放纹理图像占用的内存
}
free(TextureImage[0]); // 释放图像结构
}
return Status; // 返回 Status
}
3.曲面纹理贴图的关键
曲面纹理贴图的关键就是把曲面分成小块(本文采用四边形),纹理贴图也要相对应的分成小块,然后相对应的把纹理贴到相对应的曲面小块。注意一定要相对应,连顶点都要相对应。 先将曲面分割,并存储其分割的顶点。
float ver[21][21][3];
GLvoid initVer()
{
int i,j;
float dx=D_PI*8/20.0,dy=D_PI*8/20.0;
for(i=0;i<=20;i++)
for(j=0;j<=20;j++)
{
ver[i][j][0]=i*dx-D_PI*4;
ver[i][j][1]=j*dy-D_PI*4;
ver[i][j][2]=(float)sf(ver[i][j][0],ver[i][j][1]);
}
}
//开始贴图
cvPoint pa,pb,pc;
for(int i=0;i<20;i++)
for(int j=0;j<20;j++)
{
//第一条对角线
pa.x=ver[i+1][j+1][0]-ver[i][j][0];
pa.y=ver[i+1][j+1][1]-ver[i][j][1];
pa.z=ver[i+1][j+1][2]-ver[i][j][2];
//第二条对角线
pb.x=ver[i][j+1][0]-ver[i+1][j][0];
pb.y=ver[i][j+1][1]-ver[i+1][j][1];
pb.z=ver[i][j+1][2]-ver[i+1][j][2];
vect_mult(&pa,&pb,&pc);//计算法向,注意顺序
glNormal3f(pc.x,pc.y,pc.z);
//注意要一一对应
glBegin(GL_QUADS);
glTexCoord2f(0.05*i,0.05*j);
glVertex3f(ver[i][j][0],ver[i][j][1],ver[i][j][2]);
glTexCoord2f(0.05*(i+1),0.05*j);
glVertex3f(ver[i+1][j][0],ver[i+1][j][1],ver[i+1][j][2]);
glTexCoord2f(0.05*(i+1),0.05*(j+1));
glVertex3f(ver[i+1][j+1][0],ver[i+1][j+1][1],ver[i+1][j+1][2]);
glTexCoord2f(0.05*i,0.05*(j+1));
glVertex3f(ver[i][j+1][0],ver[i][j+1][1],ver[i][j+1][2]);
glEnd();
}
动起来这个So Simple!改变相位即可。
t+=0.05;
搞定,曲面纹理贴图技术就这么简单。
完整的代码(运行前,先在创建Data文件夹,并放一张256×256的位图以供加载纹理)
// glqm.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include <windows.h> // Windows的头文件
#include <stdio.h> // 标准输入/输出库的头文件
#include <math.h>
#include <GL/glew.h>
#pragma comment (lib, "glew32.lib")
#include <GL/glut.h>
#pragma comment (lib, "winmm.lib") /* link with Windows MultiMedia lib */
#pragma comment (lib, "opengl32.lib") /* link with Microsoft OpenGL lib */
#pragma comment (lib, "glu32.lib") /* link with OpenGL Utility lib */
#pragma comment (lib, "glut32.lib") /* link with Win32 GLUT lib */
#include <GL/glaux.h>
#pragma comment (lib, "glaux.lib")
#define D_PI 3.141592653
#pragma warning(disable:4305)
#pragma warning(disable:4244)
struct cvPoint
{
float x,y,z; //点的坐标
};
//矢量相乘C=A*B
void vect_mult(struct cvPoint *A, struct cvPoint *B, struct cvPoint *C)
{
C->x=A->y * B->z -A ->z * B->y;
C->y=A->z * B->x -A ->x * B->z;
C->z=A->x * B->y -A ->y * B->x;
}
double t=0.0;
double sf(double x,double y)
{
return cos(sqrt(x*x+y*y)+t);
}
float ver[21][21][3];
GLvoid initVer()
{
int i,j;
float dx=D_PI*8/20.0,dy=D_PI*8/20.0;
for(i=0;i<=20;i++)
for(j=0;j<=20;j++)
{
ver[i][j][0]=i*dx-D_PI*4;
ver[i][j][1]=j*dy-D_PI*4;
ver[i][j][2]=(float)sf(ver[i][j][0],ver[i][j][1]);
}
}
HDC hDC=NULL; // 窗口着色描述表句柄
HGLRC hRC=NULL; // OpenGL渲染描述表句柄
HWND hWnd=NULL; // 保存我们的窗口句柄
HINSTANCE hInstance; // 保存程序的实例
bool keys[256]; // 保存键盘按键的数组
bool active=TRUE; // 窗口的活动标志,缺省为TRUE
bool fullscreen=TRUE; // 全屏标志缺省,缺省设定成全屏模式
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); // WndProc的定义
int rx=0,ry=0,rz=0;
BOOL light; // 光源的开/关
bool lp;
GLfloat LightAmbient[]={0.5f,0.5f,0.5f,1.0f};
GLfloat LightDiffuse[]={1.0f,1.0f,1.0f,1.0f};
GLfloat LightPosition[]={0.0f,0.0f,2.0f,1.0f};
GLuint texture[3];
struct _AUX_RGBImageRec * LoadBMP(char *Filename) // 载入位图图象
{
FILE *File=NULL; // 文件句柄
if(!Filename) // 确保文件名已提供
{
return NULL; // 如果没提供,返回 NULL
}
File=fopen(Filename,"r"); // 尝试打开文件
if(File) // 文件存在么?
{
fclose(File); // 关闭句柄
return auxDIBImageLoad(Filename); // 载入位图并返回指针
}
return NULL; // 如果载入失败,返回 NULL
}
int LoadGLTextures() // 载入位图(调用//上面的代码)并转换成纹理
{
int Status=FALSE; // 状态指示器
AUX_RGBImageRec *TextureImage[1]; // 创建纹理的存储空间
memset(TextureImage,0,sizeof(void *)*1); // 将指针设为 NULL
// 载入位图,检查有无错误,如果位图没找到则退出
if(TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
{
Status=TRUE; // 将 Status 设为 TRUE
glGenTextures(3, &texture[0]); // 创建纹理
// 创建 Nearest 滤波贴图
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
// 创建线性滤波纹理
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
// 创建 MipMapped 纹理
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
if (TextureImage[0]) // 纹理是否存在
{
if (TextureImage[0]->data) // 纹理图像是否存在
{
// free(TextureImage[0]->data); // 释放纹理图像占用的内存
}
// free(TextureImage[0]); // 释放图像结构
}
return Status; // 返回 Status
}
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // 重置OpenGL窗口大小
{
if (height==0)
// 防止被零除
{
height=1;
// 将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();
// 重置模型观察矩阵
}
int InitGL(GLvoid)
// 此处开始对OpenGL进行所有设置
{
initVer();
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
glEnable(GL_LIGHT1);
if(!LoadGLTextures())
{
return false;
}
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); // 告诉系统对透视进行修正
return TRUE;
// 初始化 OK
}
int DrawGLScene(GLvoid) // 从这里开始进行所有的绘制
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity();
glTranslatef(0,0,-6.0);
glRotatef(float(rx),1.0,0.0,0.0);
glRotatef(float(ry),0.0,1.0,0.0);
glRotatef(float(rz),0.0,0.0,1.0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
initVer();//重新赋值
cvPoint pa,pb,pc;
glPushMatrix();
glScalef(0.1,0.1,0.1);
glTranslatef(-D_PI/2,-D_PI/2,0);
glRotatef(-60,1,0,0);
glRotatef(-30,0,0,1);
for(int i=0;i<20;i++)
for(int j=0;j<20;j++)
{
//第一条对角线
pa.x=ver[i+1][j+1][0]-ver[i][j][0];
pa.y=ver[i+1][j+1][1]-ver[i][j][1];
pa.z=ver[i+1][j+1][2]-ver[i][j][2];
//第二条对角线
pb.x=ver[i][j+1][0]-ver[i+1][j][0];
pb.y=ver[i][j+1][1]-ver[i+1][j][1];
pb.z=ver[i][j+1][2]-ver[i+1][j][2];
vect_mult(&pa,&pb,&pc);//计算法向,注意顺序
glNormal3f(pc.x,pc.y,pc.z);
glBegin(GL_QUADS);
glTexCoord2f(0.05*i,0.05*j);
glVertex3f(ver[i][j][0],ver[i][j][1],ver[i][j][2]);
glTexCoord2f(0.05*(i+1),0.05*j);
glVertex3f(ver[i+1][j][0],ver[i+1][j][1],ver[i+1][j][2]);
glTexCoord2f(0.05*(i+1),0.05*(j+1));
glVertex3f(ver[i+1][j+1][0],ver[i+1][j+1][1],ver[i+1][j+1][2]);
glTexCoord2f(0.05*i,0.05*(j+1));
glVertex3f(ver[i][j+1][0],ver[i][j+1][1],ver[i][j+1][2]);
glEnd();
}
glPopMatrix();
glEnable(GL_LIGHTING);
t+=0.05;//改变相位
Sleep(5000);
return TRUE;
// 一切 OK
}
GLvoid KillGLWindow(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
}
}
/**//* 这个函数创建我们OpenGL窗口,参数为:
*
* title - 窗口标题
*
* width - 窗口宽度
*
* height - 窗口高度
*
* bits - 颜色的位深(8/16/32)
*
* fullscreenflag - 是否使用全屏模式,全屏模式(TRUE),窗口模式(FALSE) */
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // 保存查找匹配的结果
WNDCLASS wc; // 窗口类结构
DWORD dwExStyle; // 扩展窗口风格
DWORD dwStyle; // 窗口风格
RECT WindowRect; // 取得矩形的左上角和右下角的坐标值
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);
return FALSE;
// 退出并返回FALSE
}
if (fullscreen)
// 要尝试全屏模式吗?
{
DEVMODE dmScreenSettings;
// 设备模式
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,"全屏模式在当前显卡上设置失败!使用窗口模式?","NeHe G",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
//如果用户选择窗口模式,变量fullscreen 的值变为FALSE,程序继续运行
fullscreen=FALSE; // 选择窗口模式
}
else
{
//如果用户选择退出,弹出消息窗口告知用户程序将结束。并返回FALSE告诉程序窗口未能成功创建。程序退出。
MessageBox(NULL,"程序将被关闭","错误",MB_OK|MB_ICONSTOP);
return FALSE;
// 退出并返回 FALSE
}
}
}
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,
// 扩展窗体风格
"OpenG",
// 类名字
title,
// 窗口标题
dwStyle |
// 必须的窗体风格属性
WS_CLIPSIBLINGS |
// 必须的窗体风格属性
WS_CLIPCHILDREN,
// 必须的窗体风格属性
0, 0,
// 窗口位置
WindowRect.right-
WindowRect.left, // 计算调整好的窗口宽度
WindowRect.bottom-
WindowRect.top, // 计算调整好的窗口高度
NULL,
// 无父窗口
NULL,
// 无菜单
hInstance,
// 实例
NULL)))
// 不向WM_CREATE传递任何东东
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"窗口创建错误","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
static PIXELFORMATDESCRIPTOR pfd= //pfd 告诉窗口我们所希望的东东,即窗口使用的像素格式
{
sizeof(PIXELFORMATDESCRIPTOR), // 上述格式描述符的大小 1, // 版本号
PFD_DRAW_TO_WINDOW | // 格式支持窗口
PFD_SUPPORT_OPENGL | // 格式必须支持OpenGL
PFD_DOUBLEBUFFER, // 必须支持双缓冲
PFD_TYPE_RGBA, // 申请 RGBA 格式
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,"不能创建一个窗口设备描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Windows 找到相应的象素格式了吗?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"不能创建一种相匹配的像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // 能够设置象素格式么?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"不能设置像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if (!(hRC=wglCreateContext(hDC))) // 能否取得OpenGL渲染描述表?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"不能创建OpenGL渲染描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if(!wglMakeCurrent(hDC,hRC)) // 尝试激活着色描述表
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"不能激活当前的OpenGL渲然描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
ShowWindow(hWnd,SW_SHOW); // 显示窗口
SetForegroundWindow(hWnd); // 略略提高优先级
SetFocus(hWnd); // 设置键盘的焦点至此窗口
ReSizeGLScene(width, height); // 设置透视 GL 屏幕
if (!InitGL()) // 初始化新建的GL窗口
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"初始化失败","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
return TRUE; // 成功
}
LRESULT CALLBACK WndProc( HWND hWnd, // 窗口的句柄
UINT uMsg, // 窗口的消息
WPARAM wParam, // 附加的消息内容
LPARAM lParam) // 附加的消息内容
{
switch (uMsg) // 检查Windows消息
{
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: // 收到Close消息?
{
PostQuitMessage(0); // 发出退出消息
return 0; // 返回
}
case WM_KEYDOWN: // 有键按下么?
{
keys[wParam] = TRUE; // 如果是,设为TRUE
return 0; // 返回
}
case WM_KEYUP: // 有键放开么?
{
keys[wParam] = FALSE; // 如果是,设为FALSE
return 0; // 返回
}
case WM_SIZE: // 调整OpenGL窗口大小
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
return 0; // 返回
}
}
// 向 DefWindowProc传递所有未处理的消息。
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, // 当前窗口实例
HINSTANCE hPrevInstance, // 前一个窗口实例
LPSTR lpCmdLine, // 命令行参数
int nCmdShow)
// 窗口显示状态
{
MSG msg;
// Windowsx消息结构
BOOL done=FALSE; // 用来退出循环的Bool 变量
// 提示用户选择运行模式
if (MessageBox(NULL,"你想在全屏模式下运行么?", "设置全屏模式",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // FALSE为窗口模式
}
// 创建OpenGL窗口
if (!CreateGLWindow("NeHe's OpenGL 程序框架",640,480,16,fullscreen))
{
return 0; // 失败退出
}
while(!done) // 保持循环直到 done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // 有消息在等待吗?
{
if (msg.message==WM_QUIT) // 收到退出消息?
{
done=TRUE; // 是,则done=TRUE
}
else // 不是,处理窗口消息
{
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 发送消息
}
}
else
// 如果没有消息
{
// 绘制场景。监视ESC键和来自DrawGLScene()的退出消息
if (active)
// 程序激活的么?
{
if (keys[VK_ESCAPE]) // ESC 按下了么?
{
done=TRUE; // ESC 发出退出信号
}
else
// 不是退出的时候,刷新屏幕
{
DrawGLScene(); // 绘制场景
SwapBuffers(hDC); // 交换缓存 (双缓存)
if (keys['L'] && !lp) // L 键已按下并且松开了?
{
lp=TRUE; // lp 设为 TRUE
light=!light; // 切换光源的 TRUE/FALSE
if (!light) // 如果没有光源
{
glDisable(GL_LIGHTING); // 禁用光源
}
else // 否则
{
glEnable(GL_LIGHTING); // 启用光源
}
}
if (!keys['L']) // L键松开了么?
{
lp=FALSE; // 若是,则将lp设为FALSE
}
}
}
if (keys[VK_F1]) // F1键按下了么?
{
keys[VK_F1]=FALSE; // 若是,使对应的Key数组中的值为 FALSE
KillGLWindow(); // 销毁当前的窗口
fullscreen=!fullscreen; // 切换 全屏 / 窗口 模式
// 重建 OpenGL 窗口
if (!CreateGLWindow("NeHe's OpenGL 程序框架",640,480,16,fullscreen))
{
return 0; // 如果窗口未能创建,程序退出
}
}
}
}
// 关闭程序
KillGLWindow(); // 销毁窗口
return (msg.wParam); // 退出程序
}