APP下载

基于纹理场的三维纹理绘制方法

2019-10-28

通化师范学院学报 2019年10期
关键词:形体顶点纹理

程 飞

1 三维纹理

在计算机图形学中,三维物体表面的纹理是增加真实感、模拟真实世界形体的有效手段.当前主要采用的表面纹理方法有:①纹理贴图[1].将一张图片按映射方法投射到形体表面.常用的商业软件(如3dmax)一般采用该方法.该方法速度快,但是在纹理连接处会有明显的不连续现象,不符合形体的物理实际(如图1所示).②纹理合成[2].纹理合成最早应用于二维图形,即给出一个小尺寸的纹理图,进而合成大尺寸的纹理图(如图2所示).纹理合成方法可以进一步推广到三维形体.纹理合成主要基于markov随机场算法,速度慢、需要采用一定的加速算法.实验表明,纹理合成对于图片的纹理特征有要求,对于一些特殊的样图(如规则样图)不能产生需要的绘制效果.以图2为例,图2(a)为样图,图2(b)为合成图,图2(a)为规则的砖墙图案,图2(b)中砖块出现了明显的混乱和错位.

图1 纹理贴图

图2 纹理合成

2 基于纹理场的三维纹理绘制算法设计

在大规模游戏场景绘制中,需要快速绘制具有真实感三维形体.采用上述两种方法绘制三维表面纹理难以满足真实感和实时性的要求.因而,本文提出一种基于纹理场[3]的三维纹理绘制方法.主要思想为:①由数学方法或者样图构建纹理场,三维纹理场以函数

color(x,y,z)=F(x,y,z)的形式给出,场中每一坐标点对应一个颜色值.将物体置于纹理场中,由所给公式计算表面网格点的颜色值.计算机图形学中,三维形体一般由点云模型给出,点云模型通过一定的算法形成三角网格,从而表示形体的外轮廓.对于三角网格涂色,可以得到三维物体的表面纹理.为简化问题,这里仅以图形学常用的Bunny模型为例,对于规则纹理场和不规则纹理场中三维形体表面纹理的绘制方法予以讨论.本文实例均以底层图形库OpenGL为工具开发.具体算法描述如下:

step1.构建纹理场.

step2.在OpenGL中,导入Bunny表面点的点云模型.

step3.利用导入的点云数据,按一定的算法得到三角线框模型.

step4.对于每一个轮廓点计算其在纹理场中的对应的颜色.

step5.绘制每个三角面片,进而绘制出整个图形.

3 绘制实例

3.1 规则纹理场中三维形体绘制实例

这里仅以黑白格纹理为例,描述具体算法.

构建纹理场.将黑白格纹理场理解为多个边长为1的黑色与白色正方体间隔叠加而成.如图3(b)所示.这里纹理场大小设置为20*20*20;将其放置在第一卦限,场中任意一点坐标为(x,y,z),对于x,y,z三个数下取整得到(x0,y0,z0),考虑x0,y0,z0的奇偶性,如果(x0,y0,z0)组合为(偶偶奇)(奇奇奇)(奇偶偶)(偶奇偶),则以该点为起点,向三个坐标轴正方向所作边长为1的正方体所确定的空间的颜色为黑色,反之为白色.

导入Bunny点云模型(ply格式),这里采用meshlab软件将其转化为obj格式,则形体由三角面片表示,每个面片包括三个顶点.建立数组index[],以标记不同面片.

建立光照等模型后,将形体放置在纹理场中,即将形体上的各个表面轮廓点映射到纹理场中.以Bunny为例,其表面点三维坐标的取值范围为[-0.2<x<0,0<y<0.2,-0.2<z<0],需要对于表面点数据进行规格化,并根据实际绘图要求确定缩放比例.这里将其变换到[0<x<20,0<y<20,0<z<20].关键代码如下:

glBegin(GL_TRIANGLES); //开始绘制物体(三角形)

