开始前需要定义全局变量,其中与光照相关的全局变量为
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 filter; // 滤波类型
GLuint texture[3]; // 3种纹理的储存空间
接下来在LoadGLTextures()函数中载入位图并生成纹理
注意点:OpenGL载入的位图一般要求宽度和高度大小为2的整数次方,如64,128,256等,但是在图像缩小到很小时,将创建一个mipmapping纹理,这时需要用一种绕过大小限制的绑定方法:gluBlind2DMipmaps();同样要注意及时释放内存。
int LoadGLTextures()//载入位图并转换成纹理
{
int Status = FALSE;
AUX_RGBImageRec* TextureImage[1];//创建纹理的存储空间
memset(TextureImage, 0, sizeof(void*) * 1);//将指针设为NULL
if (TextureImage[0] = LoadBMP("texture.bmp"))//成功载入位图
{
Status = TRUE;
glGenTextures(3, &texture[0]);//创建3个纹理
// 创建 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;
}
同时,在OpenGL初始化函数中要设置光源,这里定义了光源GL_LIGHT1的环境光、漫反射光,光源位置,并启用光源
int InitGL(GLvoid)
{
if (!LoadGLTextures())//布恩那个调用纹理载入
{
return FALSE;
}
glEnable(GL_TEXTURE_2D);//启用纹理映射
glShadeModel(GL_SMOOTH); //设置深度缓存
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST); //启用深度测试
glDepthFunc(GL_LEQUAL); //所作深度测试的类型
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);//设置环境光
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);//设置漫反射
glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);//设置光源位置
glEnable(GL_LIGHT1);//启用一号光源
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//反锯齿
return TRUE;
}
在DrawGLScene()函数中,使用光源时需要使用glNormal3f()指定法线,法线需要指向立方体外侧,而不能指向内部
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity(); // 重置当前的模型观察矩阵
glTranslatef(0.0f, 0.0f, z); // 移入/移出屏幕 z 个单位
glRotatef(xrot, 1.0f, 0.0f, 0.0f); // 绕X轴旋转
glRotatef(yrot, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转
glBindTexture(GL_TEXTURE_2D, texture[filter]); // 选择由filter决定的纹理
glBegin(GL_QUADS); // 开始绘制四边形
// 前侧面
glNormal3f(0.0f, 0.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);
// 后侧面
glNormal3f(0.0f, 0.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);
// 顶面
glNormal3f(0.0f, 1.0f, 0.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);
// 底面
glNormal3f(0.0f, -1.0f, 0.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);
// 右侧面
glNormal3f(1.0f, 0.0f, 0.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);
// 左侧面
glNormal3f(-1.0f, 0.0f, 0.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 += xspeed; // xrot 增加 xspeed 单位
yrot += yspeed; // yrot 增加 yspeed 单位
return TRUE;
}
在WinMain()函数中定义按键的作用
在NEHE教程中,按键逻辑感觉不符合主流,下面的代码稍加了改动,PageUp定义为立方体移向观察者,PageDown定义为远离观察者,Up定义为yspeed+,Down定义为yspeed-,Left定义为xspeed-,Right定义为xspeed+.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
BOOL done = FALSE;
if (MessageBox(NULL, L"你想在全屏模式下运行吗", L"设置全屏模式", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
fullscreen = FALSE;
}
if (!CreateGLWindow(L"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])//ESC按下
{
done = TRUE;
}
else
{
DrawGLScene();
SwapBuffers(hDC);
}
if (keys['L'] && !lp)//L键按下并且未松开
{
lp = TRUE;
light = !light;
if (!light)//如果没有光源
{
glDisable(GL_LIGHTING);//禁用光源
}
else
{
glEnable(GL_LIGHTING);//启用光源
}
}
if (!keys['L'])//L键松开了
{
lp = FALSE;
}
if (keys['F'] && !fp)//F键按下并且未松开
{
fp = TURE;
filter += 1;
if (filter > 2)
{
filter = 0;
}
}
if (!keys['F'])
{
fp = FALSE;
}
if (keys[VK_PRIOR])//PageDown按下了
{
z -= 0.02f;//若按下,木箱远离观察者
}
if (keys[VK_UP])//PageUp按下
{
z += 0.02f;//若按下,木箱靠近观察者
}
if (keys[VK_UP])//Up方向键按下
{
yspeed += 0.01f;
}
if (keys[VK_DOWN])//Down方向键按下
{
yspeed -= 0.01f;
}
if (keys[VK_RIGHT])//Right方向键按下
{
xspeed += 0.01f;
}
if (keys[VK_LEFT])//Left方向键按下
{
xspeed -= 0.01f;
}
if (keys[VK_F1])//F1按下
{
keys[VK_F1] = FALSE;
KillGLWindow();
fullscreen = !fullscreen;
if (!CreateGLWindow(L"OpenGL程序框架", 640, 480, 16, fullscreen))//重建OpenGL窗口
{
return 0;
}
}
}
}
}
KillGLWindow();
return(msg.wParam);
}