LoginSignup
4
2

More than 1 year has passed since last update.

シェーダープログラミングしない人のためのシェーダー入門

Last updated at Posted at 2022-12-14

前書き

ソーシャルコンピューティング研究者の@Yam3939です。
ACCESS Advent Calender 2022年の15日目へ投稿します。
昨日は、トニオ(@tonionagauzzi)さんのAndroid アプリ UnusedAppFinder をリファクタリングした話でした。
この記事は社内勉強会用に準備していた内容をマークダウンに書き直しました。

最近、これからWeb3だ、メタバースという声に誘われて3次元のレンダリングの勉強を始めました。その中に「シェーダー」の勉強もあります。
シェーダーという言葉は聞いたことがあるが、それが何をするかはまるでわからないという人のために、シェーダー入門の話をします。

想定する読者

・UnityもUnreal EngineもBlenderも触ったことがない人
・ColaboratoryでGPUオンにする以外 GPUを触ったことがない人
・PCにグラフィックカードを載せたことがない人
・3Dモデリングに縁がない人
・でもシェーダーって何ということに興味がある人

シェーダーって何?

シェーダーの定義はいろいろ

とりあえず調べたところでは人によってピンキリのようです。例えば:

  • 物体の見栄えを決めるプログラム
  • 陰影処理(シェードは影)
  • GPUのプログラム(音のシェーダもある)

結局、私の現在の解釈としては:

  • 最初は陰影を付けるのが計算量的に難しかったのでGPUに任せた。陰影のつけかたは多様なのでプログラマブルにした。GPUがプログラマブルになった瞬間に影だけでなくていろいろなイフェクトをつけられるようになった。GPUの進化と普及とともに「シェード(陰影)」はごくごく一部分になっている。

とはいえ、すべてのGPUプログラミングをカバーすることはできないので、とりあえず、ゲームエンジンのUnityでのシェーダーって何、というのを勉強していきます。

シェーダーとは何でないか?

シェーダーでないものを知ることによってシェーダーとは何かを知ることができます。

  • 直線があったときにピクセル上でかくかくしないようにするのはシェーダー以前で対応する
  • 物体の3Dモデルに対して、カメラの位置を決め、光をあてて、ピクセルの色を決めるのがシェーダー

シェーダーはプログラム

  • Unityで見た目を作るためにモデルに付け加えるいろいろなプログラムがシェーダー。UnityのAPIを使っていろいろプログラムする。
  • マテリアル、テクスチャーとともに見栄えを制御(下図参照)
    shader20221214a.PNG
  • グラフィックAPIを読んでプログラムするのがシェーダー
    20221214qiita-b.PNG

グラフィックAPIとはDirectX, OpenCV, Metal, Vulkan, ...というライブラリ群が提供するものです。当然、当該デバイスが対応するライブラリをサポートしていなければAPIを呼んでプログラムしても動きません。

レンダリングパイプラインからデバイスまで流れは

レンダリングパイプラインからデバイスまでの流れは下図のとおりです。
20221214qiita-c.PNG
デバイスで対応のAPIをサポートしていないとAPIを書いても動きません。
マルチデバイス用にはサブシェーダーと言って
シェーダー内にAPI用にそれぞれシェーダーを書いたりします(実行可能なものだけが実行されます)

Unityのレンダリングエンジンとシェーダー

Unityのレンダリングパイプライン

Unityのレンダリングパイプラインは3つあって、下図の通りです。
20221214qiita-d2.PNG
ユーザがシェーダーをかけるのはUnity SRP (Scriptable Rendering Engine)と言います。紛らわしいですが、これはレンダリングエンジンのクラスの呼称です。 Unity のSRPは上図のURP, HDRPです。
Unity 2019.3でLW RP(Light Weight)がURPという名前になりました。今後は URPが推奨されます。BRPの機能が今後URPで実装されるようです。

Unityのシェーダーグラフ

シェーダーグラフというのはグラフ表現でシェーダーの機能を実現するUIです。
グラフィックUIでデザイナーがシェーダーを使えるようにしたUnity上のシンタックスシュガーです。
例は下図のとおりです。
20221214qiita-e.PNG
出典: https://unity.com/ja/features/shader-graph

以下の例のようにノードとノードをむすぶさまざまな機能が提供されています。

  • One Minus 0-1を反転
  • Step 閾値
  • Remap 範囲調整
  • Time 経過時間
  • Multiply 掛け算
  • Lerp 線形補間
    ・・・関数の数だけグラフがある:プログラマには苦痛に見えます。もっとも自分でシェーダープログラムすればそれはそれではまる予想ができます。
    関数毎にノードが用意されているので面倒で、デモ動画でも
    グラフが錯綜して何度も整理することになっていました。

3Dモデルのレンダリングの全体像とシェーダーの位置づけ

  • ステップ1:3Dモデルを準備(描画とはピクセルに点を打つこと)
  • ステップ2:Transformの値を4x4に変換(モデル行列を渡す、グローバル座標に変換)
  • ステップ3:頂点毎に描画位置を算出(モデル、モデル行列、カメラで決まる)
  • ステップ4:表裏を調べ、裏なら描画しない(3点の右回り、左回りで表が決まる)
  • ステップ5:描画点を確定(頂点以外の点が3頂点の中に入っているかを決める)
  • ステップ6:描画点を深度バッファと比較(手前に点があれば書かない)
  • ステップ7:色を確定(ライティング、テクスチャ、シャドウ、フォグ)
  • ステップ8:点を打つ(ブレン関数を適用可、半透明、深度バッファ更新)
    ステップ1、2はCPU、ステップ3から8はGPUの仕事です。
    ステップ3が頂点シェーダーで、
    ステップ7がフラグメントシェーダーの仕事です。

