しっかり学ぶシェーダプログラミング【シェーダをさわる前に編】

# はじめに
こんにちは、[@yoship1639](https://twitter.com/yoship1639)と申します。最近ゲーム開発に勤しんでいます。

皆様、突然ですがシェーダをご存知でしょうか。
この記事を開くということは少なくともシェーダという言葉の意味を知っているはずです。
簡単に言うと「3Dプログラム上でのオブジェクトに対する色付け（陰影処理）」ですね。
最近はゲームエンジンの**Unity**とか**UE4**とかが普及して耳にする機会が多くなったのではないでしょうか。

次の質問です。
0からシェーダ書けますか？
正しく理解できていますか？

多分ほとんどの方が0からは書けないのではないかと踏んでいます。
なぜなら最新のシェーダに関する日本記事がほとんど見受けられないからです。
Physical Based Renderingが登場以降、ほとんど見かけなくなりました。
（ちなみに最新のシェーダはほぼ英語記事しかありません）

ゲームエンジンが普及し手軽に高品質なグラフィックが作れるようになったのはとても良いことですが、
D&Dとプロパティ設定はできるけどシェーダは全く書けないエンジニアにはなって欲しくないです。

なので、最初期のシェーダであるグーローシェーダから順を追ってPBR+ポストエフェクト辺りまで実際に実装・解説していきたいと思います。

##目次

1. [しっかり学ぶシェーダプログラミング【シェーダをさわる前に編】](https://qiita.com/yoship1639/items/5f8e55a89fc58ea33bb7)<-本記事
2. [しっかり学ぶシェーダプログラミング【グーローシェーダ編】](https://qiita.com/yoship1639/items/75505244b6c242d50f71)

# シェーダを触る前に知るべきこと

シェーダを触る前に基礎知識として知っておくべきことがいくつかあります。
以下の知識がない方は**シェーダはまだ早い**です。
後、基礎知識を詳細に説明すると超長文になってしまうので、わからない部分は
下記の[#基礎知識の参考になるサイト](#基礎知識の参考になるサイト)を参考にしたり各自ggってください。

## 最低でも知っておくべきこと

### シェーダで出来ること
そもそも何のためにシェーダを使うのかということですが、
**CPUだけじゃ追いつかないグラフィック処理をGPU(シェーダ)に肩代わりさせる**のがそもそもです。
CPUで十分間に合う場合シェーダじゃなくてもいいです。

### シェーダの入出力
シェーダに「3Dポリゴンデータ、材質データ、光源データ等」を入力させると
「色のついた2Dピクセルデータ」が返ってきます。

### 基本的なプログラミング
当たり前ですが、変数とか演算とかがわからない人は論外です。
シェーダは大体**C言語**スタイルのものが多いので、Cの知識があると大歓迎です。

### シェーダはGPUを使うということ
シェーダは**GPU上で動きます**。シェーダをGPUプログラミングといっても正解だと思います。

### シェーダは並列で動くということ
シェーダは**膨大な数の処理が並列**で動きます。
シェーダで書いたコードのほぼ100%がGPU内で並列で動いていると思ってください。

### シェーダとCPUは並列で動くということ
CPUとGPUは並列で動きます。
CPUがごちゃごちゃ動いてる間にGPUでグラフィック処理をします。
一応CPUとGPUの同期は取れますが極端に遅くなります。

### 3Dはほぼ三角形(ポリゴン)で出来ているということ
シェーダというより3Dの基礎知識です。
三角形のデータがバーテックスシェーダに渡されます。

### 「3Dの頂点を移動させるシェーダ」と「ピクセルに色を付けるシェーダ」があるという概念
シェーダは**複数の段階に分かれて**います。
「3Dの頂点を移動させるシェーダ」である**バーテックスシェーダ**の後に
「ピクセルに色を付けるシェーダ」である**ピクセルシェーダ(フラグメントシェーダ)**が必ず来ます。
(そのほかにもシェーダはありますが基本気にしなくていいです)


### ベクトル(Vector)
これを知っていないと何もできません。
最低でも**ベクトルの加算、減算**はしっかり理解してください。

### 行列(Matrix)
これも必須です。
行列x行列はこれでもかというくらいシェーダには存在します。
**行列の乗算**は理解してください。
行列におけるA*B != B*Aも重要です。

### 頂点(Vertex)
ポリゴンの位置を表す点。必須です。

### テクスチャ(Texture)
主に材質として扱われます。必須です。

## 知っておくと有利なこと
上記に加えて知っておくと後々楽なことをもいろいろあります。

### OpenGL・DirectXの基礎知識
出来ればほしいです。
知らなくてもシェーダは書けますが後の理解が段違いです。

### 法線(Normal)
頂点や面の向きを表します。ほぼ使います。

### ディフューズ・スペキュラ・アンビエント(Diffuse・Specular・Ambient)
材質データです。色を付ける際によく使われます。
ピクセルシェーダでほぼ必ず使います。

### ベクトルの正規化(Normalize)
ベクトルの長さを1.0にする処理です。
無茶苦茶使います。

### ベクトルの内積(Dot)
無茶苦茶使います。
内積でベクトル同士の角度がわかります。

### Sin・Cos
ぼちぼち使います。が、シェーダにおけるsin,cosは重いです。
Dot等で代用できるのにむやみやたらにsin,cosを使うのはNGです。

### if分は重いということ
シェーダにおけるif分は重いです。
条件分岐をシェーダでやるのはあまりよくありません。

### for分は重いということ
for分も重いです。
必要な所以外では使うべきではありません。

### 大体0.0~1.0の間の数字を扱うということ
シェーダでは、0.0~1.0の数字を扱うことが多いです。
1.0を超えた色がHDRになります。

### ローカル座標・ワールド座標・ビュー座標・射影座標・ビューポート座標の概念
出来れば知っていてほしいです。
3Dオブジェクトを2D画面上に変換する際に確実に使います。
シェーダでよくみるMVPはワールドxビューx射影のことです。

### まとめ
色々並べましたが、シェーダを触る前には
**プログラムの基礎知識**があり、**3Dの基礎知識**があり、**シェーダの概念知識**がないと
多分**速攻で挫折します**ので注意してください。
シェーダを書く時点である程度上級者であることが求められます。
後、ほとんどのシェーダは上記の知識で実装可能で、あとはアルゴリズムを知るだけです。

## 基礎知識の参考になるサイト
上記に勉強の参考になりそうなサイトを下記にまとめました。

- 説明しよう！シェーダーとはッ！[http://d.hatena.ne.jp/o_healer/20110408/1302236008](http://d.hatena.ne.jp/o_healer/20110408/1302236008)
- OpenGL が世界を描画する仕組み [http://tkengo.github.io/blog/2014/12/27/opengl-es-2-2d-knowledge-1/](http://tkengo.github.io/blog/2014/12/27/opengl-es-2-2d-knowledge-1/)
- ベクトルの掛け算　内積・外積 [http://physics.thick.jp/Physical_Mathematics/Section3/3-4.html](http://physics.thick.jp/Physical_Mathematics/Section3/3-4.html)
- 3Dで使う行列の基礎について [http://nogson2.hatenablog.com/entry/2017/05/22/222937](http://nogson2.hatenablog.com/entry/2017/05/22/222937)
- 3DCGの座標系の紹介 [https://blog.natade.net/2017/06/03/rendering-3dcg-coordinate/](https://blog.natade.net/2017/06/03/rendering-3dcg-coordinate/)
- 骨まで理解するPCアーキテクチャ(GPU編) [https://pc.watch.impress.co.jp/docs/column/1month-kouza/642769.html](https://pc.watch.impress.co.jp/docs/column/1month-kouza/642769.html)

## 最後に
次から実際にグーローシェーダを実装していきたいと思います。
実装はC#+OpenGL+GLSLの予定です。
時間があるときに記事にしていくので、気長にお待ちいただけると幸いです。

2018/8にtwitterをやり始めたのでよろしかったら[@yoship1639](https://twitter.com/yoship1639)
質問にも出来る限りお答えします。

### 修正履歴

2018/10/21： 頂点(Vertex)が頂点(Vector)になっていたのを修正