yu00’s blog

プログラミングに関する備忘録です

Unity Shader UV座標まとめ

はじめに

画像の移動・回転・スケールについてなぜそうなっているかを
説明します。

UV座標系

Create > Shader > Unit Shader で説明します。
作成されたシェーダにはv2fが定義されています。

struct v2f
{
    float2 uv : TEXCOORD0;
    ...
};

uvの座標系は以下のようになっています。

このuvに以下のようなテクスチャを貼ることを考えます。

この時のコードは以下のようになります。

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

このコードをQuadに適用したのが以下です。

i.uvに処理を加えるとテクスチャを変形することができます。
この処理は、uv座標から見た点pの座標を
= [i.uv.x, i.uv.y, 1]^Tとし、
テクスチャ座標から見た点pの座標をとすると、

のように座標変換Hで表すことができます。
座標変換については
https://yu00.hatenablog.com/entry/2022/07/11/103954
で説明しています。
つまり、fragではuv座標から見た点を
テクスチャ座標で表す変換を求めることになります。

次の項から移動・回転・スケールについて、座標変換とコードを
説明していきます。

移動

移動は点i.uvに平行移動行列を掛けることで変換できます。

この時のコードは以下のようになります。

float3x3 T = float3x3(
    1, 0, _tx, 
    0, 1, _ty, 
    0, 0, 1
);
float3 uv = float3(i.uv, 1);
float3 v = mul(T, uv);
fixed4 col = tex2D(_MainTex, v.xy);

または

fixed4 col = tex2D(_MainTex, i.uv + float2(_tx, _ty));

移動をQuadに適用したのが以下です。

回転

回転は点i.uvに回転行列を掛けることで変換できます。

float s = sin(_theta);
float c = cos(_theta);
float3x3 R = float3x3(
    c, -s, 0, 
    s, c, 0, 
    0, 0, 1
);
float3 uv = float3(i.uv, 1);
float3 v = mul(R, uv);
fixed4 col = tex2D(_MainTex, v.xy);

または

float s = sin(_theta);
float c = cos(_theta);
float2x2 R = float2x2(
    c, -s,
    s, c,
);
fixed4 col = tex2D(_MainTex, mul(R, i.uv));

スケール

回転は点i.uvにスケール行列を掛けることで変換できます。

float3x3 S = float3x3(
    _sx, 0, 0, 
    0, _sy, 0, 
    0, 0, 1
);
float3 uv = float3(i.uv, 1);
float3 v = mul(S, uv);
fixed4 col = tex2D(_MainTex, v.xy);

または

fixed4 col = tex2D(_MainTex, i.uv * float2(_sx, _sy));

コード全文

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _tx ("tx", Range(-1.0, 1.0)) = 0
        _ty ("ty", Range(-1.0, 1.0)) = 0
        _theta ("theta", Range(-3.14, 3.14)) = 0
        _sx ("sx", Range(0.1, 10.0)) = 1
        _sy ("sy", Range(0.1, 10.0)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _tx;
            float _ty;
            float _theta;
            float _sx;
            float _sy;

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

            fixed4 frag (v2f i) : SV_Target
            {
                float3x3 T = float3x3(
                    1, 0, _tx, 
                    0, 1, _ty, 
                    0, 0, 1
                );

                float s = sin(_theta);
                float c = cos(_theta);
                float3x3 R = float3x3(
                    c, -s, 0, 
                    s, c, 0, 
                    0, 0, 1
                );

                float3x3 S = float3x3(
                    _sx, 0, 0, 
                    0, _sy, 0, 
                    0, 0, 1
                );

                float3 uv = float3(i.uv, 1);
                float3 v = mul(mul(S, mul(R, T)), uv);

                fixed4 col = tex2D(_MainTex, v.xy);
                return col;
            }
            ENDCG
        }
    }
}

応用例 点(tx, ty)を中心にθ回転する

まず、各座標間で分かっている変換を列挙します。

より、各変換を繋げて変換行列を求めます。
順番を反転するには逆行列にします。

float3x3 T1 = float3x3(
    1, 0, _tx, 
    0, 1, _ty, 
    0, 0, 1
);
float3x3 T2 = float3x3(
    1, 0, -_tx, 
    0, 1, -_ty, 
    0, 0, 1
);

float s = sin(_theta);
float c = cos(_theta);
float3x3 R = float3x3(
    c, -s, 0, 
    s, c, 0, 
    0, 0, 1
);

float3 uv = float3(i.uv, 1);
float3 v = mul(mul(T1, mul(R, T2)), uv);

fixed4 col = tex2D(_MainTex, v.xy);

バージョン

  • Unity : 2022.3.22f1


Powered by MathJax

This page is based on MathJax technology.