在鼠标点击处绘制一个点_WebGL笔记3

写在前面

上一篇笔记中,我们通过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编程指南》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code