Problem With World Aligned Textures

Hello everyone.
I’m trying to make a sewer scene and I’m using world-aligned textures for the walls and the ceiling so that I don’t have to worry about UVs for my models because it seems like world-aligned textures don’t care about the UVs and I suck at UV unwrapping (beginner by the way), and also to make the tiling seamless between the sections of wall, but it hasn’t been working for the ceiling which seems to be because it’s curved because it does this with other curved surfaces, instead, the texture is normal at the top of the ceiling, but starts stretching and the normals get displaced as it gets to the sides of the ceiling.
I’ve been pulling my hair out for a week now trying to find anything to help me fix it but couldn’t find anything. You can see the problem in the screenshots below, I also included screenshots of the material graph in case needed.

Hi Death_Hymn,

That’s an artifact of the way the WorldAlignedTexture mapping system works (it’s blending the difference between axis).

You can improve it quite a bit by using temperal anti-aliasing - also, there are more efficient ways of doing the mapping - have a look into tri-planar mapping.

The technique in the video below is a good one:


I use the material Recourse suggested. It works well, is performant, comes with it’s own caveats but IMHO is is the generally-superior solution.

Otherwise, what you are looking for, for completeness, is called triplanar-mapping. Where you take a set of 3 samples along the X, Y, and Z axis/planes, then weight and blend them to make that seamless experience.

This is a good video for reference, and he also breaks down working normals in worldspace, so it’s a good tutorial: UE4 Tutorial 101 — Texture Projection 3/3 - Triplanar Normal Maps - YouTube

Per my initial statement, I used to use Rodrigo’s solution but have since moved onto the stochastic-triplanar solution as suggested by RecourseDesign. Particularly for landscapes, it makes capturing the alphas and rendering the entire material on the other-side of the RVT, viable. That then opens up using more than just the BaseColor, Specular, etc from the RVT, one can readily use PixelDepthOffset, Refraction and the like w/o having to make wonky sacrifices on what you actually render into the RVT… (as it only has so many channels).


I’m already using temporal anti-aliasing, it’s applied by default, and I’ve been trying tri-planer mapping the whole day and followed multiple tutorials on it, but none of them gave me better results. Also, Isn’t the world-aligned texture node the same as tri-planer mapping? it’s literally doing the same thing: projecting the texture using three planes in world space rather than using the UVs of the mesh. Am I missing something?

If you open up the default WorldAlignedTexture node - you’ll see that it’s using 3 texture samples for each pixel - the system in the video only uses 1. Those lookups are for each texture too, so if you have a BaseColor, Normal and ChannelPacked texture, thats 9 texture samples instead of 3. Sampling isn’t fast.

I recommend watching the video all the way through - I’m not usually a video person but this one is cram-packed with useful information.

At around 3.00 is where he talks about smoothing out those axis transitions.

1 Like

Again, to follow up on what RecourseDesign has offered, it’s very much a technically superior solution given how much it costs and how flexible it is.

Just having TAA ‘on’ isn’t enough you need to bring the TemporalSampleIndex into the material-graph to smear the work across time…

It cuts your samplers/samples by 1/3 and since it leverages texture-arrays adding additional ‘layers’ to a material is more just adding the texture to the array and whatever attendant settings you need. The magic is done in the addressing/UVs so they can be reused over and over…

The logic is a tad more expensive instruction-wise but overall your texture slots/samples, per-material will remain almost constant, which is huge, especially since it’s triplanar at 1/3 the overall texture-related costs.

Watch.The.Video :smiley:

See what it can do just being slapped on a landscape, it literally just-makes-the-tiling-go-away:

At this distance, if you really go looking for it, you can pick out a ‘random-pattern’, but it’s very-suitable for large, open spaces like deserts, sand/beaches, etc; places where you might not want lots of grass meshes either for performance and/or aesthetics:

Honestly, given the cost, it’s become the new base for both my landscapes and complex meshes.


I followed the tutorial RecourseDesign mentioned and these are the results I got:

I understand that this technique is much cheaper than the standard world-aligned texture node and that’s great, but this doesn’t matter if it’s not giving me the results I want. I tried following other tutorials yesterday about tri-planer mapping, they were also using one texture sample and they didn’t work either, one actually gave me better results than the one mentioned by RecourseDesign, it was stretching much less but still not good enough as the bricks were basically squares at some points, I don’t know but for some reason, I can’t get it to work, maybe I’m sessing it up? I don’t know, but honestly, at this point, I think I’m just gonna get better at UV unwrapping and use normal materials, either that or I’m gonna use a flat roof. :laughing:

You won’t ever get perfection from it - that top image is pretty close. You’re mapping a texture between 2/3 axis - merging when their angle gets close to 0 - it’s never going to be able to get it perfect.

Bricks are a great way to test it though - I used bricks too - they show everything…

Also keep in mind that you’re viewing the mapping at it’s worst - most roofs/walls etc don’t curve, so will be using constant lerp values…

Can you link to the one that produces better results please? I’m always open to learning new techinuqes :slight_smile:

1 Like

Just thinking about it - if your material is only going to be applied to the sewer, it would be quite easy to calculate a “psuedo world position” just for the sewer - your Y and Z pixel position values are correct, you just need to calculate the X which is either a ‘cos’ or ‘sqrt’ calculation - all simple stuff.

As far as finding the X and Z values, you can use the object position node - but that’s slow.

I built a “spatial UV” system into rdMexTools that adds another UV channel and gives you the pixel position just by reading the UV - very fast…

On second thought, when I think about the bricks, they are a very ordered pattern, which wouldn’t work so-well unless you had exacting values put into it.

Here, in particular, we’re not really doing triplanar so much as ‘I have a curved surface of length X and need to map it in chunks of Y (brick width) to make sure there is no seam…’

You COULD do that with either the triplanar or the stochastic, but I think we’d need to math our way to the precise scaling value, etc to make sure they all line up. Technically, I think it can be done, but practically, such values are outside what I can give you.

I think, in this particular case, given the surface and the ordered-tiling-pattern, unwrapped UVs might be the only practical way to get perfect-tiling.

Otherwise, for almost any other surface, tiling pattern, I’ve had good results. Definitely anything organic.

I think you’re right in that simple UV mapping would be the fastest way.

I just tried using math - and it needs an arcsine which uses a lot of cycles - it probably could be sped up - but as it still needs a UV set (the spatial ones) it seems a bit ott…

1 Like

So I made this curved plane pretty quickly in Blender and unwrapped its UVs and I’m getting perfect tiling, no weird stretching or anything, but the problem is how do I get the material to blend seamlessly between multiple pieces of this mesh? The only way I know of is the world-aligned materials which don’t seem to work.

1 Like

It looks like the textures themselves are tiling correctly, but the noise map used to mess it up isn’t tiling correctly. almost anyway - are you sure the UV map is extending the full amount?

1 Like

What do you mean by the UV map is extending the full amount?

You want the UVs to range between 0.0 and 1.0 - that uses the full size of the textures.

Didn’t work.

I was able to get rid of the artifacts almost completely by messing with the contrast in the world-aligned texture function (I say almost because for some reason, I couldn’t get rid of the artifact below no matter how much I change the values)

But that shouldn’t matter because the player isn’t supposed to see that part anyways so yeah.
So that’s one problem down, now the stretching, and I have an idea but I don’t know how applicable it is so bear with me if it’s a little stupid, I’m learning as I go, and I figured maybe you guys could help: since the texture is stretching gradually (at the top, it looks normal and at the bottom, it’s stretching the most) is it possible to make a gradient that goes from 1 to 0 and back to 1, and assign it to the texture size on whatever axis it’s stretching on and multiply it by some factor to un-stretch it? Basically, stretching the texture in the opposite direction? Faking it instead of actually solving it?

You can math your way to anything. Per the above, if you know the real-world-size of the distance you are traveling on the inside of the mesh and chunk it up to the real-world size of whatever a brick on that texture ends up being, then sure. But I don’t know enough to do that with you. :frowning: Not that I wouldn’t like to know how, but I can only conceptualize.

Mechanically, that seam at the corner, I’d hide that with another mesh, like a block running along the intersection, like trim. Or a pipe, pipework, maybe some some world-aligned moss painted in via vertex-coloring so the seam isn’t even there to begin with.

Sometimes it’s better to go around vs through. :smiley:

1 Like

I solved it, to an extent.

I was able to use a gradient to stretch the texture gradually on any axis I wanted:

Here, I’m stretching it on the Y-axis while keeping the scale of the other axis as 200, the float then connects to the texture size in the world-aligned texture node, also, I should note that I’m only using the Z output of the world-aligned texture node, which means you can optimize it by making a copy of the world-aligned texture function and deleting the X and Y planes and keep only the Z-plane because you only need the Z output for this.

But it’s not perfect, because there’s still this problem:

I couldn’t get the gradient to stretch both sides of the ceiling inward, if it makes one side smaller, it makes the other side bigger, so I had to use two copies of the material, one to each side of the ceiling, and I ended up with this seam between the two sides, the material does blend seamlessly between two ceilings, but for some reason, it’s not seamless between the two sides of the same ceiling.
That’s as good as I can do for now, and it’s good enough for me because I’m going to add some pipework and use it to hide this.

This video helped a lot:

“I was able to use a gradient to stretch the texture gradually on any axis I wanted”

And that is one of the key skills you will need in a material: making a gradient (alpha) to tell the material-maths to do this there, but not over-here. In many ways, it’s really all about the alpha/gradient you map over. Once you can well-define where you want this, it’s usually just a LERP away from implementing.

Looks good!

Highly recommend Tharle, he’s great. :smiley:

1 Like

Sigh :face_exhaling:… I was just starting to design the level and I found a problem with this, it appears that the more the object moves in the positive Y-axis, the more it gets stretched, there went my hopes :smiling_face_with_tear:! This thing just doesn’t want to work, does it? :melting_face: