第七回 WebGLスクール 「ブレンドファクターとアルファブレンディング」


[wgld.org](http://wgld.org/)の管理人であるdoxasさん主催の[WebGLスクール](http://webgl.souhonzan.org/entry/?v=0003)の第七回目です。
今回は色や透明度のブレンディングについてです。色の合成を行うことで、レンダリング結果をガラリと変えることができます。

![webgl01.png](https://qiita-image-store.s3.amazonaws.com/0/48677/8cb36f63-fd57-35bf-7799-38e7efd8fb79.png "webgl01.png")


### 前回までのまとめ
[第一回 WebGLスクール 「WebGLの概念」](http://qiita.com/konweb/items/a52ae0bc6aec2d2f3e6b)
[第二回 WebGLスクール 「WebGLの手続きと手順」](http://qiita.com/konweb/items/a1c58c1722c62a61f35d)
[第三回 WebGLスクール 「シェーダの基礎」](http://qiita.com/konweb/items/ec8fa8cd3bc33df14933)
[第四回 WebGLスクール 「行列とクォータニオンについて知る」](http://qiita.com/konweb/items/fd328be7f4d64e5ace02)
[第五回 WebGLスクール 「ライティングの基本」](http://qiita.com/konweb/items/21d76557f08ba24e6d3d)
[第六回 WebGLスクール 「テクスチャで画像データを使用する」](http://qiita.com/konweb/items/c848c5b96dc70aa46569)


## ブレンディングとは？
そもそもブレンディングとは何なんでしょうか？ブレンディングは色と色とを混ぜあわせるための設定です。
いろいろな演出は、実はブレンディングがあって初めて実現できているものが多い。

### ブレンディングの有効化
WebGLにおけるブレンディングは、 **意図的** に有効化してあげなければ使用することができません。
深度テストやカリングの有効化と同じように、`gl.enable()`メソッドを使いブレンディングを有効化します。

```
gl.enable(gl.BLEND);
```

有効化はこれだけでOK!


## ブレンディングのイメージ
ブレンディングは何と何を混ぜあわせるものなのかをしっかりと理解しておく必要がある。
そこででてくるキーワードが **SRC** と **DST** です。
**SRC** はSOURCEのことで **これから描こうとしているもの** を指します。
**DST** はDESTINATIONのことで **すでに描かれているもの** を指します。

すなわちブレンディングは、すでにそこに描かれている色と、これから描こうとしている色をどのように扱うかを決めること。


### RGB成分とアルファ成分
WebGLの中では色のブレンドとアルファ成分のブレンドが個別に扱われるので、ブレンディングの際にはRGB成分とアルファ成分を切り離して考えたほうがよい。


## 合成方法の指定
色成分やアルファ成分のブレンディング指定には`gl.blendFunc()`メソッドか`gl.blendFuncSeparate()`を使用する。
`gl.blendFunc()`を用いた場合は、色成分とアルファ成分に同じ合成ルールが適用される。色成分とアルファ成分を同じ合成ルールにしてしまうと、あまり正しい結果を得られないので、基本的には`gl.blendFuncSeparate()`を使ったほうが良い。

```
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
```

第一引数にSRC(これから描くもの)のRGB、第二引数にDST(すでに描かれているもの)のRGBを指定する。
第三引数にSRC(これから描くもの)のアルファ、第四引数にDST(すでに描かれているもの)のアルファを指定する。
引数には、ほかにもいろいろな種類がある。[参考:アルファブレンディング](http://wgld.org/d/webgl/w029.html)

### 計算例
例えば、以下のようなSRCとDSTがあったとする。

- SRCのRGBが[1.0,0.0,0.0]でアルファ値が0.7
- DSTのRGBが[0.0,0.0,1.0]でアルファ値が1.0

先ほどの`gl.blendFuncSeparate()`の指定方法でいくと

```
[1.0, 0.0, 0.0] * 0.7 + [0.0, 0.0, 1.0] * (1.0 - 0.7)
```

という計算になる。
計算を見ると加算処理が行われている。

加算だけでなく、減算を指定することができる。減算を使用する場合には`gl.blendEquation()`や`gl.blendEquationSeparate()`を使う。


## アルファブレンディングと深度
半透明描画を行う際に、深度に関する扱いに特に注意を払わなければならない。安易に半透明のオブジェクトを使ったレンダリングを行うと、大抵は失敗する。
深度テストはあくまでも、深度バッファの値で判断しているので深度テスト側からすると透明かどうかは関係ない。
そのため、奥にあるものから順番に描画しないとおかしなことになってしまう。

![webgl02.png](https://qiita-image-store.s3.amazonaws.com/0/48677/0f1821b9-0004-e661-e277-c1faa8880f64.png "webgl02.png")


## カリングと深度テストとブレンディング
板ポリゴンのような奥行きのないモデルを扱う場合は、アルファブレンディングは単純にできる。
反対に立体的なモデルの場合には、アルファブレンディングを扱う際は注意すべき点が多い。カリングや深度テストの設定で大きく影響がでてしまう。

## 現実世界の透明な球体
現実世界にある半透明の物体は、当然その球体の裏側まで見ることができます。

![004.jpg](https://qiita-image-store.s3.amazonaws.com/0/48677/753baae4-050d-e514-eae4-934fdff3543c.jpeg "004.jpg")

裏面が存在する立体モデルの場合、 **不透明** であればカリングで裏面を除去してしまっても問題ない。
しかし、半透明にする場合はカリングの設定などに注意しないと上手く描画されなくなってしまう。

解決する術は様々で、どのような実装にしたいかで手段は変わってくる。
例えば、裏面は描画しなくていいや。という場合はカリング有効にして裏面を無視するようにしたり。
座表変換をうまく使い、奥にあるものから順番に描画するように工夫したり。

**3Dにおいて透明は鬼門！** というわれるのはこういったことが原因のようです。


## 色、深度だけ更新する
ブレンドを用いる場合、深度や色の設定が混在するケースでは色だけを更新したい、深度だけを更新したいといった場面に出くわすことがある。
正しく設定すると、深度バッファに対する処理を無視したり、カラーバッファに対する処理を無視したりできる。
**今後に役立つので覚えておくと良い**

### 色や深度の書き込みを制御する
色と、深度、それぞれにMaskを設定できる。以下のように設定すると色、深度共に更新される状態となる。

```
gl.colorMask(true, true, true, true);
gl.depthMask(true);
```


## カリングの反転
カリングは **裏面を除去** する。というものでした。しかし、カリングを反転させることで **表面は描かずに裏面だけを描く** ことができる。

カリングで陰面除去される面を反転させるには、`gl.cullFace()`メソッドを使用する。
このメソッドには、`gl.BACL`か`gl.FRONT`を指定できる。

```
gl.cullFace(gl.FRONT); // ← 表面をカリングする面として設定
```


## マウスカーソルの位置をクォータニオン
クォータニオンを使うことで、マウスカーソルの動きに連動してカメラの位置を変更することができます。
スクリーン上のマウスカーソルの位置から、クォータニオンの生成に必要な **軸** と **回転量** を求めるとカーソル位置に応じた視線の移動が可能となる。
クォータニオンでは、 **回転の軸となるベクトル** と **回転量**　の2つが重要。

### ポイント
ポイントは軸と量です。スクリーンの中心を原点として考え、カーソルの現在位置へ向かって伸びるベクトルを算出する。
直交するベクトルを回転の軸とする。

![webgl03.png](https://qiita-image-store.s3.amazonaws.com/0/48677/b2791a33-1108-0ebf-e3c7-ad97a554d66c.png "webgl03.png")

ベクトルの **長さ(大きさ)** を求める場合には、以下のように計算する

```
var vLength = Math.sqrt(x * x + y * y);
```

クォータニオンを用いる場合には、minMatrix.jsの`qtnIV`を使用する。
このクラスには、クォータニオンを扱うためのメソッド、それをベクトルに対して適用できるメソッドが実装されている。

### クォータニオンに回転を加える
```
var qt = qtnIV.identity(qtnIV.create()); // ← クォータニオンの初期化
qtnIV.rotate(回転量, 回転軸, qt); // ← クォータニオンに回転を適用
```

変数`qt`は回転を与えられたクォータニオンとなる。
この変数`qt`に行列に適用したり、ベクトルに適用したりして使用する。

### ベクトルに適用する場合
ベクトルにクォータニオンの回転を適用するには以下のようにする。

```
qtnIV.toVecIII(元ベクトル, qt, 適用先ベクトル);
```


## 感想
ここまででWebGLスクールも半分が終わり折り返しとなりました。第一回〜四回ではほとんど基礎的なことを学び、第五回〜七回で実践的なことを学びました。
もう地球を回転させて、その周りに雲が逆回転しているなんてものは楽々レンダリングできるようになっているはずです！おそらく。

年末年始を挟むので、一旦スクールのほうはしばらく休みとなるので、自分でWebGLをたくさん触ってみようと思います。
講師の方からも何かデモを作ってきてください。と言われているので、色々考えてみようと思います。

### 続き
[第八回 WebGLスクール 「シェーダエフェクトテクニック」](http://qiita.com/konweb/items/bd392cd492ff04a24236)
[第十回 WebGLスクール 「ポストエフェクトテクニック」](http://qiita.com/konweb/items/698892586b0df3759875)
[第十一回 WebGLスクール「キューブ環境マッピング」](http://qiita.com/konweb/items/262b107b2ea9a6113776)