JavaScript Advent Calendar/WebGLコース9日目・テクスチャーの使い方
JavaScript Advent Calendar 2011 WebGL駅伝9日目、独走2日目になります。今からでも参加したい方がいましたらぜひ教えてください。
さて、今日は昨日の内容を発展させて、画像を貼ってみたいと思います。
WebGLのテクスチャーとして使える画像は、通常は辺の長さが2の累乗である必要があります。ただし、やり方によってはどんなサイズの画像でも使えます。
今回は256x256で用意したこの画像を表示してみます。ただ表示するだけではおもしろくないので、色を反転させてみます。
Learning WebGLのLesson 5に似た内容です。
シェーダー
今回用意したシェーダーはこんな感じです。昨日と比べてみてください。
<script type="text/x-vertex-shader" id="vs"> // 頂点シェーダー attribute vec2 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; void main() { gl_Position = vec4(aPosition, 0.0, 1.0); vTexCoord = aTexCoord; } </script> <script type="text/x-fragment-shader" id="fs"> // フラグメントシェーダー precision mediump float; varying vec2 vTexCoord; uniform sampler2D uSampler; void main() { gl_FragColor = vec4(1.0 - texture2D(uSampler, vTexCoord).rgb, 1.0); } </script>
今日の新しいところは、uniform sampler2D uSampler;
とtexture2D(uSampler, vTexCoord)
です。
sampler2Dというのはテクスチャーの型だと思ってください。実態は整数で、テクスチャーの入ってるレジスタ番号ですが。
そのテクスチャーのある座標の色を取得するのがtexture2D
という組み込み関数です。座標はvec2で、各要素が0から1の範囲です。0から255(元画像のサイズ)ではありません。
texture2Dはvec4を返します。rgbでその最初の3つの要素を取ってきて、1.0から引くことで反転しています。GLSLはfloatとvec3の加減算がvec3になるなど、直感的にベクトル演算が書けます。要素へのアクセスは.xとか.rとか.xyとか.rgbとか書くと思った通りのものが得られます。
頂点オブジェクトを準備
シェーダープログラムをコンパイルするところまでは昨日と同じなので省略して、頂点オブジェクトを考えてみます。
vertices
の各座標がcoords
の各座標に対応します。つまり、(-1,-1)の点はテクスチャーの(0,0)の点の色を使うという具合です。
// init vertex buffer var vertices = [ -1, -1, 1, -1, -1, 1, 1, 1, ]; var vertBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); var coords = [ 0, 0, 1, 0, 0, 1, 1, 1, ]; var texCoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(coords), gl.STATIC_DRAW);
テクスチャーの準備
いよいよテクスチャーを作ります。
// init texture var img = document.getElementById('jimmy'); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
img要素をDOMから取得して、texImage2D
でセットしています。
img要素は下のようにドキュメントから切り離されていても構いません。ただし、texImage2D
を呼ぶときは画像がロード済みでないといけません。
var img = new Image(); var texture = gl.createTexture(); img.onload = function(){ ... } img.src = 'foo.jpg';
texParameteri
とかのことは7日目の記事を参照してください。
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
はWebGLだけのAPIで、テクスチャーの座標を上下反転させて左下を(0,0)、右上を(1,1)とするためのものです。これをしないときは左上が(0,0)、右下が(1,1)になります。
描画
いつもどおりです。
// draw gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer); gl.vertexAttribPointer(aPositionLocation, 2, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); gl.vertexAttribPointer(aTexCoordLocation, 2, gl.FLOAT, false, 0, 0); gl.activeTexture(gl.TEXTURE0); // ←ここの数字と gl.bindTexture(gl.TEXTURE_2D, texture); gl.uniform1i(uSamplerLocation, 0); // ←ここの数字をあわせる gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
activeTexture
は、これからn番目のレジスターについて行うテクスチャー関連の操作を行いますという意味です。ここでは0番目のレジスターに先ほど作ったテクスチャーを割り当て、uSampler
が0番目を指すようにしています。この3行はいつもセットで使います。