Create a Pixel shader

Tutorial

intermediate

+25XP

30 mins

Unity Technologies

Create a Pixel shader

In this tutorial, you'll create a pixel shader that creates retro, pixelated visual effects by reducing texture resolution. This effect is commonly used for retro-styled games, stylized graphics, pixel art aesthetics, or special visual effects. By the end of this tutorial, you'll have a working pixel shader that you can customize and apply to any GameObject.

By the end of this tutorial, you'll understand the following:

  • How to manipulate UV coordinates to create pixelation
  • How the Floor and Divide nodes work together to create blocky sampling
  • How to fix sampling errors using gradient-based mipmapping

1. Overview

Pixel shaders create retro, pixelated visual effects that evoke classic video game aesthetics. This technique is widely used in games for the following purposes:

  • Retro-styled games and pixel art graphics
  • Stylized visual effects and artistic choices
  • Special power-ups or abilities that change visual perception
  • Nostalgia-driven design choices

The pixel effect you'll create works by modifying UV coordinates, the 2D texture coordinates that map textures onto 3D surfaces. By quantizing (rounding) these coordinates, you force the texture to be sampled in blocks rather than smoothly, creating the characteristic chunky, low-resolution appearance.

Important: This tutorial is part of the Get Started with Shader Graph course, which covers different units focused on different types of shaders. In case you haven't downloaded the project files, follow these instructions on how to create a new project and import the project assets.

2. Create the Pixel Shader Graph

Before creating the shader logic, you need to create the Shader Graph asset itself. For the pixel effect, you'll use an Unlit Shader Graph because the pixelation should be independent of lighting calculations.

To create the Pixel Shader Graph, follow these instructions:

1. Create a new Unlit Shader Graph

  • In the Project window, navigate to the Shaders folder.
  • Right-click in the Shaders folder and select Create > Shader Graph > URP > Unlit Shader Graph.
  • Rename the new shader "Pixel".
  • Double-click the Pixel shader to open it in the Shader Graph window.

You're now ready to start building your Pixel shader.

3. Create a material from the shader

Shaders define the rendering instructions, but they can't be applied directly to GameObject. To use a shader on a GameObject, you need to create a material, an instance of the shader with specific parameter values.

To create a material from the shader, follow these instructions:

1. Create a new material

  • In the Project window, right-click the Pixel shader and select Create > Material.
  • Rename it "Pixel_OldSign".

The GameObject used in this tutorial is a decorative sign. You’ll notice its drawing has a clean, cartoony style. After applying the shader, this artwork will be transformed into a pixelated version.

Note: When you create a material directly from a shader, Unity automatically assigns that shader to the new material. If you can’t see the shader assigned in the material’s Inspector window, you can drag the shader and drop it onto the material to assign it.

The material is now using your Pixel shader. Even though the shader is empty right now, the material is ready to receive the shader properties you'll create in the following steps.

4. Add a GameObject and apply material

Now you'll bring a GameObject into your scene and apply the pixel material to it. This lets you see your shader working in real-time as you build it.

To add a GameObject to your scene and apply a material to it, follow these instructions:

1. Open the Development_Scene

  • In the Project window, open the _GetStartedWithShaderGraph > Scenes folder.
  • Double-click the Development_Scene to open it.

2. Add the GameObject to your scene

  • In the Project window, locate the OldSign_1 prefab.
  • Drag the OldSign_1 prefab into the Hierarchy window.
  • In the Scene view, position the OldSign_1 over the small table that's already placed in the scene.

Note: You could also choose the OldSign_2 prefab. The GameObjects provided with this course are specially crafted to showcase shader effects effectively. If you use your own custom GameObjects, the shader will still work, but the visual results may look slightly different depending on the GameObject’s shape and UV layout.

3. Apply the pixel material

  • Select the OldSign_1 GameObject.
  • In the Inspector window, locate the Mesh Renderer component.
  • Use the foldout (triangle) to expand the Materials section if it's collapsed.
  • Drag the Pixel_OldSign material from the Project window onto the Material property box.

