はじめに
[前回のノート](/articles/attribute 变量与顶点着色器-webgl 笔记 2/) では、attribute 変数を通じて点の位置を動的に設定することを実装しました。マウスクリック位置に点を描画するには、マウスクリック位置の座標を取得すればよいだけです。最大の問題は座標変換です
一.canvas 座標の取得
イベントハンドラを追加し、イベントオブジェクトから探せばよいです。以下の通り:
// webgl は canvas 要素の参照
webgl.addEventListener('click', function(e) {
var x = e.offsetX;
var y = e.offsetY;
}
イベントオブジェクトの offsetX/Y 属性を通じて、マウスクリック位置の canvas 座標系における座標値 (x, y) を取得できます。canvas 左上角をクリックすると、取得するのは (0, 0) です
二.canvas 座標から WebGL 座標への変換
WebGL 中の原点は canvas 中心にあり、x 軸は右向きが正、y 軸は上向きが正、z 軸は画面から顔に向かう方向が正です。canvas 座標から WebGL 座標への変換方式は簡単な計算です。以下の通り:
var x = (e.offsetX - webgl.width / 2) / (webgl.width / 2);
var y = (webgl.height / 2 - e.offsetY) / (webgl.height / 2);
簡単な導出プロセスは以下の通り:
1.x'を求める
点 p が y 軸の左辺にある場合、x' = (width/2 - x) * -1 = x - width/2
さらに x 軸負半軸の長さで割って [0-1] 区間中の値を取得
x' = (x - width/2) / width/2
p が y 軸の右辺に���る場合、x' = x - width/2
さらに x 軸正半軸の長さで割って [0-1] 区間中の値を取得
x' = (x - width/2) / width/2
したがって x' = (x - width/2) / width/2
2.y'を求める
点 p が x 軸の上辺にある場合、y' = height/2 - y
さらに y 軸正半軸の長さで割って [0-1] 区間中の値を取得
y' = (height/2 - y) / height/2
p が x 軸の下辺にある場合、y' = (y - height/2) * -1 = height/2 - y
さらに y 軸負半軸の長さで割って [0-1] 区間中の値を取得
y' = (height/2 - y) / height/2
したがって y' = (height/2 - y) / height/2
三.点の描画
完全なコードは以下の通り:
webgl.addEventListener('click', function(e) {
// 座標を変換
var x = (e.offsetX - webgl.width / 2) / (webgl.width / 2);
var y = (webgl.height / 2 - e.offsetY) / (webgl.height / 2);
console.log(x, y);
// 2.attribute 変数に値を代入
// attribute 変数の記憶位置を取得
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 頂点位置を attribute 変数に渡す
gl.vertexAttrib3f(a_Position, x, y, 0.0);
// canvas をクリア
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 点を描画
gl.drawArrays(gl.POINTS, 0, 1);
});
注意:点を描画する前に毎回 clear 操作を行っています。clear をコメントアウトすると、クリック後に黒色背景と前の点がすべて消えたことがわかります。WebGL はカラーバッファを使用しており、WebGL システム中の描画操作は実際にはカラーバッファ中で描画を行っています。描画終了後、システムはバッファ中の内容を画面に表示し、その後カラーバッファはリセットされ、中の内容は失われます。リセット後の色は vec4(0.0, 0.0, 0.0, 0.0) なので、canvas は透明になります
P.S.実際には上記の clearColor は削除できます。gl.clear(gl.COLOR_BUFFER_BIT) はデフォルトで上一次の gl.clearColor で指定された値を使用するからです。如果没有であれば、デフォルト色 vec4(0.0, 0.0, 0.0, 0.0) を使用します
四.DEMO
上記のコードを含む完全な例については、以下を参照してください:http://www.ayqy.net/temp/webgl/attribute-clickit/index.html
五.まとめ
現在私たちが実装した効果は:どこをクリックしてもそこに点を描画し、どのようにクリックしても、1 つの点しか現れないということです
歴史の点を保持したい場合、現在は配列で歴史の点を記録し、その後 gl.drawArrays(gl.POINTS, 0, 1); をループしてすべての点を描画できます。しかし、このようにするのはあまりに愚かに思えます。一度に複数の点を描画できる方法はないでしょうか?あります。ただし buffer と配合して使用する必要があり、もう少し後の内容です
半天学んでもまだ点を描いているだけです。もう少し面白くするために、下一篇ではもう少し派手な内容——点の色を設定することを紹介します。点点点点して燦爛な星空を:-)
参考資料
- 《WebGL プログラミングガイド》
コメントはまだありません