类似的问题有:
最近做点云相关的事情,然而就PCL自带函数读取OBJ文件并且显示这个事情卡了我几天,在这里记录一下,希望能帮到别人。
读入obj文件时,使用:
loadOBJFile( const std::string& file_name, pcl::TextureMesh &mesh)
此时,使用如下代码读取并显示:
int main(int argc,char** argv)
{
string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
//读取
pcl::TextureMesh mesh;
pcl::io::loadOBJFile(objPath, mesh);
//显示
boost::shared_ptrviewer(new pcl::visualization::PCLVisualizer("3D viewer A"));
viewer->addTextureMesh(mesh,"mesh");
while (!viewer->wasStopped ()) // 在按下 "q" 键之前一直会显示窗口
{
viewer->spinOnce ();
}
return 0;
}
编译运行后
只显示一个窗口,里面没有任何东西,而且命令行提示:
[PCLVisualizer::addTextureMesh] No textures coordinates found!
即:没有找到纹理的坐标。
经过调试发现,loadOBJFile()能够正常读入点坐标数据,贴图文件,但不能读入每个点对应贴图文件的坐标,经过查阅资料和论坛讨论,发现这是一个PCL库的bug。
这是PCL库的bug,在最新版的PCL库(PCL 1.9.1)中还没有修复,PCL的github页上有人已经收集,整理提交了这个bug。详情可以点击这里查看。
这一块是流水账,想要解决方案的,直接看最后一部分。 点这里
为解决上面问题,第一时间去查看了官方文档,发现了还有另外一个函数也可以加载OBJ文档,而且看起来更符合我的目的(读取网格到PCL提供的纹理类型),这个函数就是:
loadPolygonFileOBJ(const std::string& file_name, pcl::TextureMesh &mesh)
这个函数是对另外一个函数的补充,重载了一下。功能从函数名上可以看出,就是从OBJ中读取网格数据。
于是赶紧换了函数,尝试一下:
int main(int argc,char** argv)
{
string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
//读取
pcl::TextureMesh mesh;
pcl::io::loadPolygonFileOBJ(objPath, mesh);
//显示
boost::shared_ptrviewer(new pcl::visualization::PCLVisualizer("3D viewer A"));
viewer->addTextureMesh(mesh,"mesh");
while (!viewer->wasStopped ()) // 在按下 "q" 键之前一直会显示窗口
{
viewer->spinOnce ();
}
return 0;
}
编译运行
只显示一个窗口,里面没有任何东西,而且命令行提示:
[PCLVisualizer::textureFromTexMaterial] No texture file given for material !
[PCLVisualizer::addTextureMesh] Failed to load texture , skipping!
也就是没有纹理贴图的数据。
经过调试发现,loadPolygonFileOBJ()能够读入点云数据和对应点云的纹理贴图的坐标,但不能读入贴图文件的数据。
这个函数也有bug!
接下来,就开始开脑洞了,毕竟两个函数读OBJ是数据到同一个类的对象中了,那能不能优势互补一下:
定义两个TextureMesh对象,分别是loadOBJFile()和loadPolygonFileOBJ()读到的结果,把loadPolygonFileOBJ()读到的对应点云的纹理贴图的坐标赋值给loadOBJFile(),这样,其中的一个不就完美了么?
于是,尝试一下:
int main(int argc,char** argv)
{
string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
//读取
pcl::TextureMesh mesh;
pcl::io::loadOBJFile(objPath, mesh);
pcl::TextureMesh mesh2;
pcl::io::loadPolygonFileOBJ(objPath, mesh2);
mesh.tex_coordinates = mesh2.tex_coordinates;
//显示
boost::shared_ptr
viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));
viewer->addTextureMesh(mesh,"mesh");
while (!viewer->wasStopped ()) // 在按下 "q" 键之前一直会显示窗口
{
viewer->spinOnce ();
}
return 0;
}
编译运行后
终于显示模型了,但贴图全贴错了
经过查阅发现,PCL库的贴图坐标点的按照顺序读入的,而不是和点云的坐标点做关联。即如果点云点和贴图坐标点不是同时读入的话,极有可能出现贴图坐标点和点云坐标对应错误,那么最后渲染出来的3D模型的纹理贴图也就是错误的。
在这次测试中,loadOBjFile()和loadPolygonFileOBJ()读入点云和坐标点的方式不一样,导致不能正常显示。
经过上面的测试,最终的方案也就基本形成了:
两个函数分别读到的数据:
loadOBJFile( const std::string& file_name, pcl::TextureMesh &mesh)
点坐标数据,贴图文件
loadPolygonFileOBJ(const std::string& file_name, pcl::TextureMesh &mesh)
点云数据,点云坐标对应的纹理贴图的坐标
因为loadPolygonFileOBJ
这个函数读到的点云坐标和纹理贴图坐标相互对于,那么,将loadOBJFile()中的纹理贴图数据赋值给loadPolygonFileOBJ()的变量即可:
测试一下:
int main(int argc,char** argv)
{
string objPath = "./texture_mesh.obj"; //当前目录下的obj文件
//读取
pcl::TextureMesh mesh;
pcl::io::loadOBJFile(objPath, mesh);
pcl::TextureMesh mesh2;
pcl::io::loadPolygonFileOBJ(objPath, mesh2);
mesh2.tex_materials= mesh.tex_materials;
//显示
boost::shared_ptr
viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));
viewer->addTextureMesh(mesh2,"mesh");
while (!viewer->wasStopped ()) // 在按下 "q" 键之前一直会显示窗口
{
viewer->spinOnce ();
}
return 0;
}
完美显示!
pcl::TextureMesh mesh;
pcl::io::loadPolygonFileOBJ(objPath,mesh);
pcl::TextureMesh mesh2;
pcl::io::loadOBJFile(objPath,mesh2);
mesh.tex_materials= mesh2.tex_materials;
boost::shared_ptr
viewer(new pcl::visualization::PCLVisualizer("3D viewer A"));
viewer->addTextureMesh(mesh,"mesh");
这样就可以显示obj文件了。这里的解决方法是简单粗暴的,是不完美的。
我测试通过的只是当纹理贴图只有一张的时候,至于多张纹理贴图是否能够正确显示,我没有做过测试。所以不敢妄言解决
最后,最好的方式还是等PCL修复上面这些bug。
虽然这两个函数都是pcl::io命名空间中定义的,但在不同的头文件中。
loadOBjFile()的头文件比较好找,函数说明下面就有:
loadPolygonFileOBJ()下面没有:
网上也找不到资料,最后还是在ros的官方文档中找到蛛丝马迹,顺藤摸瓜,在PCL源码的 vtk_lib_io.h 中找到了这个函数的定义:
完结撒花~