こんにちは、エンジニアのオオバです。

UnityはiOS、Androidといった 複数のプラットフォームに対して同時開発可能 です。

シェーダーも同じ。

Unityのようなゲームエンジンがない状態では、 iOS用、Android用と両方別々のシェーダーを書く必要があります。 なぜならiOS、Androidで内部構造は違うからです。だからそれぞれのシェーダーが必要とします。

本記事では、なぜUnityは1つのシェーダーで良いのか解説していきます。

1つのシェーダーを各環境に変換している

結論から話すとUnityは 1つのシェーダーファイルを各プラットフォームに合わせて変換 しています。

【Unity】1つのシェーダーで複数プラットフォーム対応できる理由_0

次の表は代表的なプラットフォームと使用されているGPUです。

プラットフォームGPU
iOSMetal
AndroidVulkan、OpenGLES3.0
MacMetal
WindowsDirectX

GPUとは画面に表示するための演算ユニットです。GPUがシェーダーを解釈して画面にものを表示させます。つまり、GPUの種類によってシェーダーを変える必要があるのです。

Unityを使わず、素の状態でシェーダーを書く場合は、iOS、Android向けとそれぞれシェーダーを書く必要があります。すごく大変な作業です。

Unityを使ことで、ひとつのシェーダーで複数のプラットフォームに対応できるのです。

👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

Unityのシェーダー言語「ShaderLab」とは

UnityのシェーダーはShaderLabと呼ばれるUnity独自の記法記法です。 Unityでシェーダーを書く場合はShaderLabで書く必要があります。

📚 参考サイト : ShaderLab 構文 - Unity マニュアル

ShaderLabとはいったいどういうものか解説していきます。

ShaderLabはGLSL、Cg、HLSLを含んでいる

ShaderLabは既存のシェーダー言語 GLSLCgHLSL これらの文法を組み合わせたような言語仕様です。

Cg、HLSLで書いたシェーダー(ShaderLab)はOpenGL向けに書きだされる端末の場合、HLSL2GLSLでコンパイルされます。

ここで言う「コンパイル」とはShaderLabから ターゲットとなる環境のシェーダー言語に変換される という意味です。

シェーダーはランタイム中(実行中)にコンパイルされて実行されます。このランタイム中のシェーダーコンパイルと誤認するため本記事ではShaderLabから各プラットフォームへのシェーダーコンパイルを「変換」と表現します。

変換後のシェーダーを確認する方法

各ターゲットに変換された状態も確認できます。ピクセルを赤く描画するシンプルなShaderLabを用意しました。

Shader "Simple"  
{
    SubShader  
    {
        Pass  
        {
            CGPROGRAM  
            #pragma vertex vert_img  
            #pragma fragment frag  
            #include "UnityCG.cginc"  

            fixed4 frag (v2f_img i) : SV_Target  
            {
                return fixed4(1, 0, 0, 1);  
            }
            ENDCG  
        }
    }
}

シェーダーインスペクタから変換

先のシェーダーをOpenGLES2.0の環境に変換したシェーダーを確認していきます。シェーダーファイルを選択してInspecterから変換後のシェーダーを確認できます。

【Unity】1つのシェーダーで複数プラットフォーム対応できる理由_1

するとこうなります。

※読みやすくするために整形しています。
Shader "Simple"  
{
  SubShader  
  {
    Pass  
    {
      GpuProgramID 16286  
      Program "vp"  
      {
        SubProgram "gles "  
        {
"#version 100  

#ifdef VERTEX
attribute vec4 _glesVertex;  
uniform highp mat4 glstate_matrix_mvp;  
void main ()  
{
  gl_Position = (glstate_matrix_mvp * _glesVertex);  
}
#endif

#ifdef FRAGMENT
void main ()  
{
  gl_FragData[0] = vec4(1.0, 1.0, 1.0, 1.0);  
}
#endif
        "  
        }
      }
      Program "fp"  
      {
        SubProgram "gles "  
        {
"// shader disassembly not supported on gles"  
        }
      }
    }
  }
}

変換されたシェーダーを読むと新しい発見あり

変換されたコードを読んでみると面白いです。基本的にはShaderLabと同じような処理ですが、ところどころ違いがあります。

ShaderLabのフラグメントシェーダー
fixed4 frag (v2f i) : SV_Target  
{
    return fixed4(1, 0, 0, 1);  
}
GLSLES2.0のフラグメントシェーダー
void main ()  
{
  gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0);  
}

このようにGLSLES2.0では、整数ではなく少数点表記になっています。ShaderLabの変換がよしなに各ターゲットに対して変換していることが分かります。

変換済みシェーダーの用途

変換済みのシェーダーはゲーム開発中に役立ちます。描画不具合に出会ったとき、 特に実機でテストした場合 です。

例えばAndroid端末で描画不具合が出会った場合です。

例えばAndroid端末で描画不具合が出会った場合の例

①ShaderLabではミスはない

②iOSは正常

③Androidだけおかしい

④Android向けに変換されたシェーダーを確認

このような流れで変換後のシェーダーを読むことになります。描画周りの開発に携わる開発者の方は知っておくと役立つテクニックです。

まとめ

Unityのシェーダーが各プラットフォームへ変換される仕組みについて解説してきました。

Unityのシェーダーとは
  • UnityのシェーダーはShaderLabという独自言語を使って書く
  • ShaderLabはCg、HLSLを組み合わせたような言語仕様である
  • 最終的には各環境に向けたシェーダー言語に変換される
  • 変換後のシェーダーはUnityエディタで確認可能である

Unityを使えば複数のプラットフォームに対し、1つのシェーダーで対応できます。これによって何が良いかというと ものづくりに集中できる時間が増える ということです。

iOS、Android向けに同じものを提供するためにUnityがなければひとつずつ「移植作業」をします。非常に手間です。というのも 移植元に修正が入るたびに移植作業が発生するから です。また 移植作業とは製品の質を上げる作業ではない のもあまり積極的にやりたくない理由の1つ。

しかし、 Unityを使えば1つのソースを変更することで各プラットフォームを更新できます。

つまり移植作業の大半をUnityに任せられるということです。プラットフォーム固有の事情は個別に対処する必要はありますが、だいぶ楽できます。

開発効率を上げるという意味でもUnityが扱うシェーダー言語ShaderLabを覚えるメリットは大きいです。また、Shader Graphというプログラムを書かなくてもシェーダーを生成する機能が登場しました。

オオバ的には Shader Graph から入ることをオススメします。シェーダーの流れを視覚的に理解することができるためです。

ぜひ、シェーダーを書けるようになって面白い表現を作っていきましょう。

Unityのシェーダー・3Dプログラミングを基礎から学びたい方はこちらの記事がおすすめです。この記事とあわせて読んでみてください。

「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
ぜひチャンネル登録をお願いします!

最後まで読んでいただきありがとうございました!
すばらしいシェーダーライフをお過ごしください。

オススメ記事