Trying to upgrade outline effect to URP 16

I’m trying to convert this to use the RTHandles from the depreciated RenderTargetHandle and get upto to date with the URP version 16. (currently URP 14 and broken)

It also uses the deprecated context.DrawRenderers() to allow an object to be outlined or not based on its Render layer. From reading the latest information this is no longer that straight forward? I’m completely lost…

Any help would be very much appreciated, as a suggestion it would be really nice if there was a simple/fast outline solution in the URP 16 samples that can be downloaded.

Many thanks

9562279–1352239–URP Outline.unitypackage (12.8 KB)

Hi, you need to create a RendererList and use CommandBuffer.DrawRendererList() instead.

For example,

context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);

should change to

RendererListParams rendererListParams = RendererListParams(cullingResults, drawingSettings, filteringSettings);
RendererList rendererList = context.CreateRendererList(ref rendererListParams);
cmd.DrawRendererList(rendererList);

Thank you for you help so far, used your code in the DepthNormalFeature.cs and its no longer rendering black so things are moving in the right direction (fingers crossed!)

When a object is selected it displays its normals texture (I think) and no outline, not sure what is going on really. Here is the updated OutlineFeature.cs so far.

once again any ideas/help would be very much apprenticed.

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class OutlineFeature : ScriptableRendererFeature
{
    class OutlinePass : ScriptableRenderPass
    {
        private RTHandle source { get; set; }
        private RTHandle destination { get; set; }

        public Material outlineMaterial = null;
        RTHandle temporaryColorTexture;

        public void Setup(RTHandle source, RTHandle destination)
        {
            this.source = source;
            this.destination = destination;
        }

        public OutlinePass(Material outlineMaterial)
        {
            this.outlineMaterial = outlineMaterial;
        }


        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("Outline Pass");

            RenderTextureDescriptor opaqueDescriptor = renderingData.cameraData.cameraTargetDescriptor;
            opaqueDescriptor.depthBufferBits = 0;

            Blit(cmd, source, destination, outlineMaterial, 0);

            /* if (destination == source)
             {
                 cmd.GetTemporaryRT(Shader.PropertyToID(temporaryColorTexture.name), opaqueDescriptor, FilterMode.Point);
                 Blit(cmd, source, temporaryColorTexture, outlineMaterial, 0);
                 Blit(cmd, temporaryColorTexture, source);

             }
             else
             {
                  Blit(cmd, source, destination, outlineMaterial, 0);
             }*/

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        /// Cleanup any allocated resources that were created during the execution of this render pass.
        public override void FrameCleanup(CommandBuffer cmd)
        {

           // if (destination == RenderTargetHandle.CameraTarget)
           //   cmd.ReleaseTemporaryRT(Shader.PropertyToID(temporaryColorTexture.name) );
        }
    }

 

  
    OutlinePass outlinePass;
    RTHandle outlineTexture;

    [SerializeField] Material outlineMaterial = null;
    [SerializeField] Color EdgeColour = Color.white;

    [Range(0, 4)]
    [SerializeField] float EdgeThickness = 1;
    [Space]
    [Range(0, 1)]
    [SerializeField] float DepthSensitivity = 0;
    [Range(0, 10)]
    [SerializeField] float NormalsSensitivity = 0;
    [Range(0, 10)]
    [SerializeField] float ColorSensitivity = 0;

    public override void Create()
    {
        if (outlineMaterial == null) return;
        outlineMaterial.SetColor("_edgeColor", EdgeColour);
        outlineMaterial.SetFloat("_outlineThickness", EdgeThickness);
        outlineMaterial.SetFloat("_depthSensitivity", DepthSensitivity);
        outlineMaterial.SetFloat("_colorSensitivity", ColorSensitivity);
        outlineMaterial.SetFloat("_normalsSensitivity", NormalsSensitivity);

        // was  RenderTargetHandle outlineTexture but no init on RTHandle;
        // outlineTexture.Init("_OutlineTexture");

        outlinePass = new OutlinePass(outlineMaterial);      
        outlinePass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
       
    }

    // Here you can inject one or multiple render passes in the renderer.
    // This method is called when setting up the renderer once per-camera.
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (outlineMaterial == null)
        {
            Debug.LogWarningFormat("Missing Outline Material");
            return;
        }


        renderer.EnqueuePass(outlinePass);

    }

    public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    {
       // RTHandle cameraColorTargetHandle = null;
        outlinePass.Setup( renderer.cameraColorTargetHandle, renderer.cameraDepthTargetHandle );
    }

}

