openGL教程delphi版7.docx
- 文档编号:8933687
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:14
- 大小:24.31KB
openGL教程delphi版7.docx
《openGL教程delphi版7.docx》由会员分享,可在线阅读,更多相关《openGL教程delphi版7.docx(14页珍藏版)》请在冰豆网上搜索。
openGL教程delphi版7
{
这一课我会教您如何使用三种不同的纹理滤波方式。
教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的光照。
这一课包含了很多内容,如果您对前面的课程有疑问的话,先回头复习一下。
进入后面的代码之前,很好的理解基础知识十分重要。
我们还是在第一课的代码上加以修改。
跟以前不一样的是,只要有任何大的改动,我都会写出整段代码。
首先我们还要加进SysUtils单元和Glaux单元。
}
Uses
SysUtils,
opengl,
windows,
Messages,
GlauxIn'..\..\GLAUX\Glaux.pas';
//下面几行是增加新的变量。
//我们增加三个布尔变量。
//light变量跟踪光照是否打开。
//变量lp和fp用来存储'L'和'F'键是否按下的状态。
//后面我会解释这些变量的重要性。
现在,先放在一边吧。
light:
Boolean;//光源的开/关
lp:
Boolean;//L键按下了么?
fp:
Boolean;//F键按下了么?
//现在设置5个变量来控制绕x轴和y轴旋转角度的步长,
//以及绕x轴和y轴的旋转速度。
//另外还创建了一个z变量来控制进入屏幕深处的距离。
xrot:
GLfloat;//X旋转
yrot:
GLfloat;//Y旋转
xspeed:
GLfloat;//X旋转速度
yspeed:
GLfloat;//Y旋转速度
z:
GLfloat=-5.0f;//深入屏幕的距离
//接着设置用来创建光源的数组。
//我们将使用两种不同的光。
//第一种称为环境光。
环境光来自于四面八方。
//所有场景中的对象都处于环境光的照射中。
//第二种类型的光源叫做漫射光。
//漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。
//处于漫射光直接照射下的任何对象表面都变得很亮,
//而几乎未被照射到的区域就显得要暗一些。
//这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。
//创建光源的过程和颜色的创建完全一致。
//前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
//因此,下面的代码我们得到的是半亮(0.5f)的白色环境光。
//如果没有环境光,未被漫射光照到的地方会变得十分黑暗。
LightAmbient:
Array[0..3]OfGLfloat=(0.5,0.5,0.5,1.0);//环境光参数(新增)
//下一行代码我们生成最亮的漫射光。
//所有的参数值都取成最大值1.0f。
//它将照在我们木板箱的前面,看起来挺好。
LightDiffuse:
Array[0..3]OfGLfloat=(1.0,1.0,1.0,1.0);//漫射光参数(新增)
//最后我们保存光源的位置。
//前三个参数和glTranslate中的一样。
//依次分别是XYZ轴上的位移。
//由于我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是0.0。
//第三个值是Z轴上的位移。
//为了保证光线总在木箱的前面,
//所以我们将光源的位置朝着观察者(就是您哪。
)挪出屏幕。
//我们通常将屏幕也就是显示器的屏幕玻璃所处的位置称作Z轴的0.0点。
//所以Z轴上的位移最后定为2.0。
//假如您能够看见光源的话,它就浮在您显示器的前方。
//当然,如果木箱不在显示器的屏幕玻璃后面的话,您也无法看见箱子。
//『译者注:
我很欣赏NeHe的耐心。
//说真的有时我都打烦了,这么简单的事他这么废话干嘛?
//但如果什么都清楚,您还会翻着这样的页面看个没完么?
』
//最后一个参数取为1.0f。
//这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。
LightPosition:
Array[0..3]OfGLfloat=(0.0,0.0,2.0,1.0);//光源位置(新增)
//filter变量跟踪显示时所采用的纹理类型。
//第一种纹理(texture0)使用gl_nearest(不光滑)滤波方式构建。
//第二种纹理(texture1)使用gl_linear(线性滤波)方式,
//离屏幕越近的图像看起来就越光滑。
//第三种纹理(texture2)使用mipmapped滤波方式,
//这将创建一个外观十分优秀的纹理。
//根据我们的使用类型,filter变量的值分别等于0,1或2。
//下面我们从第一种纹理开始。
//texture为三种不同纹理分配储存空间。
//它们分别位于在texture[0],texture[1]和texture[2]中。
filter:
GLuint;//滤波类型
texture:
Array[0..2]OfGLuint;//3种纹理的储存空间
ProcedureglGenTextures(n:
GLsizei;Vartextures:
GLuint);stdcall;external
opengl32;
ProcedureglBindTexture(target:
GLenum;texture:
GLuint);stdcall;external
opengl32;
FunctiongluBuild2DMipmaps(target:
GLenum;components,width,height:
GLint;
format,atype:
GLenum;data:
Pointer):
Integer;stdcall;externalglu32name
'gluBuild2DMipmaps';
{
现在载入一个位图,并用它创建三种不同的纹理。
这一课使用glaux辅助库来载入位图,
因此在编译时您应该确认是否包含了glaux库。
我知道Delphi和VC++都包含了glaux库,但别的语言不能保证都有。
『译者注:
glaux是OpenGL辅助库,根据OpenGL的跨平台特性,
所有平台上的代码都应通用。
但辅助库不是正式的OpenGL标准库,
没有出现在所有的平台上。
但正好在Win32平台上可用。
呵呵,BCB当然也没问题了。
』这里我只对新增的代码做注解。
如果您对某行代码有疑问的话,请查看教程六。
那一课很详细的解释了载入、创建纹理的内容。
在上一段代码后面及glResizeWnd()之前的位置,
我们增加了下面的代码。
这和第六课中载入位图的代码几乎相同。
}
FunctionLoadBmp(filename:
pchar):
PTAUX_RGBImageRec;
Var
BitmapFile:
Thandle;//文件句柄
Begin
IfFilename=''Then//确保文件名已提供。
result:
=Nil;//如果没提供,返回NULL
BitmapFile:
=FileOpen(Filename,fmOpenWrite);//尝试打开文件
IfBitmapFile>0Then//文件存在么?
Begin
FileClose(BitmapFile);//关闭句柄
result:
=auxDIBImageLoadA(filename);//载入位图并返回指针
End
Else
result:
=Nil;//如果载入失败,返回NiL。
End;
FunctionLoadTexture:
boolean;//载入位图并转换成纹理
Var
Status:
boolean;//Status指示器
TextureImage:
Array[0..1]OfPTAUX_RGBImageRec;//创建纹理的存储空间
Begin
Status:
=false;
ZeroMemory(@TextureImage,sizeof(TextureImage));//将指针设为NULL
TextureImage[0]:
=LoadBMP('Walls.bmp');
IfTextureImage[0]<>NilThen
Begin
Status:
=TRUE;//将Status设为TRUE
glGenTextures(1,texture[0]);//创建纹理
//第六课中我们使用了线性滤波的纹理贴图。
//这需要机器有相当高的处理能力,但它们看起来很不错。
//这一课中,我们接着要创建的第一种纹理使用GL_NEAREST方式。
//从原理上讲,这种方式没有真正进行滤波。
//它只占用很小的处理能力,看起来也很差。
//唯一的好处是这样我们的工程在很快和很慢的机器上都可以正常运行。
//您会注意到我们在MIN和MAG时都采用了GL_NEAREST,
//你可以混合使用GL_NEAREST和GL_LINEAR。
//纹理看起来效果会好些,但我们更关心速度,所以全采用低质量贴图。
//MIN_FILTER在图像绘制时小于贴图的原始尺寸时采用。
//MAG_FILTER在图像绘制时大于贴图的原始尺寸时采用。
//创建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);
//下个纹理与第六课的相同,线性滤波。
唯一的不同是这次放在了
//texture[1]中。
因为这是第二个纹理。
如果放在
//texture[0]中的话,他将覆盖前面创建的GL_NEAREST纹理。
glBindTexture(GL_TEXTURE_2D,texture[1]);//使用来自位图数据生成的典型纹理
//生成纹理
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);//线形滤波
//下面是创建纹理的新方法。
Mipmapping!
//『译者注:
这个词的中文我翻不出来,不过没关系。
看完这一段,您就知道意思最重要。
』
//您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失。
//刚才还很不错的图案变得很难看。
当您告诉OpenGL创建一个mipmapped的纹理后,
//OpenGL将尝试创建不同尺寸的高质量纹理。
当您向屏幕绘制一个mipmapped纹理的时候,
//OpenGL将选择它已经创建的外观最佳的纹理(带有更多细节)来绘制,
//而不仅仅是缩放原先的图像(这将导致细节丢失)。
//我曾经说过有办法可以绕过OpenGL对纹理宽度和高度所加的限制——64、128、256,等等。
//办法就是gluBuild2DMipmaps。
据我的发现,您可以使用任意的位图来创建纹理。
//OpenGL将自动将它缩放到正常的大小。
//因为是第三个纹理,我们将它存到texture[2]。
这样本课中的三个纹理全都创建好了。
//创建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);//(新增)
//下面一行生成mipmapped纹理。
//我们使用三种颜色(红,绿,蓝)来生成一个2D纹理。
//TextureImage[0].sizeX是位图宽度,
//TextureImage[0].sizeY是位图高度,
//(====不知为什么,delphi下这个函数没有height这个参数,
//但是帮助中却有,不知delphi再搞什么,郁闷ing......
//最后我在前面自己写了一个gluBuild2DMipmaps,
//来载入glu32.dll中的gluBuild2DMipmaps函数=====)
//GL_RGB意味着我们依次使用RGB色彩。
//GL_UNSIGNED_BYTE意味着纹理数据的单位是字节。
//TextureImage[0].data指向我们创建纹理所用的位图。
gluBuild2DMipmaps(GL_TEXTURE_2D,3,TextureImage[0].sizeX,
TextureImage[0].sizey,GL_RGB,GL_UNSIGNED_BYTE,
TextureImage[0].data);//(新增)}
End;
Ifassigned(TextureImage[0])Then//纹理是否存在
Ifassigned(TextureImage[0].data)Then//纹理图像是否存在
TextureImage[0].data:
=Nil;//释放纹理图像占用的内存
TextureImage[0]:
=Nil;//释放图像结构
result:
=Status;//返回Status
End;
//接着应该载入纹理并初始化OpenGL设置了。
//GLInit函数的第一行使用上面的代码载入纹理。
//创建纹理之后,我们调用glEnable(GL_TEXTURE_2D)启用2D纹理映射。
//阴影模式设为平滑阴影(smoothshading)。
//背景色设为黑色,我们启用深度测试,然后我们启用优化透视计算。
ProcedureglInit();//此处开始对OpenGL进行所有设置
Begin
If(NotLoadTexture)Then//调用纹理载入子例程
exit;//如果未能载入,退出
glEnable(GL_TEXTURE_2D);//启用纹理映射
glShadeModel(GL_SMOOTH);//启用阴影平滑
glClearColor(0.0,0.0,0.0,0.0);//黑色背景
glClearDepth(1.0);//设置深度缓存
glEnable(GL_DEPTH_TEST);//启用深度测试
glDepthFunc(GL_LESS);//所作深度测试的类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//高度优化的透视投影计算
//现在开始设置光源。
下面下面一行设置环境光的发光量,
//光源light1开始发光。
//这一课的开始处我们我们将环境光的发光量存放在LightAmbient数组中。
//现在我们就使用此数组(半亮度环境光)。
glLightfv(GL_LIGHT1,GL_AMBIENT,@LightAmbient[0]);//设置环境光
//接下来我们设置漫射光的发光量。
它存放在LightDiffuse数组中(全亮度白光)。
glLightfv(GL_LIGHT1,GL_DIFFUSE,@LightDiffuse[0]);//设置漫射光
//然后设置光源的位置。
//位置存放在LightPosition数组中
//(正好位于木箱前面的中心,X-0.0,Y-0.0,Z方向移向观察者2个单位<位于屏幕外面>)。
glLightfv(GL_LIGHT1,GL_POSITION,@LightPosition);//光源位置
//最后,我们启用一号光源。
我们还没有启用GL_LIGHTING,
//所以您看不见任何光线。
//记住:
只对光源进行设置、定位、甚至启用,光源都不会工作。
//除非我们启用GL_LIGHTING。
glEnable(GL_LIGHT1);//启用一号光源
End;
//下一段代码绘制贴图立方体。
我只对新增的代码进行注解。
//如果您对没有注解的代码有疑问,回头看看第六课。
ProcedureglDraw();//从这里开始进行所有的绘制
Begin
glClear(GL_COLOR_BUFFER_BITOrGL_DEPTH_BUFFER_BIT);//清除屏幕和深度缓存
glLoadIdentity();//重置当前的模型观察矩阵
//下三行代码放置并旋转贴图立方体。
//glTranslatef(0.0,0.0,z)将立方体沿着Z轴移动Z单位。
//glRotatef(xrot,1.0f,0.0f,0.0f)将立方体绕X轴旋转xrot。
//glRotatef(yrot,0.0f,1.0f,0.0f)将立方体绕Y轴旋转yrot。
glTranslatef(0.0,0.0,z);//移入/移出屏幕z个单位
glRotatef(xrot,1.0,0.0,0.0);//绕X轴旋转
glRotatef(yrot,0.0,1.0,0.0);//绕Y轴旋转
//下一行与我们在第六课中的类似。
//有所不同的是,这次我们绑定的纹理是texture[filter],
//而不是上一课中的texture[0]。
//任何时候,我们按下F键,filter的值就会增加。
//如果这个数值大于2,变量filter将被重置为0。
//程序初始时,变量filter的值也将设为0。
//使用变量filter我们就可以选择三种纹理中的任意一种。
glBindTexture(GL_TEXTURE_2D,texture[filter]);//选择由filter决定的纹理
glBegin(GL_QUADS);//开始绘制四边形
//glNormal3f是这一课的新东西。
Normal就是法线的意思,
//所谓法线是指经过面(多边形)上的一点且垂直于这个面(多边形)的直线。
//使用光源的时候必须指定一条法线。
法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面。
//如果没有指定法线,什么怪事情都可能发生:
不该照亮的面被照亮了,多边形的背面也被照亮....。
//对了,法线应该指向多边形的外侧。
看着木箱的前面您会注意到法线与Z轴正向同向。
//这意味着法线正指向观察者-您自己。
这正是我们所希望的。
//对于木箱的背面,也正如我们所要的,法线背对着观察者。
//如果立方体沿着X或Y轴转个180度的话,前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。
//换句话说,不管是哪个面,只要它朝着观察者这个面的法线就指向观察者。
//由于光源紧邻观察者,任何时候法线对着观察者时,这个面就会被照亮。
//并且法线越朝着光源,就显得越亮一些。
//如果您把观察点放到立方体内部,你就会法线里面一片漆黑。
//因为法线是向外指的。
如果立方体内部没有光源的话,当然是一片漆黑。
//前面
glNormal3f(0.0,0.0,1.0);//法线指向观察者
glTexCoord2f(0.0,0.0);
glVertex3f(-1.0,-1.0,1.0);//纹理和四边形的左下
glTexCoord2f(1.0,0.0);
glVertex3f(1.0,-1.0,1.0);//纹理和四边形的右下
glTexCoord2f(1.0,1.0);
glVertex3f(1.0,1.0,1.0);//纹理和四边形的右上
glTexCoord2f(0.0,1.0);
glVertex3f(-1.0,1.0,1.0);//纹理和四边形的左上
//后面
glNormal3f(0.0,0.0,-1.0);//法线背向观察者
glTexCoord2f(1.0,0.0);
glVertex3f(-1.0,-1.0,-1.0);//纹理和四边形的右下
glTexCoord2f(1.0,1.0);
glVertex3f(-1.0,1.0,-1.0);//纹理和四边形的右上
glTexCoord2f(0.0,1.0);
glVertex3f(1.0,1.0,-1.0);//纹理和四边形的左上
glTexCoord2f(0.0,0.0);
glVertex3f(1.0,-1.0,-1.0);//纹理和四边形的左下
//顶面
glNormal3f(0.0,1.0,0.0);//法线向上
glTexCoord2f(0.0,1.0);
glVertex3f(-1.0,1.0,-1.0);//纹理和四边形的左上
glTexCoord2f(0.0,0.0);
glVertex3f(-1.0,1.0,1.0);//纹理和四边形的左下
glTexCoord2f(1.0,0.0);
glVertex3f(1.0,1.0,1.0);//纹理和四边形的右下
glTexCoord2f(1.0,1.0);
glVertex3f(1.0,1.0,-1.0);//纹理和四边形的右上
//底面
glNormal3f(0.0,-1.0,0.0);//法线朝下
glTexCoord2f(1.0,1.0);
glVertex3f(-1.0,-1.0,-1.0);//纹理和四边形的右上
glTexCoord2f(0.0,1.0);
glVerte
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- openGL 教程 delphi