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

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