I think the effect needs to:

  • Blit from colorHandle to tempHandle with pass 0 of the outline material to read screen pixels and draw outline.
  • Blit from tempHandle to colorHandle to copy back to screen.

The colorHandle is renderingData.cameraData.renderer.cameraColorTargetHandle
The tempHandle is temporaryColorTexture

An example to allocate a RTHandle:

// get a temporary RT
RenderTexture temporaryColorTexture;

cmd.GetTemporaryRT(Shader.PropertyToID(temporaryColorTexture.name), opaqueDescriptor, FilterMode.Point);

// you should change to
RTHandle temporaryColorTexture;

RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point, temporaryColorTexture.name);

Note that the cameraDepthTargetHandle is a depth texture so you probably don’t want to blit to it.
outlinePass.Setup( renderer.cameraColorTargetHandle, renderer.cameraDepthTargetHandle );

Once again thank you for you help so far, so i’ve changed the code to this and it just renders black now?

not sure what i’m doing wrong but if i comment out this => Blit(cmd, temporaryColorTexture, source);
it selects the geometry displays the normals colour so something is going wrong when its getting blitted back?

EDIT :

changed :

RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point)

to

RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point, TextureWrapMode.Repeat, false ,0 , 0 ,temporaryColorTexture.name);

and get : NullReferenceException: Object reference not set to an instance of an object

is the temporaryColorTexture null? how do you create a new one and set its name if this is the case

EDIT : done a little digging and have resolved the NullReferenceException:

   if (temporaryColorTexture == null)
            {
                temporaryColorTexture = RTHandles.Alloc(opaqueDescriptor);
            }

but it still just renders out black?

any ideas an help is very much needed and greatly appreciated.

thankyou

class OutlinePass : ScriptableRenderPass
    {
        private RTHandle source { get; set; }
        private RTHandle destination { get; set; }

        public Material outlineMaterial = null;
        RTHandle temporaryColorTexture;

        public void Setup(RTHandle source)
        {
            this.source = source;
            //this.destination = destination;
        }

        public OutlinePass(Material outlineMaterial)
        {
            this.outlineMaterial = outlineMaterial;
        }




        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("Outline Pass");

            RenderTextureDescriptor opaqueDescriptor = renderingData.cameraData.cameraTargetDescriptor;
            opaqueDescriptor.depthBufferBits = 0;

if (temporaryColorTexture == null)
            {
                temporaryColorTexture = RTHandles.Alloc(opaqueDescriptor);
            }
        
            RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point); //, temporaryColorTexture.name
            Blit(cmd, source, temporaryColorTexture, outlineMaterial, 0);
            Blit(cmd, temporaryColorTexture, source);

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        /// Cleanup any allocated resources that were created during the execution of this render pass.
        public override void FrameCleanup(CommandBuffer cmd)
        {
           // if (destination == RenderTargetHandle.CameraTarget)
           //   cmd.ReleaseTemporaryRT(Shader.PropertyToID(temporaryColorTexture.name) );
        }
    }

 

 
    OutlinePass outlinePass;
    RTHandle outlineTexture;

    [SerializeField] Material outlineMaterial = null;
    [SerializeField] Color EdgeColour = Color.white;

    [Range(0, 4)]
    [SerializeField] float EdgeThickness = 1;
    [Space]
    [Range(0, 1)]
    [SerializeField] float DepthSensitivity = 0;
    [Range(0, 10)]
    [SerializeField] float NormalsSensitivity = 0;
    [Range(0, 10)]
    [SerializeField] float ColorSensitivity = 0;

    public override void Create()
    {
        if (outlineMaterial == null) return;
        outlineMaterial.SetColor("_edgeColor", EdgeColour);
        outlineMaterial.SetFloat("_outlineThickness", EdgeThickness);
        outlineMaterial.SetFloat("_depthSensitivity", DepthSensitivity);
        outlineMaterial.SetFloat("_colorSensitivity", ColorSensitivity);
        outlineMaterial.SetFloat("_normalsSensitivity", NormalsSensitivity);

        // was  RenderTargetHandle outlineTexture but no init on RTHandle;
        // outlineTexture.Init("_OutlineTexture");

        outlinePass = new OutlinePass(outlineMaterial);    
        outlinePass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
     
    }

    // Here you can inject one or multiple render passes in the renderer.
    // This method is called when setting up the renderer once per-camera.
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (outlineMaterial == null)
        {
            Debug.LogWarningFormat("Missing Outline Material");
            return;
        }


        renderer.EnqueuePass(outlinePass);
    }

    public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    {
        // RTHandle cameraColorTargetHandle = null;
        outlinePass.Setup(renderingData.cameraData.renderer.cameraColorTargetHandle);//, renderer.cameraDepthTargetHandle );
    }

