An example generated with the code from the Voxel Terrain Tutorial

Voxel Terrain In Unreal Engine 4 Part 4: Texturing The Terrain

Back again with another part of the Unreal Engine 4 Voxel Terrain tutorial! In this part of the Voxel Terrain tutorial series we’ll be building a material to texture our mesh, and making two small code changes to automatically apply the material. Before we get started you’ll probably want to download the assets we’ll be using in this tutorial; you can find those here. As a bonus the content file also contains the materials we’ll be making today, so feel free to use it as a reference while making your own materials. With that let’s get started on the tutorial.

The Master Material

Like I mentioned before I’ve included the material we’re making in this section in the download above, and I highly recommend you download those assets and have the material open while you follow this tutorial so you can use it as a reference. There are a lot of methods you could use to texture the mesh we’re working with, however, the simplest one to implement in the system we’ve created with the previous tutorials uses world coordinates for texture mapping. In the future I’ll be covering some alternative methods as well, because this one requires 6 samplers per texture, which can add up really quickly if you decide to add normal maps or any other optional textures.

If you’re using a platform that supports shared texture samplers you should definitely make use of them where possible; they can save an immense number of samplers in this material. In the simple grass material we’ll be creating it makes the samplers used go from 6 to 3. To allow a sampler to be used as a shared sampler simply select it and change Sampler Source from From Texture Asset to one of the two shared options; the rest will be taken care of automatically by the engine. I suppose we need to start making the material before this advice will be of any help though, so let’s get started with that.

Creating The Material

Alright, start off by opening the editor and making a new material in your content folder. Open up the material you just created if you haven’t already. We’re going to be making a simple opaque surface material so we don’t need to change any material settings from their defaults. To start off we’re going to need a Absolute World Position node, which you can find by searching for World Position, the one you need will be under Coordinates. Drag off from that and make a new Add node, and connect the second input of the new add node to a Constant 3Vector with the value (-50, -50, -50). The result of this addition node will get us an offset position, which we need to correct a texture tiling issue that would occur if we didn’t have this here.

Dragging off from the output of the add node, create a Divide node. Connect the second input of the new node to a Scalar Parameter named TextureScale. Set the default value of the new parameter to a value of 100. This will cause our offset position to be scaled from centimeters to meters. We want this because our voxels are 1 meter wide, so we want our texture to tile every meter. This is a parameter so you can change the scale to fit with the size of your voxels.

Finally, drag off from the output of the division node and create a new Multiply node. Set the value of the second input to a constant of -1. We need to invert our coordinates or else our textures will end up being upside down! With that we have the first section of our material setup and ready to go; check out the image below to make sure you got everything right though.

2bc07a513c68704d99540f89de01695d

If you have that correct then get ready for the next section, which is an equally simple setup. Go ahead and start by creating a VertexNormalWS node, and break it’s output apart using the BreakOutFloat3Components node. For each of the outputs of the BreakOutFloat3Components node drag off and create an abs node. We won’t be using the output of this section for some just yet, however, it’s good to get it out of the way because we will need it in multiple places. It should look like this if you’ve done it correctly:

4ea1d8d403982a6c477fc04f5ad4b320

Moving on, up next we have the texture mappings. To do this we need to make 3 Mask nodes, each of which will provide UVs for two texture samplers. Wire up the output of the Multiply node from earlier to the inputs of the mask nodes. Next we need to set the mask nodes to output the correct masks, specifically we need to set them to the following three output modes:

  1. Mask (GB) – Z, Y Plane
  2. Mask (RB) – Z, X Plane
  3. Mask (RG) – X, Y Plane

Once you have the three nodes set on the correct modes, you should see something like this:

e824f2b3147f9698addfb710a4f67e8e

As I said before each of those mask nodes will be connected to two different texture samplers, one for each of the two sides of the plane defined by the mask nodes. Go ahead and create six (6) TextureSampleParameter2D nodes. Name and wire them like so:

  1. Mask (GB) – Z, Y Plane
    1. RearTexture
    2. FrontTexture
  2. Mask (RB) – Z, X Plane
    1. LeftTexture
    2. RightTexture
  3. Mask (RG) – X, Y Plane
    1. BottomTexture
    2. TopTexture

Next you’ll need to make three DynamicBranch nodes, one for each pair of samplers. Wire Color Input 0 to the first sampler of the pair, and Color Input 1 to the second sampler of the pair. Then wire the Alpha input of each branch to the appropriate output on the BreakOutFloat3Components node we made previously as shown below.

  1. Z, Y Plane(Front/Back) to R output
  2. Z, X Plane(Left/Right) to G output
  3. X, Y Plane(Top/Bottom) to B output

And just to be sure you got that right here is another image showing it in the editor:

4788839062b2bb56340a29a4461fe8bb

Now we only need to combine all of the texture samples together, to do that we need three (3) Multiply nodes and two (2) Add nodes. Take the three multiplication nodes and wire each of their A inputs to one of the DynamicBranch outputs. Next wire the B inputs of the multiplication nodes to the Abs node that corresponds with the axis of the textured face. Then wire the outputs of two of the multiplication nodes to the inputs of one of the addition nodes, and add the remaining multiplication node to the output of the other addition node. Finally connect the output of the last addition node to the Base Color input on the material struct.

Before we call the material finished go ahead and make two more Scalar Parameters. Call one of them Metallic and the other Roughness, and give them default values of 0 and 1 respectively. These two parameters will allow us to control the shading of the material from material instances. Wire the two new nodes up to the inputs on the material struct with matching names, and you’ll be all done with the master material. That last bit of wiring should look like this:

1d19d6c1eb2dc112bfe478112d6389d7

Setting Up The Grass Material Instance

