WebGL2.0のためのGLSL ES 3.0

正式にWebGL2.0が使えるようになってきましたので、ここでWebGL2.0に対応する**GLSL ES 3.0**について、まとめていきたいと思います。


>自分でも調べましたが、間違った認識や情報が含まれていたり、足りない情報がある可能性が高いです。
ぜひコメントをどんどん投げていただけるとありがたいですm(_ _)m
めちゃくちゃ、読みにくいです。ゴメンなさい。

# GLSL ES 1.0 から 3.0 への変更点

## Version directive
```GLSL:vert.vs
   #version 300 es
   void main(){
     ...
   }
```
 - 先頭に`#version 300 es`を記述することによって、このシェーダーが**GLSL ES 3.0**で書かれていることを示します。この宣言が空白を除いて一番最初に来なかった場合、コンパイルエラーになってしまいます。
 
 > 例えばよく使われるhtmlのscriptタグの中にシェーダを記述する以下のような場合、`<script>`タグと`#version 300 es`の間に改行文字があるので、コンパイルエラーになります。

> ```html:html
 <script>// (ここに改行文字がある)
   #version 300 es
   ...
 </script>
 <!-- コンパイルエラー -->
```

> なので、改行せずに`<script>`タグの真横に直接書く必要があります。

> ```html:html
 <script>#version 300 es
   ...
 </script>
```

 - 何も書かれていなかった場合は、これまで通り**GLSL ES 1.0**で書かれていると認識され、中身がGLSL ES 3.0の書き方で書かれていると、コンパイルエラーになります。
 - 頂点シェーダにもフラングメントシェーダにも書く必要があります
 - 頂点シェーダとフラグメントシェーダでバージョンはそろえないとコンパイルエラーになります
 - フラグメントシェーダーではこの後に`precision mediump float;`を持ってきます（ないとコンパイルエラー）
 
```GLSL:vert.vs
   #version 300 es // ないとコンパイルエラー
   void main(){
     ・・・
   }
```

```GLSL:frag.fs
   #version 300 es // ないとコンパイルエラー
   precision highp float;
   void main(){
     ・・・
   }
```
 
## Types
### 符号なし整数に対応
 - リテラルが **u** で `uint test = 10u;`のように表します。
 - `uint`・`uvec2`・`uvec3`・`uvec4`が追加されました。

### 正方行列以外にも対応
 - 今までは `mat2` ・ `mat3` ・ `mat4` のみでしたが、`mat2x3` などにも対応しました
 - `mat2x2` や `mat3x3` などは今までの `mat2` や  `mat3` などと同じです（正確には、`mat2` や  `mat3` は正式な型名ではなく、`mat2x2` や `mat3x3` が正式な型名です）

### texture系
 - texture系には様々な変更があって、それぞれに対応する関数などもあるので、別記します。 

## Qualifiers
### `attribute`・`varying` が廃止され、`in`・`out` に
 - attribute と varying が廃止され、使うとコンパイルエラーになります。
 - その代わりに、シェーダに入ってくるもの（attribute やフラグメントシェーダの varying 変数）は `in` で表し、出て行くもの（頂点シェーダの varying 変数や `gl_FragColor`）は `out` で表します。
 - `gl_Position`　や　`gl_PointSize` は変更なしなので、頂点シェーダで `gl_Position` ではなく `out vec4 outPosition` などにしても、何も映らなくなるだけなのでご注意を。

```GLSL:vert.vs
   #version 300 es
   in vec3 position;
   in vec4 color;
   out vec4 vColor;
   uniform mat4 mvpMatrix;
   void main(){
     gl_Position = position * mvpMatrix;
     gl_PointSize = 0.2;
   }
```
```GLSL:frag.fs
   #version 300 es
   precision mediump float;
   in vec3 vColor;
   out vec4 outColor;
   void main(){
        outColor = vColor;
   }
```
  > 最後の方に書いた、将来実装される機能のための予約語の中に `attribute` や `varying`　が入っていたのですが、これは1.0との誤解を防ぐためなのか、それともこれから実装されるのかどうなんでしょうか？

  > 見た感じGLSL ES 3.1や3.2で追加されている形跡がないので、単純に誤解を防ぐためのように思われますが。

