Neon Sketch Outline Effect in Unity URP

Unity Book Viewer Setup Guide | free unity asset | Unity Book Asset
Unity Book Viewer Setup Guide | free unity asset | Unity Book Asset
August 4, 2026
How to Make NPCs Look at the Player Only When Close (Unity Guide)
How to Make NPCs Look at the Player Only When Close (Unity Guide)
August 11, 2026
Unity Book Viewer Setup Guide | free unity asset | Unity Book Asset
Unity Book Viewer Setup Guide | free unity asset | Unity Book Asset
August 4, 2026
How to Make NPCs Look at the Player Only When Close (Unity Guide)
How to Make NPCs Look at the Player Only When Close (Unity Guide)
August 11, 2026

Neon Sketch Outline Effect in Unity URP

In this tutorial, we’re going to build a colorful Neon Sketch / Edge Detection effect in Unity using URP.

The final result is a dark scene where only the outlines and important surface details of your objects are visible. Every object can have its own neon color, and Bloom turns those lines into glowing strokes.

This effect works especially well for stylized games, horror scenes, dream sequences, sci-fi environments, magical worlds, or simply giving a normal 3D scene a completely different visual identity.

What We’re Building

By the end of this tutorial, you will have:

  • A fully black background
  • Only selected objects visible in the camera
  • Neon outlines around object silhouettes
  • Internal geometry lines and surface details
  • Different neon colors for different objects
  • Bloom-based glow
  • A reusable URP Full Screen Pass setup

The nice part is that this is not just an inverted hull outline. It uses depth and normal edge detection, so it can also reveal internal edges, hard surface details, and changes in the shape of the model.

How It Works

The effect is built from a few simple parts:

  1. We use a dedicated Unity Layer to decide which objects are visible.
  2. The camera only renders that layer, so everything else disappears completely.
  3. Each visible object uses a colored URP/Unlit material.
  4. A custom full-screen shader compares the scene depth and normals to find edges.
  5. The shader reads the object color from the camera color texture.
  6. Bloom adds the neon glow.

This means a pink character can have pink outlines, a green prop can have green outlines, and an orange floor can have orange outlines, all in the same scene.

Step 1: Create a Visible Layer

Create a new Layer called something like:

NeonLayer

Assign this layer only to the objects that should appear in the final effect.

Then select your real rendering camera, usually Main Camera, and set its Culling Mask so it only renders NeonLayer.

Now every object outside that layer is completely removed from the final image. It does not render, write depth, create normals, or produce outlines.

Step 2: Set the Camera Background to Black

On the Main Camera, set:

Environment > Background Type: Solid Color
Background: Black

Your scene should now look almost empty or completely black before the edge effect is added.

Step 3: Give Objects Their Neon Colors

Create separate materials for your objects using:

Universal Render Pipeline/Unlit

For example:

M_Character_Neon  → Pink / Red
M_Lantern_Neon    → Cyan / Blue
M_Ground_Neon     → Orange
M_Props_Neon      → Green

Use Unlit materials so lighting and shadows do not change the colors. The material color becomes the source color for the neon outline shader.

Step 4: Enable Depth and Opaque Textures

In your URP settings, make sure these options are enabled:

Depth Texture: On
Opaque Texture: On

The depth texture helps the shader detect shape changes and object silhouettes.

The opaque color texture lets the shader read the color of each object, which is why every object can have a different neon outline color.

Step 5: Add a Full Screen Pass Renderer Feature

Open the active URP Renderer Data asset used by your project. (Common names: ForwardRenderer, URP_Renderer, UniversalRendererData.)

In the Inspector, click:

Add Renderer Feature
→ Full Screen Pass Renderer Feature

Rename it to something clear, such as:

Neon Edge Detection

This feature runs the edge detection shader across the entire camera image.

Step 5.1: Create a Material for the Edge Shader

After creating the Neon Edge Detection shader, you need to create a material that uses it.

In the Project window:

Right-click → Create → Material

Name it something like:

M_NeonEdge

Then, with the material selected, set its Shader to:

Custom/Neon Edge Detection

This material contains the settings for the full-screen effect, such as line thickness, edge sensitivity, and neon intensity.

Important: Do not assign M_NeonEdge to any 3D object in your scene. This is a full-screen effect material, not an object material.

Instead, assign it here:

URP Renderer Data
→ Full Screen Pass Renderer Feature
→ Pass Material
→ M_NeonEdge

Then use these Full Screen Pass settings:

Injection Point: Before Rendering Post Processing

Requirements:
- Depth
- Normal
- Color

Color is important because it lets the shader read the color of every rendered object and use that color for its neon outline.

Your actual 3D objects should still use separate colored materials, preferably:

Universal Render Pipeline/Unlit

Example:

Character → Pink Unlit Material
Lantern → Cyan Unlit Material
Ground → Orange Unlit Material
Props → Green Unlit Material

The edge shader reads those object colors from the camera color texture, detects the edges using Depth and Normals, and then draws the glowing neon lines in the correct color.

Step 5.2: Neon Edge Detection Shader (Full Code)

Paste this into a new shader file, for example:

Assets/Shaders/NeonEdgeDetection.shader

Shader code:

