Unity Version: 2021 LTS

URP Fullscreen Outline Effect - Part Two

Hi! Hopefully you've done part one before you get to this point otherwise it's going to be chaos. As this tutorial is based entirely off of the Roystan Outline Shader tutorial and this part will be more about recreating the aspects of what's written there. I'm not going to be going over the technical detail of how the outline shader works but I will be using the code and give a brief summary of what it achieves and only go into depth when we need to do something that really diverges from the original tutorial. Capeesh? Okay lets start.

Adding Shader Properties

I'll cover this one at the top as the tutorial asks that this be done quite frequently. It is pretty simple to add properties to our Outline shader that we can configure in the ScriptableRenderFeature. I'll explain the process briefly here because I expect if you can follow these tutorials and understand what you're writing you could quite easily figure it out youself. Feel free to skip if that's you!

[Serializable]
public class OutlineSettings
{
    public Shader OutlineShader;
    public Color OutlineColor;
    public int Scale = 1;
    ...

public class OutlineRenderFeature : ScriptableRendererFeature
{
    OutlinePass m_OutlinePass;

    public OutlineSettings Settings;

    Material m_Material;

    public override void Create()
    {
        if (Settings.OutlineShader != null)
            m_Material = new Material(Settings.OutlineShader);

        m_OutlinePass = new OutlinePass(m_Material,
            Settings.OutlineColor,
            Settings.Scale,
            ...

OutlineRenderFeature.cs

class OutlinePass : ScriptableRenderPass
{
    readonly float m_Scale, m_DepthThreshold, m_NormalThreshold, m_DepthNormalThreshold, m_DepthNormalThresholdScale;
    readonly Color m_Color;
    RenderTargetIdentifier m_CameraColorTarget;
    Material m_Material;
    static int colorID = Shader.PropertyToID("_OutlineColor");
    static int scaleID = Shader.PropertyToID("_Scale");
    ...

    public OutlinePass(Material material, Color color, float scale, ...)
    {
        m_Material = material;
        m_Color = color;
        m_Scale = scale;
        ...

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        var camera = renderingData.cameraData.camera;
        if (camera.cameraType != CameraType.Game)
            return;

        if (m_Material == null)
            return;

        CommandBuffer cb = CommandBufferPool.Get(name: "OutlinePass");
        cb.BeginSample("Outline Pass");

        m_Material.SetColor(colorID, m_Color);
        m_Material.SetFloat(scaleID, m_Scale);
        ...  

Sampling Camera Opaque/Depth

We can get the Opaque and Depth textures from the camera by accessing the properties that are built in to the shader. These can be accessed because the shader is called late in the rendering pipeline when these textures already exist.

TEXTURE2D(_CameraOpaqueTexture);
TEXTURE2D(_CameraDepthTexture);

SAMPLER(sampler_CameraOpaqueTexture);
SAMPLER(sampler_CameraDepthTexture);

float4 _CameraOpaqueTexture_TexelSize;

The texel size is important for following the guide on generating the UVs for the edge detection to work.

View Normals

This was probably the trickiest part of making this tutorial work with URP. I didn't want to do what the original author did in the tutorial which is just give a magic file that solves it for you.

In the SRP there is a nice easy way to grab the DepthNormals texture for a camera.

Alpha Blend

Alpha blend isn't a thing in our shader but we can achieve the same effect that the author has in their tutorial by calling the HLSL clip command to tell shader to not render the fragments that don't have any outline. This means that we will 'see though' our shader to the cameras opaque texture achiving the same sort of effect that is in the original tutorial.

Clip either does or doesn't draw a pixel, if you wanted to do an actual alpha blend it wouldn't be too hard just instead of clip you make sure that you render the fragments that have an outline with an alpha value of 1 and then every other fragment with a lower value. Not sure if this would actually look any good but have a go if you like!

Plan

  • Copy pasta copy pasta

  • Normals texture explanation

  • View direction explanation