Introduction

In this post, we’ll cover how to use a compute shader to flip an image across the x-axis, y-axis, and diagonal axis. We will also demonstrate how these operations can be combined to rotate an image.

Create a 2D Unity Project

Open the Unity Hub and create a new 2D project. I’m using Unity 2019.4.20f1, but you should be fine using other versions.

unity-hub-create-project

Create Compute Shader

In Unity, right-click an empty space in the Assets folder and open the Create submenu. Select ComputeShader from the Shader submenu and name it FlipShader.

unity-create-compute-shader

Open the new compute shader in your code editor. By default, compute shaders contain the following code.

flipshader-default-code

We’ll delete the CSMain function and create a new one for each of our three flip operations.

Define Variables

Before we create our functions, we need to define some extra variables.

  • Texture2D<float4> InputImage: stores the original image
  • int height: the height of the input image
  • int width: the width of the input image
  • int2 coords: stores the new (x,y) coordinates for individual pixel values

flipshader-define-variables

Define Flip Functions

The individual flip operations quite simple. They determine the coordinates of the pixel that will replace the values for a given pixel in the image. The RGB pixel values at the calculated coordinates will be stored at the current coordinates in the Result variable.

  • Flip x-axis: subtract the y value for the current pixel’s (x,y) coordinates from the height of the image
  • Flip y-axis: subtract the x value for the current pixel’s (x,y) coordinates from the width of the image
  • Flip diagonal: swap the x and y values for the current pixel’s (x,y) coordinates

These operations are performed on each pixel in parallel on the GPU. We’ll use the default numthreads(8, 8, 1) for each function.

flip-shader-define-functions

Create Flip Script

Back in Unity, right-click an empty space in the Assets folder and select C# Script in the Create submenu. Name the new script, Flip and open it in your code editor.

unity-create-flip-script

Define Variables

We’ll define the following variables at the top of the script.

  • public ComputeShader computeShader: The compute shader that contains the flip operations
  • public GameObject screen: The screen to which the test image is attached
  • public bool flipXAxis: Toggle whether to flip the image across the x-axis
  • public bool flipYAxis: Toggle whether to flip the image across the y-axis
  • public bool flipDiag: Toggle whether to flip the image across the diagonal axis
  • private GameObject mainCamera: Stores a reference to the Main Camera object
  • private RenderTexture image: A copy of the original test image

flip-script-define-variables

Define Start() Method

In the Start() method, we’ll store a copy the original test image in the image RenderTexture. We can do so by getting a reference to the Texture attached to the screen and using the Graphics.Blit() method. We’ll also get a reference to the camera so that we can adjust the view to fit the current image.

flip-script-start-method

Define FlipImage() Method

Next, we’ll define a new method called FlipImage to handle executing the compute shader. This method will take in the image to be flipped, an empty RenderTexture to store the flipped image, and the name of the function to execute on the compute shader.

To execute the compute shader, we need to first get the kernel index for the specified function and initialize the variables we defined in the compute shader. Once we execute the compute shader using the computeShader.Dispatch() method, we can copy the result to the empty RenderTexture we passed in. We could copy the result directly to the RenderTexture containing the original image. However, this would cause an error when flipping non-square images across the diagonal axis. This is because a RenderTexture can not dynamically change dimensions.

flip-script-flipImage-method

Define Update() Method

First, we need to make another copy of the original image so that we can edit it. We’ll store this copy in a temporary RenderTexture called rTex that will get released at the end of the method.

The steps are basically the same for performing each of the three flip operations. We first allocate a temporary RenderTexture called tempTex to store the flipped image. We then call the FlipImage method with the appropriate function name. Next, we copy the flipped image to rTex. Finally, we release the resources allocated for tempTex. The steps for flipping the image across the diagonal axis is slightly different as we can’t directly copy a flipped image with different dimensions back to rTex. Instead, we have to directly assign the currently active RenderTexture to rTex.

After we copy tempTex back to rTex we’ll update the Texture for the screen with the flipped image and adjust the shape of the screen to fit the new dimensions.

flip-script-update-method

Create Screen GameObject

Back in Unity, right-click an empty space in the Hierarchy tab and select Quad from the 3D Object submenu. Name the new object Screen. The size will be updated automatically by the Flip.cs script.

unity-create-screen-object

Create ImageFlipper

Right-click an empty space in the Hierarchy tab and select Create Empty from the pop-up menu. Name the empty object ImageFlipper

unity-create-image-cropper-object

With the ImageFlipper selected, drag and drop the Flip.cs script into the Inspector tab.

unity-inspector-attach-flip-script

Drag and drop the Screen object from the Hierarchy tab as well as the FlipShader from the Assets folder onto their respective spots in the Inspector tab.

unity-inspector-assign-parameters

Test it Out

We’ll need a test image to try out the ImageFlipper. You can use your own or download the one I used for this tutorial.

Drag and drop the test image into the Assets folder. Then drag it onto the Screen in the Scene.

unity-import-image

Next, we need to set our Screen to use an Unlit shader. Otherwise it will be a bit dim. With the Screen object selected, open the Shader drop-down menu in the Inspector tab and select Unlit.

unity-inspector-tab-shader-drop-down

Select Texture from the Unlit submenu.

unity-inspector-tab-unlit-texture

Now we can click the Play button and toggle the different flip checkboxes to confirm our script is working properly. If you check the performance stats, you should see that there is a negligible performance hit from flipping the image even when performing all three operations at once.

Default Image

default-image

Flip X-Axis

flip-x-axis

Flip Y-Axis

flip-y-axis

Flip Diagonal Axis

flip-diagonal-axis

Flip X-Axis and Y-Axis

flip-x-axis-and-y-axis

Flip X-Axis and Diagonal Axis

flip-x-axis-and-diagonal-axis

Flip Y-Axis and Diagonal Axis

flip-y-axis-and-diagonal-axis

Flip X-Axis, Y-Axis and Diagonal Axis

flip-x-axis-y-axis-and-diagonal-axis

Conclusion

That is one approach to efficiently flip images on the GPU in Unity. As demonstrated above, the operations can be combined in different ways to rotate the image as well.