渋谷ほととぎす通信

新しいこと、枯れたこと問わず大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ。基礎を大事にしています。

「シェーダ」とは何なのか、を自分の言葉でまとめてみる


f:id:esakun:20150730215258g:plain:w450

理解度がふわっとしている方向けに、シェーダを自分の言葉でまとめてみたいと思います。

アジェンダ

  1. 一言で「シェーダ」とは何なのか
  2. Unityにおけるシェーダの処理

これらを順番にまとめていきます。 ※調べていく中で発生した副産物は別途記事にしていく予定

1.一言で「シェーダ」とは何なのか

仕事をしていて、非プログラマからはシェーダってエフェクトでしょ?みたいな感じで思われている印象があります。
とりあえずググってみてみると、

シェーダ(英: shader)とは、3次元コンピュータグラフィックスにおいて、シェーディング(陰影処理)を行うコンピュータプログラムのこと。

Wikipedia:シェーダ

シェーダとは、物体そのものが元々持つ設定

教えてgoo:シェーダとは

レンダリングパイプラインにおいて、頂点単位の処理、ピクセル単位の処理 をカスタマイズするためのプログラムのこと

Slide Share:OpenGL ES2.0 一問一答

などなど。

などなどと書きつつ、あまりシェーダを一言で表現したページは見つかりませんでした。
僕なりに出した答えはこちらです。


「3Dオブジェクトをディスプレイに映し出すためのプログラム」


と定義しました。

もちろんシェーダでエフェクトを表現することもできますが、シェーダ自体がエフェクトなのではありません。あくまでシェーダとは3Dオブジェクトをディスプレイに表示させるためのプログラムのことです。 シェーダがメッシュの頂点をイイ感じに加工し、ピクセルに落とし込むことでエフェクトに見えるということです。


なぜシェーダは「3Dオブジェクトをディスプレイに映し出すためのプログラム」なのか

まずは3Dオブジェクトがディスプレイに映し出されるまでの流れを簡単に説明してみます。

f:id:bao_bao:20160508210300p:plain

  1. 表示したい3Dオブジェクトデータをとある筒に渡す
  2. とある筒からディスプレイに表示される

非常に簡略化するとこのような工程を踏みます。

このとある筒とは何かというと、これがレンダリングパイプラインと呼ばれる、ディスプレイに映し出すまでの流れ作業のような過程のことです。(グラフィックスパイプラインとも言う)

レンダリングパイプラインの中には非常に沢山の処理が含まれており、3Dオブジェクトデータをレンダリングパイプラインに渡すことで、様々な変換作業を経て3D上から2Dのディスプレイに表示されます。

下図のように、シェーダはレンダリングパイプラインの一部に含まれます。

f:id:bao_bao:20160508212120p:plain

また2種類のシェーダユニットが存在します。
本記事はシェーダの概念的な話なので話の単純化ということでSM2.0をターゲットとし、頂点シェーダ、フラグメントシェーダ以外のユニットについては割愛します。

f:id:bao_bao:20160508212530p:plain

  • 頂点シェーダ (バーテックスシェーダとも言われる)
  • フラグメントシェーダ (ピクセルシェーダとも言われる)

頂点シェーダはその名も通り、3Dオブジェクトの頂点に関わる処理を行います。 フラグメントシェーダは、ディスプレイの1ピクセル毎に処理するシェーダです。

概念的には受け取った3Dオブジェクトの頂点データを頂点シェーダで座標変換などを行い、その結果をフラグメントシェーダに渡し、各ピクセルの色を決め、ディスプレイに映し出されます。

一昔前まではシェーダを使うことはできませんでした。
ここで言うシェーダとはプログラマブルシェーダ(プログラムとして記述できるシェーダ)の事です。

固定機能パイプラインと呼ばれるハードウェア側に予め定義された固定機能を使って描画するという方法がとられていました。現在のようなプログラムで描画処理をカスタマイズするということはできません。

f:id:bao_bao:20160508214206p:plain

現在はというと。

f:id:bao_bao:20160508214746p:plain

図のように頂点シェーダフラグメントシェーダの2箇所をプログラムをすることによりでカスタマイズでき、様々な表現が可能になっています。

