murnana's diary

プリントの裏に書くとか、そんな感じです

勉強会「【はじめてのUnityShader】君の手でキャラクターの見た目を彩ろう!」に行ってきた

supporterzcolab.com

まんてらさんによるUnityシェーダー講座。内容は Unity上でのシェーダーが初心者の方 向けでした。

この記事の趣旨

「講演内容を見直したい」「もうちょっと詳しく知りたい」人向けです。

著者知識も混ぜつつ書きます。あと、分からなかったらマニュアル読めよ という暴力をふるう プログラマなので、ちまちまマニュアルのリンクを貼ります。

判りやすいのと正確さは別の話ですが、正確さに欠けていたら…マサカリください。私も知りたいです。

記事で書くこと

  • Unityでのシェーダーの構造(ShaderLabをざっくりと)
  • 頂点シェーダとフラグメントシェーダーで、モデルを1色に染める方法

記事で書かないこと

前提知識
  • Unityとは
  • Unityの基本的な使い方
  • UnityのHierarchyについて
  • コンピューターグラフィックスで使用される座標変換
そのほか
  • Properties について (次回内容のはずなので書きません)
  • テクスチャの貼り方 (次回内容なので書きません)
  • 開発環境の構築手順
  • 講演内容の再現手順
  • 宣伝

Unityシェーダー事始め

シェーダーは 画面に映像を出力する為の設計図です。本村・クリストファー・純也 さん曰く、料理のレシピに似ている

  • https://cgworld.jp/feature/1607-gtmf2016.html
  • カレーを作る手順は料理のレシピ
  • コンピュータグラフィックを描く手順はシェーダー
    • 正確には シェーダーを交えたレンダーパイプラインがレシピあたると思うがそんな細かいことはおいておこう

Unity の Matreialについて

https://docs.unity3d.com/Manual/class-Material.html

とりあえず、モデルにシェーダーを適用させるために必要なモジュールくらいでOK。

Shaderファイルについて

https://docs.unity3d.com/Manual/SL-Reference.html

Unityのシェーダーは「ShaderLab」言語で書かれています。
https://docs.unity3d.com/Manual/SL-Shader.html

ShaderLabはだいたい以下の形をとります。

Shader "MannteraSample" {
    SubShader {
        Pass {
            CGPROGRAM   // Cg言語で書く
            ENDCG
        }
    }
}
Shader

マテリアルの名前みたいなものです。アッパーキャメルで名前を付けると、大文字部分で区切るみたいです。これスラッシュじゃなかったっけ…? スラッシュで階層区切りができます。大文字区切りなんてないんや

e.g. `ManteraSample/Shader` だと、Materialは「ManteraSample > Shader」で選択可能。

なーんにも書かれていないシェーダーファイルは、「Not supported」から選択できます。

SubShader

https://docs.unity3d.com/Manual/SL-SubShader.html

Pass をまとめている親玉です。

同じシェーダーでもGraphics の設定やデバイスによって分けたい時などに、SubShader を複数作って分けることができます。

Pass

レンダーパイプライン内で実行される、シェーダ-プログラム本体です。 ここに、頂点シェーダーやフラグメントシェーダーを書きます。

Cgで書く - Cg、HLSLとは

まんてらさんはCgって言ってたけど、どっちでもいいっぽいです。
https://docs.unity3d.com/Manual/SL-ShaderPrograms.html

補足:

まじかー 書くならHLSLがよさそうですね。

Pass 内に書かれたマクロ CGPROGRAM ENDCG 内で書ける、シェーダー言語の一種です。

OpenGL には GLSLDirectX には HLSL という言語が使われます。CgNVIDIAが開発したシェーダー言語だそうです(でも古いって聞いたゾ)。

シェーダーに入力する頂点データの宣言

まず、Vertex Shader に頂点データを流し込みます。

頂点データは複数の引数で受け取ることもできますが、長くなりがちなので 構造体 として宣言を行い、1つの構造体を引数として受け取る事が多いです。

今回は以下の形で受け取ります。

struct appdata {
  float4 vertex : POSITION;
};

頂点の座標のみを受け取る構造体です。

float4 は4次元ベクトルであることを表します。 3次元ではない理由は、行列計算の都合があるためです。
(この辺のことは座標変換についての別記事を書く予定です)

POSITION は頂点座標である事を表します。この部分は セマンティクス ( Semantics ) といいます。 セマンティクスは色々ありますが、今回は説明を省きます。

頂点シェーダーからの出力と、フラグメントシェーダーが受け取るデータの宣言

もう一つ、頂点シェーダーが出力し、フラグメントシェーダーが受け取る構造体も宣言します。

正確にはフラグメントシェーダーが受け取るかもしれないデータ、ですが。

struct v2f
{
    float4 pos : SV_POSITION;
};

SV_POSITION は座標変換後の座標が入ります。

シェーダーのエントリーポイントを宣言

一つのシェーダーファイルに2つのシェーダーを収める為、宣言が必要です。

##pragma vertex vert
##pragma fragment frag

公式ドキュメント にある「HLSL snippets」を読むとわかる人は何となくつかめると思います。

##pragma vertex が頂点シェーダーの宣言 (vert がエントリーポイント)、##pragma fragment がフラグメントシェーダーの宣言(frag がエントリーポイント)です。

頂点シェーダー を書く

ここで、座標変換などを行います。

v2f vert(appdata v)
{
  v2f _out = (v2f)0;
  _out.pos = UnityObjectToClipPos(v.vertex);
  return _out;
}

appdatav2f は先ほど宣言した構造体です。

UnityObjectToClipPos 関数は、ざっくりいうと3次元の情報である頂点座標を、2D画面につぶしてくれる関数です。
詳しい話は https://docs.unity3d.com/Manual/SL-BuiltinFunctions.html で、どうぞ。

フラグメントシェーダー を書く

ここで最終的な色を決めます。

float4 frag(v2f _in) : SV_Target
{
  float4 col=(float4)0;
  col=float4(1,1,1,1);
  return col;
}

SV_Target は出力するデータを表すセマンティックスです。

今、これは単色(白色)を出力しています。
col=float4(1,1,1,1) の部分を変えれば色が変わります。


講演はここまででした。
テクスチャ貼ったりするのは次回だそうです。