写在前面
上一篇笔记中,我们通过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
五.总结
目前我们实现的效果是:点哪里在哪里画点,不管怎么点,都只会出现一个点
如果想要保留历史点,目前可以用数组记录历史点,然后循环gl.drawArrays(gl.POINTS, 0, 1);
把所有的点都绘制出来,但这样做似乎太傻了,有没有一种能够一次性绘制多个点的方法呢?有的,但要配合buffer使用,是稍微后面一点的内容
学了半天还在画点,为了稍微有趣一点,下一篇我们介绍炫一点的内容——设置点的颜色,点点点点出灿烂星空:-)
参考资料
- 《WebGL编程指南》