Terrain-Tiling-Break-Up---Terrain-Shader

https://m.youtube.com/watch?v=qPmKFRUf-zM&pp=ugUHEgVlbi1VUw%3D%3D

Frame at 3.22s

Today, we're going to talk about breaking up texture tiling artifacts on terrain. Let's go.

Frame at 13.81s

So we're in the middle of a series of videos about making terrain shaders.

Frame at 19.76s

Last week, I showed how to take individual terrain layers and combine them together to create a full shader. I've posted a link to the playlist for the full series down in the description if you'd like to go back and watch the full series. At the end of last week's video, we took a look at our results,

Frame at 37.81s

and I mentioned that we had a bit of a problem.

Frame at 42.16s

You can see here that our material is repeating over and over, and it looks unnatural. So I'm going to show you some ways of fixing that today. The first thing to know about this topic is that anything you add in the shaders to break up texture tiling is going to make the shaders more expensive to render there there's no such thing as a free solution to this so if this basic shader that i showed you last time is already too complex for your platform you may want to find some other ways to fix this problem instead of trying to make the shader do the work for example you can break up tiling by avoiding large areas like this one that we're looking at here that just have the same material repeating over and over, you know, over a long distance. If your layers all tile at different rates, you can use the layer blending itself to break up the tiling. And in areas where you see one material repeating, just paint some areas of another material, kind of like how I've done here, where I've got this other material kind of breaking up the burst one. So if we do that, we can effectively get rid of tiling artifacts without actually paying any more shader cost to do it. Another thing that you can do is break up tiling artifacts

Frame at 139.03s

by covering the terrain with foliage and trees,

Frame at 142.08s

kind of like what we're seeing here.

Frame at 147.00s

Out here, the repeating artifacts are really obvious because there's nothing else on our terrain.

Frame at 153.35s

But if we come over here, you can see that I've got these ferns, I've got rocks, trees.

Frame at 157.13s

I've added a lot of extra complexity.

Frame at 161.79s

And we don't see those tiling artifacts because of all of the other things going on in the scene.

Frame at 168.75s

So you may be able to get away with a shader that doesn't do any sort of fancy tile breakup. if the decorations on your terrain are complex enough to hide the tiling.

Frame at 185.17s

But assuming that you do want to add some sort of system to your shader for breaking up tiling artifacts, let's go ahead and do that.

Frame at 192.56s

So here's the shader that we created last week. You can see that we're sampling our color and roughness texture, and we're sampling our normal occlusion and height texture. And then we unpack those and pass out the values. And then here's the terrain material that we created with our four simple layers. And then we use the built-in functions for blending those layers together. So what we can do is we can take our simple layer material function and add functionality inside that layer to break up tiling. So let's come down here to our content drawer. I'll pick our simple layer material function and just copy and paste it. And instead of simple layer, we're going to rename this one to rotation layer because that's the name that I've given to this particular technique that we're going to be using. And let's go ahead and open this. But what I want to do next is just kind of give you an illustration for what we're going to be doing to break up our materials.

Frame at 269.45s

So we'll come back here to our scene, and I'm just going to add a couple of planes to our scene.

Frame at 275.73s

Okay, here you can see two planes, and I just created these so that I can give you

Frame at 287.81s

like a quick illustration of how this technique works Here our first plane down here on the bottom and this represents the uh the material that we that we created uh that we have on our terrain right now and this upper plane represents a second uh a second set of texture samples of our material so what we're going to do is we're going to sample our material once just like we're doing now and then we're going to sample the textures again but this time we're going to take our uv coordinates and rotate them 90 degrees so that this version down here on the bottom is the normal version and then this version above it is going to be rotated 90 degrees and then we're going to blend between these two planes or these two sets of texture samples using a mask so i just wanted to do a quick visual illustration so you

Frame at 340.41s

you could see what was happening there. All right. So here's our rotation layer.

Frame at 348.50s

