2021SC@SDUSC
UV 展开
参数曲面的参数域变量一般用UV字母来表达,比如参数曲面F(u,v)。所以一般叫的三维曲面本质上是二维的,它所嵌入的空间是三维的。凡是能通过F(u,v)来表达的曲面都是参数曲面,比如NURBS曲面。对于三角网格,如果能把它与参数平面建立一一映射,那么它也就被参数化了,这个映射就是UV展开。如下图所示,左图是右边网格在参数平面上的展开,这样每个顶点都有了一个uv参数值,这也被称为纹理坐标。
创建割缝
网格UV展开到平面的时候,如果没有割缝产生,那么每个顶点在其相邻三角形内的纹理坐标都是一样的,故可简称为顶点的纹理坐标。如果有割缝产生,割缝处的顶点在不同三角形内的纹理坐标是不一样的。这时,顶点和纹理坐标是一对多的关系。对于任意拓扑结构的网格,需要给它添加割缝,把它分割成一片一片的圆盘结构,再做展开。
UV展开的应用里,经常需要创建一些网格割缝。好的割缝,一般有这些性质:
- 长度很短
- 割线光滑
- 沿特征边
- 分布在视觉不明显的地方
- 在全自动UV展开应用里,割缝首先要能把网格割成一片一片的圆盘结构
UV展开的扭曲情况
网格展开到平面区域,除了可展曲面,其它曲面在展开后都会产生一些扭曲。一般有两种扭曲。一种是曲面本身的几何所决定的,比如球面展开到平面,一定会产生扭曲。想要减少展开的扭曲程度,可以在扭曲程度大的地方增加曲面割线。另一种是展开算法中的约束产生的扭曲,比如固定边界的UV展开。一种直观的观察展开扭曲程度的方式是,把一张棋盘格图片贴到网格上,棋盘格越均匀,UV展开扭曲越小。
UvUnwrap
任意拓扑网格全自动UV展开大概有以下步骤:自动找纹理割缝,自动UV展开,如果展开扭曲大,会自动进行再分割展开,最后自动纹理坐标打包。
//任意拓扑网格全自动UV展开类
class UvUnwrapper
{
public:
void setMesh(const Mesh &mesh);
void setTexelSize(float texelSize);
void unwrap();
const std::vector<FaceTextureCoords> &getFaceUvs() const;
const std::vector<Rect> &getChartRects() const;
const std::vector<int> &getChartSourcePartitions() const;
float getTextureSize() const;
private:
void partition();
void splitPartitionToIslands(const std::vector<size_t> &group, std::vector<std::vector<size_t>> &islands);
void unwrapSingleIsland(const std::vector<size_t> &group, int sourcePartition, bool skipCheckHoles=false);
void parametrizeSingleGroup(const std::vector<Vertex> &verticies,
const std::vector<Face> &faces,
std::map<size_t, size_t> &localToGlobalFacesMap,
size_t faceNumToChart,
int sourcePartition);
bool fixHolesExceptTheLongestRing(const std::vector<Vertex> &verticies, std::vector<Face> &faces, size_t *remainingHoleNum=nullptr);
void makeSeamAndCut(const std::vector<Vertex> &verticies,
const std::vector<Face> &faces,
std::map<size_t, size_t> &localToGlobalFacesMap,
std::vector<size_t> &firstGroup, std::vector<size_t> &secondGroup);
void calculateSizeAndRemoveInvalidCharts()