repalash/xatlas-three

Support for multiple atlases?

Closed this issue · 9 comments

Hello! I know xatlas has the ability to to specify the a target texel density and max atlas resolution such that it will layout geometry UVs over multiple atlas images.

However I don't see an example or capability of how to do that in this project or xatlas js project. Is this functionality supported? Is it possible specify a bunch of geometry with a target texel density and return the new geometry laid out into multiple atlases?

Thank you!

Hi, it's been a while, I think it should automatically generate multiple atlas if the correct packing options are set. But there won't be a way to get the chart data which has the information on which atlas the chart is in. I'll have a look and get back.

Yeah, setting the resolution and texelsPerUnit should generate multiple atlas. https://github.com/repalash/xatlas.js/blob/a1b5c47efca7bcfea6f095370e0f3ece980b33fd/source/xatlas/xatlas.h#L211

The atlasIndex is in both Chart and Vertex in xatlas - https://github.com/repalash/xatlas.js/blob/a1b5c47efca7bcfea6f095370e0f3ece980b33fd/source/xatlas/xatlas.h#L61

So, I guess the indexes in the vertices can be returned and added as an attribute to the mesh in three.js. Will do that tomorrow, unless you have a better idea of returning them for use in three.js? Also if it's nice to have the chart data in js, that can also be added.

Great thanks, this is helpful. I know you're not the original xatlas dev but I have some clarifying questions if you happen to have the answers. Also, just to clarify, I'm assuming an "atlas" is the full image that the UVs are laid out on and a "chart" is a uv island or contiguous set of connected, flattened triangles:

It's interesting to me that the atlas index is stored per-vertex and not per-object. Is it the case, then, that a single mesh can wind up with triangles in different atlases / need to indexed into different textures when sampling? If so is there a way to force xatlas to try to pack data from a single mesh into a the same atlas so it's not spread out over multiple?

I'm looking at this from the perspective of generating and using lightmaps / ao maps so I'm wondering how this is expected to be done if a single geometry has charts in multiple atlases. In terms of sampling this can be done using something like a texture array assuming the atlases are all the same resolution. But generating an ao map with that kind of structure would be a bit complicated if not impossible in some contexts (ie writing one via webgl shaders).

I suppose one approach is to post-process and break up a geometry into multiple based on the atlas index, though it's not ideal. I know other engines like Unreal will re-pack UVs for their own lightmap generation so it will work for their generation so I'm wondering what xatlas offers for this. Do you have any thoughts?

Thanks again!

Yes, atlas is the full image, but Atlas is also a struct in xAtlas, which can include multiple subatlas/images. Chart is a group of connected faces.

For generating the atlas, there are 2 steps - computing charts and packing charts. In compute, each mesh it split into multiple charts independently. During packing, these charts are placed on an atlas. When options allow multiple subatlas, the charts are placed till there isn't enough space left, and a new one is created for next charts.
The Atlas result includes the width and height which is the max width and height of all the subatlas. I am guessing there could be slight differences in the size because of padding, but not too sure. The max size should work for all.

So, each mesh will be split into charts and each chart can be in a different subatlas. Looking at the code, it doesn't look like it's possible to force each mesh into a separate atlas since it loops over the charts. For that, you would have to group the meshes separately using either the information from charts or the meshes.

For rendering to the atlas map, atlas index in an attribute won't be useful. The meshes can either be split into multiple or another idea is to sort the indexes based on the atlas and split into groups. Then each can be rendered in a separate call while using the same geometry. In three.js this can be done by applying different materials to each group and rendering one by one on render target by setting material.visible. (or maybe by setting group.materialIndex = -1)

I am not sure about Unity, Unreal, I have never seen a way to split a geometry into multiple atlas in those I think.

If thinking about an editor-like environment that automatically handles baking, it would make more sense to allow the user to group multiple objects into layers or something, and those layers are unwrapped together. Otherwise unwrapping has to be done again when adding and removing models.
Also, you could unwrap objects/groups of objects into separate atlas, and then tile/merge them into a big atlas with scaling etc based on your requirements. (instead of generating 1 atlas with multiple subatlas)

Thanks for the detailed explanation - optionally exposing this chart and atlas data would be helpful so it can be worked with for these kinds of cases. Between repacking charts and other ones discussed here it sounds like there should be some way to use that information

I am not sure about Unity, Unreal, I have never seen a way to split a geometry into multiple atlas in those I think.

As far as I can tell Unreal will take the existing UV charts and re-pack them into atlases with padding in a way that works for how the engine samples light maps. It's unclear to me if it repacks every object into an atlas per-object or if it will share the atlas space among multiple objects. With more modern graphics APIs it should be possible to randomly write to a Z-layer of a 3d texture or texture array if needed, as well, so splitting up a single mesh among multiple atlases may not be a problem.

So I quickly tried a few things and returned the sorted vertices according to the atlas index and the groups data, along with the sub atlas count and the max atlas size.
image

You can try it here - https://raw.githack.com/repalash/xatlas-three/multiple-atlas/public/pack-atlas.html
It uses xatlasjs@0.2.0-dev.1 Didn't test it properly yet, will do that and clean up the code in a few days.

Great - very nice! I pulled the branch and poked around, as well. From what I can see the "atlas" object returned from generateAtlas contains a "meshes" array which holds a list of vertex information in addition to a list of "submeshes" which specify an atlas index associated with that subrange of the mesh.

That sounds like enough information to create an atlas -> contained mesh ranges map to generate something like an AO map. But I don't see a way to access that information from outside the UVWrapper class since currently only a list of geometry is returned that is the same as the set of input geometry. Considering the function modifies all the geometry in-place with the new vertex attributes I'm wondering if it makes more sense to some version of the "atlas" object with an added field containing a node uuid -> submesh / atlas so these atlas ids can be retained. Or the geometry array can be added on to the atlas object to be returned such that the geometry array and meshes array in the same order. Then the group atlas indices can be implicitly derived.

Hopefully that made sense. I can make a quick PR to show what I intend if that's more helpful. Other than that this seems like enough to do what I want to do, though it might be a bit before I have a chance to put it to use properly.

Thanks again for this project - it looks very well done!

But I don't see a way to access that information from outside the UVWrapper class since currently only a list of geometry is returned that is the same as the set of input geometry.

It's here in a different branch -

for(let subMesh of m.subMeshes) mesh.addGroup(subMesh.start, subMesh.count, 0);

The sub meshes are added to groups in threejs buffer geometry. So, each group is a subatlas atlas. The atlasIndex should be the materialIndex in the group, but set it to 0 by mistake, I'll change that.

I'm wondering if it makes more sense to some version of the "atlas" object with an added field containing a node uuid -> submesh / atlas so these atlas ids can be retained
Sorry, didn't really understand this part.

But yeah, the function should return the full atlas data, I changed this in xatlas.js repo but not here. I originally made it for just 'unwrapping' geometries in another WebGL engine(that's why geometries are called meshes), so you pass the geometries, and it just unwraps them in-place.
Now, it makes more sense to return the atlas object with packAtlas along with the meshes and make another function unwrap that just unwraps the geometries. I'll think a bit about it.

A PR is always useful, you can create one if you have any ideas.

Closing with #7