And we need to go ahead and sample our textures twice. And so I'm just going to move. So this is the part of our shader that's unpacking our textures. I'm just going to move these over here. And here's our first texture sample. I'm going to duplicate that. So we have a second one. and we're sampling the same texture asset. So I can just wire that into the texture. But for the coordinates, we actually need to swap those around. And so I'm going to add a swizzle node here and pass our coordinates into that. And you can see here, the swizzle takes an XY, and I'm going to be passing out a YX. So by swapping the X and the Y components of our coordinates, we are rotating our texture. That's a really easy, it's basically a free way of rotating the texture 90 degrees. We could do a rotation, but there's sine and cosine math involved in rotating. And so by swizzling, by just swapping the X and the Y of our coordinates, it's a really easy kind of free way of rotating 90 degrees. now the last thing that we need to do is lerp between our two textures so we're going to take our original texture sample here the rgba and we're going to take our second texture sample here which is our rotated version and we're going to blend between those two now i'm not going to set up the blend mask right now we're going to do that in a minute But first, I want to create our second texture sample for our NOH texture. So I'm going to copy this texture sample and paste it. And again, we're going to take the swizzled version of our UV coordinates, and I'll pass that in. We'll pass in our NOH texture. And now again, we need to lerp between the first sample and the second sample. So we'll add a linear interpolate node. We'll pass in the first sample and the second sample. Okay, so once we've done that, now we need to split this up. Actually, let's see. I think we can just wire in our base color like this and also wire out our color like that. And now we need the alpha. I'm going to add a component mask node and we're going to set our component mask to just give us the alpha channel. Now we can wire this into our roughness on our set material node and also our roughness output. Okay, so we've got our color and roughness set up and now we need to set up our normal occlusion and height. So we're going to pass our red and green channels for the normal, and we're going to pass our blue channel for the ambient occlusion. And then since the split component node doesn't have an alpha output, we have to use a component mask node to access our alpha channel. And we're going to pass that into our height. So we pass it into our height for our output and also our height for our set material attributes OK so now we blending between our two texture samples both for color and roughness and also for our normal occlusion height. So the last thing that's left to be done for this shader is to sample the mask that we're going to use to blend between our two different materials, the one that's normal and the one that's rotated 90 degrees. So I'm just going to create another texture sample here. And we need to create another texture input 2D. And let's come over here and we'll call this one our blend mask. And we'll pass our texture object in just like that. And then maybe we need to give it a default. So let's bring that over here and we can find a good texture to use as our default. This texture here called masks will work really nicely. Let's take a look at this in our content drawer.

Frame at 646.29s

So if we open this up, we can take a look at the red, green, and blue. What I've got here is a bunch of different sizes of masks. The red mask is kind of large.

Frame at 658.37s

the medium mass or the green channel has a mask that's kind of medium and then the blue channel has a mass that's kind of small so i think we'll use the blue channel here you know you can choose to create any kind of mass texture you'd like but in my case i'm going to use this this blue channel

Frame at 678.49s

mask so we'll go back to our object here we've we're sampling our mask texture passing it in And then we're going to use the blue channel coming out of here to blend between our two different samples of our NOH texture and also to blend between our two different samples of our color and roughness texture. Okay, and then the one last thing that we need to do here is create a multiplier for our UV coordinates. So we have our UV coordinates here. We're using our absolute world position and scaling them and then passing that out. But we need to, let's see, I'm just going to grab this input scale here. And we're going to use this input value here as a multiplier. We'll call this blend mask multiplier. And then we're going to, actually we're going to divide. So I'm going to take this value here. Oh no. This value here. And we're going to divide it by this blend mask multiplier. I think I'll name this blend mask scale multiplier. So it's a little bit more clear. And we're going to give this a default value. experiment a little bit with this and figure out a good default value. But I'm going to go ahead and pass this into our UV coordinates. Now you might have noticed I've taken my coordinates, divided them by scale for the normal samples that I'm doing, and then divided them by this again for this mask that I'm doing. And the reason that I'm not taking my absolute world here, but I'm dividing this, is so that I can keep the same size ratio so as I change the scale of my textures, the mask scale goes with it. I really like that idea of not having to adjust the mask scale separately. So I can just set this value once and then I can scale my whole material up and down and the mask will go with it as I do that. All right, I think I'm all set. I've got my shader set up the way that I want it to. So let's assign it in our scene and we can adjust the scaling of our mask. So we'll come back here to our terrain shader. And now what I need to do is take our simple layer material function and replace it with the rotation layer. So I'm just going to come back over here to our terrain shaders again, and we'll drag in an instance of our rotation layer. And you can see that this one is a little bit more complicated because I have these extra inputs But let go ahead and replace our mossy texture here with our rotation layer version of our layer material function. And we'll wire this in just like that, as well as the height. and I'm going to go ahead and delete the simple layer version. And I think because I added that blend mask in the material function itself, that I don't actually need to add one here. But let's go ahead and make sure that I set that up correctly. I think I need to, for this texture object, I need to make sure that use preview value as default is checked. And then it'll just go ahead and use this mask texture. And I don't need to set up one here. If I want to, I could pass in a different mask here. But in this case, I'm just going to leave that empty. And the last thing that I need to do is set up my blend mask scale multiplier here. so i'm just going to create a uh a float value i'm going to give it a value of one and we'll pass this in just to start with and then we'll save it and let's take a look at our scene and see how we did

