Recreating a fullscreen shader with shader graph

I used to use a very simple screen shader effect that worked in the built-in renderer to change palettes; the idea is taken from this Youtube Video. The tl;dr is you take the red channel value from the source image, multiply that by 3 to get an index into one of four colors stored in a 4x4 matrix. Effectively, turning lightness values in your source image to a specific color in a pre-defined palette.

I’d like to keep using this but migrating it to URP has proved a bit confusing (there’s a bit of a learning curve to shader graph). I’ve tried following some instructions for setting up a “fullscreen shader” in shader graph, added as a “renderer feature” to my URP Renderer2DData. You can see in the image below I’ve tried to get the red channel value out of the incoming screen, analogous to tex2D(_MainTex, i.uv).r; and then I’ve tried to multiply that by 3 to get my array index. I’ve also added the ColorMatrix property, but where I’m stuck is how to do the ColorMatrix array access step and then pipe that to the fragment.

Palette Script

private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
    material.SetMatrix("_ColorMatrix", ColorMatrix);
    Graphics.Blit(source, destination, material);
}

private Matrix4x4 ColorMatrix
{
    get
    {
        Matrix4x4 mat = new Matrix4x4();
        mat.SetRow(0, ColorToVec(activeColor0));
        mat.SetRow(1, ColorToVec(activeColor1));
        mat.SetRow(2, ColorToVec(activeColor2));
        mat.SetRow(3, ColorToVec(activeColor3));

        return mat;
    }
}

private Vector4 ColorToVec(Color color)
{
    return new Vector4(color.r, color.g, color.b, color.a);
}

Shader (excerpt)

sampler2D _MainTex;
half4x4 _ColorMatrix;

fixed4 frag (v2f i) : SV_Target
{
	fixed x = tex2D(_MainTex, i.uv).r;
	return _ColorMatrix[x * 3];
}

The first thing I see that might help is that you don’t need the Sample Texture 2D node in your graph. The URP Sample Buffer node is already doing the sampling. So you should be able to just take the red channel of the URP Sample Buffer node (using the Split node) and multiply that by 3.

Thanks for pointing that out. For anyone curious/coming across this thread later, I got it working in the end. Had to use a Custom Function node to do the matrix/array index access. I also replaced the Matrix4x4 property with four separate color vectors so that I could set some initial values (seems that Unity doesn’t serialize Matrix4x4 which means no defaults…)

I also had to change my project from Linear to Gamma color space, because I wasn’t getting the right colors. I’m not entirely sure about this and I did try to use a Colorspace Conversion node, but the results weren’t exactly right with that either.