登山第五梯:obj、mtl、纹理图片——复杂的关系

一 简介

        纹理贴图是人们对物体真实感的新需求之一。纹理映射的概念由Catmull提出,用来表示以像素坐标(u,v)表示的纹理空间和以参数坐标(x,y,z)表示的三维空间之间的映射关系,随后,Blinn对纹理映射的概念进行了改进,使得纹理映射结果更加自然。随后Bier等提出了两步映射法,实验过程中引入中介曲面并将其作为中间映射媒介,从而构建从三维模型到纹理图像的映射关系。传统的纹理贴图大多是通过计算机来渲染添加到三维模型中,并不是物体在真实世界中所呈现的样子。

随后,所研究的纹理贴图主要是使用拍摄得到的彩色照片作为纹理图像,并通过重建点云与纹理图像的映射关系,将纹理信息赋予给三维点云,从而构建出具有真实纹理的三维数据。

二 文件结构

2.1 obj

        OBJ文件是Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种文件格式,可以通过Maya、CloudCompare等软件打开。OBJ文件可以是文本格式,因此,可以通过写字板打开进行查看和编辑。

        OBJ格式支持多边形、直线、表面和自由形态曲线。多边形和直线可以通过顶点进行描述,曲线支持B样条、贝塞尔曲线以及泰勒方程表征的曲线。

        OBJ是一种3D模型文件,其不包含材质特性和纹理图片路径,且其通常主要支持多边形模型。

以下为一个简单的正方体模型的样例:

#The units used in this file are meters.

v -1 -1 -1

v 1 -1 -1

v 1 1 -1

v -1 1 -1

v -1 -1 1

v 1 -1 1

v 1 1 1

v -1 1 1

f 1 2 3 4

f 1 2 6 5

f 2 3 7 6

f 3 4 8 7

f 4 1 5 8

f 5 6 7 8

其中,v代表顶点坐标(x,y,z),f代表面片四边形,后面的数字代表坐标索引,注意,obj文件的索引从1开始。

结果展示:

这个时候,我们发现这个正方体无法区分面片,这个时候我们可以给其加上法向量,添加法向量有两种方式:

(1)给顶点添加法向量

#The units used in this file are meters.

v -1 -1 -1

v 1 -1 -1

v 1 1 -1

v -1 1 -1

v -1 -1 1

v 1 -1 1

v 1 1 1

v -1 1 1

vn -0.33405748 -0.66648537 0.66648537

vn 0.81489873 -0.40984145 0.40984145

vn 0.33405748 0.66648537 0.66648537

vn -0.81489873 0.40984145 0.40984145

vn -0.66648537 -0.33405748 0.66648537

vn 0.40984145 -0.81489873 0.40984145

vn 0.66648537 0.33405748 0.66648537

vn -0.40984145 0.81489873 0.40984145

f 1//1 2//2 3//3 4//4

f 1//1 2//2 6//6 5//5

f 2//2 3//3 7//7 6//6

f 3//3 4//4 8//8 7//7

f 4//4 1//1 5//5 8//8

f 5//5 6//6 7//7 8//8

其中,vn表示各顶点的法向量,而f 1//1 2//2 3//3 4//4中的1//1表示顶点索引/(纹理坐标索引)/法向量索引,由于无纹理坐标,故中间可不填。

结果展示:

(2)给面片添加法向量

#The units used in this file are meters.

v -1 -1 -1

v 1 -1 -1

v 1 1 -1

v -1 1 -1

v -1 -1 1

v 1 -1 1

v 1 1 1

v -1 1 1

vn 0 0 -1

vn 0 -1 0

vn 1 0 0

vn 0 1 0

vn -1 0 0

vn 0 0 1

f 1//1 2//1 3//3 4//1

f 1//2 2//2 6//2 5//2

f 2//3 3//3 7//3 6//3

f 3//4 4//4 8//4 7//4

f 4//5 1//5 5//5 8//5

f 5//6 6//6 7//6 8//6

由上可发现,法向量个数与面片个数一致,而同一面片的法向量一致。

结果展示:

这个时候,我们就可以看到棱角分明了。

目前仅仅是创建好了3D模型,如何进行模型贴图,便需要往下看了。

2.2 mtl

mtl 文件(Material Library File)是材质库文件,与obj文件配合,把纹理颜色渲染到obj模型上。

以下用一个简单例子进行说明:

obj文件:

#The units used in this file are meters.

#applied to each of its faces.

mtllib show.mtl

v -1 -1 -1

v 1 -1 -1

v 1 1 -1

v -1 1 -1

v -1 -1 1

v 1 -1 1

v 1 1 1

v -1 1 1

vn 0 0 -1

vn 0 -1 0

vn 1 0 0

vn 0 1 0

vn -1 0 0

vn 0 0 1

usemtl my_mtl_01

g pCube1

f 1//1 2//1 3//3 4//1

f 1//2 2//2 6//2 5//2

f 2//3 3//3 7//3 6//3

f 3//4 4//4 8//4 7//4

f 4//5 1//5 5//5 8//5

f 5//6 6//6 7//6 8//6

其中增加了mtl文件路径,如mtllib show.mtl表明使用show.mtl材质库文件,而后面的usemtl my_mtl_01,表示使用材质库文件show.mtl中的材质组my_mtl_01。

