WebGL入門(2D基本編)

kaz
VELTRA Engineering
Published in
9 min readJul 28, 2017

WebGLは、OSに依存することなくウェブブラウザ上での3DCG表示を可能にします。CPUではなくGPUにて処理を行うため、グラフィックスを高速に描画します。

もちろん、3Dだけではなく2Dの描画処理も可能です。

2Dの描画であれば、HTML5のCanvasの技術で事足りますが、WebGLの入門として2D描画の理解が必要になります。

WebGL(Unity)で作られたゲームサイトもあるので、どのようなものが開発できるのか参考にもなるかと思います。
https://unityroom.com/

ちなみにWebGLはOpenGLのグラフィックAPIをCanvas上で使える様にした物です。
従って、OpenGLを理解すれば、自ずとWebGLの理解も進むので、OpenGLを先に理解するのでも問題ありません。

今回は、WebGLの基本中の基本「2Dの描画」に焦点を当てたいと思います。

大まかな流れ

■HTML

・canvasを用意

■JavaScript

・canvasの設定

・バッファオブジェクトの設定

・バーテックス(頂点)シェーダの設定

・フラグメントシェーダの設定

・プログラムオブジェクトの生成

・シェーダ側の変数受取り処理

・座標の定義

・描画!

HTML(index.html)

canvasを用意

<!DOCTYPE html><html lang=”ja”><head><script src=”webglScript.js”></script><title>WebGLおためし</title></head><body style=”text-align: center; width: 100%; height: 100%;”><canvas id=”canvas”></canvas></body></html>

■javaScript(webglScript.js)

canvasの設定

役割は、普段お絵描きするときのキャンバスをイメージすると、解りやすいですね。

var canvas = document.getElementById(“canvas”)canvas.width = 500canvas.height = 500var gl = canvas.getContext(“webgl”)gl.clearColor(1.0, 1.0, 1.0, 1.0) //Red,Green,Blue,Alphagl.clear(gl.COLOR_BUFFER_BIT)

頂点バッファの設定

WebGLに限った話ではありませんが、3Dを描画するには頂点が必要です。

(頂点が多ければ多いほど、ポリゴン感が無くなり、精細な3DCGになります。)

各頂点がどのように配置されているのか表した座標は、ローカル座標と呼びます。

これら頂点のローカル座標をWebGL内で変換し、頂点シェーダに渡しますが、このような頂点を扱うための入れ物が頂点バッファです。

var buffer = gl.createBuffer()gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

バーテックス(頂点)シェーダの設定

WebGL(OpenGL)では 3D空間の頂点情報 (x, y, z) をGPUに渡し、処理をします。(今回はx、yですが)

実際に結果を表示するのは2Dのディスプレイのため、頂点情報が最終的にはディスプレイのどこに位置するのかを計算する必要があります。

その役割を果たすのがバーテックスシェーダで、頂点の数だけバーテックスシェーダが実行されることになります。

※シェーダの設定にGLSL(OpenGL Shading Language)の知識が必要になりますが、今回は割愛します。

var vSource = [“precision mediump float;”,“attribute vec2 vertex;”,“void main(void) {“,“gl_Position = vec4(vertex, 0.0, 1.0);”,“}”].join(“\n”)var vShader = gl.createShader(gl.VERTEX_SHADER)gl.shaderSource(vShader, vSource)gl.compileShader(vShader)gl.getShaderParameter(vShader, gl.COMPILE_STATUS)

フラグメントシェーダの設定

各ピクセルにどういう色を付けるのかを決定します。今回は黒(0.0, 0.0, 0.0, 1.0)とします。

※フラグメントシェーダは、バーテックス(頂点)シェーダによって計算された最終的な頂点の数だけ実行されます。

var rgba = [0.0, 0.0, 0.0, 1.0] // Red, Green, Blue, Alphavar fSource = [    “precision mediump float;”,    “void main(void) {“,    “gl_FragColor = vec4(“+ rgba.join(“,”) +”);”,    “}”].join(“\n”)var fShader = gl.createShader(gl.FRAGMENT_SHADER)gl.shaderSource(fShader, fSource)gl.compileShader(fShader)gl.getShaderParameter(fShader, gl.COMPILE_STATUS)

プログラムオブジェクトの生成

バーテックス(頂点)シェーダ、フラグメントシェーダ、WebGLプログラムと各シェーダとのデータのやり取りを管理します。

var program = gl.createProgram()gl.attachShader(program, vShader)gl.attachShader(program, fShader)gl.linkProgram(program)gl.getProgramParameter(program, gl.LINK_STATUS)gl.useProgram(program)

シェーダ側の変数受取り処理

頂点インデックスを取得し登録。

var vertex = gl.getAttribLocation(program, “vertex”)gl.enableVertexAttribArray(vertex)gl.vertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0)

座標の定義

var vertices = [    -1 , 0, //x,y    1 , 0 //x,y]
var verticesNum = vertices.length / 2

描画!

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW)gl.drawArrays(gl.LINE_LOOP, 0, verticesNum)

それでは実行してみましょう。index.htmlを開きます。

何の変哲も無い横線が表示されました。

これは設定した2つの座標が(-1,0)と(1,0)のため、このような結果になっています。

中心が(0,0)、右上が(1,1)、左下が(-1,-1)ですね。

座標はいくらでも増やせます。座標定義を変更してみます。

var vertices = [    -0.75,  0.4,    0.75,  0.4,    -0.5, -0.7,    0.0,  0.8,    0.5, -0.7
]

ついでに色も変更します。

//フラグメントシェーダの設定で設定したカラーを以下に変更
var rgba = [1.0, 0.0, 0.0, 1.0] // Red, Green, Blue, Alpha

出力結果

今度はレンダリングしたいプリミティブを変更し、再描画してみます。

gl.drawArrays(gl.LINE_LOOP, 0, verticesNum)

を以下に変更。

gl.drawArrays(gl.LINE_STRIP, 0, verticesNum)

結果

今までは開始座標と終了座標も線で結ばれてました(LINE_LOOP)が、今度(LINE_STRIP)は結ばれなくなりました。

このままでは格好悪いので、今度は座標を追加し、結果が前と同一になるようにします。

var vertices = [-0.75,  0.4,0.75,  0.4,-0.5, -0.7,0.0,  0.8,0.5, -0.7,-0.75,  0.4]

せっかくなので、もう1つ、他のプリミティブも試してみます。

gl.drawArrays(gl.TRIANGLES, 0, verticesNum)

結果

変な形になりました。

これは「1〜3つ目の座標で三角形」、「4〜6つ目の座標で三角形」を描画するため、このような出力結果になります。

最後に、定義した座標の一部分だけを適用し、描画してみます(他の設定値はこのまま)。

「2〜4つ目の座標で三角形」を描画してみましょう。

var vertices = [
gl.drawArrays(gl.LINE_LOOP, 2, 3)
]

結果

・・・

如何でしたか?

大体はお約束的なロジックです。そう考えると2D描画処理は簡単な気がしますね!

気が向いたら、次回以降に続きを載せたいと思います。

--

--