The material we just made is supposed to be used through material instances, so go ahead and right click the material in the content browser and click Create Material Instance. Name the new material instance whatever you want; I called mine M_VoxelGrass. Open it up, we need to change some parameters. Specifically, we need to change what textures the material uses. Set the top texture to the GrassTop texture I included with my content pack, next set the the bottom texture to the Dirt texture, and finally set the rest of the sides to the GrassSide texture.

0e8db3bbe37b3f93d8b3ca0248f5ba90

Feel free to tweak the Roughness and Metallic values to your liking while you have the material instance open. Personally I think it looks best on the default settings, so I’m not going to change anything else here. Repeat this process with different textures for every terrain material you’d like to create. Now we just need to apply the material and we’ll be all done.

Applying The Material

At last we need to put our voxel terrain into the world! Without the code changes we’re going to make after we test this our material won’t be applied automatically, so to apply it we need to create a voxel terrain actor and simulate the world so we have access to the material of the procedural mesh. An important thing to do, however, is make sure that your terrain actor is at the center of the world! If your actor isn’t placed at the center (Or, at exactly 1 meter intervals from the center) then the textures won’t tile correctly.

Once you have your terrain actor placed at (0, 0, 0) go ahead and simulate the world. Your editor will probably freeze up for a minute while the voxels generate, and once it unfreezes you will probably see something like this in the sky:

fcf79920170d25b19ae3dea6c81ad1fc

Select the actor and find the Materials category in the details panel. Once you find the correct category, set Element 0 of the Materials array to your material instance. You should immediately see the terrain change to apply your material like so:

a6fc3421e76070d92f1eb2c7ca1163e9

Applying The Material Automatically

Now that we’ve tested our material, let’s make it so we can apply it automatically from the editor. This requires a few small code changes to pull off. Open the project files in your IDE if you haven’t already and open the header and implementation files for your voxel terrain actor. Starting off with the header file, find the variables you declared for the voxel terrain actor class in previous parts of the tutorial series. You only need to add one new variable to this area, specifically this one:

// The material to apply to our voxel terrain
UPROPERTY(Category = "Voxel Terrain", BlueprintReadWrite, EditAnywhere) UMaterialInterface* TerrainMaterial;

Now that you’ve declared that variable, you need to tell the mesh to make use of it. To do that open the implementation file and make your way down to the BeginPlay function. Add the following line after the call to CreateMeshSection:

Mesh->SetMaterial(0, TerrainMaterial);

It should end up looking like this:

// Finally create the mesh
Mesh->CreateMeshSection(0, Vertices, Indices, Normals, UV0, Colors, Tangents, true);
Mesh->SetMaterial(0, TerrainMaterial);

Once you have that done go ahead and compile the project and reopen your editor if you closed it. You should see a new variable on the voxel terrain actor called Terrain Material. If you set this to your grass material and hit play you should end up with the material being automatically applied to the terrain.

b1ca519bc1e205436f4bf5f53f320fd2

If that worked then you’re now done with this section of the tutorial, hooray. If it didn’t work, or you think I missed something, then let me know in the comments, and I’ll do my best to fix the issue. That concludes the basics of creating voxel terrain in Unreal Engine 4, however, that does not meant this is the end of this tutorial series. There is still a lot left to cover, for example I’m sure you’d like to be able to use multiple materials on your terrain, right? I’ll be covering how to do just that in the next part of this series, so stay tuned.

12 comments on “Voxel Terrain In Unreal Engine 4 Part 4: Texturing The TerrainAdd yours →

      1. That’s very strange! I’m not experiencing that issue locally, could you provide more details?

        Specifically I’d like to know what kind of fatal error it’s giving you, what you’re setting the noise offset to, and if possible, a stack trace to help you find out where it crashes.

        1. That’s fixed, but i don’t know how :p
          I trying to make a save system for thoses voxels and that’s a bit hard
          I’m able to save, but not to load and restore (I’m using the binary save system)

          1. My save system is now working =)
            But i don’t save the tangents, and i don’t see any changes… Same if i change the seed…
            22 Seconds to generate a 250*250*63 Voxels Chunk, and 3 to load it =)

          2. Good to hear you got that working! If you don’t save the tangents then the engine will attempt to calculate them for you, which can sometimes have undesirable effects, however, it should be fine in most cases.

            As for the seed not changing the terrain; there are a few things I can see causing that. Can you make sure that you have the virtual void PostInitializeComponents() override; function override in your class definition, and defined in your implementation file? If it’s missing then that would explain why the seed isn’t working. You can find the implementation for that function in Part 2 of the tutorial series if you’re missing it, it’s under the The PostInitializeComponents Function section. If that isn’t the cause of the issue then let me know and I’ll walk you through some other possible causes.

  1. For the seed problem, that not in fact a problem, i just said I save the terrain informations [like octaves, vertices, …] but i don’t save the tangents [i going to save it right now] but if i change the seed without the tangents save, that’s don’t change anything on a loaded terrain. And cause of that i tought tangents are useless. But i’m back from my week now, i can work on it.
    Do you have a track for me to have a system to edit in realtime my terrain and/or to have more than 1 block ?
    Thank you in past and advance for your help =)

    [And sorry for my bad english, i’m belgian =D]
    InsideBSI

    1. In short: yes tangents are a very useful thing to have on any mesh. They are what tells the engine what direction your vertices are facing, which is required for correct lighting.

      To answer your previous question about editing the terrain and having multiple voxel types: I’ll be getting back to work on this tutorial series soon! The next part will show you how to use multiple voxel types(and therefore multiple materials like grass, stone, and dirt).

  2. Hello, how i can use it in my editor? I placed VoxelTerrainActor in the scene and press play and notihng happens, please help me

Leave a Reply