Tiling material across stretched surface?

Okay, so I’m AWARE (since someone will invariably point it out anyway) that I can use the TexCoord node to control the tiling of a material, and I’m AWARE that it’s possible to use the world position of a material to tile a texture along it in such a way that it doesn’t care about world size or position.

But what I’d like to do is find a way to tile a material, on a specified axis ONLY, such that no matter the size of the object on this axis, the material will loop itself to fill it.

Note that what this means is if the object moves, the material (well, the texture anyway) will move WITH it, it won’t remain fixed in place. If the object is rotated, the texture will rotate with it. All I want is to tile rather than stretch when an object is scaled along a specific local axis, in such a way that the scaling along that axis can be arbitrary without manually modifying the material (i.e. without tweaking a parameter that controls TexCoord scaling to make sure it visually looks right).

Here’s the specific scenario: I’ve got an Actor which is, at its base, a spline. This actor, in its construction script, spawns various copies of a SplineMesh, such that their Start and End points are aligned with the points on the spline. Thus, manipulating the spline enables manipulation of the meshes. I use this to create “track”, as pictured below.

What I’d like to do is texture this in a fashion similar to a road; imagine having a dashed white line down the center (it won’t be exactly this, but for the purposes of argument let’s say it is, since the principle is the same). That means the texture must tile evenly along the mesh, regardless of the “size” of the particular Spline Mesh it’s attached to, since it would be impractical to design these splines with exactly equidistant points to guarantee an identical length for all segments of road. However, it ALSO means the texture must stretch/deform along the NON tiling axis in an ordinary manner.

In other words, I need to know how to allow a material to determine the world scale, in pixels, of ONE of its UV axes, and tile a texture along that axis without stretching, while simultaneously allowing it to stretch as needed along its other axis.

I can get the world bounds of an object but this doesn’t really help me much when dealing with a mesh which is deformed via a curve in an arbitrary way and at a non-right angle to the world axes.

98df0916c168c57df88f01057e4c9cbd0fdfe728.jpeg

Might be worth looking into the procedural mesh component and use the spline to get positions to generate vertices and its uvs.

That seems like more trouble than it’s worth if the Spline Mesh components are already working.

Is there really no shader-math way to grab the world-space distance between two UV coordinates (say 0.5,0 and 0.5,1) and use that to control TexCoord scaling, or something?

For a spline mesh, you can use ObjectRadius to scale the U or V coordinates, but you may have to play around with multipliers to get it to look proper.

1 Like

I’ve managed to hack something together by using Distance Along Spline to feed a scalar parameter in the mesh, this works okay for the UV tiling/scale.

My only problem now is figuring out how to make the material tile in such a way as to remain synchronous where the meshes touch.

You should be able to perform a ‘mosaic’ type operation on your tiling parameter to limit it along increments that divide evenly with your stripe/texture ratio.

Ie, if you knew you only had 1 stripe per UV repetition, then you would simply perform a “ceil” on the tiling factor. If you had 2 stripes per UV repetition you could do ceil(UV.Y * 2) / 2 , which should create discrete divisions every 1/2 or 0.5 units.

There is actually a material function in engine called “Mosaic UVs” that was never exposed to the library. You can find it by searching for material functions in the content browser under engine\content\functions.

After thinking about this though, you may want to subtract 0.5 first so that instead of instantly jumping a whole stripe as soon as you pass 1, it will round up or down centered around 0.5 more like you’d intuitively expect. You would do that just before the ceil. Or there may be a “round” function but that would give the same results.

Also the original question about determining the UV to world value by UVs alone is also possible. You could use that if you wanted to correct non-spline meshes.

The math for that can be found inside of the “Spline Thicken” material function. Look at the top right comment box called “UV to world ratio”. You could try extracting that part and using it.

Thanks, I’ll have a look at that… As I said, I’ve got a blueprint-specific solution in place but this generally seems like a handy thing to have, since there are many times I want to tile a material in one local-space direction.