夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

頂点シェーダーでのオブジェクトの回転を実装する

本日はShader枠です。

グラフィックパイプラインの中で3Dオブジェクトは頂点シェーダーステージで描画画面に対しての座標などが処理され、フラグメントシェーダーでピクセル色が決められています。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

頂点シェーダーでの処理を行うことでUnity上の位置を変えることなくオブジェクトの見た目を変えることができます。

redhologerbera.hatenablog.com

今回は回転するシェーダーを紹介します。

〇Unityにおける回転

Unityのシェーダーでは座標返還で行列(Matrix)が多く使用されています。

例えば3Dモデルの頂点をカメラで見えるように変換するという処理ではUnityObjectToClipPos(TransformToHClip)が使用されますが、これも内部では行列が行われています。

つまり一度カメラで見るようにUnityの座標系で計算された頂点位置を再度行列計算を行うことで任意に回転させることができます。

今回は頂点シェーダーで次のように記述しました。

        v2f vert(appdata v)
        {
            v2f o;

            // ピボットを中心に回転行列を計算します
            float3 worldPos = v.vertex.xyz - _Pivot;
            float rad = _Rotation * (3.14159265 / 180.0);
            float2x2 rotationMatrix = float2x2(cos(rad), -sin(rad), sin(rad), cos(rad));

            // 頂点座標を回転行列で変換します
            worldPos.xy = mul(worldPos.xy, rotationMatrix);
            worldPos.xy += _Pivot.xy;

            o.vertex = UnityObjectToClipPos(worldPos);
            o.uv = v.uv;
            return o;
        }

float2x2 rotationMatrixで行列を行っています。筆者も行列はそこまで詳しくないので具体的な解説は避けますが、上記のように記述することで回転をかけることができます。

得られた回転行列を頂点と行列演算で乗算することで頂点の回転した後の座標を算出しています。

以上の実相で_Rotationの値に応じて回転するオブジェクトを描画できます。

〇コード

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Custom/TransparentRotatedArea"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Rotation("Rotation", Range(0, 360)) = 0
        _Pivot("Pivot", Vector) = (0.5, 0.5, 0)
    }

    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
        Cull Off
        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;
        float _Rotation;
        float3 _Pivot;

        v2f vert(appdata v)
        {
            v2f o;

            // ピボットを中心に回転行列を計算します
            float3 worldPos = v.vertex.xyz - _Pivot;
            float rad = _Rotation * (3.14159265 / 180.0);
            float2x2 rotationMatrix = float2x2(cos(rad), -sin(rad), sin(rad), cos(rad));

            // 頂点座標を回転行列で変換します
            worldPos.xy = mul(worldPos.xy, rotationMatrix);
            worldPos.xy += _Pivot.xy;

            o.vertex = UnityObjectToClipPos(worldPos);
            o.uv = v.uv;
            return o;
        }

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

            
            return col;
        }
        ENDCG
    }}
}