切り絵が趣味の僕がプログラムや創作物について書き溜めるブログ

絵やモデリングやプログラミングなど僕が作った物について。アニメの感想や心動かされたきっかけなどを書き溜めるブログです。Boothで3Dアバターの販売もしていますhttps://paw.booth.pm/

【4日目】あっという間に完成、時間経過で色が変化する!?(シェーダー入門)

こんぱう~~~


今回は時間経過で色が変化していくテクスチャを作っていきます。
↓完成するとこんな感じになります↓

youtu.be

paw.booth.pm

(Boothで販売中です)

今回作るシェーダーの目的

 元のテクスチャの彩度、明度は変更しないで色相のみ時間経過で変化させる!!


では、さっそく作っていこう!!

 ・用意するもの

・夜空のテクスチャ(別になんでもよい)

・Unity

・Unityの起動と下準備 

f:id:kiriesto:20210717153015p:plain

さっそく下の四つをそろえていきましょう
(三日目で準備したものと画像以外は同じですね)

・色相を変えたい画像(私は夜空にした)

・Unlit Shader (gameColorという名前にした)

・3DオブジェクトのPlane

・マテリアル

 画像を数枚(フォルダーからマウスでピッてやれば入る)、
Unlit Shader(右クリックCreate>Shader>Unlit Shaderで作成できる)、
3DオブジェクトのPlane(Hierarchyで右クリック>3D Object>Plane)とマテリアル(右クリック>Create>Material )も作成しておきましょう。

Projectのmaterialをドラッグ&ドロップ(マウスでピッてやる)でHierachyのPlaneに入れるとマテリアルを適用できます。

materialのshaderを作ったUnlit Shaderにしておきましょう。


・色について

まずはプログラミングする前に、色について少し知っておきましょう。

色の表し方には大まかに二つ存在します。RGBとHSVです。

  • RGB
    RGBはそれぞれR(赤)、G(緑)、B(青)を指しており、「光の三原色」と呼ばれています。だいたいのペイントソフトだとRGBの値をそれぞれ0~255であらわされており、この数値の違いによってさまざまな色を作っています。

    例えば、黒だと R=0, G=0, B=0つまり RGB=(0, 0, 0)のときになります。

    f:id:kiriesto:20210717155207p:plain

    例えば、白だと R=255, G=255, B=255つまり RGB=(255, 255, 255)のときなので

    f:id:kiriesto:20210717155305p:plain

    赤だとRGB=(255,  0,  0)

    f:id:kiriesto:20210717155345p:plain

    緑はRGB=(0,  255,  0)

    f:id:kiriesto:20210717155440p:plain

    青はRGB=(0,  0,  255)
    f:id:kiriesto:20210717155447p:plain
  • HSV
    HSVはH(色相)、S(彩度)、V(明度)の三つの成分から色を表しています。

    f:id:kiriesto:20210717155844p:plain

    ペイントソフトだとこんな感じにあらわされて、
    ・Hは色相で0~360までの値をとって、値の大きさによって赤になったり青になったりします。

    ・Sは彩度と言って色をどれだけ強く入れるかを表しています。0~100までの値をとって表されます。この画像の場合、Sを100にすると真っ赤になるわけです。

    ・Vは明度で黒っぽくするか白っぽくするかみたいなのを調整できます。これも0~100までの値をとって表されます。0だと黒、100だと白になります。

・Unityでの色の扱い方

Unityで色を扱う場合、fixed4型に変換して使います。
では、fixed4型とはどうゆうことなのか?

fixed4型はRGBαに対応する値を格納する型になります。とる値の範囲は0~1となっており、先ほど説明したRGBの範囲0~255を255で割った値をUnityでは取り扱っています。最後のαは色の透明度を表しており、0だと透明、1だと不透明になります。

今回はαの値についてはいじらないので常に1にしてあります。

・なぜ、RGBとHSVの話をしたのか

今回の目的は色味を変えることが目的なのでRGBで色味を変えようと思うとR、G、Bとそれぞれの値をいい感じに変化させないといけなくなり、同時に3つの値をどのように変化させるかを考えないといけません。それだと大変なのでRGBではなくHSVにすることで、Hの色相だけを変化させることで、値を1つだけ変更するだけで色味を変えられるのでとっても楽になります。
なので、RGBとHSVについてお話しました。
次から、Shaderを書いていきましょう!!

 ・Shaderを書く

f:id:kiriesto:20210618202511p:plain

右クリックして、Show in Explorerを押してコードがあるファイルに行きます。
その後、テキストエディターを開きましょう!


開いたら、ブラウザでgoogleを開いてもらって”unity rgb hsv 変換”とかで調べるとよさげなコードが落ちていました

 参考にさせていただいたブログになります。

