前言
动态绘制多个点,相对于绘制单个点而言,只是多了个监听事件而已。下面,我们来看看,对于绘制多个点,我们是如何使用 webgl 来处理的。
方法展示
初始化 webgl,和初始化 shader 的方法和绘制单个点的是一样的。有需要的小伙伴可以去前面的绘制单个点
的文章中去寻找。这里,我们主要来说说,动态绘制点与绘制单个点的不同处理之处。
一 坐标位置不再是固定的,而是需要动态去获取
在绘制单个点
中,我们的坐标位置是固定的,即我们固定在某个位置绘制一个点。而现在我们需要动态绘制点,自然我们的位置也需要动态去获取了。接下来我们便看看,如何去动态获取位置。 先直接给出代码,然后再解释,为什么需要这样做:
scss
function initBuffer() {
// getAttribLocation() 方法返回给定WebGLProgram对象中某属性的下标指向位置
// 第一个参数为WebGLProgram,第二个参数为需要获取下标指向位置的GLSL变量名
let aPosition = webgl.getAttribLocation(webgl.program, "a_position");
//监听鼠标点击事件
document.addEventListener("mousedown", function (e) {
// debugger;
//屏幕坐标
let x = e.clientX;
let y = e.clientY;
//返回元素大小相对于视口的位置信息
let rect = e.target.getBoundingClientRect();
// 将屏幕坐标转换为webgl坐标
// 思路:屏幕坐标->canvas坐标系坐标->webgl坐标系坐标
// webgl坐标系:x:[-1,1],y:[-1,1],z:[-1,1]
let pointX = (x - rect.left - 250) / 250;
let pointy = (250 - (y - rect.top)) / 250;
console.log(pointX, pointy);
// 将该点放入数组中
points.push(pointX, pointy, 0.0, 1.0);
//创建32位浮点型数组
let pointPosition = new Float32Array(points);
//创建缓冲区
let pointBuffer = webgl.createBuffer();
//绑定缓冲区
webgl.bindBuffer(webgl.ARRAY_BUFFER, pointBuffer);
//绑定缓冲区对象
webgl.bufferData(
webgl.ARRAY_BUFFER,
pointPosition,
webgl.STATIC_DRAW
);
// 启用位置信息数组
webgl.enableVertexAttribArray(aPosition);
// 规定位置信息数组如何渲染
webgl.vertexAttribPointer(aPosition, 4, webgl.FLOAT, false, 0, 0);
// 清除颜色
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清除深度缓冲区和颜色缓冲区
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
// 绘制,规定绘制数组,以及绘制方式
webgl.drawArrays(webgl.POINTS, 0, points.length / 4);
});
// getUniformLocation()方法用于获取指定WebGLProgram对象中uniform变量的位置。
// 第一个参数为要获取uniform变量的WebGLProgram对象,第二个参数为要获取位置的uniform变量的名称。
let uniforproj = webgl.getUniformLocation(webgl.program, "proj");
// uniformMatrix4fv()用于设置一个4*4的矩阵类型的uniform变量值。接收4个参数
// 第一个参数为 要设置值的uniform变量的地址
// 第二个参数为 是否将矩阵转置,默认false
// 第三个参数为 要设置的值,应该是一个4*4的矩阵
// 第四个参数为 矩阵在数组中的偏移量,默认为0
webgl.uniformMatrix4fv(uniforproj, false, projMat4,0);
}
这段代码中最值得关注的是几句坐标变换
的代码:
ini
//屏幕坐标
let x = e.clientX;
let y = e.clientY;
//返回元素大小相对于视口的位置信息
let rect = e.target.getBoundingClientRect();
// 将屏幕坐标转换为webgl坐标
// 思路:屏幕坐标->canvas坐标系坐标->webgl坐标系坐标
// webgl坐标系:x:[-1,1],y:[-1,1],z:[-1,1]
let pointX = (x - rect.left - 250) / 250;
let pointy = (250 - (y - rect.top)) / 250;
为什么要这样变换呢?
该图中,我标出了几种坐标系的范围,我们可以看到:canvas 坐标=屏幕坐标-rect,webgl 坐标=[canvas 坐标-(width 或者 height)/2]/(width 或者 height 的一半)
二 点的绘制
绘制单个点时,是在 draw 函数中绘制的。动态绘制点时,我们实在每次点击之后,重新绘制的。每次都重新绘制了所有点。
ini
webgl.drawArrays(webgl.POINTS, 0, points.length / 4);
完整的代码
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>绘制点</title>
<script src="./glMatrix.js"></script>
<script>
var projMat4 = mat4.create(); // 初始化一个4*4的矩阵
var webgl; // 声明全局变量
/**
* GLSL代码
* 声明一个顶点着色器
* vec4:四维向量 ,具有xyzw四个分量,分量是浮点数
* mat4: 4*4矩阵
* **/
var vertexString = `
attribute vec4 a_position;
uniform mat4 proj;
void main(){
gl_Position =a_position;
gl_PointSize = 20.0;
}
`;
var fragmentString = `
void main(){
gl_FragColor = vec4(0,0,1.0,1.0);
}
`; // 片元着色器
// 入口函数
function init() {
initWebgl();
initShader();
initBuffer();
draw();
}
// webgl初始化函数
function initWebgl() {
// 获取canvas容器
let webglDiv = document.querySelector("#webglCanvas");
// 设置webgl上下文
webgl = webglDiv.getContext("webgl");
// 设置可视范围 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/viewport
webgl.viewport(0, 0, webglDiv.width, webglDiv.height);
// 定义阴影范围
mat4.ortho(
0,
webglDiv.clientWidth,
webglDiv.clientHeight,
0,
-1,
1,
projMat4
);
}
// shder初始化函数
function initShader() {
// createShader() 用于创建一个 WebGLShader 着色器对象,该对象可以使用 shaderSource()和 compileShader() 方法配置着色器代码。
// 参数为gl.VERTEX_SHADER 或 gl.FRAGMENT_SHADER两者中的一个。
let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
let fssagder = webgl.createShader(webgl.FRAGMENT_SHADER);
// 用于将我们创建的 WebGLShader着色器对象和GLSL程序中定义的着色器相关联。
// 第一个参数为webglShader对象,第二个参数为GLSL中定义的着色器
webgl.shaderSource(vsshader, vertexString);
webgl.shaderSource(fssagder, fragmentString);
// 编译WebGLShader着色器,使其成为为二进制数据,然后就可以被WebGLProgram对象所使用。
// 参数为一个片元着色器或顶点着色器
webgl.compileShader(vsshader);
webgl.compileShader(fssagder);
// 创建一个webglProgram对象,该对象由两个编译过后的 WebGLShader 组成 - 顶点着色器和片段着色器(均由 GLSL 语言所写)
let program = webgl.createProgram();
// attachShader() 方法负责往 WebGLProgram 添加一个片段或者顶点着色器。
// 第一个参数为webglProgram对象,第二个参数为片段或者顶点的 WebGLShader
webgl.attachShader(program, vsshader);
webgl.attachShader(program, fssagder);
// linkProgram()方法链接给定的WebGLProgram,从而完成为程序的片元和顶点着色器准备 GPU 代码的过程。参数为一个用于链接的WebGLProgram对象
webgl.linkProgram(program);
// useProgram() 方法将定义好的WebGLProgram 对象添加到当前的渲染状态中。
webgl.useProgram(program);
webgl.program = program;
}
var points = [];
// 数据缓冲区初始化函数
function initBuffer() {
//Float32Array 类型数组代表的是平JS内置的标准对象,为 32 位的浮点数型数组,其内容初始化为 0。一旦建立起来,你可以使用这个对象的方法对其元素进行操作,或者使用标准数组索引语法 (使用方括号)。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
// let pointPosition = new Float32Array([100.0, 100.0, 0.0, 1.0]);
// getAttribLocation() 方法返回给定WebGLProgram对象中某属性的下标指向位置
// 第一个参数为WebGLProgram,第二个参数为需要获取下标指向位置的GLSL变量名
let aPosition = webgl.getAttribLocation(webgl.program, "a_position");
// vertexAttrib4fv()方法可以为顶点 attibute 变量赋值。
// 第一个参数为指定了待修改顶点 attribute 变量的存储位置。第二个参数为用于设置顶点 attibute 变量的向量值。
// webgl.vertexAttrib4fv(aPosition, pointPosition);
document.addEventListener("mousedown", function (e) {
// debugger;
//屏幕坐标
let x = e.clientX;
let y = e.clientY;
let rect = e.target.getBoundingClientRect();
let pointX = (x - rect.left - 250) / 250;
let pointy = (250 - (y - rect.top)) / 250;
console.log(pointX, pointy);
points.push(pointX, pointy, 0.0, 1.0);
let pointPosition = new Float32Array(points);
let pointBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, pointBuffer);
webgl.bufferData(
webgl.ARRAY_BUFFER,
pointPosition,
webgl.STATIC_DRAW
);
webgl.enableVertexAttribArray(aPosition);
webgl.vertexAttribPointer(aPosition, 4, webgl.FLOAT, false, 0, 0);
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.POINTS, 0, points.length / 4);
});
// getUniformLocation()方法用于获取指定WebGLProgram对象中uniform变量的位置。
// 第一个参数为要获取uniform变量的WebGLProgram对象,第二个参数为要获取位置的uniform变量的名称。
let uniforproj = webgl.getUniformLocation(webgl.program, "proj");
// uniformMatrix4fv()用于设置一个4*4的矩阵类型的uniform变量值。接收4个参数
// 第一个参数为 要设置值的uniform变量的地址
// 第二个参数为 是否将矩阵转置,默认false
// 第三个参数为 要设置的值,应该是一个4*4的矩阵
// 第四个参数为 矩阵在数组中的偏移量,默认为0
webgl.uniformMatrix4fv(uniforproj, false, projMat4);
}
// webgl的绘制函数
function draw() {
// clearColor ()方法指定在清除颜色缓冲区时使用的颜色值。接收的4个参数分别表示 r,g,b,a。取值均在0和1之间。
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
// clear() 方法使用预设值来清空缓冲。预设值可以使用 clearColor() 、 clearDepth() 或 clearStencil() 设置。裁剪、抖动处理和缓冲写入遮罩会影响 clear() 方法。参数为一个用于指定需要清除的缓冲区的 GLbitfield (en-US) 。可能的值有:gl.COLOR_BUFFER_BIT(颜色缓冲区);gl.DEPTH_BUFFER_BIT (深度缓冲区) gl.STENCIL_BUFFER_BIT(模板缓冲区) 没有返回值
webgl.clear(webgl.COLOR_BUFFER_BIT);
// drawArrays() 方法用于从向量数组中绘制图元。接收3个参数
// 第一个参数为指定绘制图元的方式,可能值如下:
// gl.POINTS: 绘制一系列点。
// gl.LINE_STRIP: 绘制一个线条。即,绘制一系列线段,上一点连接下一点。
// gl.LINE_LOOP: 绘制一个线圈。即,绘制一系列线段,上一点连接下一点,并且最后一点与第一个点相连。
// gl.LINES: 绘制一系列单独线段。每两个点作为端点,线段之间不连接。
// gl.TRIANGLE_STRIP:绘制一个三角带。
// gl.TRIANGLE_FAN:绘制一个三角扇。
// gl.TRIANGLES: 绘制一系列三角形。每三个点作为顶点。
// 第二个参数为指定从哪个点开始绘制。
// 第三个参数为指定绘制需要使用到多少个点。
// webgl.drawArrays(webgl.POINTS, 0, 1);
}
</script>
</head>
<body onload="init()">
<canvas id="webglCanvas" width="500" height="500"></canvas>
</body>
</html>
总结
以上便是动态绘制多个点的所有内容,如有错误之处,还请大家留言指出,谢谢大家了。