{for(int j=0;j<P_Object->Num_Faces;j++)//遍历所有的面,开始绘制

{for(int index=0;index<1;index++) //遍历面上的顶点

{int vertIndex=P_Object->P_Faces[j].vertIndex[index];//取得每个面顶点的索引值

int cccc=100;

int xx=int(floor((P_Object->P_Verts[vertIndex].x+0.2)*cccc));//规格化以及缩放

int yy=int(floor(P_Object->P_Verts[vertIndex].y*cccc));

int zz=int(floor((P_Object->P_Verts[vertIndex].z+0.2)*cccc));

if(((xx%2==1)&&(yy%2==1)&&(zz%2==0))||((xx%2==0)&&(yy%2==0)&&(zz%2==0))||((xx%2==1)&&(yy%2==0)&&(zz%2==1))|| ((xx%2==0)&&(yy%2==1)&&(zz%2==1)) ) //映射纹理场

glColor3f(1,1,1);//位于黑格位置

elseglColor3f(0,0,0);}//位于白格位置

for(index=0;index<3;index++) //遍历面上的顶点{int vertIndex=P_Object->P_Faces[j].vertIndex[index];//取得每个面顶点的索引值

glNormal3f (P_Object->P_Normals [vertIndex].x, P_Object->P_Normals[vertIndex].y,P_Object->P_Normals[vertIndex].z);//设置法向量

glVertex3f(P_Object->P_Verts[vertIndex].x,P_Object->P_Verts[vertIndex].y,P_Object->P_Verts[vertIndex].z);//绘制顶点

}glEnd();}

图3 规则纹理场绘制效果

绘制效果如图3(c)所示,观察图形可见,图形相当于从纹理场中蚀刻得到,不会出现类似图1的视觉上纹理不连续现象.

3.2 不规则纹理场中三维形体绘制实例

这里以大理石纹理为例予以说明.

图4 不规则纹理场绘制效果

首先给出样图,如图4(a)所示.将其导入为纹理.关键代码如下:

void BuildTexture(char*szPathName)//获取图片,*szPathName为图片地址

