Sampler Object
今回のサンプルの実行結果
Sampler をオブジェクトとして扱う
前回は uniform 変数をひとつのバッファとして扱い、複数のシェーダでの共有などの効率化が行える Uniform Buffer Object について扱いました。
初期化処理などが一部冗長になる部分はあるものの、GPU 上に uniform 変数の情報をバインドできる UBO は、実装が巨大になればなるほど大きな効果を発揮するでしょう。常に利用するべきものというような感じではないかなと思いますが、状況に応じて使えるようにしておくと、場合によってはかなり大きな効果を上げる可能性もありますのでがんばって習得してみてください。
さて今回ですが Sampler Object を扱います。あまり WebGL や OpenGL に詳しくないと Sampler と言われてもそれがなんのことなのか、わからないという方もいるかもしれません。
当サイトではそもそも、Sampler にフォーカスしたテキストをこれまで書いたことはありませんでした。なんとなくやんわりと、Texture == Sampler みたいに理解している人もいるかもしれません。GLSL には Sampler2D
という型があり、これを利用してテクスチャマッピングを行うことからもわかるとおり、Sampler と Texture は密接に関係しています。
今回は、Sampler と、それを管理できる Sampler Object について見ていきたいと思います。
そもそもサンプラとは
そもそも Sampler ってなんなのでしょうか。
WebGL で利用する GLSL では、テクスチャを参照するときに sampler2D
という型を利用しますよね。このことから、なんとなくサンプラってテクスチャのことなんだろうなあ……という感じにやんわりと理解している人が多いかもしれません。
でもよくよく考えてみると、テクスチャを参照するときにどのように色をサンプリング(取得)するのかの設定って、GLSL 側にはありません。これには通常 WebGL の API である gl.texParameteri
などを用います。
テクスチャのサンプリング設定の例
// テクスチャサンプリングの設定例
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
上記のように指定すると、テクスチャのサンプリング時の設定がニアレストネイバー法で行われるようになります。
つまり、GLSL 側でテクスチャから色を取得する際に、どのようなロジックによって色が抽出されてくるのかは、シェーダ側で指定するのではなく事前にアプリケーション側で指定を行った結果によって変わってくるわけです。
このテクスチャ参照の際のサンプリングの設定は、WebGL 1.0 の場合は常に テクスチャと紐付いた状態 になっています。
つまりまったく同じ色柄の元画像を使っていても、複数のサンプリング方式を使い分けたい場合は、個別に複数のテクスチャを用意した上で、それぞれにテクスチャパラメータの設定を行ってやらなくてはならないわけです。元画像が同じテクスチャを複数用意しなくてはいけないのは、いかにも無駄に感じられますよね。
こんなときに有用なのが今回紹介する Sampler Object です。
WebGL 2.0 では、テクスチャとサンプラが完全に別々に管理できるようになっています。テクスチャはあくまでも色の情報が詰め込まれたバッファであり、それをどのようにサンプリングするのかは、別途サンプラオブジェクトのほうで指定ができるようになったわけです。
サンプラをテクスチャから切り離す
さて、それでは実際に Sampler Object を利用した処理の流れを見てみましょう。
とは言え、やること自体は非常に簡単というか単純で、単に設定項目をテクスチャから切り離し別途設定してやる感じで特に難しいことはありません。
サンプラオブジェクトは、WebGL 2.0 から利用できるものなので、生成する命令そのものが新設されています。
Sampler Object を生成する
var sampler = gl.createSampler();
こんな感じで gl.createSampler
を呼び出してやるだけで、オブジェクト自体の生成は完了です。とても簡単ですね。
あとは、テクスチャの場合と同じようにサンプリングの設定を行っていきます。
テクスチャに対して設定を行うときは gl.texParameteri
を使いましたが、これがサンプラオブジェクトに対しての設定になりますので、設定時のメソッドには gl.samplerParameteri
を使います。
与える引数の種類とかは基本的にほとんど変わらないのでここもこれまでどおりの感覚で設定してやればいいでしょう。
サンプリング設定を行う例
gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
バインドした状態にしていなくても、第一引数に対象となるサンプラオブジェクトを渡して呼び出しを行えばいい感じです。
なんかちょっとバインド無しでできてしまうというのが気持ち悪い感じもしますが……
ただ、実際にこれをテクスチャのサンプラとして利用する際には、バインド処理が必要になります。
今回のサンプルでは、最初にテクスチャを一気にバインドするフェーズを設けてあるのですが、以下の例に示すようにサンプラを利用したいときは、サンプラオブジェクトをユニット番号に紐付ける感じでバインドしてやります。
サンプラを適切にバインドしてやる
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindSampler(1, sampler);
上記の例では、アクティブなテクスチャユニットが 1 になった状態で、テクスチャをバインドしていますよね。ですから、サンプラをバインドする gl.bindSampler
の第一引数にも、1 という数値を指定しています。第二引数は、先ほどサンプリング設定を行ったサンプラオブジェクトを渡せばいいわけですね。
実際にサンプルを実行してみればわかるかと思いますが、今回は一枚のテクスチャを、異なるサンプリング方式で参照しながら、ふたつの球体を描いています。
わかりやすさを重視して、最初にテクスチャユニットの 1 番と 2 番にそれぞれ別のサンプラを設定するようなコードになっていますが、動的にバインドするサンプラオブジェクトを変更しながら描画すれば、テクスチャユニット自体も単体でも大丈夫です。
まとめ
さて Sampler Object について見てきましたがいかがでしたでしょうか。
基本的にはテクスチャの扱い方さえ理解できていれば、そんなに難しいことはないと思います。単純にサンプリング方式に関する設定だけが切り離された感じで、まったく新しい概念が出てきたということではありません。
実際にこれが便利な場面というのが想像しにくい方もいるかもしれませんが、より柔軟に WebGL を扱っていくことができるようになる機能だと思いますので、知識の片隅に、覚えておいてもいいかもしれません。
たとえば、あえて何かサンプラオブジェクトの使いみちとして思いつくところがあるとすれば……
テクスチャのサンプリングは基本的には重い処理に分類されると思いますので、スペックやユーザーの設定に応じて、テクスチャのサンプリング方式を動的に変更できるようにする、とかでしょうか。いずれにしても、WebGL 1.0 のころと比較して、まったく新しいことができるようになったというものではなく、管理がしやすくなったというふうに捉えるのがいいと思います。
今回も実際に動作するサンプルは以下のリンクから確認できます。