50
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WebGLAdvent Calendar 2016

Day 10

WebGL2.0のためのGLSL ES 3.0

Last updated at Posted at 2016-12-10

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

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

GLSL ES 1.0 から 3.0 への変更点

Version directive

vert.vs
   #version 300 es
   void main(){
     ...
   }
  • 先頭に#version 300 esを記述することによって、このシェーダーがGLSL ES 3.0で書かれていることを示します。この宣言が空白を除いて一番最初に来なかった場合、コンパイルエラーになってしまいます。

例えばよく使われるhtmlのscriptタグの中にシェーダを記述する以下のような場合、<script>タグと#version 300 esの間に改行文字があるので、コンパイルエラーになります。

html

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

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

Types

符号なし整数に対応

  • リテラルが uuint test = 10u;のように表します。
  • uintuvec2uvec3uvec4が追加されました。

正方行列以外にも対応

  • 今までは mat2mat3mat4 のみでしたが、mat2x3 などにも対応しました
  • mat2x2mat3x3 などは今までの mat2mat3 などと同じです(正確には、mat2mat3 は正式な型名ではなく、mat2x2mat3x3 が正式な型名です)

texture系

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

Qualifiers

attributevarying が廃止され、inout

  • attribute と varying が廃止され、使うとコンパイルエラーになります。
  • その代わりに、シェーダに入ってくるもの(attribute やフラグメントシェーダの varying 変数)は in で表し、出て行くもの(頂点シェーダの varying 変数や gl_FragColor)は out で表します。
  • gl_Position や gl_PointSize は変更なしなので、頂点シェーダで gl_Position ではなく out vec4 outPosition などにしても、何も映らなくなるだけなのでご注意を。
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;
   }
frag.fs
   #version 300 es
   precision mediump float;
   in vec3 vColor;
   out vec4 outColor;
   void main(){
        outColor = vColor;
   }

最後の方に書いた、将来実装される機能のための予約語の中に attributevarying が入っていたのですが、これは1.0との誤解を防ぐためなのか、それともこれから実装されるのかどうなんでしょうか?

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

centroid

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

void main(){
    texCoord = textureCoord;
}
frag.fs
centroid in vec2 texCoord;

smooth, flatに対応

  • smooth はシェーダ間の受け渡しの時に、データを補間して渡してくれます。(今まで通り)
  • flat はシェーダ間の受け渡しの時に、データの補間をしません。(詳しくはみんな大好きwgld.org)
  • フラットシェーディングなどは flat
  • centroid も smooth も flat もついていない時は、smooth で補間されます。
  • int などの型に対しては補間できないので、コンパイルエラーになります。
  • flat でどこの頂点のデータが次のシェーダに渡されるかは provoking vertex としてシステムで定義されるので変更できません。
frag.fs
smooth in vec2 texCoord; // smooth補間される
in vec2 texCoord; // smooth補間される
flat in vec2 texCoord; // 補間されない = flat

uniform block

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

void main(){
    gl_Position = vec4(position, 1.0) * mvpMatrix;
}
  • 末尾にインスタンス名を指定することもできます
vert.vs
in vec3 position;
uniform Transform {
   mat4 mvpMatrix;
} TransformMatrix;

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

layout

  • 今まで、attribute変数をJS側でバインドする時にglContext.BindAttribLocationglContext.GetAttribLocation使ってattribute変数を使っていましたが、layoutを使うとこの処理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_majorcolumn_majorに帰ることもできます
  • 初期化のように最初に指定しておくこともできます。
名前 説明
shared デフォルト。複数のプログラムで、同じレイアウトが保障されます
packed コンパイラによって、中のデータのメモリなどが最適化されるのを許可します。
std140 std140のフォーマットを使います。詳しくは下を参照
row_major デフォルト。行列を行を優先してコンパイルします
column_major 行列を列を優先してコンパイルします
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側でデータのとり違いがないように、細かくフォーマットを指定しています。
細かいフォーマットについてはこちらの記事を読んでください

組み込み変数など

  • 前述しましたが、gl_FragColorが廃止されました。
  • その他にも廃止されたり、追加されたものがあります。
  1. gl_FragColorが廃止
    out vec4 colorのように自分で定義する

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

    今までの書き方・フラグメントシェーダ
        precision mediump float;
        varying vec4 vColor;
        void main(){
            gl_FragColor = vColor;
        }
    
    GLSLES3.0での書き方・フラグメントシェーダ
       #version 300 es
       precision mediump float;
       in vec4 vColor;
       out vec4 outColor;
       void main(){
           outColor = vColor;
       }
    
  3. フラグメントシェーダーにgl_FragDepthが追加

    • フラグメントシェーダー内で、この変数に深度値を渡すことができます。

gl_FragDepthについて、実際の使い方などがわからなかったので、誰か補足していただけるとありがたいです。

  1. 頂点シェーダにgl_VertexIDgl_InstanceIDが追加

    • gl_VertexIDは現在処理している頂点のIDが格納されています
    • IDは各ドローコールで、1から処理される順に1ずつたされていったIntが帰ってきます
    • gl_VertexIDは必ず存在しますが、その中の値は定義されていない場合があるそうです。(どんな時に定義されないのかはわかりませんでした)
    • パーティクルなどで、頂点ごとに違った動きをさせることができます。
    • gl_InstanceIDインスタンシングで、現在処理しているインスタンスのIDが格納されています。
    • IDはInt型で、インスタンシングを使っていない場合は0が格納されます。
    • インスタンシングでそれぞれのインスタンスの描画結果を変えることができるようになります。
  2. その他、今まであった組み込み定数の上限が強化

| 定数名 | 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全てを表します。vec2vec2x4vec4x3のことです。同じようにmatなども行数と列数が2〜4のもの全てを指します。

  • 双曲線関数

    1. sinh(floatなど x)

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

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

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

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

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

      • 逆双曲線正接を返します
          \frac{1}{2}\ln\Bigl(\frac{z+1}{z-1})
      
      • 返り値はxと同じ型

    記述例

    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が正の時は切り捨て、負の時は切り上げになります。
      x - fract(abs(x)) * sign(x)
      
      • 返り値はxと同じ型
    2. round(floatなど x)

      • 四捨五入。
      • ただし、0.5については端末によって、切り上げられるか切り捨てられるかが異なります。
      • 返り値はxと同じ型
    3. roundEven(floatなど x)

      • 四捨五入。
      • ただし、0.5については必ず偶数の側に切り捨て・切り上げられます
      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などやuintなどに変換します。
      • 返り値はintなどuintなどです。
    7. intBitsToFloat(intなど x)uintBitsToFloat(uintなど x)

      • ビット表現を維持したままintなどやuintなどをfloatなどに変換します。
      • 返り値はfloatです。

    記述例

    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でパックしたものを復元するためのものです。

    記述例

    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の長さの行列が帰ってきます。
      outerProduct(vec2 c, vec3 r)
      
      • 返り値はmatなどです。
    2. transpose(matなど m)

      • 行列mの転置行列を返します。
      • mat3の場合
          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など)

      • 行列式を返します。
      • 引数は正方行列(mat2mat3mat4)のみです。
      • mat2・mat3の場合
          \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の場合
          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)
      • スクリーン空間のx座標についての偏微分
      • 一般的にプロシージャテクスチャのアンチエイリアスのために使われます。
      • 返り値は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

texture

OpenGL でテクスチャ配列を使う
テクスチャ配列の活用について (OpenGL)

50
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?