关于什么是楔形纹理坐标的资料很少,在写这篇博客的时候也没弄明白到底什么是楔形纹理坐标,写这篇博客的主要目的是记录下项目上遇到的问题以及解决的方法。
通常对于osg来说,纹理坐标数量应该要和顶点数量一致,但是项目中用到了其他的库对网格进行处理,处理后获取的网格中,顶点数量和纹理坐标数量并不一致,而是纹理坐标数量和三角面索引的数量一致,我理解的楔形纹理坐标意思就是纹理坐标数量和三角面索引数量相同。
用其他库处理后的网格需要转换成osg格式,如果保持格网中的顶点数量不变,通过该点的索引在纹理坐标数组中找到该点对应的纹理坐标,使得最终纹理坐标数量与顶点数量一致,这种方式会造成部分地方纹理扭曲错乱,在项目中还遇到过类似情况,在vcglib库中将网格导出成obj时需要将最后一个参数设置成tri::io::Mask::IOM_WEDGTEXCOORD,即楔形纹理坐标模式,否则也是会出现纹理错乱的情况,目前不知道是什么原因造成的,如果有知道原因的大神还请留言赐教,下面两张图,左边是纹理错乱的,右边是正常的。
下面是错乱纹理使用的代码:
osg::ref_ptr<osg::Vec3Array> osgVertices=new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> osgTextureCoords=new osg::Vec2Array;
osg::ref_ptr<osg::DrawElementsUInt> drawElemUint=new osg::DrawElementsUInt(GL_TRIANGLES);
osg::ref_ptr<osg::Geode> osgGeode=new osg::Geode;
osg::ref_ptr<osg::Geometry> osgGeometry=new osg::Geometry;
//顶点数量指针
uint64* vertexCountPoint=new uint64;
//顶点数组指针
InstaVec3F* outVertexPoint=outputMesh->GetVertexPositions(vertexCountPoint);
//纹理坐标数量指针
uint64* textureCountPoint=new uint64;
//纹理坐标数组指针
InstaVec2F* outTextureCoordsPoint=outputMesh->GetWedgeTexCoords(0,textureCountPoint);
//索引数量指针
uint64* indicesCountPoint=new uint64;
//索引数组指针
uint32* outIndicesPoint=outputMesh->GetWedgeIndices(indicesCountPoint);
//遍历添加顶点和纹理坐标
for(int ii=0;ii<*vertexCountPoint;++ii){
InstaVec3F p=*(outVertexPoint+ii);
osgVertices->push_back(osg::Vec3(p.X,p.Y,p.Z));
for(int jj=0;jj<*indicesCountPoint;++jj){
//在索引数组中找出索引值为ii的索引
if(*(outIndicesPoint+jj)==ii){
InstaVec2F texC=*(outTextureCoordsPoint+jj);
osgTextureCoords->push_back(osg::Vec2(texC.X,texC.Y));
break;
}
}
}
for(int ii=0;ii<*indicesCountPoint;++ii){
drawElemUint->push_back(*(outIndicesPoint+ii));
}
osgGeometry->setVertexArray(osgVertices);
osgGeometry->addPrimitiveSet(drawElemUint);
osgGeometry->setTexCoordArray(0,osgTextureCoords,osg::Array::Binding::BIND_PER_VERTEX);
/******************************/
设置纹理
/******************************/
osgGeode->addDrawable(osgGeometry);
对于这种情况有两种解决方法,一种是将顶点补齐至与纹理坐标数量一致,假设处理后的网格中有3500个顶点,9000个纹理坐标,9000个索引,按索引进行遍历,将每一个纹理坐标对应的顶点都添加到osg的顶点数组中,这种方式会造成大量的重复顶点,本来是3500个顶点,3000个面,转换成osg后变成9000个顶点,3000个面,但是纹理是正常的,下面是这种方式的代码:
osg::ref_ptr<osg::Vec3Array> osgVertices=new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> osgTextureCoords=new osg::Vec2Array;
osg::ref_ptr<osg::DrawElementsUInt> drawElemUint=new osg::DrawElementsUInt(GL_TRIANGLES);
osg::ref_ptr<osg::Geode> osgGeode=new osg::Geode;
osg::ref_ptr<osg::Geometry> osgGeometry=new osg::Geometry;
//顶点数量指针
uint64* vertexCountPoint=new uint64;
//顶点数组指针
InstaVec3F* outVertexPoint=outputMesh->GetVertexPositions(vertexCountPoint);
//纹理坐标数量指针
uint64* textureCountPoint=new uint64;
//纹理坐标数组指针
InstaVec2F* outTextureCoordsPoint=outputMesh->GetWedgeTexCoords(0,textureCountPoint);
//索引数量指针
uint64* indicesCountPoint=new uint64;
//索引数组指针
uint32* outIndicesPoint=outputMesh->GetWedgeIndices(indicesCountPoint);
for(int ii=0;ii<*indicesCountPoint;++ii){
//通过索引获取顶点
InstaVec3F p=*(outVertexPoint+*(outIndicesPoint+ii));
osgVertices->push_back(osg::Vec3(p.X,p.Y,p.Z));
InstaVec2F texC=*(outTextureCoordsPoint++ii);
osgTextureCoords-push_back(osg::Vec2(texC.X,texC.Y));
drawElemUint->push_back(ii);
}
osgGeometry->setVertexArray(osgVertices);
osgGeometry->addPrimitiveSet(drawElemUint);
osgGeometry->setTexCoordArray(0,osgTextureCoords,osg::Array::Binding::BIND_PER_VERTEX);
/******************************/
设置纹理
/******************************/
osgGeode->addDrawable(osgGeometry);
因为纹理坐标数量与索引数量一致,因此纹理坐标中有大量重复的坐标,第二种方式是将纹理坐标去重,然后通过索引找到该纹理坐标对应的顶点,将顶点增加至与纹理坐标数量一致,假设处理后的网格中有3500个顶点,9000个纹理坐标,9000个索引,去除重复纹理坐标后,纹理坐标数量可能是4300个,这样转换成osg后就是4300个顶点,4300个纹理坐标,3000个面,这种方式既能较少的保留顶点数量,还能得到正确的纹理,下面是这种方式的代码:
osg::ref_ptr<osg::Vec3Array> osgVertices=new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> osgTextureCoords=new osg::Vec2Array;
osg::ref_ptr<osg::DrawElementsUInt> drawElemUint=new osg::DrawElementsUInt(GL_TRIANGLES);
osg::ref_ptr<osg::Geode> osgGeode=new osg::Geode;
osg::ref_ptr<osg::Geometry> osgGeometry=new osg::Geometry;
//顶点数量指针
uint64* vertexCountPoint=new uint64;
//顶点数组指针
InstaVec3F* outVertexPoint=outputMesh->GetVertexPositions(vertexCountPoint);
//纹理坐标数量指针
uint64* textureCountPoint=new uint64;
//纹理坐标数组指针
InstaVec2F* outTextureCoordsPoint=outputMesh->GetWedgeTexCoords(0,textureCountPoint);
//索引数量指针
uint64* indicesCountPoint=new uint64;
//索引数组指针
uint32* outIndicesPoint=outputMesh->GetWedgeIndices(indicesCountPoint);
//遍历IInstaLOD的索引
for(int ii=0;ii<*indicesCountPoint;++ii){
//获取该索引的纹理坐标
InstaVec2F texC=*(outTextureCoordsPoint++ii);
osg::Vec2 vec2(texC.X,texC.Y);
//是否重复标识
bool isRepeated=false;
//遍历osgTextureCoords中的纹理坐标,判断该纹理坐标是否重复
for(int jj=0;jj<osgTextureCoords->getNumElements();++jj){
//如果重复,只添加osg纹理数组中该点的索引
if(vec2==osgTextureCoords->at(jj)){
isRepeated=true;
drawElemUint->push_back(jj);
break;
}
}
//如果不重复,则osg图元索引添加值为osg当前纹理坐标个数,从0依次
//增加,osg顶点和纹理坐标添加IInstaLOD当前索引对应的顶点和纹理坐标
if(!isRepeated){
drawElemUint->push_back(osgTextureCoords->getNumElements());
osgTextureCoords->push_back(vec2);
InstaVec3F p=*(outVertexPoint+*(outIndicesPoint+ii));
osgVertices->push_back(osg::Vec3(p.X,p.Y,p.Z));
}
}
osgGeometry->setVertexArray(osgVertices);
osgGeometry->addPrimitiveSet(drawElemUint);
osgGeometry->setTexCoordArray(0,osgTextureCoords,osg::Array::Binding::BIND_PER_VERTEX);
/******************************/
设置纹理
/******************************/
osgGeode->addDrawable(osgGeometry);