Frame at 966.39s

Okay, even with just a multiplier value of one, I can see that that technique that I added has done a pretty good job. There isn't as much artifacting as I had before. Now, off in the distance you can start seeing it repeating but here close in the foreground there is less repetition happening now i just realized that there's one thing that i forgot when you rotate a normal map you actually have to add some special handling to take care of that so let's go back to our shader really quick and fix that. So here we have our color and our roughness

Frame at 1018.34s

samples, and then we have our normal occlusion and height samples. And here our normals are coming out. Let's see. So here's our second sample here. And before we pass our second sample out to be combined with our first sample, we need to switch things around a little bit. Instead of red, green, blue, and alpha, we need them to go green, red, blue, and alpha. So I need to do a little bit of a swizzle right here. Okay, so here's what I did. I passed my red, green, and blue into this swizzle node and you can see the input says xyz and then the output says yxz so it's swapping my red and my green channels there and then I append the alpha channel to it before passing it into the lerp. Okay so that should take care of my normal. Now what I need to do is visualize the mask texture that I'm using. And so what I'd like to do is come down here to my sample and I'm using that blue channel of my mask. And just temporarily, what I'm going to do is pass this blue channel out as my height output so that I can debug it and test to see what it looks like. Then I'm going to go to my shader here and coming out of my height, I'm going to wire this height value directly into my base color just so I can see what my mask looks like on my terrain.

Frame at 1119.89s

So this is the mask that I'm using to blend between the two different versions of my material layer.

Frame at 1126.89s

And I can use this kind of debug view now to get the correct scale that I want.

Frame at 1134.05s

So let's switch to our terrain layer here. You can see that I've set my multiplier to 15.97. If I set this multiplier to one, like we had just a minute ago,

Frame at 1147.99s

you can see that this is too small for the material that we had.

Frame at 1152.52s

And so we want to find out find a value that that will blend between the two layers and kind of break up the Thailand so like I

Frame at 1165.79s

like I had I'm gonna use this value of 15.97 I think this is a pretty good value because you can

Frame at 1175.00s

you can see that the layers are repeated maybe once or twice here before we transition to the

Frame at 1183.64s

rotated version okay so let's go ahead and switch our color back to what it's

Frame at 1190.80s

supposed to be and take a look at our result okay you can see here that there

Frame at 1196.46s

is a little bit of tiling still happening but we've done a pretty good job of breaking it up by sampling a second version of our material and and rotating it 90 degrees and then blending it back in with that mask. This solution doesn't solve distant tiling. You still see the tiling if we look off here into the distance. But here, close up, we see less material tiling by using this method of blending between a standard version and the rotated version of our materials.

Frame at 1235.72s

Okay, so that's a pretty cool technique for breaking up material tiling.

Frame at 1241.52s

Let's go ahead and switch over to Unity, and I'll show you how to do something similar there.

Frame at 1247.84s

All right, so here we are in Unity. And last week, somebody mentioned down in the comments that it didn't look like the height-based blending was working at all. I don't see any height-based blending here. We're just getting like this nice, smooth, soft blending. And so I actually did take a look at it and I figured out what was going wrong. So let's take a look at our shader. We'll fix this really quickly before we start doing tile breakup.

Frame at 1280.20s

So looking at our shader here, we can see that there's this node down here at the bottom called Height-Based Splat Modify. And this is a node that ships with Unity version 6.3. So let's open this up and take a look. If we come over here, we can see that there's this custom function node called height transition value. And let's open this up and take a look at what it's doing. So if we take a look at the code that's being generated here, we're basically trying to get a value called height transition. And this is a value that comes from the material.