頂点シェーダーの例

ステップ3:頂点毎に描画位置を算出(モデル、モデル行列、カメラで決まる)
シェーダーといっても頂点の計算をするだけ:

UnityObjectToClipPos(v.vertex); //頂点の座標をクリップ空間での座標に変換
fixed4 col = tex2D(_MainTex, i.uv);

このあと、カリング、ラスタライズを行います。

  • カリング:隠れて表示されないものを取り除く
  • ラスタライズ:3頂点の間にピクセルを埋める

フラグメントシェーダーの例

ラスタライズの後、フラグメントシェーダーを実行します(
DirectXではピクセルシェーダーというらしい)。

ステップ7:色を確定(ライティング、テクスチャ、シャドウ、フォグ)

  • シェーダーの名前を付け
  • Propertiesを宣言し
  • SubShaderでPassを定義(複数あってもいい)
    -- Passはシェーダーのマテリアルとレンダリングされるオブジェクトの頂点コードとフラグメントコードの実行を表す
    -- CGPROGRAM .. ENDCGで囲まれる
    フラグメントシェーダーの例は以下の通り(出典: https://docs.unity3d.com/ja/2019.4/Manual/SL-VertexFragmentShaderExamples.html)
Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // "vert" 関数を頂点シェーダーとして使います
            #pragma vertex vert
            // "frag" 関数をピクセル (フラグメント) シェーダーとして使います
            #pragma fragment frag

            // 頂点シェーダー入力
            struct appdata
            {
                float4 vertex : POSITION; // 頂点位置
                float2 uv : TEXCOORD0; // テクスチャ座標
            };

            // vertex shader outputs ("vertex to fragment")
            struct v2f
            {
                float2 uv : TEXCOORD0; // テクスチャ座標
                float4 vertex : SV_POSITION; // クリップスペース位置
            };
        // 頂点シェーダー
            v2f vert (appdata v)
            {
                v2f o;
                // クリップスペースへの変換位置
                // (モデル*ビュー*プロジェクション行列で乗算)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // 単にテクスチャ座標を渡します
                o.uv = v.uv;
                return o;
            }
            
            // サンプリングするテクスチャ
            sampler2D _MainTex;

            // ピクセルシェーダー; 低精度を返します ("fixed4" 型)
            // color ("SV_Target" セマンティック)
            fixed4 frag (v2f i) : SV_Target
            {
                // テクスチャをサンプリングして、それを返します
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

シェーダーの暗黙の了解

プログラムしている人が作法に従っていればこうなります。
作法に従っていれば何をやっているかを推定できます。

  • Unlit 光を考慮しない
  • Lit 光を考慮するのは当然なのでわざわざつけることはない
  • Diffuse 拡散。ざらざら。てかてかしていない。
  • Specular 鏡面反射。反射光も調節できる。
  • Transparent 透明。透明にするのは処理が重い。
  • Cutout  切り取り。切ったところは透明にするので重い
  • Emission 発光。発光するのには準備が必要。
  • Bumpt でこぼこにする。実は平坦。ポリゴン増やさない工夫。
  • Toon アニメ調、コミック調
    動くところを見たい方はこちら → 実例→Unity道場2D編 Shader Graph はじめてみよう!基礎編(6月23日号)- Unityステーション

シェーダーグラフによるエファクトの例

20221214qiita-f.PNG

出典: Unityとシェーダーグラフで簡単なポストエフェクトを作ってみよう!(チュートリアル動画 #1) https://www.youtube.com/watch?v=AQGgwgo51lo 14m09s
動いていないと面白くないので
動いているところを見たい方は上記YouTubeをご参照ください。

シェーダーとゲームの連結

シェーダーで描いたものをゲームに組み込む手順は下図のとおり
20221214qiita-g.PNG
トランジションとか(次のレベルへ行く)にエフェクトをかけてかっこよくします。

出典:

その他のレンダリングエンジンの紹介

Unreal Engine

Unreal Engineの最新版はUnreal Engine 5です。
最新の機能は以下の2つです。
20221214qiita-h.PNG
Naniteは仮想化マイクロポリゴンジオメトリを使い3Dモデルを効率的かつ高速にレンダリングする高性能なシステムです。

Unreal Engineのレンダリングパイプラインは以下のとおりです。
20221214qiita-i.PNG
出典:https://blog.jp.uwa4d.com/2021/11/05/ue5%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AE%E7%B4%B9%E4%BB%8B%EF%BC%9Ananite%E7%B7%A8/

GPUの進化に合わせてG-buffer, Visibility-bufferの2つを使って
レンダリングパイプラインを構成しています。

これについてはシェーダーを勉強するところまで到達しませんでした。

Blenderのレンダリングパイプライン

Blenderのレンダリングパイプラインは下図に示すように3つです。
20221214qiita-j.PNG

レンダリングはレイヤとパスに分割され、複数組み合わせたコンポジットが提供されています。
サードパーティからAdd onとしてレンダラーが提供されています。例えば、
Turbo Tools V2 (Cycles高速化Add on)です。

Blenderのシェーダーは:

  • サーフェスシェーダーは物体の面を扱う
  • ボリュームシェーダーは内部空間の密度を扱う
  • 特殊シェーダーは面ではなく全体や結果に作用する
    具体的には下図に示すようなものがあります。
    20221214qiita-k.PNG

これも今回はシェーダーの詳細には至りませんでした。

むすび

シェーダープログラミング未経験でシェーダーについて調べてみました。
ブラウザのエンジンとゲームエンジンは似通ったところがあるので、今後、メタバースのリアルワールドエンジンもこれらの延長上に作られるのではないかと思います。
引き続き、勉強していきたいと思います。

明日は@aRyoKurodaさんです。

参考文献

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2