Shader "Custom/Neon Edge Detection"
{
    Properties
    {
        _DepthSensitivity ("Depth Sensitivity", Range(0.001, 2)) = 0.08
        _NormalSensitivity ("Normal Sensitivity", Range(0.001, 1)) = 0.35
        _Thickness ("Line Thickness", Range(0.5, 5)) = 1.2
        _Intensity ("Neon Intensity", Range(0, 20)) = 5
    }

    SubShader
    {
        Tags { "RenderPipeline" = "UniversalPipeline" }

        ZWrite Off
        ZTest Always
        Cull Off

        Pass
        {
            Name "NeonEdgeDetection"

            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl"
            #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"

            struct FSAttributes
            {
                uint vertexID : SV_VertexID;
            };

            struct FSVaryings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            float _DepthSensitivity;
            float _NormalSensitivity;
            float _Thickness;
            float _Intensity;

            FSVaryings Vert(FSAttributes input)
            {
                FSVaryings output;
                output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
                output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
                return output;
            }

            float IsBackgroundDepth(float rawDepth)
            {
                #if UNITY_REVERSED_Z
                    return step(rawDepth, 0.0001);
                #else
                    return step(0.9999, rawDepth);
                #endif
            }

            float GetEdgeAtOffset(float2 uv, float2 offset)
            {
                float centerRawDepth = SampleSceneDepth(uv);
                float sampleRawDepth = SampleSceneDepth(uv + offset);

                float centerIsBackground = IsBackgroundDepth(centerRawDepth);
                float sampleIsBackground = IsBackgroundDepth(sampleRawDepth);

                if (centerIsBackground > 0.5)
                    return 0.0;

                float centerDepth = LinearEyeDepth(centerRawDepth, _ZBufferParams);
                float sampleDepth = LinearEyeDepth(sampleRawDepth, _ZBufferParams);

                float depthDifference = abs(centerDepth - sampleDepth);
                float depthThreshold = _DepthSensitivity * max(centerDepth, 0.001);
                float depthEdge = step(depthThreshold, depthDifference);

                float normalEdge = 0.0;

                if (sampleIsBackground < 0.5)
                {
                    float3 centerNormal = SampleSceneNormals(uv);
                    float3 sampleNormal = SampleSceneNormals(uv + offset);

                    float normalDifference = 1.0 - saturate(dot(centerNormal, sampleNormal));
                    normalEdge = step(_NormalSensitivity, normalDifference);
                }

                return saturate(depthEdge + normalEdge);
            }

            float3 GetObjectColor(float2 uv, float2 offsetX, float2 offsetY)
            {
                float3 c0 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv).rgb;
                float3 c1 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + offsetX).rgb;
                float3 c2 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - offsetX).rgb;
                float3 c3 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv + offsetY).rgb;
                float3 c4 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv - offsetY).rgb;

                float l0 = dot(c0, float3(0.2126, 0.7152, 0.0722));
                float l1 = dot(c1, float3(0.2126, 0.7152, 0.0722));
                float l2 = dot(c2, float3(0.2126, 0.7152, 0.0722));
                float l3 = dot(c3, float3(0.2126, 0.7152, 0.0722));
                float l4 = dot(c4, float3(0.2126, 0.7152, 0.0722));

                float3 best = c0;
                float bestL = l0;

                if (l1 > bestL) { best = c1; bestL = l1; }
                if (l2 > bestL) { best = c2; bestL = l2; }
                if (l3 > bestL) { best = c3; bestL = l3; }
                if (l4 > bestL) { best = c4; }

                return best;
            }

            float4 Frag(FSVaryings input) : SV_Target
            {
                float2 pixelSize = 1.0 / _ScreenParams.xy;
                float2 offsetX = float2(pixelSize.x * _Thickness, 0.0);
                float2 offsetY = float2(0.0, pixelSize.y * _Thickness);

                float edge = 0.0;
                edge += GetEdgeAtOffset(input.uv,  offsetX);
                edge += GetEdgeAtOffset(input.uv, -offsetX);
                edge += GetEdgeAtOffset(input.uv,  offsetY);
                edge += GetEdgeAtOffset(input.uv, -offsetY);
                edge = saturate(edge);

                float3 objectColor = GetObjectColor(input.uv, offsetX, offsetY);

                float3 neonColor = objectColor * edge * _Intensity;
                return float4(neonColor, 1.0);
            }
            ENDHLSL
        }
    }
}

Because the Full Screen Pass runs before post-processing, Bloom can see the bright neon colors and create the glow effect.

Recommended Shader Values

These values are a good balanced starting point:

Neon Intensity: 4 to 7
Line Thickness: 0.9 to 1.2
Depth Sensitivity: 0.08
Normal Sensitivity: 0.45 to 0.65

If the image has too many noisy internal lines, increase Normal Sensitivity.

If the outlines are too thin, increase Line Thickness.

If the glow is too strong, reduce Bloom Intensity or Neon Intensity.

Important Setup Rules

Keep these rules in mind:

  • Use colored URP/Unlit materials on your visible 3D objects.
  • Put visible objects on NeonLayer.
  • Let the Main Camera render only that layer.
  • Keep M_NeonEdge assigned only to the Full Screen Pass feature.
  • Set the camera background to pure black.
  • Enable HDR and Post Processing for Bloom.

Final Result

You now have a flexible neon sketch effect that can turn a normal 3D Unity scene into a colorful glowing line-art world.

You can use it as a full visual style, a special gameplay mode, a horror “vision mode”, a scanning mode, an enemy highlight system, or a transition effect between normal and surreal environments.

Because the color comes from each object’s material, the effect is easy to expand. Just give a new object a new Unlit color material, place it on NeonLayer, and it automatically gets its own glowing outline.

 

Here is the video about this lesson:

Leave a Reply

Your email address will not be published. Required fields are marked *


Skip to toolbar