Maybe it’s cause by temporaryColorTexture.name because I don’t see there’s a name defined for it.

You can try providing a name of the RTHandle (render texture) directly in the function, such as:
RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point, TextureWrapMode.Repeat, false ,0 , 0 ,"_OutlineColorTexture");

Also, you haven’t release your RTHandle which may cause GPU memory leak. (See this post for details)

In URP 16:

public override void FrameCleanup(CommandBuffer cmd)
{
    cmd.ReleaseTemporaryRT(Shader.PropertyToID(temporaryColorTexture.name) );
}

// change to
public void Dispose()
{
    temporaryColorTexture?.Release();
}

Hey Thanks for you quick reply :slight_smile: have added the Despose()

done a little testing and

 RenderingUtils.ReAllocateIfNeeded(ref temporaryColorTexture, opaqueDescriptor, FilterMode.Point)

does the job of creating the RTHandle and I tested the blit without the material

 Blit(cmd, source, temporaryColorTexture); // , outlineMaterial, 0)
            Blit(cmd, temporaryColorTexture, source);

and it is back to just showing the normal shading

from this it may be possible to conclude that the material is not working in the blit (not sure if it should just be pink?) and I really have no idea how to do this stuff. not sure where to go next really…

this is the shader

EdgeSelective.shader

Shader "EdgeSelective"
{
    Properties
    {
        _depthSensitivity("DepthSensitivity", Range(0.0, 1.0)) = 0.0
        _normalsSensitivity("NormalsSensitivity", Range(0.0, 10.0)) = 0.0
        _colorSensitivity("ColorSensitivity", Range(0.0, 10.0)) = 0.0
        _edgeColor("edgeColor", Color) = (1, 1, 1, 1)
        _outlineThickness("EdgeThickness", Range(0.0, 4.0)) = 1.0
    }
        SubShader
        {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100

            ZTest Always

            Pass
            {
                Name "Unlit"
                ZTest Always
                ZWrite Off
                Cull Off
                HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma shader_feature ALL_EDGES
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
            #include "EdgeSelective.hlsl"

            float _depthSensitivity;
            float _normalsSensitivity;
            float _colorSensitivity;
            float _maskSensitivity;
            float _backfaceSensitivity;
            float _outlineThickness;
            half4 _edgeColor;
            half4 _backfaceEdgeColor;

            struct Attributes
            {
                float4 positionOS       : POSITION;
                float2 uv               : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float2 uv        : TEXCOORD0;
                float4 vertex : SV_POSITION;

                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_TRANSFER_INSTANCE_ID(input, output);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
                output.vertex = vertexInput.positionCS;
                output.uv = input.uv;

                return output;
            }

            half4 frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                float2 uv = input.uv;

                half4 _out;

                Outline_float(uv, _outlineThickness, _depthSensitivity, _normalsSensitivity, _colorSensitivity, _edgeColor, _out);

                return _out;
            }
            ENDHLSL
        }

    }
    FallBack "Hidden/Universal Render Pipeline/FallbackError"
}

and this is the include

EdgeSelective.hlsl

TEXTURE2D(_CameraColorTexture);
SAMPLER(sampler_CameraColorTexture);
float4 _CameraColorTexture_TexelSize;

TEXTURE2D(_CameraDepthNormalsTexture);
SAMPLER(sampler_CameraDepthNormalsTexture);


float3 DecodeNormal(float4 enc)
{
    float kScale = 1.7777;
    float3 nn = enc.xyz*float3(2*kScale,2*kScale,0) + float3(-kScale,-kScale,1);
    float g = 2.0 / dot(nn.xyz,nn.xyz);
    float3 n;
    n.xy = g*nn.xy;
    n.z = g-1;
    return n;
}

inline float DecodeFloatRG(float2 enc)
{
    float2 kDecodeDot = float2(1.0, 1 / 255.0);
    return dot(enc, kDecodeDot);
}

inline float3 DecodeViewNormal(float4 enc4)
{
    float kScale = 1.7777;
    float3 nn = enc4.xyz*float3(2 * kScale, 2 * kScale, 0) + float3(-kScale, -kScale, 1);
    float g = 2.0 / dot(nn.xyz, nn.xyz);
    float3 n;
    n.xy = g * nn.xy;
    n.z = g - 1;
    return n;
}


