【发布时间】:2021-05-25 12:51:37
【问题描述】:
背景资料
多边形网格使用 UV 集将图像纹理映射到其表面。使用 UDIM 纹理,您可以使用整数瓦片来应用不同的纹理,就像 UV 瓦片的整数范围内一样,例如tile [0, 0] 可以具有与 UV tile [0, 1] 不同的纹理。
给定 UV 壳/点,我想快速检测 UV 集当前使用的整数图块。 在实践/生产中,UV 很少跨越 UV 瓦片边界,因为它们是按 UDIM 瓦片布局的。不过,我也想准确检测这些不太常见的情况。
目标
给定一个网格的 UV,我想检测哪些 UV 瓦片被占用。上面的示例图像在 UV 瓦片的边界框中有壳,但从技术上讲,UV 可以跨越 UV 瓦片的边界并延伸到下一个(甚至更远)。
我想查询/检测正在使用的 UV 瓦片:
- UV 点可以全部位于图块之外(例如,图块 [0, 0] 周围的多边形的 4 个点)并且图块包含在多边形中。瓦片应包含在 UV 中。
- UV 点可以围绕瓷砖(例如,超过 4 个点的 NGon UV 围绕瓷砖弯曲(因此边界框会撞击瓷砖,但 UV 实际上不包括瓷砖)。
- 其他情况是 UV 可以仅与 Tile 重叠/相交或完全包含在 Tile 中。然后应该包括图块。
我尝试在 Autodesk Maya 中使用 Python 进行此操作,但我对任何快速的数学方法都很满意,而且我可以轻松弄清楚如何使用 Python 进行编码。
本质上,它是在查询 2D 多边形与哪些整数图块/单元格重叠。我不需要知道交叉点 - 只需要知道 UV 瓦片包含哪些 UV。
我宁愿避免包含仅在边界上命中的图块。例如,如果多边形仅与 [0, 0] 重叠,则 [0, 1] 上的 UV 点/边不需要包含图块 [0, 1]。
到目前为止我所拥有的
目前我只在 Maya 中完成了一些原型,这些原型基于 gist here 中的边界进行了比较。 cmets 中还有其他一些方法。代码:
from maya import cmds
def get_bounding_box_tiles(bb):
u_minmax, v_minmax = bb
# If the max is exactly on the integer boundary we allow it to be
# part of the tile of the previos integer so we can subtract one.
# But since we'll need to add one to iterate "up to" the maximum for
# the `range` function we will instead add one to the opposite cases.
# Inputs are tuples so don't assign elements but override tuple
if u_minmax[1] != int(u_minmax[1]):
u_minmax = (u_minmax[0], u_minmax[1] + 1)
if v_minmax[1] != int(v_minmax[1]):
v_minmax = (v_minmax[0], v_minmax[1] + 1)
tiles = []
for v in range(*map(int, v_minmax)):
for u in range(*map(int, u_minmax)):
tiles.append((u, v))
return tiles
def get_uv_udim_tiles(mesh, uv_set=None):
"""Return the UV tiles used by the UVs of the input mesh.
Warning:
This does not capture the case where a single UV shell
might be layout in such a way that all its UV points are
around another tile since it uses the UV shell bounding
box to compute the used tiles. In the image below imagine
the lines being the UVs of a single shell and the `x` being
an emtpy UV tile. It will not detect the empty tile.
/ - \
| x |
Args:
mesh (str): Mesh node name.
uv_set (str): The UV set to sample. When not
provided the current UV map is used.
Returns:
list: sorted list of uv tiles
"""
kwargs = {}
if uv_set is not None:
kwargs["uvSetName"] = uv_set
bb = cmds.polyEvaluate(mesh, boundingBox2d=True, **kwargs)
tiles = get_bounding_box_tiles(bb)
if len(tiles) == 1:
# If there's only a single tile for the bounding box
# it'll be impossible for there to be empty tiles
# in-between so we just return the given tiles
return tiles
# Get the bounding box per UV shell
uv_shells = cmds.polyEvaluate(mesh, uvShell=True, **kwargs)
if uv_shells == 1:
# If there's only a single UV shell it must span
# all the UV tiles
return tiles
tiles = set()
for i in range(uv_shells):
shell_uvs = cmds.polyEvaluate(mesh, uvsInShell=i, **kwargs)
shell_bb = cmds.polyEvaluate(shell_uvs, boundingBoxComponent2d=True, **kwargs)
shell_tiles = get_bounding_box_tiles(shell_bb)
tiles.update(shell_tiles)
return sorted(tiles, key=uv2udim)
def uv2udim(tile):
"""UV tile to UDIM number.
Note that an input integer of 2 means it's
the UV tile range using 2.0-3.0.
Examples:
>>> uv2udim((0, 0)
# 1001
>>> uv2udim((0, 1)
# 1011
>>> uv2udim((2, 0)
# 1003
>>> uv2udim(8, 899)
# 9999
Returns:
int: UDIM tile number
"""
u, v = tile
return 1001 + u + 10 * v
# Example usage
for mesh in cmds.ls(selection=True):
tiles = get_uv_udim_tiles(mesh)
print mesh
print tiles
for tile in tiles:
print uv2udim(tile)
相关
- How to check intersections with two rotated rectangles - 这将是一个潜在的解决方案。但是,从技术上讲,单个 UV 多边形在此实现失败的地方可能是凹的。我还假设,因为我们正在检查轴对齐的整数图块,所以算法可能更简单。
- AABB and Concave Polygon Intersection - 仅适用于凹多边形 - 但除了类似的问题。
【问题讨论】:
标签: python maya uv-mapping maya-api