kumak1’s blog

kumak1のイラストや技術ログ

UnityでShaderを書くときはなるべく #pragma vertex vert_img を使わないようにする

Unityでもビジュアルスクリプティングが叫ばれる昨今ですが、 レビューしたり、後からの修正のしやすさを考えると、スクリプトでゴリゴリ書きたいわたしです。

Render Texture を操作する Shader を書きたくなった時、 下記のように記述して、vertex shader を UnityCG.cginc に宣言済みの vert_img 関数を指定すると、 fragment shader の記述に集中できるので楽チンですね。

#include "UnityCG.cginc"
#pragma vertex vert_img

思わぬ落とし穴

私は PostProcess をモリモリ自作していて、そこで Render Texture を操作する Shader をよく記述します。 のだけども、iOS 用にビルドしてiPhoneの実機で表示確認してみると、Mac の Unity Editor の表示と全く違う表示がされた・・

f:id:kumak1:20210704014222p:plain
上はMac,下はiPhoneでの実際の画像

なんで・・

原因は先ほど利用した vertex shader の v2f_img です。 UnityCG.cginc で定義されている記述はこちら。

struct appdata_img
{
    float4 vertex : POSITION;
    half2 texcoord : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f_img
{
    float4 pos : SV_POSITION;
    half2 uv : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

v2f_img vert_img( appdata_img v )
{
    v2f_img o;
    UNITY_INITIALIZE_OUTPUT(v2f_img, o);
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

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

そう、 half が悪さをしていました。 PC(Mac)とMobile(iOS)で表示が異なる場合は、だいたい精度の問題ですね・・。

struct appdata_imghalf2 texcoord : TEXCOORD0;struct v2f_imghalf2 uv : TEXCOORD0; となっているため、Render Texture の座標精度が悪くて崩れてしまうのだ。

Unity公式だってテクスチャ座標を扱うときは float使え って言ってる。 (また iPhone12 の画面の解像度は 2,532 x 1,170仮数が10bitしかない half では、2532は表現しきれない。)

対処法

残念だが v2f_img の利用はやめよう。 便利だったけれど。お世話になったけれど。 無理せず float2 で宣言し、 appdata_base を受け取るようにしよう。 ( appdata_basetexcoordfloat4 で宣言されている )

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

v2f vert(appdata_base v)
{
    v2f o;
    UNITY_INITIALIZE_OUTPUT(v2f, o);
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

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

まとめ

アイコンなど、少ない低解像度の画像を扱うときは v2f_img を使っても良いが、 Post Process などで高解像度の画像を扱うときは v2f_img を使ってはけない。