A render target is basically a texture that you can write to at runtime. On the engine side of things, they store information such as base color, normals and ambient occlusion.
On the user side of things, render targets were mainly used as a sort of secondary camera. You could point a scene capture at something and store the image to a render target. You could then display the render target on a mesh to simulate something like a security camera.
With the release of 4.13, Epic introduced the ability to draw materials directly to render targets using Blueprints. This feature allowed for advanced effects such as fluid simulations and snow deformation. Sounds pretty exciting, right? But before you get into such advanced effects, it’s always best to start simple. And what’s more simple than just painting onto a render target?
In this tutorial, you will learn how to:
- Dynamically create a render target using Blueprints
- Display a render target on a mesh
- Paint a texture onto a render target
- Change the brush size and texture during gameplay
Start by downloading the materials for this tutorial (you can find a link at the top or bottom of this tutorial). Unzip it and navigate to CanvasPainterStarter and open CanvasPainter.uproject. If you press Play, you will see the following:
The square in the middle (the canvas) is what you will be painting on. The UI elements on the left will be the texture you want to paint and its size.
To start, let’s go over the method you will use to paint.
The first thing you will need is a render target to act as the canvas. To determine where to paint the render target, you do a line trace going forward from the camera. If the line hits the canvas, you can get the hit location in UV space.
For example, if the canvas is perfectly UV mapped, a hit in the center will return a value of (0.5, 0.5). If it hits the bottom-right corner, you will get a value of (1, 1). You can then use some simple math to calculate the draw location.
But why get the location in UV space? Why not use the actual world space location? Using world space, you would first need to calculate the hit’s location relative to the plane. You would also need to take into account the plane’s rotation and scale.
Using UV space, you don’t need to do any of these calculations. On a perfectly UV mapped plane, a hit in the middle will always return (0.5, 0.5), regardless of the plane’s location and rotation.
First, you will create the material that will display the render target.
Creating the Canvas Material
Navigate to the Materials folder and then open M_Canvas.
For this tutorial, you will create render targets dynamically in Blueprints. This means you will need to set up a texture as a parameter so you can pass in the render target. To do this, create a TextureSampleParameter2D and name it RenderTarget. Afterwards, connect it to BaseColor.
Don’t worry about setting the texture here — you will do this next in Blueprints. Click Apply and then close M_Canvas.
The next step is to create the render target and then use it in the canvas material.
Creating the Render Target
There are two ways to create render targets. The first is to create them in the editor by clicking Add New\Materials & Textures\Render Target. This will allow you to easily reference the same render target across multiple actors. However, if you wanted to have multiple canvases, you would have to manually create a render target for each canvas.
A better way to do this is to create render targets using Blueprints. The advantage to this is that you only create render targets as needed and they do not bloat your project files.
First, you will need to create the render target and store it as a variable for later use. Go to the Blueprints folder and open BP_Canvas. Locate Event BeginPlay and add the highlighted nodes.
Set Width and Height to 1024. This will set the resolution of the render target to 1024×1024. Higher values will increase image quality but at the cost of more video memory.
Next is the Clear Render Target 2D node. You can use this node to set the color of your render target. Set Clear Color to (0.07, 0.13, 0.06). This will fill the entire render target with a greenish color.
Now you need to display the render target on the canvas mesh.
Displaying the Render Target
At the moment, the canvas mesh is using its default material. To display the render target, you need to create a dynamic instance of M_Canvas and supply the render target. Then, you need to apply the dynamic material instance to the canvas mesh. To do this, add the highlighted nodes:
First, go to the Create Dynamic Material Instance node and set Parent to M_Canvas. This will create a dynamic instance of M_Canvas.
Next, go to the Set Texture Parameter Value node and set Parameter Name to RenderTarget. This will pass in the render target to the texture parameter you created before.
Now the canvas mesh will display the render target. Click Compile and then go back to the main editor. Press Play to see the canvas change colors.
Now that you have your canvas, you need to create a material to act as your brush.
Creating the Brush Material
Navigate to the Materials folder. Create a material named M_Brush and then open it. First, set the Blend Mode to Translucent. This will allow you to use textures with transparency.
Just like the canvas material, you will also set the texture for the brush in Blueprints. Create a TextureSampleParameter2D and name it BrushTexture. Connect it like so:
Click Apply and then close M_Brush.
The next thing to do is to create a dynamic instance of the brush material so you can change the brush texture. Open BP_Canvas and then add the highlighted nodes.
Next, go to the Create Dynamic Material Instance node and set Parent to M_Brush.
With the brush material complete, you now need a function to draw the brush onto the render target.
Drawing the Brush to the Render Target
Create a new function and name it DrawBrush. First, you will need parameters for which texture to use, brush size and draw location. Create the following inputs:
- BrushTexture: Set type to Texture 2D
- BrushSize: Set type to float
- DrawLocation: Set type to Vector 2D
Before you draw the brush, you need to set its texture. To do this, create the setup below. Make sure to set Parameter Name to BrushTexture.
Now you need to draw to the render target. To do this, create the highlighted nodes:
Begin Draw Canvas to Render Target will let the engine know you want to start drawing to the specified render target. Draw Material will then allow you to draw a material at the specified location, size and rotation.
Calculating the draw location is a two-step process. First, you need to scale DrawLocation to fit into the render target’s resolution. To do this, multiply DrawLocation with Size.
By default, the engine will draw materials using the top-left as the origin. This will lead to the brush texture not being centered on where you want to draw. To fix this, you need to divide BrushSize by 2 and then subtract the result from the previous step.
Afterwards, connect everything like so:
Finally, you need to tell the engine you want to stop drawing to the render target. Add an End Draw Canvas to Render Target node and connect it like so:
Now whenever DrawBrush executes, it will first set the texture for BrushMaterial to the supplied texture. Afterwards, it will draw BrushMaterial to RenderTarget using the supplied position and size.
That’s it for the drawing function. Click Compile and then close BP_Canvas. The next step is to perform a line trace from the camera and then paint the canvas if there was a hit.
Line Tracing From the Camera
Before you paint on the canvas, you will need to specify the brush texture and size. Go to the Blueprints folder and open BP_Player. Afterwards, set the BrushTexture variable to T_Brush_01 and BrushSize to 500. This will set the brush to a monkey image with a size of 500×500 pixels.
Next, you need to do the line trace. Locate InputAxis Paint and create the following setup:
This will perform a line trace going forward from the camera as long as the player is holding down the key binding for Paint (in this case, left-click).
Now you need to check if the line trace hit the canvas. Add the highlighted nodes:
Now if the line trace hits the canvas, the DrawBrush function will execute using the supplied brush variables and UV location.
Before the Find Collision UV node will work, you will need to change two settings. First, go to the LineTraceByChannel node and enable Trace Complex.
Second, go to Edit\Project Settings and then Engine\Physics. Enable Support UV From Hit Results and then restart your project.
Once you have restarted, press Play and left-click to paint onto the canvas.
You can even create multiple canvases and paint on each one separately. This is possible because each canvas dynamically creates its own render target.
In the next section, you will implement functionality so the player can change the brush size.
Changing Brush Size
Open BP_Player and locate the InputAxis ChangeBrushSize node. This axis mapping is set to use the mouse wheel. To change brush size, all you need to do is change the value of BrushSize depending on the Axis Value. To do this, create the following setup:
This will add or subtract from BrushSize every time the player uses the mouse wheel. The first multiply will determine how fast to add or subtract. For safe measure, a Clamp (float) is added to ensure the brush size does not go below 0 or above 1,000.
Click Compile and then go back to the main editor. Use the mouse wheel to change the brush size while you paint.
In the final section, you will create functionality to let the player change the brush texture.
Changing the Brush Texture
First, you will need an array to hold textures the player can use. Open BP_Player and then create an array variable. Set the type to Texture 2D and name it Textures.
Afterwards, create three elements in Textures. Set each of them to:
These are the textures the player will be able to paint. To add more textures, simply add them to this array.
Next, you need a variable to hold the current index in the array. Create an integer variable and name it CurrentTextureIndex.
Next, you need a way to cycle through the textures. For this tutorial, I have set up an action mapping called NextTexture set to right-click. Whenever the player presses this button, it should change to the next texture. To do this, locate the InputAction NextTexture node and create the following setup:
This will increment CurrentTextureIndex every time the player presses right-click. If the index reaches the end of the array, it will reset back to 0. Finally, BrushTexture is set to the appropriate texture.
Click Compile and then close BP_Player. Press Play and press right-click to cycle between the textures.
Where to Go From Here?
You can download the completed project using the link at the top or bottom of this tutorial.
Render targets are extremely powerful and what you’ve learnt in this tutorial is only scratching the surface. If you’d like to learn more about what render targets can do, check out Content-Driven Multipass Rendering in UE4. In the video, you will see examples of flow map painting, volume painting, fluid simulation and more.
Also check out the live training for Blueprint Drawing to Render Targets to learn how to create a height map painter using render targets.
If there are any effects you’d like to me cover, let me know in the comments below!