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.
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:
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:
- Mask (GB) – Z, Y Plane
- Mask (RB) – Z, X Plane
- Mask (RG) – X, Y Plane
Once you have the three nodes set on the correct modes, you should see something like this:
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:
- Mask (GB) – Z, Y Plane
- Mask (RB) – Z, X Plane
- Mask (RG) – X, Y Plane
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.
- Z, Y Plane(Front/Back) to R output
- Z, X Plane(Left/Right) to G output
- 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:
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:
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
Feel free to tweak the
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:
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:
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:
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.
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.