techblog.kayac.com

「RGBからHSVに変換する処理」と「HSVをRGBに変換する処理」が書いてあったので少し書き換えて使わせていただきます。

 

使い方は、
 夜空の画像(RGB)
    ↓ 「RGBからHSVに変換」
 夜空の画像(HSV
    ↓
 色相にタイム(frac(T))を加算(色の変化のため)
    ↓ 「HSVからRGBに変換」
 色が変わった夜空の画像(RGB)
    ↓
  完成!!

という感じになります。

ではどんな風に書いているのか詳しく見ていきましょう。
(記事の一番最後に完成したシェーダーを置いておきます)

f:id:kiriesto:20210717192711p:plain
140行目 時間を取得(10で割って色の変わる速度を調節しています)
143行目 メイン画像を取得(夜空の画像)
145行目 関数rgb2hsv()を使ってRGBをHSVに変換
146行目 col.rがH(色相)になっているのでそれにTを足して色相を変化させています
147行目 関数hsv2rgb()を使ってHSVをRGBにもどして、描画させます

以上、関数rgb2hsv()と関数hsv2rgb()の中身はほぼコピペなので説明できませんが、他に何かわからないことがあればコメントいただけたらと思います!!

今日も最後まで読んでくださりありがとうございました(_  _)

それではいい夢を~~~
  前回の記事

kiriesto.hatenablog.com

サンプルコード

Shader "Unlit/gameColor"
{
    Properties
    {
        _MainTex ("Texture"2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

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


        // RGB->HSV変換
        float4 rgb2hsv(float4 rgb)
        {
            float3 hsv;

            // RGBの三つの値で最大のもの
            float maxValue = max(rgb.r, max(rgb.g, rgb.b));
            // RGBの三つの値で最小のもの
            float minValue = min(rgb.r, min(rgb.g, rgb.b));
            // 最大値と最小値の差
            float delta = maxValue - minValue;
            
            // V(明度)
            // 一番強い色をV値にする
            hsv.z = maxValue;
            
            // S(彩度)
            // 最大値と最小値の差を正規化して求める
            if (maxValue != 0.0){
                hsv.y = delta / maxValue;
            } else {
                hsv.y = 0.0;
            }
            
            // H(色相)
            // RGBのうち最大値と最小値の差から求める
            if (hsv.y > 0.0){
                if (rgb.r == maxValue) {
                    hsv.x = (rgb.g - rgb.b) / delta;
                } else if (rgb.g == maxValue) {
                    hsv.x = 2 + (rgb.b - rgb.r) / delta;
                } else {
                    hsv.x = 4 + (rgb.r - rgb.g) / delta;
                }
                hsv.x /= 6.0;
                if (hsv.x < 0)
                {
                    hsv.x += 1.0;
                }
            }
            
            return float4 (hsv, rgb.a);
        }
        
        // HSV->RGB変換
        float3 hsv2rgb(float3 hsv)
        {
            float3 rgb;

            if (hsv.y == 0){
                // S(彩度)が0と等しいならば無色もしくは灰色
                rgb.r = rgb.g = rgb.b = hsv.z;
            } else {
                // 色環のH(色相)の位置とS(彩度)、V(明度)からRGB値を算出する
                hsv.x *= 6.0;
                float i = floor (hsv.x);
                float f = hsv.x - i;
                float aa = hsv.z * (1 - hsv.y);
                float bb = hsv.z * (1 - (hsv.y * f));
                float cc = hsv.z * (1 - (hsv.y * (1 - f)));
                if( i < 1 ) {
                    rgb.r = hsv.z;
                    rgb.g = cc;
                    rgb.b = aa;
                } else if( i < 2 ) {
                    rgb.r = bb;
                    rgb.g = hsv.z;
                    rgb.b = aa;
                } else if( i < 3 ) {
                    rgb.r = aa;
                    rgb.g = hsv.z;
                    rgb.b = cc;
                } else if( i < 4 ) {
                    rgb.r = aa;
                    rgb.g = bb;
                    rgb.b = hsv.z;
                } else if( i < 5 ) {
                    rgb.r = cc;
                    rgb.g = aa;
                    rgb.b = hsv.z;
                } else {
                    rgb.r = hsv.z;
                    rgb.g = aa;
                    rgb.b = bb;
                }
            }
            return rgb;
        }
        

            fixed4 frag (v2f i) : SV_Target
            {
                float T = _Time.y/10;
                

                fixed4 col = tex2D(_MainTex, i.uv);
                
                col = rgb2hsv( col );
                col.r = frac(col.r + T);
                col.rgb = hsv2rgb(col.rgb);

                // sample the texture
                
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}