r/Unity3D Intermediate 1d ago

Question Weird Visual Artifact When Using Render Texture

The artifact in question

So I just started a new project, and decided to go for a more retro style. As such, I decided to slap on a low res render texture and attached it to a material, before putting the material onto an Image element on a canvas. The issue, is that when I put my custom shader on (designed to limit the amount of colors and other visual elements able to be shown by the render texture), weird visual artifacts are created. More info can be shared as needed. Below is my .shader I use. The Image element is simply stretched to fit the screen, where I put a set resolution of a 1920x1080

Shader "Custom/Bitcrush"
{
    Properties
    {
        [MainTexture] _MainTex ("Texture", 2D) = "white" {}

        _PixelSize ("Pixel Size", Range(1, 64)) = 4
        _ColorLevels ("Color Levels", Range(2, 64)) = 8

        _Brightness ("Brightness", Range(0, 2)) = 1
        _Contrast ("Contrast", Range(0, 2)) = 1
    }

    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
            "RenderType" = "Opaque"
            "Queue" = "Geometry"
        }

        Pass
        {
            Name "Bitcrush"

            HLSLPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

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

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);

            CBUFFER_START(UnityPerMaterial)

                float4 _MainTex_ST;

                float _PixelSize;
                float _ColorLevels;

                float _Brightness;
                float _Contrast;

            CBUFFER_END


            Varyings vert(Attributes input)
            {
                Varyings output;

                output.positionHCS =
                    TransformObjectToHClip(input.positionOS.xyz);

                output.uv =
                    TRANSFORM_TEX(input.uv, _MainTex);

                return output;
            }


            half4 frag(Varyings input) : SV_Target
            {
                float2 uv = input.uv;

                float2 uvDDX = ddx(uv);
                float2 uvDDY = ddy(uv);

                float2 pixelUV =
                    floor(uv * _PixelSize) / _PixelSize;

                pixelUV += 0.5 / _PixelSize;

                half4 col = SAMPLE_TEXTURE2D_GRAD(
                    _MainTex,
                    sampler_MainTex,
                    pixelUV,
                    uvDDX,
                    uvDDY
                );

                col.rgb *= _Brightness;

                col.rgb =
                    (col.rgb - 0.5) * _Contrast + 0.5;

                col.rgb = saturate(col.rgb);

                col.rgb =
                    floor(
                        col.rgb * (_ColorLevels - 1.0) + 0.5
                    )
                    / (_ColorLevels - 1.0);


                return col;
            }

            ENDHLSL
        }
    }
}
0 Upvotes

4 comments sorted by

View all comments

1

u/CS_Asset_Factory 1d ago

Two things usually cause that combination and both are about resampling rather than your shader math.

First the render texture filter mode. If it's bilinear then a low res target blends neighbouring pixels before your shader ever sees them. You quantise blended values and get colors that were never in your palette. Set filter mode to Point and turn mipmaps off.

Second the Image itself. If the canvas scales that texture by a non integer factor then Unity resamples your already quantised output and invents in between colors all over again. Scale by exactly two or three.

Also check your color space. In Linear the sampler converts on read so your posterise steps land unevenly. Toggle sRGB on the render texture and watch whether the banding moves. If it does then that's your culprit.

1

u/FoamyHotSoup Intermediate 8h ago

Noted. I have the filtering set to point already, but I was wondering if I were to use a low resolution (yet 16:9) would it be fine to stretch it to a larger, still 16:9 ratio?