void DecodeDepthNormal(float4 enc, out float depth, out float3 normal)
{
    depth = DecodeFloatRG(enc.zw);
    normal = DecodeViewNormal(enc);
}

void Outline_float(float2 UV, float OutlineThickness,
float DepthSensitivity, float NormalsSensitivity, float ColorSensitivity, half4 OutlineColor,
out half4 Out)

{
    float halfScaleFloor = floor(OutlineThickness * 0.5);
    float halfScaleCeil = ceil(OutlineThickness * 0.5);
    float2 Texel = (1.0) / float2(_CameraColorTexture_TexelSize.z, _CameraColorTexture_TexelSize.w);

    float2 uvSamples[4];
    float depthSamples[4] ;
    float3 normalSamples[4], colorSamples[4];

    uvSamples[0] = UV - float2(Texel.x, Texel.y) * halfScaleFloor;
    uvSamples[1] = UV + float2(Texel.x, Texel.y) * halfScaleCeil;
    uvSamples[2] = UV + float2(Texel.x * halfScaleCeil, -Texel.y * halfScaleFloor);
    uvSamples[3] = UV + float2(-Texel.x * halfScaleFloor, Texel.y * halfScaleCeil);

    for(int i = 0; i < 4 ; i++)
    {
        float4 samples = SAMPLE_TEXTURE2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture, uvSamples[i]);//
        DecodeDepthNormal(samples, depthSamples[i], normalSamples[i]);//
        colorSamples[i] = SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, uvSamples[i]).xyz;
    }

    // Depth
    float depthFiniteDifference0 = depthSamples[1] - depthSamples[0];
    float depthFiniteDifference1 = depthSamples[3] - depthSamples[2];
    float edgeDepth = sqrt(pow(depthFiniteDifference0, 2) + pow(depthFiniteDifference1, 2)) * 100;
    float depthThreshold = (1/DepthSensitivity) * depthSamples[0];
    edgeDepth = edgeDepth > depthThreshold ? 1 : 0;

    // Normals
    float3 normalFiniteDifference0 = normalSamples[1] - normalSamples[0];
    float3 normalFiniteDifference1 = normalSamples[3] - normalSamples[2];
    float edgeNormal = sqrt(dot(normalFiniteDifference0, normalFiniteDifference0) + dot(normalFiniteDifference1, normalFiniteDifference1));
    edgeNormal = edgeNormal > (1/NormalsSensitivity) ? 1 : 0;

    // Color
    float3 colorFiniteDifference0 = colorSamples[1] - colorSamples[0];
    float3 colorFiniteDifference1 = colorSamples[3] - colorSamples[2];
    float edgeColor = sqrt(dot(colorFiniteDifference0, colorFiniteDifference0) + dot(colorFiniteDifference1, colorFiniteDifference1));
    edgeColor = edgeColor > (1/ColorSensitivity) ? 1 : 0;

    float edge = max(edgeDepth, max(edgeNormal, edgeColor));

    half4 original = SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, uvSamples[0]);  
    Out = ((1 - edge) * original) + (edge * lerp(original, OutlineColor,  OutlineColor.a));
}

here is the depth normal render that is now working just in case some clever dev feels kind enough to try and get it all working

using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
using UnityEngine.Rendering.Universal;

public class DepthNormalsFeature : ScriptableRendererFeature
{
    class DepthNormalsPass : ScriptableRenderPass
    {
      
        Material depthNormalsMaterial = null;
        FilteringSettings m_FilteringSettings;
        readonly int renderTargetId;
        readonly List<ShaderTagId> shaderTagIds = new List<ShaderTagId>();
        RTHandle rtIdentifier = null;
        //RenderTargetIdentifier renderTargetIdentifier;
        // RTHandle rtIdentifier;

        public DepthNormalsPass(RenderQueueRange renderQueueRange, int targetId, int layerMask, Material material)
        {
            renderTargetId = targetId;

            m_FilteringSettings = new FilteringSettings(renderQueueRange, -1, (uint)(1 << layerMask ));
            depthNormalsMaterial = material;
            shaderTagIds.Add(new ShaderTagId("UniversalForward"));
            shaderTagIds.Add(new ShaderTagId("UniversalForwardOnly"));
        }


        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
            descriptor.depthBufferBits = 24;
            descriptor.colorFormat = RenderTextureFormat.ARGB32;

