立方体纹理映射.docx
《立方体纹理映射.docx》由会员分享,可在线阅读,更多相关《立方体纹理映射.docx(8页珍藏版)》请在冰豆网上搜索。
![立方体纹理映射.docx](https://file1.bdocx.com/fileroot1/2022-10/11/bb2815ba-c0bb-4eb5-9fc3-c7ccb003dc42/bb2815ba-c0bb-4eb5-9fc3-c7ccb003dc421.gif)
立方体纹理映射
立方体纹理映射
1问题描述与算法思想
1.1纹理映射简介
纹理映射(TextureMapping)是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程。
在三维图形中,纹理映射(TextureMapping)的方法运用得最广,尤其描述具有真实感的物体。
比如绘制一面砖墙,就可以使用一幅具有真实感的图像或者照片作为纹理贴到一个矩形上,这样,一面逼真的砖墙就画好了。
如果不用纹理映射的方法,这墙上的每一块砖都要作为一个独立的多边形来绘制。
另外,纹理映射能够保证在变换多边形时,多边形上的纹理也会随之变化。
例如,用透视投影模式观察墙面时,离视点远的墙壁的砖块的尺寸就会缩小,而离视点近的就会大些,这些是符合视觉规律的。
此外,纹理映射也被用在其他一些领域。
如飞行仿真中常把一大片植被的图像映射到一些大多边形上用以表示地面,或者用大理石、木材等自然物质的图像作为纹理映射到多边形上表示相应的物体。
纹理对象通过一个单独的数字来标识。
这允许硬件能够在内存中保存多个纹理,而不是每次使用的时候再加载它们,从而减少了运算量,提高了速度。
纹理映射是真实感图像制作的一个重要部分,运用它可以方便的制作出极具真实感的图形而不必花过多时间来考虑物体的表面细节。
然而纹理加载的过程可能会影响程序运行速度,当纹理图像非常大时,这种情况尤为明显。
如何妥善的管理纹理,减少不必要的开销,是系统优化时必须考虑的一个问题。
还好,相关软件提供了纹理对象对象管理技术来解决上述问题。
与显示列表一样,纹理对象通过一个单独的数字来标识。
立方体映射(cube-map)纹理是一种特殊类型的纹理,用于环境映射,使用一组图像并把他们作为立方体的面。
立方体映射的6个面用正方形并且大小相同的6个子纹理表示。
要从立方体纹理中采样的时候,使用的纹理坐标是3维,并且被看做来自原点的方向。
方向指向用来读取纹理的立方体映射表面的位置。
立方体纹理映射主要思想是通过观察向量和表面的法向量反射来确定采样的纹理坐标。
1.2实验目的
1)掌握位图纹理读入方法;
2)掌握立方体纹理映射算法。
1.3功能要求
1)建立三维坐标系Oxyz,远点位于屏幕客户区中心,x轴水平向右为正,y轴垂直向上为正,z轴垂直于屏幕指向观察者。
2)设置屏幕背景色为黑色。
3)读入六张构成天空盒的位图作为纹理映射到立方体的可见表面上。
4)按下鼠标左键缩小立方体,按下鼠标右键增大立方体。
5)使用键盘方向旋转纹理立方体。
6)使用动画按钮播放或停止立方体动画。
1.4算法原理(算法思想)
立方体进行纹理映射是纹理对象并不是直接绑定到着色器,而是绑定到一个纹理单元,纹理单元的索引将会传递给做涩琪。
要绑定到一个纹理单元,先要将其激活,可以使用glActiveTexture()函数。
可以使用多个纹理单元,每个纹理单元可以绑定到相同的或者不同的纹理对象。
只要纹理对象的类型不同,一个纹理单元可以绑定多个纹理对象。
通过采样器变量来使用多个纹理。
在片元着色器中,采样函数需要通过采样器变量访问变量来访问多个纹理单元。
采样器对象与纹理对象不同。
纹理对象中包含了纹理数据以及配置采样操作的参数,这些参数是采样状态额一部分。
另外也可以创建一个采样对象,用采样状态参数配置它,并把它绑定到纹理单元中。
这样,采样器对象会覆盖纹理对象中定义的采样状态。
2总体设计:
功能类的结构设计
1)定义CFace类,将面片定点索引号和位图定点索引号绑定在一起,Normal为面片的法失量;
2)定义COLORREF类型的Image二维数组,用voidCTestView:
:
ReadImage(intnface)函数读入纹理图片;
3)voidCTestView:
:
ReadFace()定义面表,将四边形面片三维顶点的索引号和位图二维顶点的索引号对应起来;
4)voidCTestView:
:
DrawObject(CDC*pDC)绘制立方体,在绘制立方体之前先对立方体进行背面剔除以提高效率,绘制时先调用ReadImage()函数读入纹理,再调用ZBuffer类的SetPoint()函数绑定纹理,最后根据TextureMap()函数根据位图颜色绘制立方体的可见面;
5)在CZBuffer类中添加纹理映射函数TextureMap(),填充立方体每个可见面内部时,使用COLORREF类型的clr变量读出Image纹理数组对应点的颜色值进行绘制;
6)voidCTestView:
:
InitParameter(),透视变换参数初始化,设定用户坐标系的视点球坐标;
7)voidCTestView:
:
PerProject(CP3P),进行透视变换。
实现世界坐标系到观察坐标系的转换及屏幕坐标系到二维坐标系的转换;
8)voidCTestView:
:
OnLButtonDown(UINTnFlags,CPointpoint),鼠标左键函数;voidCTestView:
:
OnRButtonDblClk(UINTnFlags,CPointpoint),鼠标右键函数。
点击鼠标左键使立方体缩小,点击右键使立方体增大;
9)voidCTestView:
:
OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags),键盘控制函数,点击键盘不同按键控制立方体的旋转;
10)voidCTestView:
:
ClearImaMem(),最终删除纹理对象。
3详细设计(流程图)
调用各按钮操作函
开始
定义纹理对象
生成纹理对象数组
删除纹理对象
结束
绘制立方体
加载纹理图片
建立点表和面表
调用鼠标操作函数
调用键盘操作函数
将位图拷贝到缓冲区
透视变换参数初始化
4源程序(核心功能程序)
4.1绘制立方体
voidCTestView:
:
DrawObject(CDC*pDC)//绘制立方体
{
CPi3Point[4];//面的顶点坐标
CP2Texture[4];//面的纹理坐标
CZBuffer*zbuf=newCZBuffer;
zbuf->InitDeepBuffer(800,800,-1000);
for(intnFace=0;nFace<6;nFace++)
{
CVectorVS(P[F[nFace].p[1]],ViewPoint);//面的视矢量
CVectorV01(P[F[nFace].p[0]],P[F[nFace].p[1]]);//面的一条边矢量
CVectorV12(P[F[nFace].p[1]],P[F[nFace].p[2]]);//面的另一条边矢量
CVectorVN=V01*V12;//面的法矢量
if(Dot(VS,VN)>=0)//背面剔除
{
for(intnEdge=0;nEdge{
PerProject(P[F[nFace].p[nEdge]]);
Point[nEdge]=ScreenP;
Texture[nEdge]=F[nFace].t[nEdge];
}
ReadImage(nFace);
zbuf->SetPoint(Point,Texture,4);//初始化(绑定顶点和各个顶点的纹理坐标点)
zbuf->CreateBucket();//创建桶表
zbuf->CreateEdge();//创建边表
zbuf->TextureMap(pDC,Image);//纹理映射
zbuf->ClearMemory();
ClearImaMem();
}
}
deletezbuf;
}
4.2读入图片
voidCTestView:
:
ReadImage(intnface)//读入图片
{
BYTETexture[]={IDB_FRONT,IDB_BACK,IDB_LEFT,IDB_RIGHT,IDB_TOP,IDB_BOTTOM};
CBitmapNewBitmap;
NewBitmap.LoadBitmap(Texture[nface]);//调入位图资源
NewBitmap.GetBitmap(&bmp);//获得CBitmap的信息到Bitmap结构体中
intnbytesize=(bmp.bmWidth*bmp.bmHeight*bmp.bmBitsPixel+7)/8;//获得位图的总字节数
im=newBYTE[nbytesize];//开辟装载位图的缓冲区
NewBitmap.GetBitmapBits(nbytesize,(LPVOID)im);//将位图拷贝到缓冲区
Image=newCOLORREF*[bmp.bmHeight];//建立二维颜色数组
for(intn1=0;n1{
Image[n1]=newCOLORREF[bmp.bmWidth];
}
for(intn1=bmp.bmHeight-1;n1>=0;n1--)//位图高度
{
for(intn2=0;n2<=bmp.bmWidth-1;n2++)//位图宽度
{
intpos=n1*bmp.bmWidthBytes+4*n2;//位置
Image[n1][n2]=RGB(im[pos+2],im[pos+1],im[pos]);
}
}
delete[]im;
}
4.3鼠标键盘操作函数
voidCTestView:
:
OnLButtonDown(UINTnFlags,CPointpoint)//鼠标左键函数
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
R+=100;
Invalidate(FALSE);
CView:
:
OnLButtonDown(nFlags,point);
}
voidCTestView:
:
OnRButtonDblClk(UINTnFlags,CPointpoint)//鼠标右键函数
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
R-=100;
Invalidate(FALSE);
CView:
:
OnRButtonDblClk(nFlags,point);
}
voidCTestView:
:
OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
if(!
Play)
{
switch(nChar)
{
caseVK_UP:
afa=-5;
tran.RotateX(afa);
break;
caseVK_DOWN:
afa=5;
tran.RotateX(afa);
break;
caseVK_LEFT:
beta=-5;
tran.RotateY(beta);
break;
caseVK_RIGHT:
beta=5;
tran.RotateY(beta);
break;
default:
break;
}
Invalidate(