はじめに
画像の移動・回転・スケールについてなぜそうなっているかを
説明します。
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
