Curious if it's possible to sample multiple tiling textures from a single texture atlas in babylon.js

Hmm… Ok so if I understand this correctly, you want to have a master texture that has multiple “sections” of “tiles.”

So in the example provided, you want to be able to have a material that can tile using a section of this master texture. Is that correct?

And you want more procedural control to control the size of the tiles and such?

I think this is definitely doable. I would steer you towards the Node Material to get something like this working.

I’ll toy around with this and see if I can get a primitive example going.

1 Like

Yes, seems like you understand what we’re trying to accomplish, @PirateJC! Thanks for trying to help us figure this out!

Ok I have some good news and some bad news. First credit MUST go to @PatrickRyan for helping sort through this and figure out how to achieve it.

Let’s start with the good news.

Here’s what we came up with using the Node Material Editor:

https://playground.babylonjs.com/#MXZMIW#6

Note that this takes a LONG time to load because the Node Material has your 4k texture embedded in it.

Once the node material has actually loaded click on it and scroll down until you see Inputs:

The texture input allows you to select 1 of the 4 “tiles” from your master texture.

The tiling input allows you to specify how much resolution you want. Basically how many times you want that texture to tile.

Here’s a look at the node material:

It probably looks like a lot, but it’s actually fairly simple. We start by taking the mesh’s uv and scaling that by an input the user gets to specify and then passing that to a sawtooth node to repeat a pattern of mapping from -1 to 1 over and over again in both directions.

From there we remap each “tile” to a specific region of the texture as set by the “texture” user input:

The specific values to remap to are driven by this ugly looking thing:

But what’s really happening there is simple pathing. Basically if I type in this number, then give me this region, type in a different number, get a different region. To be honest this isn’t SUPER necessary, but just kind of fun and cool.

And then we simply wire in the texture to those new UV coordinates:

Ok so what do we have here? A tiling texture with user input based on a portion of a source mesh.

Ok so now for the bad news.

To your original question, you’re looking for a more performant way to load and assign textures.

In reality, this method isn’t very performant at all. We have a 4K texture here and in order to get this to work, you have to run this entire operation for EVERY PIXEL of the texture! That’s a LOT of compute power to accomplish this.

Under the hood the Node Material wants to optimize this shader so it will run as quickly as possible. This means that the “scale” node in this tree will actually be evaluated in the vertex shader when the it gets compiled, NOT the fragment shader as you’d think given where it’s placed in this tree. Again, that’s a shader compilation optimization we do to make things go quickly.

However if you let this optimization happen, then we don’t have enough resolution in the geometry to render the texture appropriately to how we want it tiled.

You can play around with this by changing the target of the vertex node here:

The way to get the texture to render in the tiled manner we want, we have to force this scale operation to happen in the fragment shader which runs on every single pixel…not very performant unfortunately.

So where does that leave us on the most effective way to do this? Well you essentially have 4 options as I understand it:

  1. You COULD choose to let the NME optimization happen, but ensure you have enough resolution in your geometry to be affective.
  2. You COULD force the Node Material scale operation to happen in the fragment shader is we’re doing here.
  3. You could go the manual route of having separate textures for each tile instead of combining them.
  4. Some other option from someone much smarter than me :slight_smile:

If I had to guide you to 1 of these options it would probably be anything other than option 2 which is effectively what this post is demonstrating.

LOL

Ok I know this is a LOT to digest, but hopefully this has been helpful to know that it’s possible, but also might not be the best route to choose.

Have a fantastic weekend!

5 Likes

Oh and one other thing. In a VERY funny bit of timing, the very talented @roland just happened to create a custom tiling and offset node for the Node Material and put it in the Custom Nodes section of our Assets repo:

3 Likes

@PirateJC Wow, I’m speechless… Thank you so much for taking the time to dive into this. I’m excited to study this node setup. Hopefully I’ll be able to reverse engineer it! I’d love to check out Roland’s setup as well. How can I view his work in the material node editor?

1 Like

@Sol_Hunter, if you go to our assets repo and clone it to your local machine, you will have access to all of our public assets. You can then add the custom node to NME by adding it to custom frames at the top of the node list:

image

Just click on Add... and navigate to the Tile and Offset json in your local clone of the assets repo and click open. It will be added to your custom frames whenever you use NME and can be placed on the canvas like any other node. It is, in essence, a frame with nodes contained in it that you can wire up either in the collapsed mode or open mode. You can delete any custom frames you don’t want with the trash icon and you can save your own by creating a frame around a group of nodes (shift + click + drag) and then clicking on the frame, select export.

image

You can then add your custom node back into NME with the add button. This is a good way to shortcut operations you do all the time. Hope this helps.

3 Likes

Hey @PirateJC! Thank you for the compliment! :partying_face: You are too nice to me! :blush: But don’t stop please :smiley: :smiley: :smiley: :pray:

I created a UV Twirl custom node last night. It will land in the repo as well but I need the help of the community. Maybe I get the answer here quicker :slight_smile:

Have a nice day guys!

3 Likes

Love your system for selecting a texture by entering a number from 1-4 in the texture input field! Really clever! I’ve been messing around with your node tree for a few hours now and I found that changing the remap source values to -.5 and .5 ended up tiling the whole texture. https://playground.babylonjs.com/#MXZMIW#7 For some reason -1 and 1 was cropping the texture so that there were visible seams wherever the texture would repeat. Then I tweaked the values some more to cover up the padding I added to the borders of each texture in the atlas.




Again, thank you so much for helping get this figured out! Really excited to experiment more with this. Now I need to figure out how to do this multiple times in the same material to be able to apply different sections of the atlas to different sections of a mesh.

3 Likes

I’m probably getting over my skis here, but what about stashing the tile index data in the mesh’s vertice data?

Do you mean doing the tiling with the geometry and uvs? Something like this? (I’m assuming you’re suggesting something more sophisticated)

The limitation here is that you can’t procedurally control the scale of the texture this way and it gets messy if you want to change things.

By the way, did someone change my original question? I remember being more specific about what I was asking for…

Seems like it was changed to: “I’m trying to reduce the number of textures required in my .glb files and was hoping to figure out how to combine tiling textures into one atlas. Does anyone here have experience with this?”. I know how to combine multiple textures into one atlas. What I didn’t know was how to create a material to handle the tiling of multiple textures contained within a texture atlas.

1 Like

Yeah similar to that. I kept thinking based off the NME example that you could use the vertice data to store a simple enum or flag value indicating the sector of the atlas that the UVs represent. Thought being the vertex shader could grab that and compute the actual UV values?

Ed: but then I kept coming back to the simpler UV offset and scaling settings…

Hi there, I am trying to replicate what you made here but it seems to be cropping the texture causing it not to tile correctly. Do you have an idea what the issue is? Here’s the node file AtlasTextureTiling.json - Google Drive

Yes, assigning UV in the vertex shader is the way to go. Just have one material and one attribute indicating which of the atlas’ tile-sets should be used.

2 Likes