シェーダー本を書いて得た知見まとめ

# はじめに

これは、『[BASIC×SHADER: Unityで学ぶシェーダープログラミング](https://amzn.to/2O7vSVC)』というシェーダー本 (Kindle) を書いて得た知見のまとめです。

本の概要を「文章」にすると、

> 本書は、これからシェーダープログラミングを学びたい方を対象にした入門書です。シェーダーの事前知識は不要ですが、「何かしらのプログラミング言語」と「高等学校程度の数学」の理解を前提としています。
>
> 取り扱っている内容は、次のとおりです。
>
> * シェーディング言語 (Cg) の基礎文法
> * シェーダープログラミングのためのベクトルと行列の復習
> * ライティング/テクスチャリング/シャドウ/フォグ/ポストエフェクトの基礎実装

「画」にすると、

![frames.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54402/46504538-e0e3-13ad-186f-e40940e746bc.gif)

こんなかんじです。上記のシェーダーは全て [GitHub](https://github.com/midnightSuyama/BASICxSHADER) で配布しているので、学習にご活用ください。

## 成果物

* [BASIC×SHADER (Kindle)](https://amzn.to/2O7vSVC)
* [BASIC×SHADER (GitHub)](https://github.com/midnightSuyama/BASICxSHADER)
* [pygments-shader (PyPI)](https://pypi.org/project/pygments-shader/)
* [asciidoctor-kindle (RubyGems)](https://rubygems.org/gems/asciidoctor-kindle)

# 既刊本

執筆のきっかけは、「シェーダーを始めたいけど学習方法がわからない。森に帰りたい。」という声でした。

シェーダーの入門書を読んでも、題材の動作環境が古くてサンプルコードを試すだけで大変。新しめの洋書を買ってきても、英語が読めなくて大変。計算式を眺めても、ベクトルと行列がわからなくて大変。気づけば冷蔵庫のプリンが無くなっていて大変。

そんな姿を目の当たりにしていたので、微力ながら助力できることはないだろうかと考えていました。シェーダーの入門書を現環境向けに書けば実践できるのではないだろうか、日本語で書けば読み進められるのではないだろうか、ベクトルと行列の復習を書けば理解できるのではないだろうか、そして、許してくれるのではないだろうかと考えていました。プリンを食べながら。

まずは、既刊のシェーダー本についてまとめておきます。シェーダーを題材にした和書の 9 割程度 (雑誌や同人誌を除く) と、洋書の半数程度 (英語に限る) が手元に揃っているので、この機会に各々読み返しました。出版年の古い本は、サンプルコードによる実践が困難のため、理論だけを学ぶつもりでご覧ください。

## シェーダー入門 (和書)

和書に関して、シェーダーを題材とした商業出版は近年見当たりません。そもそもの数が少なく、オススメできる入門書は数冊程度です。

### 『[DirectX 9 シェーダプログラミングブック](https://www.amazon.co.jp/dp/4839912475)』 (2004)

和書の中では、間違いなく最高の入門書。シェーダープログラミングの基礎を一通り学べます。サンプルコードは DirectX (HLSL)。

### 『[DirectX シェーダプログラミング 仕組みからわかるゲームエフェクトテクニック](https://www.amazon.co.jp/dp/4797344962)』 (2007)

数式を抑え、図を多用した解説。ポストエフェクト寄りの内容です。サンプルコードは DirectX (HLSL)。

### 『[3D-CGプログラマーのためのリアルタイムシェーダー入門【理論と実践】](https://www.amazon.co.jp/dp/4777514013)』 (2008)

和書では珍しい、数式を中心とした解説。Amazon では「プラグラマー」で検索しないと出てきません。サンプルコードは OpenGL (Cg)。

## シェーダー入門 (洋書)

洋書のシェーダー本は、近年も新刊が出版されています。出版物の半数程度しか目を通せていないので、抜け漏れが多くあるかと思いますが、既読範囲で良かった入門書をまとめておきます。

### 『[Shaders for Game Programmers and Artists](https://www.amazon.co.jp/dp/1592000924)』 (2004)

RenderMonkey を使ったチュートリアル形式。タイトルの「for Artists」は、「事前のプログラミング知識が不要」という意味であり、取り扱うシェーダーはシンプルです。サンプルコードは RenderMonkey (HLSL)。

### 『[Advanced Lighting And Materials With Shaders](https://www.amazon.co.jp/dp/1556222920)』 (2004)

照明計算の基礎理解に最適。理論と実装を明確に分けている章構成が特徴的です。サンプルコードは DirectX (HLSL) と OpenGL (GLSL)。

### 『[Graphics Shaders: Theory and Practice, Second Edition](https://www.amazon.co.jp/dp/1568814348)』 (2011)

理論に比重を置いた入門書。今確認したら、記載されているサンプル配布 URL がフィッシングサイトと化しており、[OSU](http://web.engr.oregonstate.edu/~mjb/cgeducation/) に移転したようです。サンプルコードは OpenGL (GLSL)。

### 『[Introduction to 3D Game Programming With DirectX 12](https://www.amazon.co.jp/dp/1942270062)』 (2016)

DirectX の入門書ですが、ページの多くがシェーダーに割かれており、DirectX 12 環境で学習するなら最良の選択肢。サンプルコードは DirectX (HLSL)。

### 『[Physically Based Shader Development for Unity 2017](https://www.amazon.co.jp/dp/1484233085)』 (2017)

本題は物理ベースレンダリングですが、シェーダー入門者を想定しており、前半で基礎知識を学べます。サンプルコードは Unity (Cg)。

### 『[Unity 2018 Shaders and Effects Cookbook, Third Edition](https://www.amazon.co.jp/dp/1788396235)』 (2018)

とにかく Unity で動かして学ぶスタイル。サーフェイスシェーダーへの依存が強めです。サンプルコードは Unity (Cg)。

### 『[OpenGL 4 Shading Language Cookbook, Third Edition](https://www.amazon.co.jp/dp/1789342252)』 (2018)

GLSL 学習の定番書。最新の Third Edition は 4.6 に対応しています。サンプルコードは OpenGL (GLSL)。

日本語版の『[OpenGL 4.0 シェーディング言語 実例で覚えるGLSLプログラミング](https://www.amazon.co.jp/dp/4862461891)』もありますが、翻訳元は [First Edition](https://www.amazon.co.jp/dp/1849514763) です。

### 『[Real-Time 3D Graphics with WebGL 2, Second Edition](https://www.amazon.co.jp/dp/1788629698)』 (2018)

WebGL を使った 3D グラフィックスの入門書なのですが、シェーダーを含めて説明が丁寧。JavaScript さえ理解していれば読み進められます。サンプルコードは WebGL (ESSL)。

## シェーダー関連

シェーダーの入門書ではありませんが、副読に有用な本を記載しておきます。

### 『[ShaderX Series](https://www.amazon.co.jp/dp/1556220413)』 (2002–2009)

シェーダープログラミングの Tips。1 と 2 は、[ShaderX/GPU Pro/GPU Zen Books](http://www.realtimerendering.com/resources/shaderx/) で無償公開。

### 『[The Cg Tutorial: The Definitive Guide to Programmable Real-Time Graphics](https://www.amazon.co.jp/dp/0321194969)』 (2003)

Cg 言語の公式ガイド。[The Cg Tutorial](https://developer.download.nvidia.com/CgTutorial/cg_tutorial_frontmatter.html) で無償公開。日本語版の『[The Cg Tutorial 日本語版](https://www.amazon.co.jp/dp/4939007553)』もあります。

### 『[GPU Gems Series](https://www.amazon.co.jp/dp/0321228324)』 (2004–2007)

GPU プログラミングの Tips。[GPU Gems](https://developer.nvidia.com/gpugems/GPUGems/gpugems_pref01.html) で無償公開。日本語版の『[GPU Gems 日本語版](https://www.amazon.co.jp/dp/4939007693)』もあります。

### 『[Mathematics for 3D Game Programming and Computer Graphics, Third Edition](https://www.amazon.co.jp/dp/1435458869)』 (2011)

3D プログラミングのための数学。日本語版の『[ゲームプログラミングのための3Dグラフィックス数学](https://www.amazon.co.jp/dp/4939007375)』もありますが、翻訳元は [First Edition](https://www.amazon.co.jp/dp/1584500379) です。

### 『[ゲーム制作者になるための3Dグラフィックス技術 増補改訂版](https://www.amazon.co.jp/dp/4844333542)』 (2013)

3D グラフィックス技術の概要。元ネタの連載記事は「[3Dグラフィックス・マニアックス](https://news.mynavi.jp/series/graphics)」で閲覧可能。

### 『[Physically Based Rendering: From Theory to Implementation, Third Edition](https://www.amazon.co.jp/dp/0128006455)』 (2016)

物理ベースレンダリングの教科書。改訂版の [Online Edition](http://www.pbr-book.org/) が無償公開。

### 『[Real-Time Rendering, Fourth Edition](https://www.amazon.co.jp/dp/1138627003)』 (2018)

リアルタイムグラフィックスの辞典。日本語版の『[リアルタイム レンダリング 第2版](https://www.amazon.co.jp/dp/4939007359)』もありますが、翻訳元は [Second Edition](https://www.amazon.co.jp/dp/1568811829/) です。

# Kindle 出版

前述のとおり、シェーダーの入門書は洋書を含めれば選択肢があるものの、和書は DirectX 9 世代で途絶えています。単純に需要がないだけなのか、出版業界の事情はわかりませんが、執筆しなければならなくなってしまった事情だけなら誰よりもわかる自負がある。筆を走らせます。

当初、原稿は Markdown で書いていましたが、表現力の問題から次第に HTML タグを直書きするようになり、ヤバみが深まってきたところで AsciiDoc に移行しました。[Asciidoctor](https://asciidoctor.org/) で adoc を html に変換して、[KindleGen](https://www.amazon.com/gp/feature.html?docId=1000765211) で html を mobi に変換します。

## シンタックスハイライト

![kindle1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54402/4f00c58d-e10d-e25d-57e5-ea6718a5a3bc.png)

Asciidoctor のシンタックスハイライトは、[CodeRay](http://coderay.rubychan.de/), [highlight.js](https://highlightjs.org/), [prettify](https://github.com/google/code-prettify), [Pygments](http://pygments.org/), [Rouge](http://rouge.jneen.net/) に対応しています。しかし、Unity の *.shader をサポートしているライブラリが見当たりません。実際、ShaderLab を掲載しているサイトの多くは、C や GLSL 用の字句解析器を無理矢理適用している印象が強い。

今回は Pygments の Lexer を書いたので、

```shell
$ pip install pygments-shader
```

で [pygments-shader](https://pypi.org/project/pygments-shader/) をインストールすれば、*.shader の可読性を改善できます。以前、Emacs 向けの [shader-mode](https://melpa.org/#/shader-mode) を自作した際にキーワードを整理していたので、この情報を流用して作りました。

### pygments.rb

Python から直接利用する場合は問題ありませんが、Asciidoctor では [pygments.rb](https://github.com/tmm1/pygments.rb) の Pygments を読み込んでしまうため、

```ruby
require 'pygments'

path = # Pygments Path
Pygments.start(path)
Pygments::Lexer.create(name: 'Shader', aliases: ['shader'], filenames: ['*.shader'], mimetypes: ['text/x-shader'])
```

のような `pygments_init.rb` を用意して、

```shell
$ asciidoctor -r ./pygments_init example.adoc
```

で対応しました。

## mobi 変換

![kindle2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54402/5828c8fb-f182-c77a-f75e-6247d7810db8.png)

Asciidoctor には [asciidoctor-epub3](https://github.com/asciidoctor/asciidoctor-epub3) が用意されており、epub 変換を経由して mobi を出力できます。しかし、epub 変換に伴う見た目の変貌が気になるため、今回は html 変換を経由して mobi を出力する Asciidoctor の Extension を作りました。

```shell
$ gem install asciidoctor-kindle
```

で [asciidoctor-kindle](https://rubygems.org/gems/asciidoctor-kindle) をインストールして、

```shell
$ asciidoctor-kindle example.adoc
```

で html を基にした mobi を出力します。html から mobi への変換作業は KindleGen に一任しており、asciidoctor-kindle の主な処理は KindleGen のための最適化です。

```shell
$ asciidoctor -r asciidoctor-kindle example.adoc
$ kindlegen -o kindle-published.mobi kindle-package.opf
```

のように処理を分割することも可能です。[Nokogiri](https://nokogiri.org/) などで html を編集するスクリプトを間に挟めば、Asciidoctor の制約に縛られない微調整ができます。

### mobi/azk

iOS とそれ以外の環境では、Kindle のファイルフォーマットが異なります。iOS は azk、他環境は mobi。azk の生成方法は [Kindle Previewer](https://kdp.amazon.co.jp/ja_JP/help/topic/G202131170) によるエクスポートのみ。azk の確認方法は iOS 端末上の Kindle のみ。

厄介なことは、mobi を iOS 端末に転送すると開けてしまうため、CSS 改善案が全く思いつかない姿に絶望。azk に変換してから転送しても、他環境の mobi と異なる姿に失望。結局、mobi と azk を確認しながら CSS を書くことになります。

### charset

```html
<meta charset="UTF-8">
```

を認識しないため、文字化けが発生する。

```html
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
```

の記述が必要です。Asciidoctor の出力には含まれないため、asciidoctor-kindle で対応しました。

### style

```html
<style>
```

を認識しないため、スタイルが適用されない。

```html
<style type="text/css">
```

の記述が必要です。Asciidoctor の出力には含まれないため、asciidoctor-kindle で対応しました。

### monospace

```css
code {
  font-family: monospace;
}
```

を iOS が認識しないため、コードが等幅にならない。

```html
<html lang="ja">
```

の `lang` を削り対応しました。

普段は `Courier` などを優先させていて気づかなかったのですが、この問題はブラウザでも発生します。確認したところ、`lang="ja"` と `monospace` が揃うと Safari で等幅表示が崩れる。`<pre>` や `<code>` の中身の言語に関係なく、`lang` にデフォルトで等幅フォントのない言語を指定すると `sans-serif` になる模様。

### %, vw

```css
.stretch {
  width: 100%;
}
```

を iOS と macOS が認識しないため、幅の指定ができない。

```css
.stretch {
  width: 100%;
  width: 100vw;
}
```

の記述で対応しました。

### path

KindleGen がファイル構成を名前だけで判断するため、

```
images/chapter1/figure.jpg
images/chapter2/figure.jpg
```

のようなファイルが上書きされてしまう。

```
images/chapter1-figure.jpg
images/chapter2-figure.jpg
```

のように、1 フォルダで管理して重複を避けました。

# おまけ

## テクニカルライティング

冒頭にて、冷蔵庫のプリンを勝手に食べてしまうカスのような描写がありましたが、実は、そのプリンは私が買ってきたものであり、声の主は奪い損ねたことに落胆していたのです。明かされる衝撃の事実。物語は新たな局面を迎える。

というような驚きはいらない。技術書と文学書では、役割が異なります。わかりやすさを優先する文章と、芸術性を求めるような文章では、書き方が異なります。どちらにしても、プリンカスと誤解されるような文章は避けたい。

今回の執筆を機に、テクニカルライティング関連の本を 20 冊ほど買い読んでみました。同じ題材ゆえに似たようなことしか書かれていない、と思いつつも全て読み終えたので、その中から光る数冊を紹介します。

### 『[まんがでわかる 理科系の作文技術](https://www.amazon.co.jp/dp/4120050424/)』

わかりやすい「文章」を書くための本。『[理科系の作文技術](https://www.amazon.co.jp/dp/4121006240)』を再構成した漫画です。物足りない部分は原作で補うとしても、とても読みやすく、この分野の入門に最適です。

### 『[日本語の作文技術](https://www.amazon.co.jp/dp/4022618450)』

わかりやすい「文」を書くための本。何となく感じていた「わかりやすさ」と「わかりにくさ」の要因を言語化しており、今回読んだ中では唯一無二の内容でした。

### 『[数学文章作法 基礎編](https://www.amazon.co.jp/dp/448009525X)』

数学の本ではなく、テクニカルライティングの原則を集約した本。同分野の中では、最も簡潔明瞭でした。続編の『[数学文章作法 推敲編](https://www.amazon.co.jp/dp/4480095268)』もあります。

## 「シェーダ」と「シェーダー」

長音符号について、現在の JIS 規格では、

> なお，英語の語末の -er，-or，-ar などは，ア列の長音とし，長音符号を用いて表すものに当たるとみなす。

としていますが、注記として、

> 学術用語においては，原語（特に英語）のつづりの終わりの -er，-or，-ar などを仮名書きにする場合に，長音符号を付けるか，付けないかについて厳格に一定にすることは困難であると認め，各用語集の表記をそれぞれの専門分野の標準とするが，長音符号は，用いても略しても誤りでないことにしている。

を付け加えています。

「シェーダ」でも「シェーダー」でも、どちらでもよいのですが、本の中では表記を統一したい。選ぶための指標が欲しい。各企業の和文資料を眺める日々が始まりました。

* Microsoft - [シェーダーを操作する](https://docs.microsoft.com/ja-jp/visualstudio/designers/working-with-shaders?view=vs-2019)
* Apple - [Metal 2](https://developer.apple.com/jp/metal/)
* Google - [Android の Vulkan シェーダー コンパイラ](https://developer.android.com/ndk/guides/graphics/shader-compilers?hl=ja)
* Mozilla - [シェーダーエディター](https://developer.mozilla.org/ja/docs/Tools/Shader_Editor)
* Khronos Group - [クロノス・グループ、Vulkan 1.0を発表](https://jp.khronos.org/news/press/vulkan-1.0)
* NVIDIA - [Cg](https://www.nvidia.co.jp/object/cg_jp.html)
* AMD - [開発者用AMD Radeon™ ProRender](https://www.amd.com/ja/technologies/radeon-prorender-developers)
* Unity Technologies - [マテリアル、シェーダー、テクスチャ](https://docs.unity3d.com/ja/current/Manual/Shaders.html)
* Epic Games - [シェーダー開発](https://api.unrealengine.com/JPN/Programming/Rendering/ShaderDevelopment/index.html)
* Autodesk - [ShaderFX について](https://help.autodesk.com/view/MAYAUL/2019/JPN/?guid=GUID-EBC6DF48-857D-4230-9D3C-0B04DAF58403)
* SideFX - [CORE 機能](https://www.sidefx.com/ja/products/houdini/core-features/)

この中では、Khronos Group, Autodesk, SideFX が「シェーダ」で、それ以外は「シェーダー」でした。ピックアップした URL 内の表記がその企業の総意とは限りませんが、どちらかと言うと「シェーダー」が多い傾向。また、Unity の古いマニュアルでは「シェーダ」と「シェーダー」で表記が混在していましたが、現在は「シェーダー」で統一されています。

そのほか、参考にしたスタイルガイドを記載しておきます。

### 『[外来語の表記](http://www.mext.go.jp/b_menu/hakusho/nc/k19910628002/k19910628002.html)』

> 英語の語末の‐er,　‐or,　‐arなどに当たるものは，原則としてア列の長音とし長音符号「ー」を用いて書き表す。ただし，慣用に応じて「ー」を省くことができる。

### 『[外来語（カタカナ）表記ガイドライン第3版](https://www.jtca.org/standardization/katakana_guide_3_20171222.pdf)』

> 英語の語尾の‐er、‐or、‐ar、 *y にあたるものは、原則として長音とし長音符号「ー」を用いて書き表す。

### 『[JTF日本語標準スタイルガイド（翻訳用）](https://www.jtf.jp/jp/style_guide/pdf/jtf_style_guide.pdf)』

> カタカナ語の末尾の長音は原則として省略しません。

### 『[Microsoft Japanese Style Guide](http://download.microsoft.com/download/b/3/4/b34f7185-afca-4846-8fbf-672a1b9afce2/jpn-jpn-styleguide.pdf)』

> In principle, use a long vowel when a source English term has following suffixes: -er, -or, -ar

## Cg 標準ライブラリ関数

Cg 言語の解説は、公式の『[Cg Toolkit User's Manual](http://developer.download.nvidia.com/cg/Cg_3.0/CgUsersManual.pdf)』を参考にしました。この資料には、日本語版の『[Cg Toolkit User's Manual 日本語版](http://developer.download.nvidia.com/cg/Cg_3.0/Cg_Users_Manual_JP.pdf)』もあるのですが、見比べてみると「Cg 標準ライブラリ関数」の様子がおかしい。何度見ても、日本語版にだけ `saturate()` がない。単純な誤植なのか、訳者が GLSL 強硬派なのか、気になる。

上記の資料では代表的な関数だけを紹介しており、他の関数は「[Cg Standard Library](http://developer.download.nvidia.com/cg/index_stdlib.html)」で確認できます。代表的な関数は、対応プロファイルで線引きしているようなのですが、なぜか `trunc()` がない。`frac()` はあるのに `trunc()` がない。これは原文の時点で紹介が抜けており、謎。

# おわりに

執筆の終盤で気付いたのですが、商業誌の外に目を向けると、同じ志の先輩方が既に執筆されていました。『[Unityシェーダープログラミングの教科書](https://booth.pm/ja/items/660001)』や『[Unity Shader Programming](https://booth.pm/ja/items/931290)』は執筆の動機を打ち消す良書であり、同人誌を含めて調査していれば執筆する必要はなかったのです。森に帰りたい。