うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】Y軸固定ビルボードシェーダーの実装方法

今回は、Unity Shaderを使ってY軸固定ビルボードシェーダーを作成する方法を解説します。ビルボードシェーダーは、木や草、エフェクトなど、常にカメラに向けて表示したいオブジェクトに非常に便利です。

しかし、すべての軸で回転すると不自然になる場合があります。例えば、木や草は上下方向(Y軸)に対して回転しない方が自然です。そのため、Y軸を固定したビルボードを作成します。

シェーダー

以下がY軸固定ビルボードシェーダーのコードです。

shader コードをコピーする

Shader "Custom/Billboard"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Transparent" }
        
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;

                // オブジェクトのスケールを取得
                float3 scale = float3(
                    length(unity_ObjectToWorld._m00_m10_m20),
                    length(unity_ObjectToWorld._m01_m11_m21),
                    length(unity_ObjectToWorld._m02_m12_m22)
                );

                // オブジェクトの中心位置を取得
                float3 worldPos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz;
                
                float3 cameraPos = _WorldSpaceCameraPos;
                float3 toCamera = normalize(cameraPos - worldPos);
                float3 up = float3(0, scale.y, 0);
                float3 right = normalize(cross(up, toCamera)) * scale.x;
                
                // ビルボードの頂点位置を計算
                float3 pos = worldPos + right * v.vertex.x + up * v.vertex.y;

                o.vertex = UnityWorldToClipPos(pos);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

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

Shader詳細

1. オブジェクトの中心位置を取得:

worldPos でオブジェクトのワールド座標における中心位置を取得します。

float3 worldPos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz;

2. ビルボードの面を定義するための軸を計算:

上方向(up)とカメラへの方向ベクトル(toCam)の外積から、ビルボードの右方向(right)を計算します。

float3 right = normalize(cross(up, toCam));

3. 各頂点の新しい位置を計算:

オブジェクトの中心位置(worldPos)を基準に、頂点のローカル座標(v.vertex.x、v.vertex.y)を使って新しい頂点位置を計算します。

float3 pos = worldPos + right * v.vertex.x + up * v.vertex.y;

動作確認

下の画像の左側は、ビルボードシェーダーを適用した木の画像です。カメラを回転させても木が常に正面を向いていることがわかります。