ニット調画面

環境

Unity2022.2.14f1

概要

画面をニット調にするテストです。

コード

using UnityEngine;
using UnityEngine.Rendering;

public class Knit : MonoBehaviour
{
    [SerializeField] private Material _material = null;
    [SerializeField] private int _tileSize = 16;

    private void Start()
    {
        var camera = GetComponent<Camera>();
        var tileNum = new Vector2Int(camera.pixelWidth / _tileSize, camera.pixelHeight / _tileSize);

        var renderTex = new RenderTexture(tileNum.x, tileNum.y, 24, RenderTextureFormat.Default);
#if true
        renderTex.filterMode = FilterMode.Point;
#endif
        var rts = new RenderBuffer[]
        {
            renderTex.colorBuffer,
        };
        camera.SetTargetBuffers(rts, renderTex.depthBuffer);
        _material.SetVector("_TileNum", new Vector2(tileNum.x / 2, tileNum.y));

        var commandBuffer = new CommandBuffer();
        commandBuffer.name = "BlitRenderTexture";
        commandBuffer.SetRenderTarget(-1);
        commandBuffer.Blit(renderTex, BuiltinRenderTextureType.CurrentActive, _material);
        camera.AddCommandBuffer(CameraEvent.AfterEverything, commandBuffer);
    }
}
Shader "Hidden/Knit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _ColorBias("Color Bias", Range(0.0, 1)) = 0.0
        _ColorScale("Color Scale", Range(0.5, 3)) = 1.5
        _SeamBias("Seam Bias", Range(0.0, 0.5)) = 0.2
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float2 _TileNum;
            float _ColorBias;
            float _ColorScale;
            float _SeamBias;

            float Seam(float2 uv)
            {
                uv.y = (1.0 - uv.y) * 0.5 + 0.5;
                float sb = length(uv - float2(0.5, 1.0));
                float sl = length(uv - float2(0.0, 0.5));
                float sr = length(uv - float2(1.0, 0.5));
                sl = max(sb, sl);
                sr = max(sb, sr);
                float s = min(sl, sr);
                return smoothstep(s - _SeamBias, s + _SeamBias, 0.5);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 tile = i.uv * _TileNum;
                float2 uvInTile = frac(tile);
                fixed4 col = tex2D(_MainTex, i.uv);

                float mask = Seam(uvInTile);
                col.rgb = (col.rgb + _ColorBias.xxx) * _ColorScale * mask;
                return col;
            }
            ENDCG
        }
    }
}

画像を歪ませる版

Shader "Hidden/Knit2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _ColorBias("Color Bias", Range(0.0, 1)) = 0.0
        _ColorScale("Color Scale", Range(0.5, 3)) = 1.5
        _SeamBias("Seam Bias", Range(0.0, 0.5)) = 0.2
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float2 _TileNum;
            float _ColorBias;
            float _ColorScale;
            float _SeamBias;

            float Seam(float2 uv)
            {
                //y = 0.0 ~ 1.0 -> 1.0 ~ 0.5
                uv.y = (1.0 - uv.y) * 0.5 + 0.5;
                const float scale = 0.85;
                float sb = length(uv - float2(0.5, 1.0)) * scale;
                float sl = length(uv - float2(0.0, 0.5)) * scale;
                float sr = length(uv - float2(1.0, 0.5)) * scale;
                sl = max(sb, sl);
                sr = max(sb, sr);
                float s = min(sl, sr);
                return smoothstep(s - _SeamBias, s + _SeamBias, 0.5);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 tile = i.uv * _TileNum;
                float2 uvInTile = frac(tile);

                tile.y += abs(uvInTile.x - 0.5);
                float2 uv = tile / _TileNum;
                fixed4 col = tex2D(_MainTex, uv);

                tile = uv * _TileNum;
                uvInTile = frac(tile);

                float mask = Seam(uvInTile);
                col.rgb = (col.rgb + _ColorBias.xxx) * _ColorScale * mask;
                return col;
            }
            ENDCG
        }
    }
}

RenderTextureを使わず、ImageEffectでやる場合

Shader "Hidden/ImageEffectKnit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _TileSize("Tile Size", int) = 16
        _ColorBias("Color Bias", Range(0.0, 1)) = 0.0
        _ColorScale("Color Scale", Range(0.5, 3)) = 1.5
        _SeamBias("Seam Bias", Range(0.0, 0.5)) = 0.2
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            uint _TileSize;
            float _ColorBias;
            float _ColorScale;
            float _SeamBias;

            float Seam(float2 uv)
            {
                uv.y = (1.0 - uv.y) * 0.5 + 0.5;
                float sb = length(uv - float2(0.5, 1.0));
                float sl = length(uv - float2(0.0, 0.5));
                float sr = length(uv - float2(1.0, 0.5));
                sl = max(sb, sl);
                sr = max(sb, sr);
                float s = min(sl, sr);
                return smoothstep(s - _SeamBias, s + _SeamBias, 0.5);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                uint2 tileNum = uint2(_MainTex_TexelSize.z / (_TileSize * 2), _MainTex_TexelSize.w / _TileSize);
                float2 tile = i.uv * tileNum;
                float2 uvInTile = frac(tile);

                float2 uv = (floor(tile) + 0.5) / tileNum;
                fixed4 col = tex2D(_MainTex, uv);

                float mask = Seam(uvInTile);
                col.rgb = (col.rgb + _ColorBias.xxx) * _ColorScale * mask;
                return col;
            }
            ENDCG
        }
    }
}

感想

renderTex.filterMode = FilterMode.Point;

コメントアウトしてバイリニア補間にしたほうが色が漏れてそれっぽく見えました。

参考

Shader - Shadertoy BETA