2020年2月8日この記事ではUnityにおけるシェーダプログラムの読み方・書き方を解説します!その中でもUnity特有の機能であるShaderLabと、頂点シェーダ・フラグメントシェーダというものを中心に扱っていきます。現在はShaderGraphなどのビジュアルエディタを利用することでシェーダプログラムを書かなくてもシェーダを作ることが可能です。そんな時代ですがシェーダプログラムを読み書きできるようになっておくメリットも大きいので、今回はあえてビジュアルエディタについては一切取り扱いません。透明度のプロパティを追加フラグメントシェーダで計算ブレンドモードの変更Zバッファレンダーキューの設定Zバッファ書き込みの設定AlphaBlendUnlit全文シェーダ参考サイトあとがき今回やることUnityで右クリックメニューから作成できるシェーダのテンプレート「UnlitShader」内の全ての要素を解説します。「これはおまじないです」は一切使いません。UnlitShaderとはライトの影響を考慮せず、落ち影などを付けないシンプルなシェーダです。本当に全ての要素を解説するので、この記事だけで普通の記事10個分くらいはボリュームがあります。残念ながらシェーダー芸人が作るような華やかな画面は出てきません。基本形であるUnlitShaderを正しく理解することは間違いなく良いシェーダが書けるようになることに繋がるので、ぜひ最後まで読んでいただけると嬉しいです。シェーダは見えないところでUnityが色々と自動で処理してくれています。時にそれが混乱する原因になるので、つまづきやすいところは詳しく解説します。該当箇所を理解するのに必要なレンダリングの基礎知識も一緒に説明します。最後にUnlitShaderを改造して半透明描画ができるようにします。シェーダの解説に入る前に、必要となる前提知識を説明していきます!レンダリングパイプラインシェーダを理解する上でレンダリングパイプラインの話は避けられないので、最初に解説しておきます。レンダリングパイプラインとは、3D空間上に配置されたオブジェクトがデバイスの画面に表示されるまでの処理の流れのことです。環境によって内容が異なるので細かくは解説しませんが、シェーダはこのレンダリングパイプラインから適切なタイミングで呼び出されるように作られています。シェーダはCPUではなくGPUで実行されるというのも重要なポイントです。どんなパラメータを受け取りたいかいつ呼び出して欲しいかどんな描画内容にするかこれらはシェーダ側に記述することで指定することができます。これがシェーダを書くという事です。シェーダ側に「こういうパラメータを受け取りたいな」「こんな時に呼んで欲しいな」と記述されていても、レンダリングパイプラインがそれに対応していなかったら正しく動きません。SRPについて従来のUnityにおけるレンダリングパイプラインはUnityによって用意されたものを使うしかありませんでしたが、現在はScriptableRenderPipeline(SRP)を使用することでカスタマイズできるようになりました。Unityでプロジェクトを作成する際は、テンプレートからUnityによって用意されたSRPであるHDRPやLWRP(現在はURP)を選択できるようになっています。これから解説するシェーダの内容は、あくまで標準のレンダリングパイプラインを使用した場合の話です。SRPを使用する場合は事情が異なりますので、そこはご了承下さい。Extras」のいずれかを選択してプロジェクトを作成した際は、標準のレンダリングパイプラインです。テンプレートの選択なお、こちらの記事を作成した際のUnityバージョンは2019.1.8f1、テンプレートは「3D」です。標準でも2種類ある実は標準のレンダリングパイプラインの中にも「Forward」と「Deferred」の2種類があります。はじめにレンダリングに関する理解を深めるため、ForwardレンダリングとDeferredレンダリングの違いを自分なりに軽くまとめてみました。**Forwardレンダリング**Forwardレンダリングでは、基本的に以...今回は、デフォルトで使用されるForwardを扱っていきます。Unityシェーダ大きく3つに分類されます。固定機能シェーダ今回テーマとなる頂点フラグメントシェーダのように自由に描画処理の内容を書けるシェーダをプログラマブルシェーダと呼びます。固定機能シェーダは、予め用意された機能を組み合わせて描画を行います。プログラマブルシェーダが動かないハードウェア(ほとんどありませんが...)では、固定機能シェーダを使用します。できることが非常に限られてしまうため、テクスチャをそのまま表示するだけといったシンプルな内容でしか使用しません。サーフェスシェーダ3Dモデルの表示に特化したシェーダです。最小限の内容を用意するだけで、ライトの計算や影付けの計算はUnity側で用意されている処理に任せることができます。マテリアルを作成した時にデフォルトで設定されているStandardシェーダはサーフェスシェーダが使われています。たしかに便利なのですが、用途が限られてしまうこと、SRPでは廃止されていることなどを踏まえて今回は解説しません。頂点フラグメントシェーダ頂点シェーダ・フラグメントシェーダの2種類を使用します。頂点シェーダは頂点単位、フラグメントシェーダはピクセル単位の処理を行います。今回解説するUnlitShaderを含め、頂点フラグメントシェーダは幅広く利用されています。シェーダを調べているとUnity以外の環境(DirectX、OpenGLなど)も含め、頂点フラグメントシェーダに出会うことが多々あります。これらを読むことができればShaderGraphなどのビジュアルエディタに応用することもできるので、覚えるメリットは大きいです。そのため今回のテーマを頂点フラグメントシェーダに設定しました。頂点フラグメントシェーダについてはShaderLabの解説の後に詳しく説明します。ShaderLabそれではシェーダの書き方に入っていきます!UnityシェーダはShaderLabというものを使用して記述します。拡張子は「.shader」です。C#スクリプトと異なり、シェーダはテストプレイ中でも書き換えて保存したら起動し直さなくても反映されます。シェーダを書くエディタについてシェーダプログラムで補完が効くようにするにはプラグインが必要になる場合が多いです。VisualStudio2019を使っておけばシンタックスハイライトはされます。シェーダに記述エラーがある場合はシェーダを適用しているオブジェクトがピンク色で表示されるのですぐに分かります。C#を書くのに使っているエディタと同じで基本的には問題ありません。UnlitShaderの作成アセット作成メニューからUnlitShaderを作成します。Shader」を選択します。作成できたら、ダブルクリック等で開きます。作成されたUnlitShaderのコード全文はこちらです。)で見ることが大切です。これは他のプログラムでも同じですね。最後に説明するPassブロックの中に書くのが頂点フラグメントシェーダです。それ以外はUnity側にシェーダの設定内容を伝えるための記述なので、厳密にはシェーダではありません。各ブロックについて解説していきます。シェーダの中身}一番外側のブロックには、シェーダ名を書きます。ファイル名と一致させないといけないなどのルールはありません。他のシェーダと同じ名前を付けることはできません。名前を「MyUnlitShader」に変更しておきます。"Unlit/MyUnlitShader"マテリアル設定これでマテリアルからMyUnlitShaderを選択できるようになっているはずです。実際にマテリアルに設定してみます。CreateメニューからMaterialを選択してマテリアルを作成したら、Inspector上でシェーダを変更します。マテリアルにシェーダを設定このマテリアルは後ほど利用します。{}}マテリアルのInspectorで設定したいプロパティを記述します。プロパティの形式は以下の通りです。付いているのは慣習です。行末にセミコロン(;)は付けません。指定されている型「2D」はTexture2Dのことです。要するに普通のテクスチャですね。Texture2Dプロパティデフォルト値には、テクスチャがNoneの時に使用される単色の仮テクスチャを名前で設定できます。いくつか試してみたところ、利用できるのはwhite、black、redの3種類だけでした。これ以外を指定するとグレーになります。デフォルト値をblackに変えてみるとこんな感じにマテリアルのInspectorでの表示も変わります。書いてしまうとエラーになります。オプションは基本的に使わないので、特に解説はしません。使うプロパティUnlitShaderではテクスチャのみ指定されていますが、よく使うものとしては以下のものがあります。0.1小数点の値を自由に設定できます。Intfloatと同じように整数もintで利用できます。5Range指定範囲内の値をスライダーで設定できます。0.75RangeプロパティColor色をカラーピッカーで設定できます。1)Colorプロパティ色についてシェーダ上では、色は0〜1の値で表現されます。0〜255に慣れている場合はちょっとややこしいかもしれませんが、覚えておきましょう。シェーダへの受け渡し機能Propertiesブロックで設定されたプロパティは、頂点フラグメントシェーダ側で用意した同じ名前のプロパティに自動的に受け渡されます。対応するのは、Passブロック中の真ん中あたりにあるこれです。_MainTex_ST;シェーダ側でのsampler2D型はTexture2Dのことです。ここは問題ないですね。floatについてまずfloat4についてですが、これはC#スクリプト側でのVector4と同じと考えておけば大丈夫です。w)の4つのfloat値を格納できる型ですね。floatと書いた場合は、C#のfloatと同じです。正体は、テクスチャをInspectorで設定する時に表示されているこの部分です。_MainTex_STの正体ここで設定されたTilingとOffsetの値を「テクスチャ名_ST」のfloat4プロパティに自動で格納してくれる仕組みです。以下のように格納されます。YTilingとOffsetとは?TilingとOffsetを利用したことがない方もいると思うので、こんな感じのテクスチャを板ポリ(Quad)に貼り付けてテストしてみます。Tilingを増やすと、ポリゴン内でテクスチャUVが繰り返します。縮小して詰め込むイメージですね。Y:2OffsetはUVをそのままずらします。1を入れると1タイル分ずれてしまって見た目が変わらないので0.5を入れてみます。Y:0横方向に対して元のテクスチャの半分だけ、UVがずれました。TilingとOffsetを使わない場合TilingとOffsetが不要なテクスチャであれば、以下のようにNoScaleOffset属性を付けることでInspectorにも表示されなくなり、_STが付いた変数を宣言する必要もなくなります。シェーダの中身}シェーダの中身はSubShaderブロック内に記述します。SubShaderは複数書くことができます。用途に合わせたシェーダが定義されていない場合このような場合はSubShaderをスキップし、実行できるSubShaderが見つかるまで上から順番に探してくれます。どのSubShaderも実行できない場合は最後に書いたFallBackが実行されます。"Standard"例えばこのように書いた場合は、FallBackはStandardシェーダになります。FallBackさせてみるハードウェアで未対応の場合にスキップされるのは古いハードウェアで最新機能を使おうとした場合で、そこまで発生することはありません。用途に合わせたシェーダが定義されていない場合の方は簡単に再現できるので、試してみましょう。UnlitShaderに書かれているのは、オブジェクトの描画だけを行うシェーダです。これは、影の描画には対応していません。Standardシェーダを適用した真っ白な床に対して、MyUnlitShaderを適用したBoxを置いてみました。オブジェクトは描画されましたが影は描画されていません。MyUnlitShaderのFallBackにStandardシェーダを指定してみます。"Standard"すると、影が描画されるようになりました。影の描画は、影を与える側と影を受ける側の両方がシェーダで対応されていることで成立します。影を受けることに対応しているStandardシェーダを床に設定しても、与える側であるBoxが対応していなければ影は落ちないわけですね。FallBackにStandardシェーダを指定したことで、オブジェクトの描画はMyUnlitShader影を与える処理はStandardシェーダこのように自動的に使い分けてくれるようになったわけです!もちろん、MyUnlitShaderの中に影を与える処理を自前で実装することもできます。TagsUnity側にシェーダの設定を伝えるためにタグを付けます。UnlitShaderでは1つしかないですが、複数ある場合は次のように書きます。"RenderType"="Opaque"}タグはこの後出てくるPassブロックの中にも書くことができます。SubShaderと同じようにPassも同一SubShader内に複数書くことができるのですが、SubShaderに書いたTagは全てのPassに対して適用されます。Passの中に書いたTagは、そのPassにしか適用されません。SubShaderでしか使えないタグ、Passでしか使えないタグがあります。一覧がまとめられている記事があったので載せておきます。ShaderLabのSubShaderセクション、Passセクションには、どちらもTagを設定することができます。どちらも専用Tagです。Light...僕もよく使うものしか覚えていません。UnlitShaderに書かれているRenderTypeを押さえておきましょう。1つよく使うQueueは、この記事の最後で半透明描画を試す時に一緒に解説します。RenderType色々な種類があるのですが、シェーダがどのようなグループに属しているのかを指定します。Unity側でシェーダの種類を判定するのに利用します。普通にシェーダを利用していて、RenderTypeが影響することは滅多にありません。影響があるものとして、オブジェクトを半透明にする場合はOpaqueではなくTransparentを指定するとパフォーマンスが向上することを確認しています。半透明ならTransparent、それ以外ならOpaqueぐらいで考えておいても、とりあえず問題ありません。数少ない用途の1つにレンダリング時にシェーダを差し替える機能があります。興味のある方はこちらを読んでみて下さい。Shaderを使ってレンダリング時にシェーダを差し替える方法です。100次にLODです。略で、しきい値の役割を果たしています。こちらも、SubShaderを使い分けるための仕組みです。LODをシェーダに書くだけでは何も変化しませんが、C#スクリプトから指定することで有効になります。わかりやすく検証してくれている記事があったので、紹介しておきます。エンジニアのfujimaruです。Detail)を実現する仕組みがありますが、参考になりそうな資料が無いので、簡単なコ...例えばカメラに近い時だけリッチに見せるためのシェーダに切り替える。ゲームのグラフィックス品質設定に合わせてシェーダを切り替えるといったことができます。LODを特に使わないのであれば、省略しても問題ありません。他のオプションTagsやLOD以外にもオプションがいくつか存在します。詳しくは後ほど半透明描画を試す時に解説します。シェーダ本体}いよいよ最後のPassブロックです。先述した通りPassの冒頭に必要に応じてタグを書きます。Passでしか使えないタグはライティングに関わる部分が主で、今回は特にタグは登場しません。このPassの中に固定機能シェーダサーフェスシェーダ頂点フラグメントシェーダ上記いずれかをシェーダ本体として記述することになります。Passを複数書いた場合は、上から順番に全て実行されます。Passが複数あるシェーダをマルチパスシェーダと言います。ここまで解説してきた内容がUnity特有の機能であるShaderLabでした。ここから先は、頂点フラグメントシェーダについて解説します。最初に頂点フラグメントシェーダがそれぞれどういった処理を行うものなのか、解説していきます。ここを把握せずにシェーダを読もうとすると混乱してしまうので、ぜひ押さえておきましょう。頂点シェーダについて3DモデルでもパーティクルでもUIでも、それらは全てメッシュの集まりです。メッシュは、ポリゴンの集まりです。Unityではポリゴンは全て三角形で、3つの頂点によって三角形を表現します。三角形ポリゴン頂点シェーダにはこの頂点の情報が渡されるので、頂点単位で処理を行いフラグメントシェーダに渡すデータを作成します。中に入っている座標は、3D空間における座標です。このままではスクリーン上のどの位置に表示すればいいか分からないので、カメラから見た時にスクリーン上のどの位置に表示されるかを計算し、フラグメントシェーダに渡します。詳しい話は割愛しますが、Unityではこれを1つの関数を実行するだけで計算してくれます。正確には頂点シェーダで最後まで計算するわけではなく、-1〜1に収まるような値にして渡します。最後にGPUが端末の解像度に合わせて自動的にマッピングしてくれます。解像度が横1280x縦720なら、横0〜1280、縦0〜720に変換します。ラスタライズフラグメントシェーダを実行する前に、ラスタライズという処理が自動で実行されます。先ほどの三角形ポリゴンと同じものが頂点シェーダから渡された場合、以下のように処理されます。ラスタライズのイメージ頂点座標は先述した座標変換によって-1〜1に変換されています。(ざっくり目分量なので正確ではないです)たとえば赤い点がこれからフラグメントシェーダで処理したいピクセルだとすると、3つの頂点座標から補間した値を計算してフラグメントシェーダに渡します。この時点で、描画される位置は決定されます。図では座標を例にしていますが、テクスチャUVや色情報も同様に補間されます。同じ処理を図でいうところの青色で塗りつぶされている範囲全てに対して行い、同じくフラグメントシェーダも塗りつぶされている範囲全てのピクセルに対して実行します。フラグメントシェーダピクセル単位の処理を行うので、ピクセルシェーダと呼ばれることもあります。フラグメントシェーダでは、画面に出力する色を決定します。逆に言うと、フラグメントシェーダでは色以外の情報を出力することはできません。位置を調整したい場合などは、頂点シェーダの時点で決めておく必要があります。Cg言語それでは頂点フラグメントシェーダの書き方に入っていきます!UnlitShaderの頂点フラグメントシェーダの部分だけ抜き出したものを再度載せます。要素を順番に見ていきます。最初は冒頭の「CGPROGRAM」について。プログラム終了UnlitShaderには、Cg言語というシェーダ言語が使われています。名前の通りC言語と非常によく似ています。これが冒頭でちらっとC言語が分かるとGoodと書いた理由でした。標準レンダリングパイプラインでUnityが用意してくれているシェーダは基本的にCgで書かれています。他の言語DirectXで使用されるHLSLや、OpenGLで使用されるGLSLがあります。どちらもUnityで使用可能です。HLSL、GLSLを使用する場合はシェーダを囲むコマンドも変わります。ENDGLSL最初に解説したScriptableRenderPipelineで用意されているシェーダは、HLSLで書かれています。今後はHLSLが主流になっていくのかもしれません。CgとHLSLは細かい違いこそあれ、基本的なところはあまり変わらないので今回はそのままCgで解説していきます。multi_compile_fogプラグマ(pragma)とは、コンパイラに対して情報を渡すための命令のことです。ここで使われているもの以外にも色々な種類がありますが、UnlitShaderで使われているものについて見ていきましょう。冒頭の2つは、頂点シェーダ・フラグメントシェーダそれぞれがどの関数なのかを指定します。シェーダ内には関数をいくつも定義できるので、どれが頂点シェーダでどれがフラグメントシェーダなのかをコンパイラに伝えてあげる必要があるわけですね。始まるのはシェーダバリアントという機能を使うための命令です。UnlitShaderは所々に「FOG」とついた命令があるのが分かると思います。OtherSettingsの中で設定できます。本来フォグは霧を表現するためのものなので白色にすることが多いですが、ここではわかりやすくするために赤色で設定しています。このように、奥に行けば行くほど色が乗るような表現ができます。ところでこのフォグ、チェックボックスでオンにしなければ動作することはありません。シェーダ側にはフォグの処理を書いておかないと、オンにした時にフォグが動きません。普通にフォグの処理を書いてしまうと、もし使わなかった時に処理がムダになってしまうのです。そこで登場するのがシェーダバリアントです。multi_compile_fogを書くことでフォグ処理が入ったシェーダフォグ処理が入っていないシェーダ2種類のシェーダを自動で生成して、フォグがオンになっているかどうかを見て自動で切り替えてくれます。フォグのようにUnityが用意している機能だけでなく、自分で作った機能もシェーダバリアントで切り替えることができます。慣れないうちはシェーダバリアントの挙動を理解するのが難しいかもしれません。今回は詳しく解説しませんが、使いこなせるようになると非常に強力な機能です。"UnityCG.cginc"インクルード(include)は、指定したファイルの中身をそのままこの位置に貼り付ける命令です。複数のシェーダを書いていて、同じ内容をどのシェーダにも書いていると思ったらファイルを分けてインクルードで読み込むようにすることで共通処理を再利用しやすくなるのでとても便利です。インクルード用ファイルの拡張子は以下の通りです。Cg.cgincHLSL.hlslGLSL.glslincUnityに標準で入っているシェーダを見ていると、Passブロックの中に書かれているのはプラグマとインクルードだけ、というものもたくさん存在します。処理の実体は全てインクルードファイルに持たせることも可能というわけです。指定はシェーダファイル自身からの相対パスもしくは「Assets/Shaders/Example.cginc」のように指定しますが、ここで使われているUnityCG.cgincのようにUnityに組み込まれているファイルについてはファイル名だけで読み込めます。UnityCG.cgincUnlitShaderでインクルードされている「UnityCG.cginc」は、Unityで用意されている便利な関数などがいろいろ詰め込まれています。中身は膨大なので僕も全部把握しているわけではないですが、UnlitShaderの中でUnityCG.cgincの中身を利用している箇所が出てきたら都度解説します。UnityCG.cgincを含め、Unityに入っているシェーダはUnityのアーカイブダウンロードページからダウンロードできます。お使いのUnityバージョンのダウンロードタブから「ビルトインシェーダ」をクリックして下さい。WinでもMacでも内容は同じです。UnityCG.cgincは「CGIncludes」フォルダの中に入っています。新しくシェーダを作る時は、内容が近いシェーダをベースに書き換えて作るのが楽です。ネット上から探してきてもいいですが、ビルトインシェーダを参考にするのも手っ取り早い方法なのでぜひダウンロードしておきましょう。TEXCOORD0;};こちらは頂点シェーダへ入力する頂点データの定義です。struct(構造体)で定義するルールですが、名前は自由です。セマンティクスコロン(:)の後に書かれているPOSITION、TEXCOORD0はセマンティクスといい、種類は以下の通りです。POSITION頂点座標(float3〜4)TEXCOORDnn番目のテクスチャUV。※プラットフォームによっては4以降も使用可NORMAL頂点の法線(float3)TANGENT接線(float4)COLOR頂点カラー(float4)これら全てを毎回使うわけではないので、対象のシェーダで必要なものだけを受け取るために定義する仕組みです。UnlitShaderでは、頂点座標とテクスチャUVを1つ受け取るのでこのような定義になっています。変数名は自由ですが、セマンティクスとデータ型は決められたものを使わなければいけません。floatのアクセス方法floatについてはC#スクリプト側のVectorのようなものと説明しましたが、頂点カラーのような色もfloatで扱います。そのため、以下のようにxyzwでのアクセスの他に、rgbaでのアクセスもできるようになっています。値そのものはどちらでアクセスしても同じです。3)一部だけ取り出す書き方が出来るのもシェーダの特徴です。代入元の方が要素が少ないとエラーになります。代入元が1つだけなら代入先でアクセスしている要素全てにコピーされます。SV_POSITION;};こちらは頂点シェーダからフラグメントシェーダへ受け渡すデータの定義です。こちらもstructで定義します。ポリゴン単位でラスタライザに渡されてラスタライズ処理され、補間された値がフラグメントシェーダに渡されます。fragment)が分かりやすいのでよく使われている印象です。以降はv2fと呼びます。v2fのセマンティクスv2fにおけるセマンティクスの一覧は以下の通りです。(他にも一応ありますが、使わないので省略しています)SV_POSITION座標変換された後の頂点座標(float4)TEXCOORDnテクスチャUVなどCOLORn色など用途が決まっている頂点シェーダへの入力と異なり、v2fは自由な部分が多いです。SV_POSITIONは必須です。画面のどこに描画するかどうかはラスタライズの時点で決定されるためですね。残りのTEXCOORDとCOLORは、どんな値を入れてもいいし、型も自由です。古いGPUではTEXCOORDを使うかCOLORを使うかで扱いに違いがあったようですが、現在特に違いはありません。そのため、分かりやすく色を扱うならCOLOR、そのほかの値は全てTEXCOORDにすることが多いです。用途自体は自由ですが、数に注意です。種類ごとの数に制限はありませんが、全て合計して9個以上になると、プラットフォームやGPUの種類によっては補間処理されずそのまま値が渡されてきます。基本的には8個までに抑えるようにしましょう。UNITY_FOG_COORDSUNITY_FOG_COORDSは、UnityCG.cgincで定義されています。TEXCOORD##idx;厳密には更に定義が分かれていて複雑なのですが、その部分を展開すると上記のようになります。defineは、特定の文字列を定義しておいて、コンパイル時にそれがプログラムの中に出てきたら別の文字列に置き換える命令です。置き換え前の後ろに付けた()の中に文字列を入れることで、それを置き換え後の特定の文字列と##で結合することが出来る仕組みです。UNITY_FOG_COORDS(1)は、コンパイル時には以下のように変換されます。TEXCOORD1;フォグ処理をするために必要なfogCoord変数を定義する必要があるけれど、何番目のTEXCOORDを使ったらいいかはシェーダによって変わるので指定できる仕組みになっているわけですね。そのため以下のように書くとエラーになります。TEXCOORD1が重複してエラー};この場合はUNITY_FOG_COORDS(2)と書く必要があるわけですね。シェーダバリアントの仕組みまた、フォグのチェックがオンになっていない時はシェーダバリアントによって何も記述されないような仕組みになっています。definedは、()の中に入れた文字列がdefineで定義されているかどうかを調べる命令です。フォグのModeがLinearなら「FOG_LINEAR」がdefineで事前に定義されています。そのため、defined(FOG_LINEAR)がtrueになります。反対にフォグのチェックがオフの時はいずれも定義されないので、elseに入って何も出力されません。このようにdefineを使って記述を切り分けるのがシェーダバリアントの仕組みです。if文に似ていますが、全く性質の異なるものだということを覚えておきましょう。_MainTex_ST;ここについてはPropertiesブロックからプロパティが受け渡されるものと説明しました。ここで宣言したプロパティはグローバル変数であり、どの関数からもアクセスできます。Propertiesブロックからの受け渡しC#スクリプトからのアクセス頂点シェーダからのアクセスフラグメントシェーダからのアクセスこれら全てに対応しています。反対に頂点シェーダの中で宣言した変数は頂点シェーダの中でしか使うことは出来ませんし、フラグメントシェーダの中で宣言した変数はフラグメントシェーダの中でしか使うことは出来ません。o;}形式としては以下の通りです。関数名がvertで頂点データの型がappdataなのは先述した通りです。頂点データの変数名は「v」とすることが一般的です。フラグメントシェーダへの出力も変数名は自由ですが「o」とすることが多いです。それでは頂点シェーダの処理について1行ずつ見ていきます。UnityObjectToClipPos(v.vertex);UnityObjectToClipPosはUnityCG.cgincで定義されている、3D空間からスクリーン上の位置への座標変換を行ってくれる関数です。実行している内容としては以下のようなものなので古いシェーダだとそのまま書かれていることもありますが、UnityObjectToClipPosを使った方がパフォーマンスが良いようです。v.vertex)また、これと全く同じものを書くとUnityによって勝手にUnityObjectToClipPosに置き換えられます。mul()は積を計算する関数です。ここではUNITY_MATRIX_MVPという行列と頂点座標であるv.vertex(float4のベクトル)の積を計算しています。組み込み関数Unityによって定義されている関数ではなく、mul()のようにCgやHLSLで定義されている関数を組み込み関数と言います。GPUによって最大限の最適化が行われているため、高速に動作するものが多いです。どんな組み込み関数があるのか知っておき、いつでも使えるようにしておくことで複雑な処理を書いてしまうことを避けられるのでぜひ活用していきましょう。行列について4×4の行列であればfloat4x4のように宣言します。行列とは何なのか?という説明を始めてしまうとそれだけで1つの大きなテーマになってしまうので、ここではmul()についての詳細とUNITY_MATRIX_MVPの簡単な説明に留めておきます。行列がよく分からない方のために参考リンクを載せておきます。アタリマエ!外部サイト行列とは?行列の足し算・引き算・かけ算とその有用性【3次元以上のデータを一括計...行列(英語でMatrix)とは、数字・記号・式などを縦と横に並べたもののことを言います。このページでは「行」と「列」の考え方と行列の足し算・引き算・かけ算、その使い道について解説していきます。mul()についてmul()は実はCg(HLSL)にはあってGLSLには存在しない組み込み関数です。ように「*」演算子を使って行列同士も、行列とベクトルの積も計算することができます。Cg(HLSL)においては行列の積、行列とベクトルの積を計算する際にmul()を使用します。ちなみにCg(HLSL)でも同じ次元数の行列同士なら「*」演算子を使うことができますが、mul()とは挙動が異なります。syghの新フラグメント置き場GLSLでは行列の乗算に*演算子を使います。HLSLではmul()関数を使います。vOu...「*」演算子を使った場合は、数学的な行列の積計算(それぞれ行と列の内積を求める)ではなくて、単に各成分の値をかけ合わせるだけになります。行列を使ってこのような計算を行うことは滅多にありませんから、GLSLでは「*」演算子で行列の積を計算できるようになっているのでしょうね(ややこしいですが...)。UNITY_MATRIX_MVPUNITY_MATRIX_MVPは、名前そのままで「Unityによって定義されたMVP行列」です。座標変換はモデル(Model)変換、ビュー(View)変換、プロジェクション(Projection)変換の3段階で行われます(厳密には頂点シェーダの後に透視変換が行われて完了します)。それぞれの変換がどういったものなのか詳しく知りたい方は、こちらを読んでみて下さい。復習と用語の整理を兼ねてまとめてみました。これら3つの変換を1回の計算で終わらせられるように、Unityが事前に計算しておいたものをUNITY_MATRIX_MVPの中に入れてくれているわけです。具体的にはそれぞれ行列化したものをかけ合わせています。Projection」の順番に掛け合わせているのかというと、そういうわけでもないのです。ややこしい話なのでここで覚える必要はないですが一応解説しておくと、このMVPという名前はDirectXに由来しています。DirectXとOpenGLでは行列のメモリ上の配置が異なります。4×4の行列が要素数16個の配列だとイメージした時に、0〜3の要素に1行目が入るものを行オーダー、1列目が入るものを列オーダーと言います。(以降は行オーダーなら4〜7に2行目、8〜11に3行目、列オーダーなら4〜7に2列目、8〜11に3列目と続きます)OpenGL、そしてUnityは列オーダー、DirectXは行オーダーです。列オーダーと行オーダーでは行列をかける順序が反転します。これはメモリ配置の仕方が違っても、行列計算の数学上のルールはそのまま使うためです。Model」の順番でかけます。最後に頂点座標のベクトル(v.vertex)との積を求めて座標変換は終了でした。先程のmul()計算においてv.vetexが右側に来ていたのは、これが理由です。v.vertex)詳しくはこちらを参照して下さい。自分で直に行列変換周りの処理を書くときに、掛ける順番やオーダー周りについていつも混乱するのでまとめておきます。これは、行列のオーダーには直接関係はありませんがよく混乱するので書いておきます。Unityでは「左手系」の座標系を採用して...座標変換されていることを確認してみる先述した通り、座標変換後はスクリーン上のどの位置なのかを-1〜1で表現します。頂点シェーダの処理を少し書き換えて、画面の中央に表示されている板ポリが画面の右上にくるように調整してみます。_ProjectionParams.x;右上なのでそれぞれ+1します。_ProjectionParams.xを掛けている理由ですが、これはY軸がプラットフォームによって反転しているためです。プラットフォームによる違いを吸収するための値が_ProjectionParams.xの中に入っています。UNITY_MATRIX_MVPや_ProjectionParamsのようにUnity側が事前に値を入れてくれている変数の一覧はこちらに載っています。シェーダー用のビルトインのグローバル変数をいくつか提供しています。現在のオブジェクトの変形行列、ライトのパラメーター、現在の時間などです。つの違いは、宣言する必要...Y軸のプラットフォームによる違いを考慮せずに座標変換後の値を操作してしまうと思った通りの位置に表示されなくなってしまうため注意して下さい。右上に表示されるようになった緑色の3の部分だけ見えているということは、きれいに板ポリの中心部分がスクリーンの右上にずれたことが分かりますね。_MainTex);テクスチャのUV値を計算する処理です。全部大文字なのでピンときた方もいるでしょうか。TRANSFORM_TEXはUnityCG.cgincでdefineされたものです。name##_ST.zw)_STのように文字列に対して結合する場合は##が必要ですが、変数名としてそのまま使う場合はtex.xyのように利用できます。展開すると以下のようになります。_MainTex_ST.zw;頂点データから受け取ったUV値にTilingとOffsetの値を反映させるための処理です。これはつまり、TilingとOffsetを使わないテクスチャならそのままUV値を渡してあげれば問題ないということになります。これも覚えておきましょう。v.uv;UNITY_TRANSFER_FOGUNITY_TRANSFER_FOG(o,o.vertex);こちらもUnityCG.cgincでdefineされているフォグの処理です。プラットフォームによって使い分けられていますが、基本形は以下です。(outpos).z展開すると以下のようになります。o.vertex.z;フォグは奥に行けば行くほど色が乗る処理でしたね。頂点シェーダの段階では、座標変換後のz値(奥行き情報)をfogCoordに移しています。ここで移しておく必要があるかということですが、o.vertex、つまりSV_POSITIONはスクリーン上のどのピクセルを対象にフラグメントシェーダを実行するか決めるための特殊なセマンティクスです。他の値と異なり、そのまま補間されて渡されるわけではなく全く異なる値に変換されてしまいます。そのため、TEXCOORDで定義したfogCoordに移しています。col;}形式としては以下の通りです。フラグメントシェーダにはセマンティクスが必要です。古いシェーダでは「COLOR」が使われていることもありますが現在は「SV_Target」で問題ありません。v2fの変数名は頂点シェーダからの入力(input)ということで「i」とすることが多いです。戻り値がfloatではなくfixedになっていますね。実はシェーダ上で小数点を扱う時はfloat以外にhalf、fixedがあります。分かれているかというと、パフォーマンスを限界まで上げる必要があるモバイル端末に対応するためです。それぞれ使うビット数と扱える値の範囲、精度が異なります。Unityのドキュメントに載っているので引用します。fixed11bit、-2〜2の範囲、1/256の精度half16bit、-60000〜60000の範囲、小数点以下3桁までの精度float32bit、範囲は他の言語と同じで非常に広いfixedは1/256より小さい値が扱えないので使える場面が限られますが、色は0〜1で表現することは先述した通りです。色の階調は256段階なので、1/256より小さい値はそもそも必要ありません。fixedはまさに色を格納するための入れ物と言えます。計算途中の段階でfixedを使ってしまうと1/256より小さい値がカットされてしまう分、誤差が出る可能性があります。最終的な出力を格納するためであればfixedで全く問題ないですが、計算段階で誤差が出ては困る場合はhalfを使いましょう。fixed、half、floatが使い分けられるのはあくまでモバイル端末のGPUに限った話です。PCではfixedやhalfと書いてもfloatとして扱われます。floatとhalfの使い分けではなぜ今までfloatしか出てこなかったかというと、扱ってきたのが座標とUV値だったからです。頂点シェーダの段階では3D空間の座標を計算に利用するわけですが、Unity上で座標は-100000〜100000の値を扱うことができます(これ以上は警告が出ます)。halfでは-60000〜60000までしか表現できないので、範囲が足りません。座標変換が行われた後の値は-1〜1に凝縮されているので、小数点以下3桁まででは精度が足りません。xの値はスクリーンの幅が1000より大きいと表示される位置がずれてしまう可能性があります。UVも同様に、小数点以下の小さな値でズレが生じてしまうことを防ぐためにfloatを使うことが多いです。座標やUV値のように細かい精度が必要ない値であれば、halfを使っても大丈夫です。パフォーマンスを限界まで高めるためにfloatを避けてhalfやfixedを積極的に使うこともあります。テクスチャサンプリングそれでは処理の中身を見ていきます。i.uv);まずこちらですが、tex2D()は組み込み関数です。指定したテクスチャから、指定UV値の色を取り出す処理です。この色を取り出すことを、テクスチャサンプリングと言います。注意することがあるとすればUV値の起点はテクスチャの中央ではなく左下であることです。モデリングをされている方ならご存知の通りですね。UnlitShaderではこれ以降の計算はフォグの1回だけなので、fixedで色を受け取っているようです。色を反転させてみるではここでフラグメントシェーダを少し編集して、色が反転することを確認してみましょう。色の反転はとても簡単です。このように1から本体の色を引けばOKです。このままだとα値まで反転してしまいますが、このシェーダではα値を考慮しないので影響はありません。色が反転した先ほどから使っているテクスチャの色が反転したことが確認できました。UNITY_APPLY_FOGいよいよ最後の1行です。フォグの適用処理です。col);例のごとくUnityCG.cgincでdefineされています。実は頂点シェーダで出てきたUNITY_TRANSFER_FOGはモバイル版が複雑だったので無視してPC版を載せたのですが、今度はPC版が複雑なのでモバイル版を載せます。それでもdefineが3段階に分かれていて超ややこしいです...。ある意味最後に相応しいかもしれません。Squared)による違いが出る部分です。モバイルでは高速化するため頂点シェーダで事前に計算行い、PCでは精度を高めるためにフラグメントシェーダで計算を行うようです。このままでは意味が分からないので、こちらもパズルを解いて展開します。僕もフォグの処理内容を見るのは初めてです。saturate(i.fogcoord.x));かなりシンプルになりました!unity_FogColorは、フォグの設定ウィンドウで設定した色が渡されます。lerpまずlerpですが、線形補間を行う組み込み関数です。厳密には違いますが計算イメージはこんな感じです。4フォグのように色を計算する場面ではよく登場するので、覚えておきましょう。saturate受け取った値を0〜1の間におさめてくれる組み込み関数です。マイナスの値は0に、1を超えた値は1に、それ以外の値はそのまま返します。saturateに渡しているi.fogcoord.xは頂点シェーダの段階で座標変換後のz値を格納したものでしたから、奥行き値を0〜1に収めている処理です。テクスチャ色に寄せるかフォグ色に寄せるか以上を踏まえると、奥行き値に応じてサンプリングしたテクスチャ色に寄せるか設定したフォグ色に寄せるかこれを判断して最終的な色を決定する処理ということが分かりました!順番、逆じゃない?ところでもう一度lerpを見てみましょう。順番は逆じゃないの?と思った方もいるでしょうか。これは僕が省略してしまった部分です。超絶ややこしいのでプログラムは省略することに変わりはないのですが、説明はちゃんとします。Nearクリップ、Farクリップ頂点シェーダで座標変換した後のxとyの値は-1〜1でスクリーンの端から端までに対応していました。PanelsのNearクリップ、Farクリップが関係しています。Nearクリップ・Farクリップこの設定であれば、カメラからの距離が0.3〜1000に収まっているものだけ描画します。座標変換後のz値は、これと対応しています。ちなみにマイナス方向は使わず、0〜1になります。(後述しますがプラットフォームにより変わる)Unity5.5から挙動が変わった実はこのz値、Unity5.5から0がFar、1がNearに変更されました。Unity5.4までは0がNear、1がFarでした。当時はこの変更でバグが頻発しました...。こんなややこしい変更が入ってしまったのかはUnityドキュメントのUnity5.5へのアップグレードガイドで解説されています。Zバッファ(デプスバッファ)の方向が反転されました。ファイティングが削減されシャドウの質が向上します。これは特に、小さなニアのクリッピング平面と大きなファーのクリッピング平面を使用する場合に顕著です。Unityドキュメントから引用ここで登場しているZバッファについては、後ほど半透明描画と一緒に解説します。Zファイティングというのは2つのポリゴンが同じ位置に重なった時にちらついて見えてしまう現象のことです。要は反転させることでハードウェア処理的にZファイティングの軽減と影の質が向上する効果を得られるから反転したよ。ちなみに反転後かどうかはdefinedで取得できます。defined(UNITY_REVERSED_Z)OpenGL環境では更に挙動が異なるUnityを使っていても、最終的にハードウェアに合わせてDirectX、OpenGL、Metalなどプラットフォームに合わせたグラフィックスAPIに変換されます。Unity本体(C#側で実行する処理)も含めDirectXやMetalは座標が左手座標系で扱われてZ値のプラス方向が奥になります。OpenGL系統は右手座標系でZ値のマイナス方向が奥です。Unityにおいても、シェーダがOpenGL系統で実行される場合はシェーダ内のみ右手座標系になります。最近のiOSやMacはMetalが動きますが、まだOpenGL系も現役なので考慮しないといけません。OpenGL系ではUnity5.5以降、Farが0でNearが-1です。フォグの処理ではUnity5.5以降かどうか、OpenGL系かどうかも考慮して0がFar、1がNearとして扱えるように変換されていたわけです。そのため非常にややこしい分岐が発生していてコードは載せませんでした。こんなことを気にしないといけないのはあくまでフォグのように座標変換された後のz値を使わないといけない場合です。通常シェーダを書く際はUNITY_MATRIX_MVPの行列がこのあたりのプラットフォームによる差異を全て吸収してくれているので気にする必要はありません。いえここが原因で悩んでしまうこともあり得るので覚えておいて損は無いでしょう。C#側は左手座標系の前提で問題ありませんが、C#側で作成した行列をシェーダに渡す時は注意が必要です。詳しくはこちらを参照して下さい。いうより、明けましておめでとうございます。understrap-re以上でUnlitShaderの解説は終了です。お疲れ様でした!これで少なくともUnlitShaderの中に出てくる要素についてわからないことは1つも無くなったはずです。シェーダに手を付け始めた頃の僕も含め、シェーダをある程度書いている人でも個々の要素についてはなんとなーくの理解で済ませていることが多いはずです。これからは「シェーダ完全に理解した」と言ってしまいましょう(笑)ここから先は応用の内容に入っていきますが、とても重要なのでよろしければ最後までお付き合い下さい。スポンサーリンク半透明描画用にカスタマイズ新しくシェーダを作成それでは新しくUnlitShaderを作成し、半透明用に内容を書き換えていきます。名前は「AlphaBlendUnlit」としました。"Unlit/AlphaBlendUnlit"透明度のプロパティを追加Propertiesブロックに透明度を制御するパラメータを追加します。1}正確には不透明度です。0〜1で設定したいので、Rangeにしました。このプロパティをシェーダで受け取るためにグローバル変数も用意します。_Alpha;floatほどの精度は必要ないので、half型にしました。フラグメントシェーダで計算テクスチャサンプリングした後の色のα値に対して、_Alphaを乗算します。_Alpha;乗算を行うため、精度によるズレが発生しないようfixed4をhalf4に変更しました。いえ誤差レベルなのでfixed4のままでも問題ないと思います。透明度の計算はこれで終了です。ここまでの内容だけではフラグメントシェーダから出力したα値は特に利用されません。半透明にするには、ブレンドモードを変更しアルファブレンドが行われるようにする必要があります。以降はSubShaderの冒頭に記述を追加していきます。LODは特に使わないので削除しました。RenderTypeもTransparentにしておくとパフォーマンスが上がるので変更しておきます(Opaqueのままでも、半透明描画自体は可能です)。これでは全く意味が分からないので、まずはこのBlend指定の形式について見ていきます。[DstFactor]SrcFactor、DstFactorと言われてもまだ意味が分からないですね。逆算すると分かりやすいです。決めるための係数です。最終的に出力される色は、これらの係数が掛けた値を足し合わせたものです。それでは改めてアルファブレンドの指定を見ていきましょう。これを計算式に当てはめてみます。α値)そう、lerpです!!つまりアルファブレンドとは既に描画されている色とフラグメントシェーダの出力色を、α値を係数にして線形補間するブレンド方法アルファブレンドを指定したことで無事に半透明で描画されるようになりました。他のブレンド方法SrcFactorとDstFactorには以下のものが利用できます。組み合わせについては、よく使うものだけ覚えておけば基本的に問題ありません。覚えなくてもどこかにメモしておけば大丈夫です。Oneそれぞれ1を掛けるということは、そのまま足すということです。明るい表現ができることから、エフェクトでよく使われます。One加算合成だと眩しくなってしまうことが多いため、既に描画されている色も考慮しつつ加算する合成方法です。Zero単純に色をかけ合わせます。描画されていた色に馴染ませる効果があります。SrcColor1以下の数で掛け算をするということは必ず数が小さくなる(色が暗くなる)ので、足し合わせることで明るさを維持する効果があります。乗算合成Zバッファ半透明描画はできるようになりましたが、これで終了ではありません。このように手前にあるものを半透明にすると、奥にあるものがくり抜かれてしまいます。奥にあるものがくり抜かれてしまうこの現象はZバッファによって引き起こされています。Zバッファ(デプスバッファ)とはフラグメントシェーダから出力され、ブレンド計算した後の色はそのままスクリーンに反映されるわけではありません。カラーバッファというピクセル単位で色を格納するための領域にコピーされ、全ピクセルの色情報を格納し終わった後にまとめてスクリーンに反映されます。実はカラーバッファの他にZバッファというものも用意されています。Zバッファには色情報ではなく、座標変換した後のz値が格納されます。座標変換後のz値は、0がFarで1がNearでしたね。この値がそのまま入っています。カメラにClearFlagsという設定項目がありますが、これは対象カメラの描画を実行する前に初期化するバッファを指定するものです。ClearカラーバッファもZバッファもそのままZバッファを使って何をしているのかというと、奥にあるオブジェクトが描画されてしまうことを防いでいます。この処理をZテストと言います。Zテスト(デプステスト)Zテストの内容はSubShader内に記述することで変更することができます。省略した場合は「LEqual」になります。の略なので、値が小さいか等しかったら。すると、LEqualは次の条件を満たした時に成功になります。dstZUnity5.5以降はZバッファの値が反転していますが、Zテストのタイプ名は変更されていないので意味が逆になっている点に注意して下さい。1がNearなので、描画しようとしているピクセルのほうが等しいか近くにある場合は成功。Zテストが成功したときのみ、カラーバッファとZバッファに新しい値が格納されます。いいのでは?ということでちゃんと用意されています。Lessしかし、これはZファイティングが起きやすく、使いづらいです。Zファイティングそのため、デフォルトはLEqualです。Zバッファが反転した効果もあり、Zファイティングはかなり起きにくくなっています。Greater(GEqual)Lessと条件が反転します。手前にオブジェクトがある時だけ奥のものが描画されることになるので、ちょっと特殊な表現が可能です。AlwaysZテストを必ず成功で返します。隠れていても必ず描画されるといった表現が可能です。勘の良い方は気づいているかもしれませんが、奥にあるものを描画したくないなら奥から手前に向かってオブジェクトを描画すれば奥のものは自然に隠れてくれます。あれ、Zテストいらない...?いえいえ、ちゃんとZテストには意味があります。Zテストが失敗した時は、フラグメントシェーダがそもそも実行されません。奥から手前に向かって描画する場合、一度塗ったのに後から上塗りされて隠れてしまうようなピクセルに対してもフラグメントシェーダを実行することになります。手前から奥に向かってZテストを実行しながら描画すれば、不要な処理を省略することができます。これによる負荷軽減効果はとても大きいです。そのため、Unityのレンダリングパイプラインではオブジェクトは手前から奥に向かって順番に描画されます。Zテストと半透明は相性が悪い最初の奥のものがくり抜かれてしまう現象に戻りますが、Zテストと半透明は相性が悪いです。Zテストは「奥のオブジェクトは手前のオブジェクトで隠れてるはず」という前提に基づいて実行しています。そのため半透明にした場合でも奥が見えて欲しいのにZテストに失敗してくり抜かれてしまうわけです。レンダーキューの設定そこで、半透明オブジェクトを描画する時は奥から手前に向かって描画するようにします。これは、レンダーキューの設定によって実現できます。SubShaderのTagsに追加します。"RenderType"="Transparent"}レンダーキューは、マテリアルの設定で上書きすることもできます。Inspectorから設定できるこの項目ですね。レンダーキューシェーダで設定していない場合はデフォルトのGeometryで2000ですが、Transparentに変更したことで3000になりました。レンダーキューは、オブジェクトの描画順を制御するためのものです。シンプルに、数字が小さいものから順番に描画される仕組みです。2999までと3000以降では挙動が違う2999までと、3000以降(Transparent)では描画順のソート方法が異なります。2999までは、全く同じレンダーキューの値であれば手前から奥に向かってソートします。3000以降は、全く同じレンダーキューの値であれば奥から手前に向かってソートします。2999までは不透明なオブジェクトを描画するために使い、3000以降は半透明なオブジェクトを描画するために使うレンダーキューということです!そのため、3000番はTransparentという名前が付いています。奥のものも見えるようにこれで半透明描画は無事にできるようになりました。奥のものがちゃんと見えています。奥のものも見えるようになったしかし、使い方によってはこれで不都合が起きてしまうことがあります。ソートはオブジェクト単位であるこちらはAssetStoreから拝借した家のモデルを半透明にして、更にその中に半透明のボックスを入れたものです。ボックスを前後に動かすと、ある一定の位置からボックスが消えてしまうのが分かると思います。こんなことが起きてしまうのかというと、同一レンダーキューにおける奥から手前のソートはオブジェクト単位で実行されるためです。ソートの基準になる座標はモデルの中心なので、家のように大きいモデルだとはっきりと不都合が出てきます。簡単な対応法としてはレンダーキューを調整することです。家の方に適用しているマテリアルのレンダーキューを3001に変更し、ボックスよりも後に描画されるようにします。レンダーキューを3001に変更ちなみにシェーダ側のTagsで3001にする場合は以下のように書きます。"Queue"="Transparent+1"こうすることでかなり手前に持ってきてもちゃんと描画されます。手前に持ってきても描画されるようになったしかし更に問題は残ります。ボックス自体も半透明なので、ボックスが先にZバッファ書き込みを行ってしまうことでボックスより後ろの部分については描画されなくなります。ボックスより奥の部分は描画されないこのように家の中に入っている状態であれば見た目的にそんなに違和感を感じないので問題ないかもしれませんが、はっきりと違和感が出てしまうのはボックスを家よりも手前に持ってきた時です。ボックスが家の外に出ている時これでは半透明というよりも色が薄くなっただけのように見えてしまいますね。家よりも手前にきている時は、ボックスのレンダーキューを家のレンダーキューよりも上げた方が自然に見えます。残念ながら、レンダーキューを良い感じに自動調整してくれる機能はありません。レンダーキューの調整は手動か、C#スクリプト側で条件を付けて調整してあげる必要があります。Zバッファ書き込みの設定いよいよ最後の項目です。レンダーキューを調整するのは大変ですし、これまで解説してきたような知識を持っていなければレンダーキューの調整という発想すら出てきません。そこでUnityのビルトインシェーダに含まれる半透明系のシェーダでは、Zバッファの書き込みをしないという選択をとっています。これはSubShaderのオプションに一行追加するだけで簡単に設定できます。Offしかしこれでも問題は残ります。ソート順の影響は残るまず、Zバッファの書き込みをやめても結局ソート順の影響は受けてしまいます。ソート順の影響は受けるアルファブレンドはα値を利用して既に描画されている色との線形補間を行うものでしたね。Zバッファを書き込まないということは、後から描画されるオブジェクトが自分のα値を使って色を決めてしまうということになります。後から描画されるオブジェクトのα値が1なら既に描画されている色は一切考慮されません。それでもオブジェクトが消失してしまうよりはまだ良いかもしれません。見えて欲しくないところまで見えるZバッファは同一オブジェクト内におけるポリゴン同士の前後関係を正しく表示することにも役立っています。Zバッファの書き込みをやめてしまうことで同じメッシュ内の全てのポリゴンがZテストに成功します。α値を1まで上げると本来は不透明なはずですが、見えないはずの奥の柱なども見えています。off複雑な形状をしたモデルであればあるほど、エグい見た目になってしまいます。結局どうしたらいいの?残念ですが、これはケースバイケースとしか言えません。半透明を自然に見せることについては、シェーダ単体で出せる完全な解決法がないのです。レンダーキューを調整したり、シェーダを使い分けていく、その他ゲームで見せたい画に合わせて工夫していくことが必要です。僕はZバッファの書き込みをやめることは極力避けて、レンダーキューの調整で頑張るようにしています。見た目的にもよくないしモデラーの立場から考えても見えて欲しくないところが見えるのはイヤですからね。これはあくまで3Dオブジェクトを半透明にしようとした場合の話で、2Dの場合はZバッファの書き込みはオフで問題ありません。いった諸問題や、色を何度も塗り直す関係でパフォーマンスへの影響も大きいことから、半透明は避けた方が良いというのはよく言われることですね。Inspectorからも設定可能にするそれでは最後にZバッファの書き込みをマテリアルのInspectorからOn/Offできるようにしておきましょう。先述した通り、ケースバイケースで対応できるようにするためです!Unityのビルトインシェーダに合わせて、デフォルトはOffにしておきます。Propertiesブロックに以下を追加します。0SubShaderのZバッファ書き込み設定を以下のように変更します。[_ZWrite]Toggle属性はマテリアルのInspector上にチェックボックスを表示して、オフなら0、オンなら1を対象プロパティに設定してくれます。囲むことで使用できます。ZWriteの設定は0がOffで0以外ならOnに対応するので、これでデフォルトはOffに、チェックを付ければOnになります。最後に今回作成したシェーダの全文を載せておきます。シェーダ参考サイト今回の内容からのステップアップ、理解を深めるために良い記事などをいくつか紹介しておきます。シェーダの情報を探す時は、最初に解説したレンダリングパイプラインは何を使っているのか、サーフェスシェーダの内容なのか、頂点フラグメントシェーダの内容なのかといったことを見落としてしまうと混乱します。今回はUnity標準のレンダリングパイプラインで、Forwardレンダリングでした。Forwardレンダリングを使っているのにDeferredレンダリングの内容を参考にしようとすると落とし穴にはまります。ラスタライズの概念は、Forwardレンダリングにしかありません。シェーダはややこしい概念が多いため、誤情報もよく見かけます。エフェクト統一シェーダがあると捗るよねSEGAさんの技術ブログ記事です。シェーダバリアントを活用して複数の機能を1つのシェーダにまとめる知見が得られます。BlogみなさんUnityしてますか?僕はここ5年くらいはUnityしまくってます。モバイル開発はUnityばっかりになりました。他にも有力なゲームエンジンはあるのですけど、いろいろなハードに出すということや運営面を考えると、セガでは現状Unityがとても強...LIGHT11記事の中でもいくつかリンクを貼らせて頂いたブログです。分かりやすいUnityシェーダの記事が多いのでオススメです。LIGHT11外部サイトLIGHT11LIGHT11Renderingタグからレンダリング関連に絞って記事一覧が見れます。今回はライトの影響を考慮しないUnlitShaderを解説しました。ライティング関連の記事も多いのでライティングについて学びたい場合は特にオススメです。7日間でマスターするUnityシェーダ入門サーフェスシェーダを利用して色々なシェーダを作る内容です。サーフェスシェーダについても知っておきたい方はもちろん、作ったシェーダは頂点フラグメントシェーダに応用することも可能です。おもちゃラボこの記事は、これまでにおもちゃラボで紹介してきたUnityのシェーダ入門記事40本のまとめです。1日に5記事読めば7日間で読み切れるはず...今のところ(笑)シェーダって時々聞くけど難しそう・・・というイメージをお持ちの方も多いと思います。Unityを使えば...グラフィックスパフォーマンスの最適化Unityドキュメントです。モバイルやVRなど、パフォーマンスが要求される環境に向けてシェーダを書く場合は必読です。マニュアル多くのゲームにとって高いパフォーマンスは重要なことです。ゲームのレンダリングのスピードを最大化するための簡単なガイドラインを以下でまとめます。CGエンジニア検定/コンピュータグラフィックス僕が書いたCGエンジニア検定の紹介記事です。検定そのものは学生さんならオススメですが、この中で紹介している本『コンピュータグラフィックス』は今回解説したようなレンダリングの基礎知識が詰まっているので、体系的にCG技術について学んでみたいと思った方はぜひ。Career今回はゲーム開発、特にグラフィックスプログラミングをするなら知っておいて損は無いCGエンジニア検定について紹介します。現役ゲームプログラマーが自信を持ってオススメします。『数学ガールの秘密ノート』シリーズも紹介していますが、今回は省略した数学(ベクトルや行列など)を理解するのにとても役立ちます。数学をある程度理解しておくことでシェーダを書く効率が上がるのは間違いないので、苦手という方にはオススメです。あとがきこんな長い記事を最後まで読んで頂き本当にありがとうございました!この記事には、これからシェーダを学びたいと思っている人がいたら「これ読んで!」と言うつもりで僕の持っている知識を詰め込みました。この記事が皆さんのシェーダ理解に役立てたのなら何よりです。内容が良かったらぜひ他の人にもオススメしてもらえると嬉しいです。この記事は、今後も加筆・修正していく予定です。分かりにくかった点、改善して欲しい点、もっと詳しく知りたかった点などあればTwitterもしくはお問い合わせページからご連絡下さい。次に書くシェーダ関係の記事は画面全体に対して効果を与えるポストエフェクトをテーマにする予定です。アマガミナブログUnityエンジニア天神いなスマートフォンゲーム会社で新作ゲーム開発のリードエンジニアをしながら個人ゲームの開発もしています。当サイトでは、UnityやC#、シェーダなどゲーム開発の技術的なことを扱ったり、自作ツールの紹介などをしています。