show.mtl文件:

newmtl my_mtl_01

Ka 1 1 1

Kd 1 1 1

d 1

Ns 0

illum 1

其中,newmtl表示定义新的材质组,后面为材质组名称;

Ka为环境反射,如Ka 1 1 1表示用r=1,g=1,b=1的颜色值作为环境光,三个参数的取值范围为[0,1];

Kd为漫反射,Kd 1 1 1同样表示用r=1,g=1,b=1的颜色值。

d为渐隐指数,可理解为透明度,默认为1,表示不透明。取值范围为[0,1];

Ns为反射指数,取值范围为[0,1000];

illum为照明度,取值范围为[0.10]。

上面文件的展示:

接着把Kd改为红色

newmtl my_mtl_01

Ka 1 1 1

Kd 1 0 0

d 1

Ns 0

illum 1

然后把透明度d改为0.5

newmtl my_mtl_01

Ka 1 1 1

Kd 1 0 0

d 0.5

Ns 0

illum 1

2.3 纹理图片

纹理图片输入一张传感器采集的RGB图像即可,文件周骓可为jpg、png。

三 效果展示

show.obj:

#The units used in this file are meters.

#applied to each of its faces.

mtllib show.mtl

v -1 -1 -1

v 1 -1 -1

v 1 1 -1

v -1 1 -1

v -1 -1 1

v 1 -1 1

v 1 1 1

v -1 1 1

vt 0 0

vt 0 1

vt 1 0

vt 1 1

vn 0 0 -1

vn 0 -1 0

vn 1 0 0

vn 0 1 0

vn -1 0 0

vn 0 0 1

usemtl my_mtl_01

g pCube1

f 1/1/1 2/2/1 3/4/3 4/3/1

f 1/1/2 2/2/2 6/4/2 5/3/2

f 2/1/3 3/2/3 7/4/3 6/3/3

f 3/1/4 4/2/4 8/4/4 7/3/4

f 4/1/5 1/2/5 5/4/5 8/3/5

f 5/1/6 6/2/6 7/4/6 8/3/6

其中,vt为纹理坐标,通常取值范围为[0,1];

show.mtl:

newmtl my_mtl_01

Ka 1 1 1

Kd 1 1 1

d 1

Ns 0

illum 1

map_Kd mosquito.jpg

其中,map_Kd mosquito.jpg表示指定纹理文件路径。

mosquito.jpg:

模型纹理贴图结果:

四 代码展示

以下为obj文件的保存函数:

//此处输入的点云坐标携带法向量,且UV坐标与顶点坐标一一对应
void saveObj(const pcl::PointCloud<pcl::PointNormal>& pCloud,
       const std::vector<texturePos>& pUV,
       const pcl::PolygonMesh& triangles,
       const Eigen::Vector3d& offset,
       const std::string& outFolder,
       const std::string& objFile,
       const std::string& mtlFile,
       const std::string& imgFile)
{
       {
              std::ofstream outfile(outFolder+"\\"+objFile);
              outfile.setf(std::ios::fixed, std::ios::floatfield);
              outfile.precision(6);
              outfile << "mtllib " << mtlFile << ".mtl" << std::endl;
              //header
              outfile << "#The units used in this file are meters." << std::endl;
              //输出顶点v
              for (auto& pt : pCloud)
              {
                     outfile << "v" << " " << pt.x + offset[0] << " " << pt.y +  offset[1] << " " << pt.z + offset[2] << std::endl;
              }
              //输出顶点纹理坐标vt
              for (auto& pt : pUV)
              {
                     outfile << "vt" << " " << pt.u << " " << pt.v << std::endl;
              }
              outfile << "usemtl " << mtlFile << std::endl;
              //输出顶点法向量vn
              for (auto& pt : pCloud)
              {
                     outfile << "vn" << " " << pt.normal_x << " " << pt.normal_y  << " " << pt.normal_z << std::endl;
              }
              //输出面f
              outfile << "g pCube1" << std::endl;
              for (auto& f : triangles.polygons)
              {
                     //outfile << "f" << " " << f.vertices[0] + 1 << " " <<  f.vertices[1] + 1 << " " << f.vertices[2] + 1 << std::endl;
                     outfile << "f" << " " << f.vertices[0] + 1 << "/" <<  f.vertices[0] + 1 << "/"<< f.vertices[1] + 1 <<" "
                           << f.vertices[1] + 1 << "/" << f.vertices[1] + 1 <<"/"<< f.vertices[1] + 1 << " "
                           << f.vertices[2] + 1 << "/" << f.vertices[2] + 1 <<"/"<< f.vertices[1] + 1 <<  std::endl;
              }
              outfile.close();
       }
       {
              std::string tempFile =outFolder+"\\"+ mtlFile + ".mtl";
              std::ofstream outfile(tempFile);
              outfile.setf(std::ios::fixed, std::ios::floatfield);
              outfile.precision(6);
              outfile << "newmtl my_mtl" << std::endl
                     << "Ka 1 1 1" << std::endl
                     << "Kd 1 1 1" << std::endl
                     << "d 1" << std::endl
                     << "Ns 0" << std::endl
                     << "illum 1" << std::endl
                     << "map_Kd " << imgFile << std::endl;
              outfile.close();
       }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云登山者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值