🌈

そろそろShaderをやるパート5 UVを回転させる

2020/10/17に公開

そろそろShaderをやります

そろそろShaderをやります。そろそろShaderをやりたいからです。
パート100までダラダラ頑張ります。10年かかってもいいのでやります。
100記事分くらい学べば私レベルの初心者でもまあまあ理解できるかなと思っています。

という感じでやってます。

※初心者がメモレベルで記録するので
 技術記事としてはお力になれないかもしれません。

下準備

下記参考
そろそろShaderをやるパート1 Unite 2017の動画を見る(基礎知識~フラグメントシェーダーで色を変える)

デモ

回転してますね~

Shaderサンプル


Shader "Custom/Rotation"
{
    //Inspectorに出すプロパティー
    Properties
    {
        //テクスチャー(オフセット、タイリングの設定なし)
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
        //回転の速度
        _RotateSpeed ("Rotate Speed", float) = 1.0
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            //頂点シェーダーに渡ってくる頂点データ
            struct appdata
            {
                //コロン以降の大文字はセマンティクスと呼ばれる 
                //この変数は ○○を受け取ります みたいなやつらしい
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0; //1番目のUV座標 という意味らしい 
            };

            //フラグメントシェーダーへ渡すデータ
            struct v2f
            {
                float2 uv : TEXCOORD0; //テクスチャUV
                float4 vertex : SV_POSITION; //座標変換された後の頂点座標
            };

            sampler2D _MainTex;
            float _RotateSpeed;

            //頂点シェーダー
            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex); //3D空間座標→スクリーン座標変換
                o.uv = v.uv; //受け取ったUV座標をフラグメントシェーダーで使うので代入しとく
                return o;
            }

            //フラグメントシェーダー
            fixed4 frag(v2f i) : SV_Target
            {
                // Timeを入力として現在の回転角度を作る
                half timer = _Time.x;
                // 回転行列を作る
                half angleCos = cos(timer * _RotateSpeed);
                half angleSin = sin(timer * _RotateSpeed);
                /*       |cosΘ -sinΘ|
                  R(Θ) = |sinΘ  cosΘ|  2次元回転行列の公式*/
                half2x2 rotateMatrix = half2x2(angleCos, -angleSin, angleSin, angleCos);
                //中心
                half2 uv = i.uv - 0.5;
                // 中心を起点にUVを回転させる
                i.uv = mul(uv, rotateMatrix) + 0.5;

                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

【参考リンク】:【Unity】【シェーダテクニック】テクスチャを回転させる

2次元回転行列

2次元直交座標平面において,原点を中心にθだけ回転する変換を表す回転行列をR(θ)とする

R(\theta )= \begin{pmatrix}cos\theta & -sin\theta \\sin\theta & cos\theta \end{pmatrix}

【参考リンク】:2次元回転行列の導出

要するに任意のベクトルを回転させたかったら
この公式かけたらええんやでってことですよね?

コード内で担っている箇所は下記です。

フラグメントシェーダー内のコード
        //フラグメントシェーダー
        fixed4 frag(v2f i) : SV_Target
        {
            // Timeを入力として現在の回転角度を作る
            half timer = _Time.x;
            // 回転行列を作る
            half angleCos = cos(timer * _RotateSpeed);
            half angleSin = sin(timer * _RotateSpeed);
            /*       |cosΘ -sinΘ|
              R(Θ) = |sinΘ  cosΘ|  2次元回転行列の公式*/
            half2x2 rotateMatrix = half2x2(angleCos, -angleSin, angleSin, angleCos);
            //中心合わせ
            half2 uv = i.uv -0.5;
            // 中心を起点にUVを回転させる
            i.uv = mul(uv, rotateMatrix) + 0.5;

            fixed4 col = tex2D(_MainTex, i.uv);
            return col;
        }

half2x2で2行2列の行列を作成しています。

2次元直交座標平面において,原点を中心にθだけ回転する変換を表す回転行列をR(θ)とする

という説明通り、half2 uv = i.uv - 0.5;の箇所で
座標の原点である(0,0)とUV座標の中心を合わせています。

その後、UVをずらすことで回転の中心を画像の中心と合わせています。

今の説明を図解すると下記画像のようになります。

原点を中心にθ分回転するので今説明した中心合わせを行わないと
下記GIFのような回転の仕方になります。

_Time

_Timeは時間経過で変化させたいときに使います。
たぶんスーパーアルティメットよく使います。

【参考リンク】:_Time

セマンティクス

構造体の中でコロン(:)を付けて大文字で書いてあるやつのことです。
この変数にはポジションを入れてあるよ!
みたいなのをGPUに教えてるらしいです。
【参考リンク】:シェーダを書けるプログラマになろう #1 シェーダを理解しよう - Unity道場2019 2月

TEXCOORD0が一番目のUVというのはよく意味が分かりませんでした(圧倒的知識不足)
他の記事書いてるときにわかったような気がしたので後々書きます。


2020/10/20 追記

書きました。
【参考リンク】:そろそろShaderをやるパート7 マスクしてUVを回転させる

参考リンク

Unityシェーダプログラム入門 UnlitShaderの要素を全て解説

Discussion