ハーフトーン - saitocastel1900/UnityShader GitHub Wiki

  • 影を点々にして、アニメ調演出をするときに使われるやつ
  1. まずは点々を適当に描画してみる
  • 今回は、スクリーン座標をもとに点々を書いていくので、スクリーン座標を求めていますが、このような処理が必要です「float2 screenPos = i.screenPos.xy / i..w;」。ん?すでにスクリーン座標を求めていない?「o.screenPos = ComputeScreenPos(o.vertex);」と思ったのですが、この処理だけではダメなようで(このままだとクリッピング座標)、先ほど提示した通りwで除算することでスクリーン座標が求まります。また、クリッピング座標の範囲は(0.0~w)のようですが、スクリーン座標は、0.0から1.0なので色々直す必要がある.これを物体からの距離が入っているwで除算することで、物体とカメラの距離(視点)に基づいて0.0から1.0に直すことができるみたい
    詳しくはこちらを:https://light11.hatenadiary.com/entry/2018/06/10/233954

  • あとはアスペクト比から、点を書き込むマス目の中心を求める。「floor(screenPos.x / cellSize.x) * cellSize.x 」で基準となる点を求めてから、「+ cellSize.x / 2」で相対的なマスの中間地点を足すことで求める
    F31ED573-EB03-4CAF-B5E0-5FCD05A5B1C6_1_201_a

  • そして、求めた中点から、塗るピクセルとの距離を求めて、その距離に応じて点々を描画する範囲かを判定して、塗るべきだったる塗るといった感じ。コードとしては「 float2 diff = (screenPos - cellCenter) / cellSize;」で、距離を矩形の大きさで割ることで矩形のサイズ単位(相対的)にしている模様

Shader "Unlit/HalftoneUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _HalftoneScale("HlaftoneScale",Range(0.001,0.1))=0.02
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"

        }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _HalftoneScale;

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

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

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

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                //これでスクリーン座標を求めているみたい
                float2 screenPos = i.screenPos.xy / i.screenPos.w;

                //アスペクト比
                float aspect = _ScreenParams.x / _ScreenParams.y;

                float2 cellSize = float2(_HalftoneScale,_HalftoneScale * aspect);

                float2 cellCenter;
                cellCenter.x = floor(screenPos.x/cellSize.x) * cellSize.x + cellSize.x / 2;
                cellCenter.y = floor(screenPos.y / cellSize.y) * cellSize.y + cellSize.y / 2;

                float2 diff = screenPos - cellCenter;
                diff.x /= cellSize.x;
                diff.y /= cellSize.y;

                float threshold = 0.3;
                col.rgb *= lerp(1,0.5,step(length(diff),threshold));
                 
                return col;
            }
            ENDCG
        }
    }
}
  1. 影と同じように書き込みたいので、光のベクトルと物体の法線から内積をとって、影かを判定して、それをもとに書き込む。コードとしてはこんな感じ「float3 normal = normalize(i.normal); float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

             float threshold = 1 - dot(normal,lightDir);
             float col =   lerp(1,_ShaderColor,step(length(diff),threshold));」  
    
Shader "Unlit/HalftoneUnlitShader"
{
    Properties
    {
        _HalftoneScale("HlaftoneScale",Range(0.001,0.1))=0.02
        _ShaderColor("ShaderColor",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"

        }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            
            float _HalftoneScale;
            float3 _ShaderColor;

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 screenPos: TEXCOORD1;
                float3 normal : NORMAL;
            };

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

            fixed4 frag(v2f i) : SV_Target
            {
                //これでスクリーン座標を求めているみたい
                float2 screenPos = i.screenPos.xy / i.screenPos.w;

                //アスペクト比
                float aspect = _ScreenParams.x / _ScreenParams.y;

                //マス目の大きさ(矩形)
                float2 cellSize = float2(_HalftoneScale,_HalftoneScale * aspect);

                float2 cellCenter;
                //スクリーン座標をセルの大きさで除算することで、セルの中心点をスクリーン座標基準ではなく、セル基準で考えている
                cellCenter.x = floor(screenPos.x / cellSize.x) * cellSize.x + cellSize.x / 2;
                cellCenter.y = floor(screenPos.y / cellSize.y) * cellSize.y + cellSize.y / 2;

                float2 diff = (screenPos - cellCenter) / cellSize;

                float3 normal = normalize(i.normal);
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                float threshold = 1 - dot(normal,lightDir);
                float col =   lerp(1,_ShaderColor,step(length(diff),threshold));

                return fixed4(col,col,col,1);
            }
            ENDCG
        }
    }
}