Jeff Molofee(NeHe)的OpenGL教程
- 纹理滤波方式
原 文:Lesson 7: Texture Filters, Lighting & Keyboard Control
译 者:CKER
这一课我会教您如何使用三种不同的纹理滤波方式。教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的光照。这一课包含了很多内容,如果您对前面的课程有疑问的话,先回头复习一下。进入后面的代码之前,很好的理解基础知识十分重要。我们还是在第一课的代码上加以修改。跟以前不一样的是,只要有任何大的改动,我都会写出整段代码。程序开始,我们先加上几个新的变量。
#include <windows.h> // Windows的头文件
#include <stdio.h> // 标准输入/输出库的头文件 (新增)
#include <gl/gl.h> // OpenGL32库的头文件
#include <gl/glu.h> // GLu32库的头文件
#include <gl/glaux.h> // GLaux库的头文件
HGLRC hRC=NULL; // 永久着色描述表
HDC hDC=NULL; // 私有GDI设备描述表
HWND hWnd=NULL; // 保存我们的窗口句柄
HINSTANCE hInstance; // 保存程序的实例
bool keys[256]; // 用于键盘例程的数组
bool active=TRUE; // 窗口的活动标志,缺省为TRUE
bool fullscreen=TRUE; // 全屏标志缺省设定成全屏模式
下面几行是新的。我们增加三个布尔变量。light变量跟踪光照是否打开。变量lp和fp用来存储‘L’和‘F’键是否按下的状态。后面我会解释这些变量的重要性。现在,先放在一边吧。
BOOL light; // 光源的开/关
BOOL lp; // L键按下了么?
BOOL fp; // F键按下了么?
现在设置5个变量来控制绕x轴和y轴旋转角度的步长,以及绕x轴和y轴的旋转速度。另外还创建了一个z变量来控制进入屏幕深处的距离。
GLfloat xrot; // X 旋转
GLfloat yrot; // Y 旋转
GLfloat xspeed; // X 旋转速度
GLfloat yspeed; // Y 旋转速度
GLfloat z=- 5.0f ; // 深入屏幕的距离
接着设置用来创建光源的数组。我们将使用两种不同的光。第一种称为环境光。环境光来自于四面八方。所有场景中的对象都处于环境光的照射中。第二种类型的光源叫做漫射光。漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照射到的区域就显得要暗一些。这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。
创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
因此,下面的代码我们得到的是半亮( 0.5f )的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。
GLfloat LightAmbient[]= { 0.5f , 0.5f , 0.5f , 1.0f }; // 环境光参数 (新增)
下一行代码我们生成最亮的漫射光。所有的参数值都取成最大值 1.0f 。它将照在我们木板箱的前面,看起来挺好。
GLfloat LightDiffuse[]= { 1.0f , 1.0f , 1.0f , 1.0f }; // 漫射光参数 (新增)
最后我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。由于我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是 0.0f 。第三个值是Z轴上的位移。为了保证光线总在木箱的前面,所以我们将光源的位置朝着观察者(就是您哪。)挪出屏幕。我们通常将屏幕也就是显示器的屏幕玻璃所处的位置称作Z轴的 0.0f 点。所以Z轴上的位移最后定为 2.0f 。假如您能够看见光源的话,它就浮在您显示器的前方。当然,如果木箱不在显示器的屏幕玻璃后面的话,您也无法看见箱子。最后一个参数取为 1.0f 。这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。
GLfloat LightPosition[]= { 0.0f , 0.0f , 2.0f , 1.0f }; // 光源位置 ( 新增 )
filter 变量跟踪显示时所采用的纹理类型。第一种纹理(texture 0)使用gl_nearest(不光滑)滤波方式构建。第二种纹理(texture 1)使用gl_linear(线性滤波)方式,离屏幕越近的图像看起来就越光滑。第三种纹理(texture 2)使用mipmapped滤波方式,这将创建一个外观十分优秀的纹理。根据我们的使用类型,filter变量的值分别等于0,1或2。下面我们从第一种纹理开始。
GLuint texture[3]为三种不同纹理分配储存空间。它们分别位于在texture[0]、texture[1]、texture[2]中。
GLuint filter; // 滤波类型
GLuint texture[3]; // 3种纹理的储存空间
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // WndProc定义
现在载入一个位图,并用它创建三种不同的纹理。这一课使用glaux辅助库来载入位图,因此在编译时您应该确认是否包含了glaux库。我知道Delphi和VC++都包含了glaux库,但别的语言不能保证都有。(译者:glaux是OpenGL辅助库,根据OpenGL的跨平台特性,所有平台上的代码都应通用。但辅助库不是正式的OpenGL标准库,没有出现在所有的平台上。但正好在Win32平台上可用。呵呵,BCB当然也没问题了。)这里我只对新增的代码做注解。如果您对某行代码有疑问的话,请查看教程六。那一课很详细的解释了载入、创建纹理的内容。
在上一段代码后面及ReSizeGLScene()之前的位置,我们增加了下面的代码。这和第六课中载入位图的代码几乎相同。
AUX_RGBImageRec *LoadBMP(char *Filename) // 载入位图
{
FILE *File=NULL; // 文件句柄
if (!Filename) // 确认文件名已初始化
{
return NULL; // 没有返回 NULL
}