よって、繰り返しになりますがシェーダとは一言で表すと


3Dオブジェクトをディスプレイに映し出すためのプログラム


と定義しました。

逆にシェーダが存在しなければ、ディスプレイは真っ暗のままになってしまいます。何かが表示しているということは、プログラマが何かしらのシェーダを書いているということになります。


2.Unityにおけるシェーダの処理

UnityのシェーダはShaderLabと呼ばれるUnity独自の記法で書かれています。
参考 : ShaderLab シンタックス - Unity マニュアル

Unityのシェーダは全てShaderLabで書かなければなりません。

GLSL、Cg、HLSLはどこにいったのか?

ShaderLabの言語仕様は、Cg、HLSLを組み合わせたようなものになっています。

Cg、HLSLで書いたシェーダ(ShaderLab)は、OpenGL向けに書きだされる端末の場合、HLSL2GLSLでコンパイル*1され、GLSLに書き出される仕組みになっています。
※Metal環境向けならMetalのシェーダ言語(MSL)に書きだされます

確認方法

変換されたファイルを見ることで確認できます。

ShaderLab界では少なめのシェーダコードがこちらです。

このコードをOpenGLES2.0の環境に変換してみます。
Shaderファイルを選択しInspecterで変換後のシェーダを確認できます。

f:id:bao_bao:20160508234656p:plain

するとこうなります。
※見やすくするために整形しています

複数環境選択して、変換後のシェーダを確認すると各環境でどのようなシェーダが使われているか分かります。先のソースコードのように、見慣れたGLSLが記述されているため、OpenGLES2.0環境ではGLSLが使われているということを確認できます。


GLSLが見慣れないという方はWebGLでシェーダを書くのが低コストで勉強できると思います。

余談ですが、ShaderファイルのインスペクタにはCompiled Codeとコンパイル済みコードとあります。 ここで言うコンパイルとはShaderLabから各環境への言語の変換処理のことです。バイナリになるわけではありません。
変換されたものは、ただのテキストなので目視で確認できます。

各環境のコードは1ファイル内に#ifdefで切り分けられており、OpenGLES2、Metalなど環境毎で実行時にコンパイル(ここではバイナリへのコンパイル)されて処理されます。

まとめ

前半シェーダとは何か?ということについて、一言で表すと
「3Dオブジェクトをディスプレイに映し出すためのプログラム」だという事を説明しました。


後半Unity内でのシェーダについて割りとゆるめな内容でまとめてみました。

  • UnityのシェーダはShaderLabという独自言語を使って書く
  • ShaderLabはCg、HLSLを使うことが出来る*2
  • 最終的には各環境に向けたシェーダ言語に変換される
  • 実行時にシェーダはコンパイルされる(実行前ではただの文字列でしか無い)


ゲームエンジンを使わずにOpenGLやDirectXを書いてみると更に理解が深まるかと思います。 例えばUnityでは勝手にシェーダのコンパイルを行ってくれますが、本来はそれ自体開発者がプログラムを書かないといけない処理ですし、それ以外にも多くの手続きが存在します。

実際にWebGL、DirectXを自前で書いてみましたが、Unity内でやっている処理をイメージしやすくなりましたので、少しハードルは高いですがオススメです。

これらの記事が参考になるかもしれません。


参考書籍

ゲーム制作者になるための3Dグラフィックス技術 増補改訂版
ゲーム制作者になるための3Dグラフィックス技術 増補改訂版

こちらの西川善司さんの本が、初心者にもわかりやすくとてもオススメです。
多少の前提知識は必要ですが、3DゲームにおけるGPUのの歴史・変遷、また描画テクニックが分かりやすく図解ありで紹介されています。ネットにも同じような情報が散乱していますが、じっくり読みたい人には良いかもしれません。

環境

  • MacOS 10.10.5
  • Unity5.3.4f1
こちらの記事もあわせてどうぞ

*1:ここで言うコンパイルとはShaderLabからターゲットとなる環境のシェーダ言語に変換される意味で、ランタイム中のバイナリ変換のことではありません

*2:厳密にはGLSLを直接書くことも出来る