            cmd.GetTemporaryRT(renderTargetId, descriptor, FilterMode.Point);
            // renderTargetIdentifier = new RenderTargetIdentifier(renderTargetId);
            rtIdentifier = null;
            ConfigureTarget(rtIdentifier, renderingData.cameraData.renderer.cameraDepthTargetHandle );
            ConfigureClear(ClearFlag.All, Color.black);
        }


        // Here you can implement the rendering logic.
        // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
        // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
        // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
           
            DrawingSettings drawSettings = CreateDrawingSettings(shaderTagIds, ref renderingData, SortingCriteria.None);
            drawSettings.overrideMaterial = depthNormalsMaterial;
            CommandBuffer cmd = CommandBufferPool.Get();

            using (new ProfilingScope(cmd, new ProfilingSampler("DepthNormals Prepass")))
            {
                RendererListParams rendererListParams = new RendererListParams(renderingData.cullResults, drawSettings,  m_FilteringSettings);
                RendererList rendererList = context.CreateRendererList(ref rendererListParams);
                cmd.DrawRendererList(rendererList);                 
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        /// Cleanup any allocated resources that were created during the execution of this render pass.
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            cmd.ReleaseTemporaryRT(renderTargetId);
        }

        public void Dispose()
        {
            rtIdentifier?.Release();
        }
    }


    DepthNormalsPass depthNormalsPass;
    Material depthNormalsMaterial;

    [Range(0, 5)]
    [SerializeField] int RenderlayerMask;
    public override void Create()
    {
        int renderTargetId = Shader.PropertyToID("_CameraDepthNormalsTexture");
        depthNormalsMaterial = CoreUtils.CreateEngineMaterial("Hidden/Internal-DepthNormalsTexture");
        depthNormalsPass = new DepthNormalsPass(RenderQueueRange.all, renderTargetId, RenderlayerMask, depthNormalsMaterial );
        depthNormalsPass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
        RenderingLayerOutline.ID = RenderlayerMask;
    }

    // Here you can inject one or multiple render passes in the renderer.
    // This method is called when setting up the renderer once per-camera.
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(depthNormalsPass);
    }

  
}

I see the shader requires render textures named “_CameraColorTexture” (might be the “_CameraOpaqueTexture” now) that don’t exist in URP 3D renderer.

Since your effect is executed after transparent rendering, I don’t suggest changing it to “_CameraOpaqueTexture”. You can add a _MainTex in the shader properties and replace all “_CameraColorTexture” with it. The cmd.Blit() automatically sets the source RTHandle to the _MainTex.

// Shader
Properties
{
    _MainTex ("Texture", 2D) = "white" {}
}

// Include file
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
float4 _MainTex_TexelSize;
// and more...

Changed the names from _CameraColorTexture to _MainTex, and it still renders black, will I also need to somehow assign a render texture to the material for it to work? (going to log off for the weekend soon)

Once again thank you so much for all you help an effort and hopefully we are getting closer to sorting this out :slight_smile:
have a good weekend.

Have you tried adjusting the material properties?

You may change the Out = ... to Out = original; for debugging.

My shader understanding is very basic, the shader was written by another forum member so I’m quite out of my depth trying to fix this and would happily pay some one to sort this out. My project relies on a simple fast outline shader for selection high light and this is the third time its broken having moved to URP, hopefully the URP will stabilise now its moved over to RTHandels as its a fantastic render and i’m looking forward to showing some of the work i’ve done with it once the outline is fixed

The URP is moving to Render Graph in Unity 2023.3, which will disable any previous working image effects and need a complete re write.

So URP wont stabilize, it will completly break in next versions unfortunately.

It is going the exact opposite way of a working platform so far, the whole URP thing seems like a bad afterthought.

hmm this is a bit disappointing if its the case, can some one from unity confirm this?

will they have tools that will upgrade the code to the Render Graph?

Link
https://discussions.unity.com/t/930355 page-3

There will not be any cenverterer, will just break the effects and need to rewrite them using a totally different scheme.

Maybe will be not that bad.
I have just imported my crossSection asset to 2023.3.0b1.
The asset contains a dedicated outline and other stencil effects based on ScriptableRenderFeature, ScriptableRenderPass and RenderObject.
All is working in URP 17 the same as it was in URP 16 - just after rewritig a few shaders.

Did you enable the Render Graph ?

In that mode any custom render features are disabled by default.

Now I understood it.
The default is compatibility mode, when creating a new project or opening an old one.
This will give some time until the Render Graph gets established and then you will have to rewrite everything to Render Graph.
9567085--1353496--Screenshot 2024-01-07 143810.png