【WebGL之巅】05-通过鼠标点击绘点_v2_彩色版

By yesmore on 2021-07-25
阅读时间 6 分钟
文章共 1.5k
阅读量

对应《WebGL编程指南》代码:05-ColorPoints

要点:注册js事件、匿名函数、坐标转换、g_points.push()

知识点

一、为何使用匿名函数?

先看示例:

1
2
3
4
// 注册鼠标点击事件响应函数(匿名函數)
canvas.onmousedown = function(event){
click(event, gl, canvas, a_Position);
};

当你要画一个点时,需要传入三个变量(gl、canvas、a_Position),这三个变量是定义在main()函数中的局部变量。

当用户点击鼠标时,浏览器会自动调用注册到<canvas>的ommousedown属性上的函数,并传入event(后面简写为‘e’)参数,通常你会这样写:

1
2
canvas.onmousedown = mousedown	// 注册响应函数
function mousedown(e) { ... }// 接收事件响应参数

但是,这样写会出现一个问题,定义在main()函数外部的mousedown()函数就无法获取 main函数中的局部变量,而使用匿名函数就可以解决这个问题。

当用户点击鼠标后,程序先调用匿名函数function(e),再调用匿名函数中的click()传入参数。

当然,这里也可以使用ES6的箭头函数更加简洁:

1
canvas.onmousedown = ev => click(ev, gl, canvas, a_Position);
二、坐标转换

区别:

​ ①客户区原点(0,0):位于浏览器视图区的左上角顶点处

​ ②canvas原点:位于绘图区左上角顶点处(…)

​ ③WebGL原点:位于绘图区中心点

1
2
3
4
5
// 获取<canvas>在浏览器客户区中的坐标
var rect = ev.target.getBoundingClientRect();
x = ((x - rect.left) - canvas.height / 2) / (canvas.height / 2);
y = (canvas.width / 2 - (y - rect.top)) / (canvas.width / 2);

rect.left:原点在客户区中x坐标 使用*{margin:0;padding:0;list-style: none;}清除默认样式后,与客户区原点重和,即(0,0)
rect.top:原点在客户区中y坐标 若不清除默认样式后,canvas原点可能位于(8,8),仅限博主浏览器测试。
x 相对于浏览器左上角顶点处的鼠标横轴位置
y 相对于浏览器左上角顶点处的鼠标纵轴位置
x - rect.left:鼠标点击处相对于canvas原点位置 将(x,y)转换成canvas坐标系的坐标
y - rect.top:鼠标点击处相对于canvas原点位置 将(x,y)转换成canvas坐标系的坐标
canvas.height/width 绘图区的宽高
(canvas.height/width) / 2 绘图区的中心点
(x - rect.left) - canvas.width / 2 WebGL系统的原点
canvas.height / 2 - (y - rect.top) WebGL系统的原点
((x - rect.left) - canvas.width / 2) / (canvas.width / 2) 将canvas坐标系下的坐标转换为WebGL坐标系中,最终渲染位置
(canvas.height / 2 - (y - rect.top)) 将canvas坐标系下的坐标转换为WebGL坐标系中,最终渲染位置
三、为啥用g_points.push()

因为WebGL系统中的绘制操作实际上是在颜色缓冲区中进行绘制,绘制结束后系统将缓冲区中的内容显示在屏幕上,然后颜色缓冲区就会被重置,其中内容就会丢失。也由此,每次点击后,浏览器会重新绘制之前的并绘制新点击的。

四、uniform变量

作用:从js程序向顶点和片元着色器传输“一致的”(不变的)数据。

1
2
3
4
声明变量:<存储限定符><类型><变量名>
uniform vec4 u_FragColor
精度限定词:指定变量的范围和精度
precision mediump float(中等精度)

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ColoredPoints</title>
<style>
/* body {
margin:0;
padding:0;
} */
</style>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use the browser supporting "canvas".
</canvas>

<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="ClickedPoints.js"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//ColoredPoints.js
//顶点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;'+
'void main(){'+
'gl_Position=a_Position;'+
'gl_PointSize=10.0;'+
'}';

//片元着色器程序
var FSHADER_SOURCE=
'precision mediump float;'+
'uniform vec4 u_FragColor;'+
'void main(){'+
'gl_FragColor = u_FragColor;'+
'}';

function main() {
//获取canvas元素
var canvas = document.getElementById("webgl");
if(!canvas){
console.log("Failed to retrieve the <canvas> element");
return;
}

//获取WebGL绘图上下文
var gl = getWebGLContext(canvas);
if(!gl){
console.log("Failed to get the rendering context for WebGL");
return;
}

//初始化着色器
if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){
console.log("Failed to initialize shaders.");
return;
}

//获取a_Position变量存储位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0){
console.log("Failed to get the storage location of a_Position");
return;
}

//获取u_FragColor 变量存储位置
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
if(!u_FragColor){
console.log('Failed to get the storage location of u_FragColor');
return;
}

canvas.onmousedown = function(ev){
click(ev,gl,canvas,a_Position,u_FragColor);
};

//指定清空<canvas>颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);

//清空<canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
}

var g_points = [];
var g_colors = [];
function click(ev, gl, canvas, a_Position,u_FragColor){
var x= ev.clientX;
var y = ev.clientY;

var rect = ev.target.getBoundingClientRect();
x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
//将坐标存储到g_points数组中
g_points.push([x, y]);

// 教程示例
// if(x >= 0.0 && y >= 0.0){ //第一象限
// g_colors.push([1.0, 0.0, 0.0, 1.0]); //红色
// }else if(x < 0.0 && y < 0.0){ //第三象限
// g_colors.push([0.0, 1.0, 0.0, 1.0]); //绿色
// }else{
// g_colors.push([1.0, 1.0, 1.0, 1.0]); //白色
// }

// 修改示例
g_colors.push([Math.random(), Math.random(), Math.random(), Math.random()])

//清除<canvas>
gl.clear(gl.COLOR_BUFFER_BIT);

var len = g_points.length;
for(var i = 0; i < len; i++){
var xy=g_points[i];
var rgba = g_colors[i];

//将点的位置传递到a_Position变量中
gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
//将点的颜色传递到u_FragColor变量中
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);

//绘制点
gl.drawArrays(gl.POINTS, 0, 1);
}
}

效果

1


Tips: Please indicate the source and original author when reprinting or quoting this article.