OpenGL入门笔记(五)

本文介绍了纹理映射技术的基本概念及其实现步骤。通过实例演示了如何使用OpenGL加载位图并将其转换为纹理,最终应用到3D模型表面。文章还详细解释了纹理映射过程中需要注意的关键点。

以前也听说过纹理映射,不过一直没明白是怎么回事情,只是以为是在绘制好的三维图形表面再进行更为细致的绘制,从而绘制出物体表面的纹理,就像画桌子把桌面的条纹也画出来一样。今天学习的就是如何使用2D纹理绘制图形。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

在计算机图形学中,纹理映射(texture mapping)把存储在内存里的位图包裹到3D渲染物体的表面。纹理给物体提供了丰富的细节,用简单的方式模拟出了复杂的外观。一个图像(纹理)被贴(映射)到场景中的一个简单形体上,就像印花贴到一个平面上一样。例如要画一堵墙,如果不使用纹理映射,就只有把每块砖都画成一个独立的多边形,那么就需要画成千上万块,并且画出的墙也显得不够真实,而如果我们把墙画成一个多边形,通过纹理映射把一副墙的图像粘贴到这个多边形上,就可以很好地实现需求了。

ContractedBlock.gifExpandedBlockStart.gif纹理映射Demo
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->None.gifGLfloatxrot;
None.gifGLfloatyrot;
None.gifGLfloatzrot;
None.gif
None.gifGLuinttexture[
1];
None.gif
None.gif
None.gifAUX_RGBImageRec
*LoadBMP(char*Filename)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//加载纹理映射所需的位图
InBlock.gif
FILE*File=NULL;
InBlock.gif
InBlock.gif
if(!Filename)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
returnNULL;
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gifFile
=fopen(Filename,"r");
InBlock.gif
InBlock.gif
if(File)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.giffclose(File);
InBlock.gif
returnauxDIBImageLoad(Filename);//加载位图并返回指向位图的指针
ExpandedSubBlockEnd.gif
}

InBlock.gif
InBlock.gif
returnNULL;
ExpandedBlockEnd.gif}

None.gif
None.gif
intLoadGLTextures()//LoadBitmapsAndConvertToTextures
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intStatus=FALSE;//StatusIndicator
InBlock.gif

InBlock.gifAUX_RGBImageRec
*TextureImage[1];//创建纹理的存储空间
InBlock.gif

InBlock.gifmemset(TextureImage,
0,sizeof(void*)*1);//清除图像记录,确保其内容为空
InBlock.gif
InBlock.gif
//LoadTheBitmap,CheckForErrors,IfBitmap'sNotFoundQuit
InBlock.gif
if(TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifStatus
=TRUE;glGenTextures(1,&texture[0]);
InBlock.gif
//TypicalTextureGenerationUsingDataFromTheBitmap
InBlock.gif
glBindTexture(GL_TEXTURE_2D,texture[0]);
InBlock.gifglTexImage2D(GL_TEXTURE_2D,
0,3,TextureImage[0]->sizeX,TextureImage[0]->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0]->data);
InBlock.gifglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
InBlock.gifglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
if(TextureImage[0])
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
if(TextureImage[0]->data)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.giffree(TextureImage[
0]->data);//释放纹理图像内存
ExpandedSubBlockEnd.gif
}

InBlock.gif
ExpandedSubBlockEnd.giffree(TextureImage[
0]);}

InBlock.gif
InBlock.gif
returnStatus;
ExpandedBlockEnd.gif}

None.gif
None.gif
intInitGL(GLvoid)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
if(!LoadGLTextures())
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
returnFALSE;
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gifglEnable(GL_TEXTURE_2D);
//允许纹理映射
InBlock.gif
glShadeModel(GL_SMOOTH);glClearColor(0.0f,0.0f,0.0f,0.5f);
InBlock.gifglClearDepth(
1.0f);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL);glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
InBlock.gif
returnTRUE;
ExpandedBlockEnd.gif}

None.gif
None.gif
intDrawGLScene(GLvoid)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gifglClear(GL_COLOR_BUFFER_BIT
|GL_DEPTH_BUFFER_BIT);
InBlock.gifglLoadIdentity();glTranslatef(
0.0f,0.0f,-5.0f);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*图形绕x,y,z轴进行旋转*/
InBlock.gifglRotatef(xrot,
1.0f,0.0f,0.0f);
InBlock.gifglRotatef(yrot,
0.0f,1.0f,0.0f);
InBlock.gifglRotatef(zrot,
0.0f,0.0f,1.0f);
InBlock.gif
InBlock.gifglBindTexture(GL_TEXTURE_2D,texture[
0]);//选择要使用的纹理进行绑定
InBlock.gif