The Pixel_OldSign material is now applied to the OldSign_1 GameObject. However, the OldSign_1 GameObject currently looks plain because the shader is empty, but as you build the shader in the following steps, you'll see the pixel effect develop in real-time.

5. Create UV node

To create the pixel effect, you need to modify the UV coordinates. UV coordinates map 2D textures onto 3D surfaces. They define which part of a texture corresponds to each part of the mesh. Each vertex on a 3D model has UV coordinates (U and V values, typically ranging from 0 to 1) that tell Unity where on the texture to sample colors.

To create a UV node, follow these instructions:

1. Add a UV node

  • Right-click in the Workspace, select Create node, and search for and and add a UV node (Input > Geometry > UV).
  • Confirm the Channel property is set to UV0.

UV0 is the primary UV channel: the default texture coordinate set used by most meshes. Some meshes have additional UV channels (UV1, UV2, etc.) for lightmaps or other special purposes, but UV0 is the standard choice for main textures.

6. Create the PixelSize property

For this shader, the PixelSize property will control how large or small the pixel blocks appear on the screen.

To create the PixelSize property, follow these instructions:

1. Add a Float property

  • In the Blackboard, select the Add (+) button.
  • Select Float from the dropdown.
  • Rename it "PixelSize".

2. Configure the property as a slider

  • Select the PixelSize property in the Blackboard.
  • In the Graph Inspector, set the Mode property to Slider and set the slider range as follows: Min = 8, Max = 512, and Default Value = 64.

These values define a practical range for controlling the pixelation effect. The minimum value 8 allows for very large pixel blocks, producing a strong pixelated look. The maximum value 512 allows for much smaller blocks, creating a more detailed image.

This range works well for most screen resolutions because it lets you experiment with both coarse and fine pixelation without using values that would either break the effect or make the changes too subtle to notice. The default value 64 provides a balanced starting point where the pixelation is clearly visible but not overly exaggerated.

Note: The slider range values were chosen based on experimentation and aren’t definitive. You can adjust the range if you find different values give you better results.

7. Scale UV by PixelSize

The next step in creating pixelation is to scale the UV coordinates up by multiplying them by the PixelSize.

To scale the UV coordinates by PixelSize, follow these instructions:

1. Multiply UV with PixelSize

  • Right-click in the Workspace, select Create node, and search for and add a Multiply node (Math > Basic > Multiply).
  • Connect the UV Out output to the A input of the Multiply node.
  • Drag the PixelSize property from the Blackboard into the Workspace.
  • Connect the PixelSize output to the B input of the Multiply node.

This operation scales the UV coordinates up. Normally, UV coordinates range from 0 to 1, but multiplying them by PixelSize expands that range. For example, if PixelSize = 64, the UV values will now range from 0 to 64.

8. Apply Floor function

The last step prepared the coordinates for the Floor operation. By increasing the range first, you scaled the UV coordinates to define the size of the grid that will be created. Then, when the Floor node is applied, the values will be rounded down to the nearest whole number, snapping the coordinates to discrete (non-continous) steps. This makes the Floor node the key to creating the blocky, stepped values that produce pixelation.

To apply a Floor function, follow these instructions:

1. Create a Floor node

  • Right-click in the Workspace, select Create node, and search for and add a Floor node (Math > Round > Floor).
  • Connect the Multiply Out output to the In input of the Floor node.

The Floor node rounds down values to the nearest integer, creating stepped/pixelated values instead of smooth gradients (for example, Input = 3.2, Output = 3).

When applied to UV coordinates, this creates discrete blocks in the UV space. Instead of smoothly transitioning from one texture coordinate to another, the coordinates snap to integer values, producing the blocky, pixelated look.

9. Divide to complete pixelation

Now you need to scale the floored UV coordinates back down to the original range. By dividing by the same PixelSize you’re scaling the coordinates back to the 01 UV range, but now many pixels share the same UV value. This causes groups of screen pixels to sample the same point in the texture, producing the pixelated effect.

To divide Floor output by PixelSize, follow these instructions:

1. Divide Floor output by PixelSize

  • Right-click in the Workspace, select Create node, and search for and add a Divide node (Math > Basic > Divide).
  • Connect the Floor Out output to the A input of the Divide node.
  • Drag the PixelSize property from the Blackboard into the Workspace.
  • Connect the PixelSize output to the B input of the Divide node.

The Divide node divides A by B. Here it scales the floored UVs back down to the 0-1 range that texture sampling expects.

10. Connect the texture to the effect

Right now you’ve defined the UV coordinates that change a texture into discrete blocks, creating the pixelated sampling effect. But your Pixel shader still needs the texture to pixelate.

To connect the texture to the pixel logic, follow these instructions:

1. Add a Texture2D property

  • In the Blackboard, select the Add (+) button and select Texture2D.
  • Rename it "MainTexture".
  • Set the Default Value property to OldSigns_Albedo.

Texture2D properties store 2D image data (textures) that can be sampled by shader nodes. This is the most common texture type for surface materials.

2. Create a Sample Texture 2D node

  • Right-click in the Workspace, select Create node, and search for and add a Sample Texture 2D node (Input > Texture > Sample Texture 2D).

The Sample Texture 2D node reads color data from a texture using UV coordinates. UV coordinates define which area of the 2D texture is displayed on each part of the 3D model. By using the UVs we created in the previous steps, the node reads the texture at those positions and outputs the corresponding color values.

3. Connect the texture and pixelated UVs

  • Drag the MainTexture property from the Blackboard into the Workspace.
  • Connect the MainTexture output to the Texture(T2) input of the Sample Texture 2D node.
  • Connect the Divide Out output to the UV(2) input of the Sample Texture 2D node.

The texture is now sampled with pixelated UV coordinates.

11. Connect to Base Color

The final step of the shader is to connect the pixelated texture sampling to the Main stack. This applies the result of your pixelation logic to the material so the effect becomes visible on the object.

To connect to the Base Color, follow these instructions:

1. Connect to the Base Color

  • Locate the Master Stack on the right side of the Workspace.
  • Find the Base Color node block on the Fragment stack.
  • Connect the Sample Texture 2D RGBA(4) output to the Base Color input.
  • Save your shader with Ctrl+S (macOS: Cmd+S).

The Base Color node block of the Fragment Stack defines the main surface color of your material. By connecting your pixelated texture sampling here, you're telling Unity to use the blocky, low-resolution texture appearance as the object's base color.

Your basic pixel shader is now complete and functional. You can see the pixelated effect in the Main Preview window of the Shader Graph.

12. Adjust shader parameters

The shader is built, even though you’ve assigned the MainTexture property, references could have been lost. You need to assign the appropriate texture for your object in the material’s Inspector window.

To adjust the shader parameters, follow these instructions:

1. Ensure the material is selected

  • In the Project window, select the Pixel_OldSign material.

2. Verify and assign texture

  • In the Inspector window, locate the MainTexture property.
  • In case there’s no reference, assign the OldSigns_Albedo texture.

Important: Even if default textures were assigned inside the Shader Graph window, materials may lose those references. Always double-check that all textures are properly assigned.

3. Adjust other shader parameters

  • In the Inspector window, locate the PixelSize property you defined for the shader.
  • Position your Scene view to see the GameObject from different angles.
  • Continue adjusting PixelSize until you achieve the desired appearance for your pixelation.
Optional Step

13. Optional: Lines issues appearing after pixelation

You might notice that the pixel effect creates strange lines in some areas, especially when viewing the GameObject from a distance or at steep angles. This happens because the texture sampling system struggles to choose the correct texture resolution when the UVs are heavily modified for the pixelation effect.

To understand why this happens, you need to know that Unity uses MipMaps when rendering textures. MipMaps are smaller, pre-generated versions of a texture that Unity automatically uses when a GameObject appears smaller on screen or is viewed at sharp angles. This helps improve performance and prevents visual noise.

However, the pixel shader you created modifies the UV coordinates in a way that breaks Unity's automatic MipMap selection. Because the UVs are no longer smooth or continuous, Unity sometimes selects the wrong texture resolution, which causes the visible lines at certain angles.

Optional Step

14. Optional: Fix issue with gradient sampling