{…………………………

lWidthPixels=MulDiv(lWidth,GetDeviceCaps(hdcTemp,LOGPIXELSX),2540);//取得IPicture宽度(转换为Pixels格式)

lHeightPixels =MulDiv(lHeight,GetDeviceCaps(hdcTemp,LOGPIXELSY),2540);//取得IPicture高度(转换为Pixels格式)hbmpTemp=CreateDIBSection(hdcTemp,&bi,DIB_RGB_COLORS,(void**)&pBits,0,0); //在位图上绘制IPicture

pPicture->Render(hdcTemp,0,0,lWidthPixels,lHeightPixels,0,lHeight,lWidth,-lHeight,0);tupianchang=lWidthPixels;//图片长tupiankuan=lHeightPixels;//图片宽

for(long i=0;i<lWidthPixels;i++)//循环遍历所有的像素

{for(long j=0;j< lHeightPixels;j++)

{xiangsu[i][j][0]=int(pPixel[0]);//将图片的R值存入xiangsu[i][j][0]

xiangsu[i][j][1]=int(pPixel[1]);//将图片的 G 值存入 xiangsu[i][j][1]

xiangsu[i][j][2]=int(pPixel[2]);}//将图片的B值存入xiangsu[i][j][2]

建立纹理场.现实世界中,一定大小的大理石切块的某一方向的各个切片图案基本相同,如图4(b)所示.可以将纹理场理解为图片纹理沿一定方向的延伸.图像的像素点按照一定的映射关系映射到三维空间,得到纹理场.关键代码如下:

void huiwenlichang()

{for(int i=1;i< tupianchang-1 ;i++) //循环遍历所有的像素

{for(int j=1;j<tupiankuan-1;j++)

{glColor3ub(xiangsu[i][j][0],xiangsu[i][j][1],xiangsu[i][j][2]);

glTranslatef(i*0.0002,j*0.0002,0);DrawCube(0.0002,0.0002,0.15);}}}

形体表面的三维纹理计算.将形体放置在纹理场中,首先计算各个表面轮廓点在纹理场中的对应位置,找到该点的纹理场数据,作为图形绘制的颜色值.本例中,导入的图片大小为768*1 024,考虑Bunny模型的实际大小,以int(floor((P)/0.2*tupianchang))的形式进行缩放,建立形体和纹理场的对应关系.然后再以三角面片的形式绘制图形.

glBegin(GL_TRIANGLES); //绘制三角面片

for(int j=0;j<P_Object->Num_Faces;j++)//遍历所有的面,

{for(int index=0;index<3;index++) //遍历面上的顶点

{int vertIndex=P_Object->P_Faces[j].vertIndex[index];//取得面顶点的索引值

x=P_Object->P_Verts[vertIndex].x;

y=P_Object->P_Verts[vertIndex].y;

z=P_Object->P_Verts[vertIndex].z;

int x0=int(floor((x+0.1)/0.2*tupianchang));

int y0=int(floor((y)/0.2*tupiankuan));

glColor3ub(xiangsu[x0][y0][0],xiangsu[x0][y0][1],xiangsu[x0][y0][2]);

glNormal3f (P_Object->P_Normals [vertIndex].x, P_Object->P_Normals[vertIndex].y,P_Object->P_Normals[vertIndex].z);//设置法向量

glVertex3f(P_Object->P_Verts[vertIndex].x,P_Object->P_Verts[vertIndex].y,

P_Object->P_Verts[vertIndex].z); //绘制顶点

}

}

glEnd();

//结束绘制

绘制效果如图4(c)所示.

4 绘图效果分析

本质上,纹理映射算法需要将样图上的点的纹理坐标(s,t)与空间点(x,y,z)建立对应关系.映射关系是纹理映射算法的关键.纹理映射可以应用于以下三种情况:①形体表面轮廓由明确数学表达式给出(主要是二次曲面,如圆柱、圆锥等规则形体),其表面点和点的纹理坐标,都易于计算,可以用for循环快速实现纹理绘制.②对于样条类曲面(如Nurbs以及Bezier曲面),OpenGL应用内置evaluator自动生成纹理[1],纹理对应关系依赖曲面的型值点的数目,纹理尺寸无法控制.③对于3ds格式表示的形体.3ds格式是本身附加纹理坐标的(在3dmax中以uv坐标的形式表示),不需要用户建立映射关系.给定样图以后,OpenGL可以自动生成三维表面纹理,同样对于纹理的尺寸无法控制.该方法对于不具有(u,v)纹理坐标非3ds格式的模型难以实现,如本文所用Bunny模型(obj格式).且上面三种方法在纹理结合处都难以取得较好的绘制效果.而纹理合成算法对于随机图形有较好的效果,对于规则图形会产生严重形变,如图2所示.而且其实时性差,难以适用于游戏场景的绘制.

比较而言,纹理场算法具有较好的适应性.①对于obj、3ds、md2等各种格式的三维图形都可以处理,各种格式的模型导入OpenGL中,都可以取得其表面点的三维坐标.②纹理场的尺寸可控.对于规则纹理场,纹理空间由数学形式给出,可以调整参数改变纹理尺寸.以图3(b)为例,该空间包含20*20*20个黑白立方体,可以进一步细分为40*40*40个立方体.对于不规则纹理场,采用不同分辨率的图形作为样图,就可以实现不同分辨率的纹理.③纹理的方向可控.调整纹理场的方向,纹理场中形体就会得到不同的纹理.图5显示了规则纹理场中模拟木纹不同方向的Bunny表面纹理.纹理场以多个同心圆模拟木材纹理场.图5(a)纹理中心位于底部,图5(b)纹理中心穿过头部.三种纹理算法的比较如表1所示.

图5 规则纹理场中模拟木纹不同方向的Bunny表面纹理

表1 三种纹理算法的比较

5 推广和总结

实验表明,OpenGL框架内基于函数生成纹理场的算法比较基于样图的算法具有更高的处理速度.本文使用的Bunny具有34 838个顶点,69 452个面.图6是基于样图的Bunny纹理,样图大小1 024*726,在Pent T4200处理器的绘制时间为8.7 s;而图5的绘制时间为2.7 s.究其原因,样图的导入需要一定的时间和空间开销,其构建纹理场的时间远大于基于函数的纹理场的算法所需要的时间.观察图5和图6,二者绘制效果并无明显区别.

图6 基于样图的Bunny表面纹理

纹理场算法从数学函数或者样图出发,构建纹理场,从纹理场中蚀刻形体,避免了纹理贴图的接缝及视觉不连续问题.与纹理合成算法相比较,绘图具有实时性,还可以避免纹理合成中的错位问题.该算法能满足游戏场景的绘图需要,是一种构建真实感的三维表面纹理的有效方法.

猜你喜欢

形体顶点纹理
过非等腰锐角三角形顶点和垂心的圆的性质及应用(下)
过非等腰锐角三角形顶点和垂心的圆的性质及应用(上)
浅谈形体训练在声乐表演中的作用
基于BM3D的复杂纹理区域图像去噪
基础版几何形体变化在服装创意立裁中的应用
使用纹理叠加添加艺术画特效
色彩的解放——印象派绘画将色彩从形体中解放出来
TEXTURE ON TEXTURE质地上的纹理
消除凹凸纹理有妙招!
数学问答