### centroid
 - MSAA（アンチエイリアシングの技法の一種）で起きていた問題（テクスチャ座標が1.0を超えたりしてしまう）を解決するやつです。
 - 通常はテクスチャ座標を求める時に、単純にピクセルの中心を補間するが、centroidを使った場合、重心ベースの補間をしてくれます。
 - 頂点シェーダでは `centroid out`、フラグメントシェーダでは `centroid in` のみ有効になります。

```GLSL:vert.vs
in vec2 textureCoord;
centroid out vec2 texCoord;

void main(){
    texCoord = textureCoord;
}
```

```GLSL:frag.fs
centroid in vec2 texCoord;
```

### smooth, flatに対応
- smooth はシェーダ間の受け渡しの時に、データを補間して渡してくれます。（今まで通り）
- flat はシェーダ間の受け渡しの時に、データの補間をしません。([詳しくはみんな大好きwgld.org](https://wgld.org/d/webgl2/w005.html))
- フラットシェーディングなどは flat
- centroid も　smooth も flat もついていない時は、smooth で補間されます。
- int などの型に対しては補間できないので、コンパイルエラーになります。
- flat でどこの頂点のデータが次のシェーダに渡されるかは `provoking vertex` としてシステムで定義されるので変更できません。

```GLSL:frag.fs
smooth in vec2 texCoord; // smooth補間される
in vec2 texCoord; // smooth補間される
flat in vec2 texCoord; // 補間されない = flat
```

## uniform block
- 使いまわしたりするunform変数とかをJS側でバッファーにして、送ることができるようになりました。
- その時にシェーダで受け取る役割をします。
- sampler系は使うことができません
- 中にネストでuniform blockを使うこともできません
- ブロックの中で定義したものは、そのままメインスコープとして使うことができます

```GLSL:frag.vs
in vec3 position;
uniform Transform {
    mat4 mvpMatrix;
};

void main(){
    gl_Position = vec4(position, 1.0) * mvpMatrix;
}
```

- 末尾にインスタンス名を指定することもできます

 ```GLSL:vert.vs
in vec3 position;
uniform Transform {
    mat4 mvpMatrix;
} TransformMatrix;

void main(){
    gl_Position = vec4(position, 1.0) * TransformMatrix.mvpMatrix;
}
```

## layout

 - 今まで、attribute変数をJS側でバインドする時に`glContext.BindAttribLocation`や`glContext.GetAttribLocation`使ってattribute変数を使っていましたが、`layout`を使うとこの処理GLSL側ですることができるようになります。

```GLSL:vert.vs
layout(location = 0) in vec3 position;
void main(){
    gl_Position = vec4(position, 1.0) * TransformMatrix.mvpMatrix;
}
```

- UniformBlockの場合のlayoutもあり、ちょっと特殊です
- UniformBlockは普通に使おうとすると、JS側でのバインドなどの処理が多いので、ある程度GLSL側でlayoutを使うことによってそれが軽減できます。
- UniformBlockの場合特にlocationはないので、中のデータのフォーマットなどを指定します
- 何個でも同時に指定できますが、上の三つ（フォーマット系）と下の2つ（行列系）で、同じ系統のものを指定した場合、他のフォーマットはそのままで置き換えられます。
- 内部で、一つだけ`row_major`や`column_major`に帰ることもできます
- 初期化のように最初に指定しておくこともできます。

| 名前 | 説明 |
|:--|:--|
| shared  | デフォルト。複数のプログラムで、同じレイアウトが保障されます  |
| packed | コンパイラによって、中のデータのメモリなどが最適化されるのを許可します。 |
| std140 | std140のフォーマットを使います。詳しくは下を参照 |
| row_major | デフォルト。行列を行を優先してコンパイルします |
| column_major | 行列を列を優先してコンパイルします |

 ```GLSL:vert.vs
layout(shared, row_major) uniform; 

layout (std140) uniform Transform {
    mat4 mvpMatrix;
} TransformMatrix;

layout (packed, row_major) uniform Transform2 {
    mat4 mvpMatrix2;
    layout(column_major) mat2 testMatrix;
} TransformMatrix2;
```
(できたら、UniformBufferObjectについても解説したいですが、doxasさんあたりがわかりやすい解説をしていただけると思うので僕からは省きます)

> ### `std140`とは（割とちゃんと理解できているか怪しいところなので、詳しい方教えていただけるとありがたいです）
> 簡単に言うと、今までのAttribute変数の`getAttribLocation`などJS側でインデックスをとってきてバインドするように、UniformBufferObjectの場合も同じような処理が必要です。
> しかし、GLSL3.0になって、attribute変数（今はattributeではないですが）にlayoutでインデックスをGLSL側から登録できるようになりました。同じようにUniformBlockの場合も、わざわざJS側でインデックスを調べなくてもいいように作られたのが`std140`です。
> 実際の所どんなものかというと、インデックスを登録しないので、GLSL側とJS側でデータのとり違いがないように、細かくフォーマットを指定しています。
> 細かいフォーマットについては[こちらの記事](http://ramemiso.hateblo.jp/entry/2014/01/14/140716)を読んでください

## 組み込み変数など
- 前述しましたが、gl_FragColorが廃止されました。
- その他にも廃止されたり、追加されたものがあります。

1. `gl_FragColor`が廃止
    → `out vec4 color`のように自分で定義する
2. マルチレンダーターゲットようの`gl_FragData[n]`が廃止
→ `layout(location = n) out vec4 color`のようにlayoutを用いて、自分で定義する

    ```GLSL:今までの書き方・フラグメントシェーダ
        precision mediump float;
        varying vec4 vColor;
        void main(){
            gl_FragColor = vColor;
        }
    ```

    ```GLSL:GLSLES3.0での書き方・フラグメントシェーダ
       #version 300 es
       precision mediump float;
       in vec4 vColor;
       out vec4 outColor;
       void main(){
           outColor = vColor;
       }
    ```

3. フラグメントシェーダーに`gl_FragDepth`が追加
   - フラグメントシェーダー内で、この変数に深度値を渡すことができます。
> gl_FragDepthについて、実際の使い方などがわからなかったので、誰か補足していただけるとありがたいです。

4. 頂点シェーダに`gl_VertexID`や`gl_InstanceID`が追加
   - `gl_VertexID`は現在処理している頂点のIDが格納されています
   - IDは各ドローコールで、1から処理される順に1ずつたされていったIntが帰ってきます
   - `gl_VertexID`は必ず存在しますが、その中の値は定義されていない場合があるそうです。（どんな時に定義されないのかはわかりませんでした）
   - パーティクルなどで、頂点ごとに違った動きをさせることができます。
   - `gl_InstanceID`は[インスタンシング](https://wgld.org/d/webgl/w075.html)で、現在処理しているインスタンスのIDが格納されています。
   - IDはInt型で、インスタンシングを使っていない場合は0が格納されます。
   - インスタンシングでそれぞれのインスタンスの描画結果を変えることができるようになります。

5. その他、今まであった組み込み定数の上限が強化

| 定数名 | 2.0 | 3.0 |
|:--|:--|:--|:--|
| gl_MaxVertexAttribs | 8 | 16 |
| gl_MaxVertexUniformVectors | 128 | 256 |
| gl_MaxVertexOutputVectors | none | 16 |
| gl_MaxFragmentInputVectors | none | 15 |
| gl_MaxVertexTextureImageUnits | 0 | 16 |
| gl_MaxCombinedTextureImageUnits | 8 | 32 |
| gl_MaxTextureImageUnits | 8 | 16 |
| gl_MaxFragmentUniformVectors | 16 | 224 |
| gl_MaxDrawBuffers | 1 | 4 |
| gl_MinProgramTexelOffset | none | -8 |
| gl_MaxProgramTexelOffset | none | 7 |
| gl_MaxVaryingVectors | 8 | none |

## 組み込み関数
> `floatなど`とはfloatで構成されるvec2なども含まれます。同様にboolやint、uintも同じです。
> `vecなど`とはいろんな長さのvec全てを表します。`vec2`・`vec2x4`・`vec4x3`のことです。同じように`matなど`も行数と列数が2〜4のもの全てを指します。

- 双曲線関数
    1. `sinh(floatなど x)`
        - 双曲線正弦を返します

        ```math
            \frac{e^x - e^{-x} }{2.0}
        ```
        - 返り値はxと同じ型
    2. `cosh(floatなど x)`
        - 双曲線正弦を返します

        ```math
            \frac{e^x + e^{-x} }{2.0}
        ```
        - 返り値はxと同じ型
    3. `tanh(floatなど x)`
        - 双曲線正接を返します

        ```math
            \frac{sinh x}{cosh x}
        ```
        - 返り値はxと同じ型
    4. `asinh(floatなど x)`
        - 逆双曲線正弦を返します

        ```math
            \ln \Bigl(x+\sqrt{x^2+1} \bigr)
        ```
        - 返り値はxと同じ型
    5. `acosh(floatなど x)`
        - 逆双曲線正弦を返します

        ```math
            \ln \Bigl(x + \sqrt{x+1}\sqrt{x-1}\bigr)
        ```
        - 返り値はxと同じ型
    6. `tanh(floatなど x)`
        - 逆双曲線正接を返します

        ```math
            \frac{1}{2}\ln\Bigl(\frac{z+1}{z-1})
        ```
        - 返り値はxと同じ型
        

    ### 記述例
    ```glsl
    float res1 = sinh(float x);
    float res2 = cosh(float x);
    float res3 = tanh(float x);
    float res4 = asinh(float x);
    float res5 = acosh(float x);
    float res6 = atanh(float x);
    ```
- 一般共通関数
    1. `trunc(floatなど x)` 
        - 0に近づくように小数点以下が切り捨てられます。
        - xが正の時は切り捨て、負の時は切り上げになります。

        ```glsl
        x - fract(abs(x)) * sign(x)
        ```
        - 返り値はxと同じ型
    2. `round(floatなど x)`
        - 四捨五入。
        - ただし、0.5については端末によって、切り上げられるか切り捨てられるかが異なります。
        - 返り値はxと同じ型
    3. `roundEven(floatなど x)`
        - 四捨五入。
        - ただし、0.5については必ず偶数の側に切り捨て・切り上げられます

        ```glsl
        round(1.5) == round(2.5) == 2
        ```
        - 返り値はxと同じ型
    4. `isnan(floatなど x)`
        - xが非数であればtrue、それ以外はfalseを返します。
        - 返り値は`bool`など
    5. `isinf(floatなど x)`
        - xが無限大であればtrueを、そうでなければfalseを返します。
        - 返り値は`boolなど`
    6. `floatBitsToInt(floatなど x)`・` floatBitsToUint(floatなど)`
        - ビット表現を維持したまま`float`などを`int`などや`ｕｉｎｔ`などに変換します。
        - 返り値は`intなど`や`uintなど`です。
    7. `intBitsToFloat(intなど x)`・`uintBitsToFloat(uintなど x)`
        - ビット表現を維持したまま`int`などや`ｕｉｎｔ`などを`float`などに変換します。
        - 返り値は`float`です。

    ### 記述例
    ```glsl
    float res1 = trunc(float x);
    float res2 = round(1.4); //= 1.0
    float res3 = roundEven(2.5); //= 2.0
    bool res4 = isnan(float x);
    bool res5 = ininf(float x);
    int res6 = floatBitsToInt(float x);
    float res7 = intBitsToFloat(int x);
    ```
- pack・unpack関数
    1. `packSnorm2x16（vec2）`・`packUnorm2x16(vec2)`
        - vec2を16bit固定小数点数に変化後に一つの整数型へパック
        - 引数は`vec2`であれば何でも大丈夫です。
        - 返り値は`uint`です。
    2. `unpackSnorm2x16(uint)`・`unpackUnorm2x16(uint)`
        - 1でパックしたものを復元するためのものです。
        - 返り値は`vec2`です。
    3. `packHalf2x16(vec2)`
　　    - 1の浮動小数点版です。
　　    - 返り値は`uint`です。
    4. `unpackHalf2x16(uint)`
　　    - 3でパックしたものを復元するためのものです。

    ### 記述例
    ```glsl
    uint res1 = packSnorm2x16(vec2 x);
    vec2 res2 = unpackSnorm2x16(res1);
    uint res3 = packHalf2x16(vec2 x);
    vec2 res4 = unpackHalf2x16(res3);
    ```
- 行列関数
    1. `outerProduct(vecなど c, vecなど r)`
        - cの列ベクトル、rの行ベクトルの外積を返します。
        - cの長さ×rの長さの行列が帰ってきます。

        ```glsl
        outerProduct(vec2 c, vec3 r)
        ```
        - 返り値は`matなど`です。
    2. `transpose(matなど m)`
        - 行列mの転置行列を返します。
        - mat3の場合
        
        ```math
            m = \begin{pmatrix}
            a_1 & a_2 & a_3 \\
            a_4 & a_5 & a_6 \\
            a_7 & a_8 & a_9
            \end{pmatrix}\qquad
            transpose\left(m\right) = \begin{pmatrix}
            a_1 & a_4 & a_7 \\
            a_2 & a_5 & a_8 \\
            a_3 & a_6 & a_9
            \end{pmatrix}
        ```
        - mは上書きされません
        - 返り値はmと同じ`matなど`です。
    3. `determinant(正方matなど)`
        - 行列式を返します。
        - 引数は正方行列（`mat2`・`mat3`・`mat4`）のみです。
        - mat2・mat3の場合

        ```math
            \begin{align}
            & m2 = \begin{pmatrix}
            a_1 & a_2 \\
            a_3 & a_4
            \end{pmatrix}\qquad
            determinant\left(m2\right) = \begin{vmatrix}
            a_1 & a_2\\
            a_3 & a_4
            \end{vmatrix} = a_1a_4 - a_2a_3 \\
            & m3 = \begin{pmatrix}
            a_1 & a_2 & a_3 \\
            a_4 & a_5 & a_6 \\
            a_7 & a_8 & a_9
            \end{pmatrix}\quad
            determinant\left(m3\right) = \begin{vmatrix}
            a_1 & a_2 & a_3 \\
            a_4 & a_5 & a_6 \\
            a_7 & a_8 & a_9
            \end{vmatrix} = a_1a_5a_9 + a_2a_6a_7 + a_3a_4a_8 - a_1a_6a_8 - a_2a_4a_9 - a_3a_5a_7 \\
            \end{align}
        ```
        - 返り値は`float`です。
    4. `inverse(matなど m)`
        - 行列mの逆行列を返します。
        - mat2の場合
        
        ```math
            m = \begin{pmatrix}
            a_1 & a_2 \\
            a_3 & a_4 
            \end{pmatrix}\qquad
            inverse\left(m\right) = \frac{1}{a_1a_4-a_2a_3}\begin{pmatrix}
            a_4 & -a_3 \\
            -a_2 & a_1
            \end{pmatrix}
        ```
- フラグメントシェーダ関数
    1. `dFdx(float p)`
        - スクリーン空間のｘ座標についての偏微分
        - 一般的にプロシージャテクスチャのアンチエイリアスのために使われます。
        - 返り値はpと同じ`floatなど`です。
    2. `dFdy(float p)`
        - スクリーン空間のy座標についての偏微分
        - 一般的にプロシージャテクスチャのアンチエイリアスのために使われます。
        - 返り値はpと同じ`floatなど`です。
    3. `fwidth(float p)`
        - 絶対偏微分の合計を返します。
        - `abs(dFdx(p)) + abs(dFdy(p))`を返します。
-  その他の既存の関数の変更
    1. min関数・max関数・clamp関数の引数がint系・uint系にも対応
    2. `mix(floatなど x, floatなど y, boolなど a)`が追加？

       > 今までのものと何が違うのかがわかりません
わかる方教えていただけるとありがたいです。



## Texture
後で追記します

# 予約語一覧
| 予約語 |(現在使われている変数名など)|||
|:--|:--|:--|:--|
| const  | uniform  |
| layout |
| centroid | flat | smooth |
| break | continue | do | for |
| while | switch | case | default |
| if | else |
| in | out | inout|
| float | int | void | bool |
| true | false |
| invariant |
| discard | return |
| mat2 | mat3 | mat4 |
| mat2x2 | mat2x3 | mat2x4 |
| mat3x2 | mat3x3 | mat3x4 |
| mat4x2 | mat4x3 | mat4x4 |
| vec2 | vec3 | vec4 |
| ivec2 | ivec3 | ivec4 |
| bvec2 | bvec3 | bvec4 |
| uint | uvec2 | uvec3 | uvec4 |
| lowp | mediump | highp | precision |
| sampler2D | sampler3D | samplerCube |
| sampler2DShadow | samplerCubeShadow |
| sampler2DArray |
| sampler2DArrayShadow |
| isampler2D | isampler3D | isamplerCube |
| isampler2DArray|
| usampler2D | usampler3D | usamplerCube |
| usampler2DArray |
| struct |

| 予約語 |（将来使われる可能性がある語）|||
|:--|:--|:--|:--|
| attribute | varying |   |   |
| coherent | volatile | restrict | readonly |
| writeonly |
| resource | atomic_uint |
| noperspective |
| patch sample |
| subroutine |
| common | partition | active |
| asm |
| class | union | enum | typedef |
| template | this | packed |
| goto |
| inline | noinline | volatile | public |
| static | extern | external | interface |
| long | short | double | half |
| fixed | unsigned | superp |
| input | output |
| hvec2 | hvec3 | hvec4 |
| dvec2 | dvec3 | dvec4 |
| fvec2 | fvec3 | fvec4 |
| sampler3DRect |
| filter |
| image1D | image2D | image3D | imageCube |
| iimage1D | iimage2D | iimage3D | iimageCube |
| uimage1D | uimage2D | uimage3D | uimageCube |
| image1DArray | image2DArray |
| iimage1DArray | iimage2DArray | uimage1DArray | uimage2DArray |
| image1DShadow | image2DShadow |
| image1DArrayShadow | image2DArrayShadow |
| imageBuffer | iimageBuffer | uimageBuffer |
| sampler1D | sampler1DShadow | sampler1DArray | sampler1DArrayShadow |
| isampler1D | isampler1DArray | usampler1D | usampler1DArray |
| sampler2DRect | sampler2DRectShadow | isampler2DRect | usampler2DRect |
| samplerBuffer | isamplerBuffer | usamplerBuffer |
| sampler2DMS | isampler2DMS | usampler2DMS |
| sampler2DMSArray | isampler2DMSArray | usampler2DMSArray |
| sizeof | cast |
| namespace | using |
# 参考
[OpenGL ES - Wikipedia](https://ja.wikipedia.org/wiki/OpenGL_ES)
### texture
[OpenGL でテクスチャ配列を使う](http://blog.techlab-xe.net/archives/2894)
[テクスチャ配列の活用について (OpenGL)](http://blog.techlab-xe.net/archives/2928)