let webglDiv = document.getElementById('practice');
let webgl = webglDiv.getContext('webgl');
/*着色器*/
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
// attribute 只能用于传递定点数据
attribute vec4 aPosition ;
void main(){
// 要绘制的点的坐标
gl_Position = aPosition;
// 点的大小
gl_PointSize = 20.0;
}
`;
// 片元着色器
const FRAGMENT_SHADER_SOURCE = `
void main(){
//这里的vec4代表是 vec4(1.0,0.0,0.0,1.0) r,g,b,a
gl_FragColor = vec4(1.0 , 0.0 , 0.0 , 1.0) ;
}
`;
const program = initShader(webgl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
// 获取变量
const aPosition = webgl.getAttribLocation(program,'aPosition');
const points = [];
webglDiv.onclick = function (event){
// x轴的一半
let divisorX = webglDiv.offsetWidth/2;
// y轴的一半
let divisorY = webglDiv.offsetWidth/2;
let x = (event.offsetX - divisorX)/divisorX;
let y = (divisorX - event.offsetY)/divisorY;
//绘制多个点
// points.push({
// x,y
// })
// for (let i = 0; i < points.length; i++) {
// const {x, y} = points[i];
// webgl.vertexAttrib2f(aPosition, x, y)
// webgl.drawArrays(webgl.POINTS, 0, 1)
// }
// 变量赋值
webgl.vertexAttrib4f(aPosition,x, y,0.0,1.0);
// 执行绘制
// drawArrays(绘制的图形,从哪个点开始,使用几个顶点)
webgl.drawArrays(webgl.POINTS, 0, 1);
}
1、坐标转换
因为webgl的坐标系x轴和y轴只有1,0,-1,原点为0,通过点击函数获取位置信息得到的是px并且是canvas坐标系,所以要将获取的点击位置信息转换成webgl坐标
- webgl x轴为 从左到右为 -1,0,1,y轴从上到下为 1,0 ,-1
- 假设canvas高和宽为500px, 对应的canvas x坐标为 0,250,500,想要转换成webgl坐标需要将canvas x轴减去canvas宽的一半,变成 -250,0,250,再除于250,变回得到-1,0,1
- 对应的canvas y坐标为 0,250,500,想要转换成webgl坐标需要将canvas宽的一半减去canvas y轴,变成 250,0,-250,再除于250,便会得到1,0,-1
2、webgl同步绘图
代码中使用points数组存储之前获取的点,并在下次绘图时反复使用webgl.vertexAttrib2f(aPosition, x, y)、webgl.drawArrays(),为什么不是覆盖而是添加,是因为同步绘图的现象。
原因是 webgl 底层内置颜色缓冲区。它在电脑中会占用一块内存,在我们使用 webgl 绘图的时候,是在颜色缓冲区中画出来,但是图像暂时还未渲染出来。只有 webgl 自己知道,如果我们想要将图像绘制的时候,便执行 webgl.drawArrays(),那就照着缓冲区的图像去画。
- 颜色缓冲区中存储的图像,只在当前线程有效,我们先在js 主线程中绘图,主线程结束后,会再去执行信息队列里的异步线程
- 在执行异步线程时,颜色缓冲区就会被webgl系统重置,之前的图像也就没了
或者可以理解为每次点击事件中的循环里调用了drawArrays。这里并没有涉及到新的帧绘制,因为所有的drawArrays调用都在同一个事件处理函数中,也就是在同一帧中。所以,你在循环中的每次drawArrays调用都在绘制一个新的点,而不会清除之前的点。
当你在循环内部调用drawArrays时,每次迭代都会执行一次绘制调用,因此每次都会在画布上绘制一个点。这是因为你在循环中不断地将新的顶点位置通过vertexAttrib2f设置给aPosition,然后调用drawArrays来绘制这个点,所以每次迭代都会在画布上添加一个新点。
但是,当你在不同的点击事件中调用drawArrays时,每次点击都会触发一个新的帧绘制。在这种情况下,如果你没有采取措施来保存之前的点,那么每次新的帧绘制都会清除画布,只显示最新绘制的点。
所以,你在数组中的drawArrays调用没有清除之前的点,是因为所有的绘制操作都在同一帧中。而在不同的点击事件中调用drawArrays,每次点击都会触发一个新的帧绘制,所以需要采取措施来保存之前的点。
WebGL默认情况下在每次绘制新帧时不会保留上一帧的内容。这意味着除非你采取措施来保存这些点,否则每次调用drawArrays时,画布都会被清空,只显示最新绘制的点。为了在每次点击时都绘制一个新点而不清除之前的点,你需要将所有点的数据保存起来,并且在每次点击时重新绘制整个点集。这通常通过以下步骤实现:
- 维护一个数组来存储所有点的位置。
- 每次点击时,将新点的位置添加到数组中。
- 在每次点击后,清除画布并重新绘制所有点。
流程图