Frame at 1325.19s

But if we take a look at our material in the scene, you can see that there's no value over here on our material called height transition. This node was depending on the fact that a terrain material would have that material value added automatically, and it's not there.

Frame at 1343.80s

And so we need to alter our height-based splat modify to add in this value ourselves. So let's go ahead and open up the Blackboard here. And we need to add a new parameter and we'll call it, let's call it height sharpness. And we'll come over here to our inspector. And here we want to pick this promote to final shader. and then show in inspector so that our value is is exposed in our material and then we're going to set the mode to be a slider and we're going to slide between zero and one and we'll go ahead and save this oh and there is one other thing that we need to fix here let's turn off our blackboard in our inspector. And we have this value here that turns off our height-based blending based on a keyword. But again, I don't think this keyword is actually functioning correctly. And so let's just go ahead and bypass this. What this does is it turns off and on height-based blending. But I think we're only ever getting the off version of this. So let's just bypass this terrain blend height node here and we'll just skip it and actually we'll just delete it. So if I save this here now when

Frame at 1431.17s

we come over to our material now you can see I have this height sharpness value that we can adjust to make our our terrain blending work Alright so we should be able to move our slider back and forth and see our height sharpness working but I created it but I forgot to add it to the graph so let's come over here here's our height sharpness in our blackboard I'll just drag it into the graph and instead of our height transition value here we're gonna pass in our height sharpness value and now we can use our heart height sharpness slider here to make our transitions nice and smooth and blended or to make them nice and sharp so as I turn this down you can see now I've got a hundred percent sharpness on the height blending and then as I increase it I can kind of blend it out and make it smoother. So that was kind of what we were hoping for last week, but we didn't get it because our height-based splat modify node was not set up correctly. Okay, I think we're ready to start doing some blending to get rid of our tiling artifacts. So let's pick our simple layer node, our simple layer subgraph here. I'm going to copy and paste it, and we're going to call this one rotation layer instead of simple layer. And then we'll open up this subgraph and get to work. So here's the portion of the subgraph that's unpacking things. I'm just going to slide this over so we have some more room. And we need to do our texture sampling twice, just like we did in Unreal. So I'm going to copy this second layer here, or this, I'm going to make a copy of our sample texture node. And the second version needs to swap the X and the Y of the coordinates. So we'll add a swizzle node.

Frame at 1571.40s

And instead of X and Y, we're going to do Y, X. And we'll pass those coordinates in just like that. And now we need to lerp between this sampler and our second one. So we'll add a lerp node. We'll pass the red and green, the RGBA in. and into this one as well. And then we can, coming out of here, we can plug this into color in our pack material subgraph and also color on our output. And then we need the alpha channel separate. So we can add another swizzle node and just take the alpha channel. And this will pass into our smoothness on our pack material subgraph and also the smoothness on our output. Okay, that's the color and smoothness. Then we need to do the same thing down here for our NOH. So we'll copy this texture sampler and we'll pass in our swizzled texture coordinates. and then we'll add a lerp node here to blend between our first RGBA value and our second RGBA value and again we need to make sure that we take care of our normal map here since we're rotating our normals we need to swap the x and the y so I'm going to add a swizzle node here and we need y X Z W then we're going to pass that into our lerp and that is going to take care of our normals now we need to pass our values out from our lerp so we've blended between our two different layer samples now we need to split these values out and we're going to take the red and the green for the normal we're gonna take the blue or the occlusion and we're taking to take the alpha or the height all right perfect so the last thing that we need to do is create our mask texture sample And we go ahead and do that down here at the bottom So I'll copy this texture sampler here. And we need to add a couple of parameters in our Blackboard. So the first one we're going to add is a texture 2D. And we'll call this one our blend mask. and I'm going to go ahead and assign a default value for it here. We'll use our masks texture. We also need to create a subgraph drop down that will allow us to specify which channel of the texture we're going to sample or we're going to use as the mask. so I'm going to call this mask channel and then over here in our inspector we're going to we're going to name these so that it's clear what we're talking about we're going to do the red channel the green channel the blue channel and the alpha channel and I'm going to set it to default to the blue channel because I like using that one the best okay so now we're going to bring in that dropdown we just created. And for our sampler here, we're going to pass our four different channels into our dropdown. So on our subgraph, when we use that dropdown, we can choose the red, green, blue, or alpha channels as our mask. And now I can take the result of that mask channel drop down block and output those and use that as our lerp to blend between our two different samples our noh texture there and our cs texture there okay and then the last thing that we need to do is set up our a mask scale multiplier so i'm going to create a vector 2 value here and we'll call this mask scale multiplier. And again, I'm going to give this a value of 15.97 because that's the tiling value that I've found that I really like for that blue channel mask. And then we'll bring this in. And I need the coordinates that we've created up here. and we're going to divide these coordinates by our mask scale multiplier and then we're going to use this to sample our mask. Okay that's all set. So you can see that we are sampling our color and our smoothness twice and we're sampling our normal occlusion and height twice. And then we're blending between those two samples based on this mask here that we've given a different tile frequency. And all of that is working together to break up our texture tiling artifacts. So the last thing that we need to do is take this rotation layer sub graph and put it into our terrain shader so here I've got four simple layers and I can replace one or more of these simple layers with the more complex version that I just created now one thing that I will recommend here you don't have to use that rotation layer sub graph that we just created for all four of these. Maybe you have different materials and some of them look a lot more tily than others. You should only use a technique here that's more complex for the ones where you really need it. You don't necessarily have to use all four of these with this tile breakup technique. You can just replace one of them with it.