You can fix this issue by changing the Sample Texture 2D node to use Gradient mode for MipMaps.

Gradient mode allows the shader to provide more accurate information about how the texture is sampled across the screen. This helps Unity choose the correct MipMap level, eliminating the visual errors and producing a cleaner pixelated result.

To fix the MipMap issue with gradient sampling:

1. Disconnect the UV connection

  • Disconnect the UV Out output to the Multiply A connection (right-click the connection line and select Delete).

2. Create a Swizzle node

  • Right-click in the Workspace, select Create node, and search for and add a Swizzle node (Channel > Swizzle).
  • Connect the UV Out output to the In input of the Swizzle node.
  • Set the Mask property to XY.

The Swizzle node reorders or selects specific components from a vector. In this case, it extracts just the XY components from the UV coordinates (discarding Z and W if present). This prepares clean 2D UV data for gradient calculation.

3. Create a DDX node

  • Right-click in the Workspace, select Create node, and search for and add a DDX node (Math > Derivative > DDX).
  • Connect the Swizzle Out output to the In input of the DDX node.

The DDX node calculates the rate of change (gradient) of values horizontally across the screen. This tells Unity how quickly UV coordinates are changing from pixel to pixel in the X direction, which is essential information for proper mipmap selection.

4. Create a DDY node

  • Right-click in the Workspace, select Create node, and search for and add a DDY node (Math > Derivative > DDY).
  • Connect the Swizzle Out output to the In input of the DDY node.

The DDY node calculates the rate of change vertically. Together with the DDX node, these provide complete gradient information: how UV coordinates change both horizontally and vertically across the screen.

5. Configure Sample Texture 2D for Gradient mode

  • Select the Sample Texture 2D node.
  • In the Graph Inspector window, locate Node Settings.
  • Set the Mip Sampling Mode property to Gradient.

Gradient mode uses the explicitly provided DDX and DDY values to select the correct mipmap level, rather than Unity's automatic calculation. Since you've modified the UVs so dramatically for pixelation, the automatic calculation produces incorrect results. Providing explicit gradients fixes this.

6. Connect the gradients

  • Connect the DDX Out output to the Sample Texture 2D DDX input.
  • Connect the DDY Out output to the Sample Texture 2D DDY input.

7. Reconnect the UV

  • Connect the Swizzle Out output to the Multiply A input (replacing the original UV connection).
  • Save your shader with Ctrl+S (macOS: Cmd+S).

The lines should now be eliminated, and the pixel effect will look clean and consistent across different viewing angles and distances.

15. Create prefab and save

Once your Pixel shader is finished, save your work and create a prefab so the OldSign_1 GameObject can be reused easily.

To create a prefab and save your work, follow these instructions:

1. Create a prefab of your object

  • In the Project window, open the Prefabs > _My Objects folder.
  • Drag the OldSign_1 GameObject from the Hierarchy window into the _My Objects folder. This creates a prefab that stores the GameObject, its material, and its components.
  • In the Hierarchy window, select the OldSign_1 GameObject and, in the Inspector window, disable the checkbox in the GameObject header to deactivate it. This helps keep the scene organized.
  • From the main menu, select File > Save.

Important: Saving (Ctrl+S (macOS: Cmd+S)) with the Shader Graph window open only saves the shader. Scene changes must be saved separately.

The prefab is now saved and ready to reuse, and the shader can be applied to any other GameObjects by creating new materials from it.

16. Review your Shader

Take a moment to review the final shader graph created in this tutorial and compare it with your own. Make sure all nodes are properly connected and that all properties are correctly declared.

17. Next steps

Congratulations!

You’ve created a simple pixel shader using UV coordinates, remapping, and mathematical functions. You learned how to use nodes such as Floor, Multiply, and Divide to remap texture coordinates and pixelate the base texture of any GameObject.

In the next tutorial, you’ll learn another way to achieve a similar effect using Render Textures. In Unity, there are often multiple approaches to accomplish the same visual result. In this case, you will use Render Textures to pixelate the entire Game View, creating the appearance of a fullscreen effect.

Complete this Tutorial