UV Packing, problems and solutions #105680
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#105680
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
DRAFT ONLY WORK IN PROGRESSS
This is the proposed roadmap for UV Packing within Blender 3.6, to resolve #68889.
How did we get here?
Up until recently, UV Packing in Blender has been stalled because there was two competing UV Packing engines.
One, inside of
Blender > Geometry > UV_Parametrizer
, had more features (better UDIM support, ignore pinned verts.)The second, a fork, lived in
Blender > Editors > UVEdit > UVEdit_Islands
and operated on BMesh directly. It supported non-manifold geometry, non-square materials, and packing UVs which spanned multiple objects.(\note Neither of them directly supported the
Mesh
data structure used by Geometry Nodes. As before, the UV Pack node in GN take a detour throughUV_Parametrizer
to group faces into UV Islands.)As the two packers had diverged, and there was no way from the user interface to know which packer was used by any given input, this posed a considerable hidden maintenance problem.
If we wanted continue with two packing engines, every new feature would need to be implemented in both packers, with all of the problems that entails.
(We also evaluated just abandoning either one of the packing engines, however the two packers had different feature sets and were not "bug compatible". Migrating an operator from one packer to the other almost always revealed a slight incompatibility and there was no clean way to do this. )
The first step was to convert both packing engines from C to C++.
After that, following considerable cleanup, merging and integration, there is now one unified packing engine which is a complete superset of the two previous UV Packing engines.
The final commits to merge the two engines are
cb1af1fbd9
and6766a7911f
.The new C++ packing engine can now be found in
Blender > Geometry > UV_pack
and has no dependencies on BMesh, SpaceImage, Materials, the UVEditor or anything outside of just the core blender internal libraries.There is still work to be done cleaning up the API and smoothing off the rough edges. However, With the merge completed, we are now in a good place to make progress on new features and enhancements.
The Alpaca ("L-Packer")
At around this time, an issue popped up on the tracker that highlighted a core weakness in the packing strategy. Packing large numbers of islands would appear to freeze the interface.
Blender didn't crash, it was just waiting hours for the packing to complete.
For this reason, the Alpaca was created to fix a number of problems.
N
), it's time complexity isO(NlogN)
. It packs about as fast as you can go on the CPU, packing 100,000 islands in less than 10 seconds.o That means if you have another packer, "AwesomePacker" that is slow for large
N
, you can carve off the largest 1000 islands, pack then using your "AwesomePacker, and then pack the remainingN-1000
islands using the Alpaca. The composed "AwesomePacker+Alpaca" combination will be both Fast and Efficient.o => If an alternate packer can't beat the Alpaca, there's no advantage to integrating it into Blender.
The Alpaca is merged as
b1185da403
, and will be available in Blender 3.6.Why is packing hard?
Fundamentally, UV Packing can be thought of as two sub-problems. The two problems are different in nature, and attempting to solve one of these problems really well often results in poor efficiency and/or poor time complexity for the other.
As the familiar heuristic goes:
Put the big things in first.
Once the big things are locked in place, put all the little things in too.
... and then how do you choose the cutoff between the two algorithms?
Why is packing Big things a hard problem?
Lets start with
N==1
, here's the current output:Can we do better? It turns out, yes we can:
This turns out to be relatively "fast", we can get an exact solution using a technique called rotating-calipers
What about
N==2
?TODO: Show that for
1 < N < 20
, UV packing is tricky unless you use calculus.If you use calculus, you can get exact solutions for
1 < N < 20
Minkowski Difference Phi-function GJK algorithm etcThe problem of course, as
N
increases, these method get sloooooow.Why is packing lots and lots of small things a hard problem?
TODO: Fundamentally a combinatorics problem, how do you make it run efficiently? See 2DVSBPP etc.
TODO: Difference between "Greedy" vs "Backtracking" vs "Dynamic Programming" algorithms.
TODO: Difference between "Randomized" vs "Brute Force"
Roadmap, where to from here?
Identify and fix non-square material use cases (TODO: ...)
o Update, PR is #105784 , commit is
c38fe87127
.Alpaca_Rotate (i.e. Prove we can support rotation in the packing API by introducing the
Alpaca_Rotate
variant which improves packing efficiency by rotating islands to 0 or 90 degrees, at the cost of increased complexity.)mul_v2_m2_add_v2v2
o The current packers damage UVs due to round-off with float precision, particularly with UDIM and high island counts. Using
mul_v2_m2_add_v2v2
should fix this, but is it enough?Solve the
N==1
case exactly.o See "Packing Big Things" for a visual example.
o Can we post process other packers with a quick
N==1
solution by treating all the other islands as one large island?To then solve the
1 < N < 20
cases exactly, we'll need a Non-Linear Optimizer. Which brings us to:Fetch Quest, Obtain a Non-linear Optimizer
o Any solution will need a non-linear solver. Where do we get it from?
o Can we get a non-linear solver in Blender?
. Is the one in
eigen
suitable?. https://github.com/chokkan/liblbfgs/blob/master/include/lbfgs.h ?
. ..other options..
. Create one from scratch ???
. Just do without ????
Support for Convex hulls in Packing API.
o Packers which pack using convex hulls
Sidebar: Why is packing "easy"
One great advantage that UV Packing has is that's easy to choose between two competing UV Packing algorithms. Just run them both, and choose the one with better packing efficiency.
Indeed, this generalizes to having several packing algorithms. Make sure they all terminate in a reasonable amount of time and then just run them in parallel. After a "reasonable" amount of time, halt them all and choose the one which currently has the best results.
If any of them have the "composable" property, we can even mix and match packing algorithms and use the best parts of each.
...Beyond Convex...
What's the relative priority for artists beyond the standard convex hull packing?
(\note TODO: Acknowledge 3rd party UV packers already exist. Where is the best user value for Blender Users here? Who does each of these features actually help in the real world?)
Support for Concave hulls in Packing API.
o Packers which pack using concave hulls
o WIP: #105821
Support for holes in Packing API.
o Packers which pack using holes
Pack to non-square region in output #78398 #78396
Better UDIM support
Pinned Island support, pack around existing pinned islands. pack inside existing pinned islands.
More control over per-island scaling, scaleing by groups, pre-condition with Average Island Scale, scale_max / scale_min, dealing with "dust" islands.
Can we "mirror-flip" islands?
Should we optionally "weld" islands together if they are originally co-incident in UV but not in 3D?
Thinking outside the square
Online packing, start the packer, let it run, updating the UI with current progress, user clicks "Done!" when they are satisfied with the result.
Can we get better results by relaxing the input constraints, e.g. Does allowing the algorithm the freedom to rescale the small islands by +-0.1% lead to huge improvements in efficiency / performance?
Packing Lightmaps ? What's different?
Repacking. Given an existing layout that's mostly good but has a few small problems. Just by making small adjustments, can we improve pack efficiency? Eliminate overlaps? Fix margin problems? Clean up the dust?
Repacking, texture edition. Given a layout with textures, can we repack islands with similar colors together? With identical colors overlapping ?
Repacking, spectral edition. Given a layout with texture and texture-frequency information, can we repack islands such that the re-baked textures have maximal preserved bandwidth.
Pixel aware packing, packing to pixel boundaries
Float16 aware packing, packing that's aware of float32->float16 or float32->int16 quantization effects.
...
Literature Search
Rotating Calipers https://en.wikipedia.org/wiki/Rotating_calipers
o N=1
o Fast,
O(1)
o Produces provably optimal solution(s).
. Solution may not by unique due to symmetry.
o See #p_chart_minimum_area_angle in
uv_parametrizer.cc
Alpaca
b1185da403
o Greedy AABB packer.
o Uses AABB for occupancy.
o Sorts islands by area of AABB.
o Fast,
O(NlogN)
(i.e. input must be sorted)Alpaca_Rotate (WIP)
o Greedy AABB packer.
o Uses AABB for occupancy.
o Sorts islands by length of largest edge.
o Fast,
O(NlogN)
(i.e. input must be sorted)Tetris Algorithm
o First described in Lévy et al., 2002
o Greedy convex packer, no holes.
. Some limited concavities may be filled.
o Uses "horizon line" for occupancy.
o Supports arbitrary rotation. (for a performance cost)
o Appears to be
O(N^3)
due to binary search on scale parameter.https://github.com/jpcy/xatlas/blob/master/source/xatlas/xatlas.cpp
o Greedy concave packer with holes.
o Uses bitmap for occupancy information.
o Sorts islands in order of decreasing perimeter.
o Support rotation of 0 degrees or 90 degrees. (Could be extended?)
o Bitmap size also depends on
N
, so time complexity ofO(N^4)
or worse.o Exhibits quantization effects due to use of bitmap.
o "Randomized" and "Brute Force" modes of operation.
http://www.ludicon.com/castano/blog/articles/lightmap-parameterization/
o Same as xatlas
Texture Mapping Progressive Meshes
o Same as this tweet
o Sort AABBs by decreasing area.
o Arrange AABBs in a zigzag.
o Most likely
O(N^2)
due to binary search on scale parameter.Mesh Parameterization: Theory and Practice
o Link is stale, use wayback machine.
o "The optimal packing problem is NP-hard, thus only heuristic or approximate packing algorithms exist."
o Suggest "Tetris Algorithm",
Multi-Chart Geometry Images
o "Packing a set of 2D shapes is a provably hard problem [Milenkovic 1998]... "
o Suggest "Tetris Algorithm"
D-Charts: Quasi-Developable Mesh Segmentation
o Suggest "Tetris Algorithm"
Bounded-distortion Piecewise Mesh Parameterization
o Packing not mentioned
Precomputed Global Illumination in Frostbite
o "Typical lightmap utilization that we see in final production levels is ~50-75%, which is quite bad."
o Greedy concave packer with holes.
o Similar approach to xatlas with aggressive optimization.
o Uses bitmap for occupancy information.
o Sorts islands in order of decreasing perimeter.
o No rotation.
o Appears to be
O(N^3)
.. Might be
O(N^4)
depending on bitmap resizing.o Brute force search, with acceleration tricks.
. Cache for different sized gaps.
. Bitmap is implemented as a bit-mask, uses fast bit-wise operators.
o Packing is aware of video-card compression artefacts. (BC6H)
o Output is non-square, and not a power-of-2 (!)
o Claimed efficiency of 96%
. Misleading, as includes "margin" in the efficiency calculation, when the "margin" would normally be considered wasted space.
...
DRAFT ONLY, WORK IN PROGRESSSS
To summarize my comments made elsewhere, we should be looking at non-convex packing solutions since we know they give better results, various software and popular Blender add-ons do this successfully, and there is at least one popular open source implementation in xatlas.
So before making a roadmap for convex packing or going into specific algorithms like L-BFGS, there should be a quick survey of papers and code that is out there. Then we can find a few good candidate approaches to non-convex packing and decide which way to go.
The simplest solution being to just integrate the relevant xatlas code, and then see what can be improved from there. But there might be alternatives. A good starting point for a survey is all the links here:
https://github.com/jpcy/xatlas#technical-information--related-publications
The packing algorithm from this presentation may also be useful, it sounds similar to xatlas:
https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/gdc2018-precomputedgiobalilluminationinfrostbite.pdf
Out of the options, I think xatlas and the similar frostbite algorithm that are based on rasterizing bitmasks is the right approach. Tetris packing can't pack as tightly due to holes.
For time complexity there's average case and worst case, and I expected the average case is quite acceptable most of the time. When we hit the worst case there should be ways to take some shortcuts to lower the quality a bit.
So my suggestions would be to extract the relevant code from xatlas and try to integrate it with our packing code. And then validate how well it works, see where we can improve it. It's probably not worth trying to keep that as an external dependency, as there's a bunch of other things in there and we likely want to customize the code anyway.
Would also be good to get the opinion of @ideasman42.
Regarding the implementation in #105821.
The xatlas implementation has a bunch more code and heuristics, as well as a brute force mode. So I'm wondering how to ensure our implementation is at least as good.
I think it would make sense to still also integrate the xatlas code unmodified, so we have a reference and can be sure we didn't accidentally miss something when adapting the algorithm. It can be a quick ugly hack not meant to actually make it into Blender.
While I am also anxiously waiting for a built-in solution, I want to mention the addon I wrote that does this: https://github.com/muhuk/grid-uv-packer
Overlapping islands support? Support for verticality? Or what kind of list is required?
Flipping islands is not recommended in cases of textures with text on it, and, probably, normal maps.
Looks like we're just going to do an
xatlas
style brute-force packer first, and then swing back and look if any of these other ones are still required.