Frame at 1983.10s

And so that's what we're going to do. Let's take a look here really quick. I think maybe this moss layer is going to be the one that displays the most tiling artifacts. So let's paint down some more of that moss just so that we can see it. So that we can see what our tiling artifacts look like. I'm going to select our Fells Mossy layer here. And we just paint down a big chunk of moss over in this area so that you can see And right now that is a pretty repetitive material there And so let's see what happens when we replace that subgraph with our rotation layer. Let's see, that's our third one. So here in our terrain sample, we need to replace this one right here with our more complex version. So we're going to set here. We've got zero one. We're going to set this to layer two. And then we're going to pass out the material from there. we need to connect up the sampler state and we've already got our default mask set and our our default uh scale multiplier set so that should be all that we need to do oh we need to pass it out to our height blending here as well okay so we just replaced our simple layer with our rotation layer let's save it and we'll switch back to our scene. And what we should see is that our tiling artifacts are significantly reduced. Now I'm still seeing a lot of tiling artifacts here. And I think that's just because we've repeated our materials so many times. But if we do zoom in here, we have less tiling close up than we did before. Now one thing that I could do is just kind of play around with is just kind of play around with our multiplier value here until we get something that works a little bit better.

Frame at 2129.50s

Okay, I found the problem. What I had done is I created this blend mask input here, but I didn't actually hook it up to the sampler, and so it was sampling the wrong thing. But I was able to discover that by passing my mask channel out as the height and then setting or just wiring that height directly out so that it displays as color. But let's go ahead and fix that now.

Frame at 2161.23s

So we're not passing that out anymore. But now that we're sampling that mask correctly, it's breaking up our texture tiling quite a bit more. And I really like how this is working. Let's make sure that our value is set right here. Again, I want to set this to 15.97 for both of these. Yeah, there we go. Now we've got our tile breakup going on. This is working better. Okay, whew! I'm glad we got that sorted out. All right, so you can see we're sampling our textures twice, once rotated and once normal and then blending between them based on this mask texture here. And it's a pretty decent technique. And again, you don't need to use it on all of your layers, just the one or just the few that are specifically having problems with repetition. You know, this dirt layer here, it's just using the simple one, but because there isn't a lot of like contrasty detail in it, you don't see as much texture tiling happening on that layer

Frame at 2244.22s

as you do on this moss layer here. So it's important that you not take a technique like this and just kind of apply it wholesale across everything. Just apply it where you need it. okay that brings us to the end of the video thanks a lot for watching today next week I may decide to show you another technique for breaking up texture tiling or I we could also I could show you a technique that I've developed for making really efficient terrain that uses fewer texture samples not more like I showed you today. Let me know down in the comments which of those two you'd like to see. And we'll go ahead and put that video together for you for next week. Thanks for watching. Have a great week, everybody.