[Unity]ShaderLab入門とか

今年から会社で所属している部署が変わりました。

今まではWebの設計、見積もり、バックエンド実装(たまーにフロントも)でした
が、今年頭から3DCG系の、要するにゲームみたいなの作るところに
異動になりました。

もともと3DCG系のプログラミングができるから異動したわけではなく
ほとんど未経験だったところに、ちょうどいい規模の案件と
お世話になってるディレクターさんの調整が入って、希望したところ異動になりました。

というわけで今ヒーヒー言いながら勉強中です><

主にUnityを使って開発をしているんですが、やっぱり今までの業務と
一番違うのが、シェーダーの記述、です。
(Blender, Photoshop, AfterEffectsとかもちょっとずつやっていきたい)

今日はUnityで利用されるシェーダー(ShaderLab, Cg/HLSL)について、
同梱されているものを参考におさらいしていきます。

参考リンク Unity Shader まとめ

まずはダウンロード

Unity ダウンロードアーカイブ

上記のリンクから、自分が利用しているUnityのバージョンに対応する
ビルトインシェーダーをダウンロードしてきます。

これに、Unityをインストールした時に最初から含まれるデフォルトで
利用できるシェーダーが含まれています。

この中から比較的シンプルなものを解説に用います。

※ここで取り上げるのはVertex/Fragment shaderです。
SurfaceShader、ImageEffect、ComputeShader等は取り上げません。

Sprite-Default.shader

Sprite用のシェーダーです
以下がソースコードになります。

最小構成

上記にあるものを急に見てもよくわからないので、上記の
うちからシェーダーとして定義された最小構成を抜粋します

Propertiesブロック

参考: Unity ShaderLab : プロパティ

サンプルコードからPropertiesの内容だけ抜粋します。

基本的な文法

  • [PerRendererData] _MainTex (“Sprite Texture”, 2D) = “white” {}
    • [PerRendererData]
    • このように角括弧でくくられたものはプロパティの属性の宣言です。
    • この属性の意味自体は、ちょっと曖昧なので上記参考リンクを参照してください
    • _MainTexという変数を定義
    • インスペクタ上では Sprite Textureと表示される
    • 2Dという型 (テクスチャを選択させるときに使う)
    • デフォルト値として”white”を選択
  • _Color (“Tint”, Color) = (1,1,1,1)
    • _Colorという変数を定義 
    • インスペクタ上では Tint と表示される
    • Colorという型 (色を選択させる)
    • デフォルト値としてRGBA(1,1,1,1) = 透過なしの白 を設定
  • [MaterialToggle] PixelSnap (“Pixel snap”, Float) = 0
    • [MaterialToggle]
    • この属性がついた値はインスペクタ上でチェックボックス切り替えになる。
    • PixelSnapという変数を定義
    • インスペクタ上は Pixel Snap と表示される
    • Float型で値は0

全部書くとこんな感じで、おおまかにいうと、数値、色、ベクトル、テクスチャが定義できる
以下に定義をまとめて列挙しておきます。

SubShaderブロック

参考:Unity ShaderLab:サブシェーダー

ここに具体的な処理内容を書いていきます。
ここはかなり大きくなるのでさらに分割

ついでなんだけど以下公式から引用

Unity がレンダリングするサブシェーダーを選択するとき、定義された各パスごとに(光の相互作用によっては更に多く)オブジェクトを 1 回レンダリングします。オブジェクトのレンダリングは高価な処理であるため、シェーダーをできるかぎり最小の量のパスで定義すべきです。当然、いくつかのグラフィックのハードウェアでは必要なエフェクトを 1 回のパスでは実現できないことがあるので、その場合は複数のパスを使用するしかありません。

これ、WebGLのfurshaderをUnityに移植する、って記事があって
そこで言われてた件ですね。

記事にあるShader購入してソース見てみたんですが、このパスを何回も書くことで
処理させていました。うまい方法あったらいいな。。。

Qiita: ファーシェーダーを移植してみた

Tagsブロック

参考:[Unity ShaderLab: サブシェーダータグ]

単純なKey-Valueですね。好きなだけ定義できるみたいです。