InBlock.gifglBegin(GL_QUADS);
InBlock.gif
//FrontFace
InBlock.gif
glTexCoord2f(0.0f,0.0f);glVertex3f(-1.0f,-1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,0.0f);glVertex3f(1.0f,-1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,1.0f);glVertex3f(1.0f,1.0f,1.0f);
InBlock.gifglTexCoord2f(
0.0f,1.0f);glVertex3f(-1.0f,1.0f,1.0f);
InBlock.gif
//BackFace
InBlock.gif
glTexCoord2f(1.0f,0.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
InBlock.gifglTexCoord2f(
1.0f,1.0f);glVertex3f(-1.0f,1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,1.0f);glVertex3f(1.0f,1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,0.0f);glVertex3f(1.0f,-1.0f,-1.0f);
InBlock.gif
//TopFace
InBlock.gif
glTexCoord2f(0.0f,1.0f);glVertex3f(-1.0f,1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,0.0f);glVertex3f(-1.0f,1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,0.0f);glVertex3f(1.0f,1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,1.0f);glVertex3f(1.0f,1.0f,-1.0f);
InBlock.gif
//BottomFace
InBlock.gif
glTexCoord2f(1.0f,1.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,1.0f);glVertex3f(1.0f,-1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,0.0f);glVertex3f(1.0f,-1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,0.0f);glVertex3f(-1.0f,-1.0f,1.0f);
InBlock.gif
//Rightface
InBlock.gif
glTexCoord2f(1.0f,0.0f);glVertex3f(1.0f,-1.0f,-1.0f);
InBlock.gifglTexCoord2f(
1.0f,1.0f);glVertex3f(1.0f,1.0f,-1.0f);
InBlock.gifglTexCoord2f(
0.0f,1.0f);glVertex3f(1.0f,1.0f,1.0f);
InBlock.gifglTexCoord2f(
0.0f,0.0f);glVertex3f(1.0f,-1.0f,1.0f);
InBlock.gif
//LeftFace
InBlock.gif
glTexCoord2f(0.0f,0.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
InBlock.gifglTexCoord2f(
1.0f,0.0f);glVertex3f(-1.0f,-1.0f,1.0f);
InBlock.gifglTexCoord2f(
1.0f,1.0f);glVertex3f(-1.0f,1.0f,1.0f);
InBlock.gifglTexCoord2f(
0.0f,1.0f);glVertex3f(-1.0f,1.0f,-1.0f);
InBlock.gifglEnd();
InBlock.gif
InBlock.gifxrot
+=0.3f;
InBlock.gifyrot
+=0.2f;
InBlock.gifzrot
+=0.4f;
InBlock.gif
returnTRUE;
ExpandedBlockEnd.gif}

None.gif

总结一下,使用纹理映射分为以下几个步骤:

1) 从位图文件加载纹理映射所用到的位图图像

2) 将加载到的位图图像转换为纹理

3) 在对OpenGL的初始化设置中允许进行纹理映射

4) 在具体的绘制图形代码中选择要使用的纹理

5) 将纹理正确的映射到要绘制的图形上


其中有几个地方需要注意的:

1) 用作纹理的图像的宽和高必须是2n次方;宽度和高度最小必须是64象素;并且出于兼容性的原因,图像的宽度和高度不应超过256象素。如果原始素材的宽度和高度不是64128256象素的话,应该使用图像处理软件重新改变图像的大小。

2) glGenTextures(1, &texture[0])告诉OpenGL我们想生成一个纹理名字(如果想载入多个纹理,就加大数字)。glBindTexture(GL_TEXTURE_2D, texture[0])告诉OpenGL将纹理名字texture[0]绑定到纹理目标上。2D纹理只有高度(在Y轴上)和宽度(在X轴上)。主函数将纹理名字指派给纹理数据。本例中我们告知OpenGL&texture[0]处的内存已经可用。我们创建的纹理将存储在&texture[0]的指向的内存区域。

3) 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。数字3是数据的成分数。因为图像是由红色数据,绿色数据,蓝色数据三种组分组成。TextureImage[0]->sizeX是纹理的宽度,TextureImage[0]->sizey是纹理的高度。数字零是边框的值,一般就是零。GL_RGB告诉OpenGL图像数据由红、绿、蓝三色数据组成。 GL_UNSIGNED_BYTE意味着组成图像的数据是无符号字节类型的。最后...TextureImage[0]->data告诉OpenGL纹理数据的来源。这里指向存放在TextureImage[0]记录中的数据。



4)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


5
glBindTexture(GL_TEXTURE_2D, texture[0]);     // 选择纹理

这是选择我们使用的纹理。如果您在您的场景中使用多个纹理,您应该使用来 glBindTexture(GL_TEXTURE_2D, texture[ 所使用纹理对应的数字 ]) 选择要绑定的纹理。当您想改变纹理时,应该绑定新的纹理。有一点值得指出的是,您不能在glBegin()glEnd()之间绑定纹理,必须在glBegin()之前或 glEnd()之后绑定。


6

为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。


glTexCoord<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="2" unitname="F">2f</chmetcnv>
的第一个参数是X坐标。<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">0.0f</span></chmetcnv>是纹理的左侧。<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue=".5" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">0.5f</span></chmetcnv>是纹理的中点,<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1.0f</span></chmetcnv>是纹理的右侧。glTexCoord<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="2" unitname="F">2f</chmetcnv>的第二个参数是Y坐标。<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">0.0f</span></chmetcnv>是纹理的底部。<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue=".5" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">0.5f</span></chmetcnv>是纹理的中点,<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1.0f</span></chmetcnv>是纹理的顶部。

所以纹理的左上坐标是X:<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">0.0f</span></chmetcnv>,Y:<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1.0f</span></chmetcnv>,四边形的左上顶点是X:<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="True" hasspace="False" sourcevalue="1" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">-1.0f</span></chmetcnv>,Y:<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="F"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1.0f</span></chmetcnv>。其余三点依此类推。


7

纹理坐标在背面的时候一定要注意想清楚其坐标轴的位置关系,要与图形的位置正确对应。


200741801.JPG

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值