すべてユーザ定義じゃなくて、Unityがデフォルトで解釈するタグがあります。
そのうちのQueueのみ紹介。ほかはリファレンス確認してみてください

  • Queue
    • レンダリングの順番を定義
    • 値は次の通り(すべて内部的に整数インデックスされている)
    • Background
      • インデックス 1000
      • 他より前にレンダリングされる。skyboxなどに使用
    • Geometry(default)
      • インデックス 2000
      • デフォルト値でほとんどのオブジェクトに使用される。不透明なオブジェクトに使用
    • AlphaTest
      • インデックス 2450
      • アルファテストする形状で使用。
      • アルファテストのオブジェクトをすべての不透明オブジェクトの後に描画したほうが効率的なのでGeometryとは別になってる
    • Transparent
      • インデックス 3000
      • Geometry, AlphaTestの更に後。
      • アルファブレンディングするものはすべてここにあるべき(ガラスやパーティクルエフェクト等)
    • Overlay
      • インデックス 4000
      • オーバレイエフェクトのためのもの
      • 最後にレンダリングするべきものはここ
    • 値は "Queue" = "Geometry+1" のようにしてインデックスを明治することもできる

その他

これら。

これらはPassブロック内にも記述できる

参考:Unity ShaderLab : カリングと深度テクスチャ

このうちBlendだけはかなり細かい

参考: Unity ShaderLab : Blending

Passブロック

いよいよ本体。 ここは更に分割

CGPROGRM ~ ENDCG

Cgプログラムスニペットを含む場所を定義する。
要するにCGPROGRAMからENDCGまでの間に含まれるものは、Cgで書かれたプログラム。

#pragma

参考:Unity Cgスニペット

意味は以下のとおり

  • #pragma vertex vert
    • このシェーダーのVertexシェーダーはvertという関数ではじまることを定義
  • #pragma fragment frag
    • このシェーダーのFragmentシェーダーはfragという関数ではじまることを定義
  • #pragma multi_compile _ PIXELSNAP_ON

あと良く見かけるので

とか。下記引用です。

全ての OpenGL ライクなプラットフォームは “シェーダーモデル 3.0 相当の能力を持つ”ものとして扱われることに注意してください。WP8/WinRT プラットフォーム(DX11 フィーチャーレベル 9.x)はシェーダーモデル 2.0 相当の能力のみを持つものとして扱われます。

#include

これはC言語と一緒。
外部のモジュールやライブラリをインポートするときに利用します。

Unityでは、上記の UnityCG.cginc にUnityに関連する便利関数や構造体が
いっぱい詰まったものがよくincludeされているのを見かけます。

struct

これもC言語と大体一緒で、独自の構造体を定義しています。
少し違うのが、各行の末についている :(コロン)と COLORとかの文字。

僕はこれが一番頭が混乱しました。

これだけを挙げると

float4型(4つのfloatを持つ構造体)のvertexという変数を定義し
ここにSV_POSITIONを受け取るように、という意味なんですが、SV_POSITION
ってなんぞ、ということです。

これはセマンティクスと呼ばれるものです。
あとで纏める参考リンクから引用しただけになりますが

グラフィックパイプラインの各ステージ間で伝達する変数が何なのかを伝えるタグの役割をします。

※グラフィックパイプラインについては別途調べてください><

パイプラインの前のステージから、何を受け取って、何を返すか、という情報を
データ型とは別に宣言しています。

※蛇足1
(グラフィックのハードや歴史について一切無知なので、前提抜きでいうと)
そもそも汎用型にセマンティクスを宣言するんじゃなくて、専用の型を用意するか、
全部受け取って、全部返すようにはできないのかな。。。と思いました。。。
パフォーマンスの影響があるのかな・・・?
ハードが違っても吸収できるように、かな。

※蛇足2
v2fっていう構造体名はvertext to fragmentってこと、ですよね?たぶん。

ここで神資料

MSDN セマンティクス(DirectX HLSL)

ここに載ってるものがもらえるし、渡せる、って感じでいいのかな・・・・

随所に出てくる変数宣言

みたいなのがPassブロック内にいくつかありますが、これはPropertiesブロック内で
宣言したものと同名にすることで、そのプロパティの情報が格納されるようになります。

マスクやテクスチャ、何かしらの数値など、Inspectorから設定して
Shader内部で利用したいときにこのように宣言します。

Vertexシェーダ(頂点シェーダ)

ここで何をやっているのか。コメントに書いてみました。

Fragmentシェーダー

vertexシェーダーのあとに処理されるのがこちら
引数にはvert()で返されたOUTが入ってくる。

こんな感じ。こういう固定値出すだけのシェーダは簡単で、とにかく書き方とよく使われる型とか
構造体とかをまず覚えたいですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です