文章目录
Canvas是什么
canvas是透明的,2d坐标系统的x轴(正方向朝右)
,y轴(正方向朝下)
。
<canvas id="canvas2d" class="m-auto" width="720" height="100"> </canvas>
<script>
var canvas = document.getElementById("canvas2d") as HTMLCanvasElement;
if (!canvas) {
console.log("Failed to retrieve the <canvas> element");
} else {
// 获取canvas上下文
var ctx = canvas.getContext("2d");
if (ctx) {
// 设置填充颜色
ctx.fillStyle = "oklch(0.769 0.188 70.08)";
// 绘制矩形
ctx.fillRect(0, 0, 720, 100);
}
}
</script>
绘制第一个3d图形
绘制三维图形与二维类似,也遵循类似的步骤,获取canvas元素、获取绘图上下文、开始绘图。
<canvas id="canvas3d" class="m-auto" width="720" height="400"> </canvas>
<script type="module">
function canvas3D() {
var canvas = document.getElementById("canvas3d");
if (!canvas) {
return console.log("Failed to retrieve the <canvas> element");
}
console.dir(canvas);
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log("Failed to get the redering context for WebGL");
return;
}
// 指定清空canvas的颜色
gl.clearColor(0.0, 0.5, 0.5, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
}
canvas3D();
</script>
gl.clearColor(red, green, blue, alpha)
,指定绘图区域的背景色。
定义 | 名称 | 解释 |
---|---|---|
参数 | red | 指定红色值(从0.0到1.0) |
green | 指定绿色值(从0.0到1.0) | |
blue | 指定蓝色值(从0.0到1.0) | |
alpha | 指定透明度(从0.0到1.0) |
如果任何一个分量小于0.0或者大于1.0,那么就会分别截断为0.0或1.0
一旦指定了背景色之后,背景色就会驻存在WebGL系统(WebGL System)中,在下一次调用gl.clearColor()方法前不会改变。如果将来什么时候你还想用同一个颜色再清空一次绘图区,没必要再指定一次背景色,你可以调用gl.clear()函数,用之前指定的背景色清空(即用背景色填充,擦除已经绘制的内容)绘图区域。
清空Canvas
- gl.COLOR_BUFFER_BIT -> 指定颜色缓冲区
- gl.DEPTH_BUFFER_BIT -> 指定深度缓冲区
- gl.STENCIL_BUFFER_BIT -> 指定模板缓冲区
gl.clearColor(gl.COLOR_BUFFER_BIT)
清空颜色缓冲区。因为webgl的gl.clearColor
继承opengl,基于多基本缓冲区模型,gl.COLOR_BUFFER_BIT
代表的是颜色缓冲区。webgl还有深度缓冲区和模板缓冲区。
绘制一个点
const VSHADER_SOURCE = `
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 设置坐标
gl_PointSize = 5.0; // 设置点的大小
}
`;
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置点的颜色
}
`;
let canvas = document.getElementById('drawPoint');
// @ts-ignore
var gl = window.getWebGLContext(canvas);
if (!gl) {
console.log('Failed to gwt the rendering context for WebGL.');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置清屏颜色为黑色
gl.clear(gl.COLOR_BUFFER_BIT); // 清除颜色缓冲区
gl.drawArrays(gl.POINTS, 0, 1); // 设置绘制模式为点,顶点索引从0开始,共绘制1个点
顶点着色器
顶点着色器
控制点的位置和大小,它用来描述顶点特性(如位置,颜色等)的程序。顶点是指二维和三维空间的一个点,比如二维或三维图形的端点或交点。
- 内置变量
- vec4 gl_Position -> 表示顶点位置(该属性必须被赋值)
- float gl_PointSize -> 表示顶点尺寸(默认1.0)
片元着色器
进行逐片元处理如光照的程序。片元
是一个图形学术语,可理解为像素(图像的单元,包括这个像素的位置,颜色和其他信息)。
- 内置变量
- vec4 gl_FragColor -> 指定片元的颜色(RGBA格式)
绘制
gl.drawArrays可用于绘制各种图形,简单介绍如下
gl.drawArrays(mode,frst,count)
执行顶点着色器,按照 mode 参数指定的方式绘制图形。
参数 | 说明 | 类型 |
---|---|---|
mode | 指定绘制的方式,可接收以下常量符号: gl.POINTS ,gl.LINES ,gl.LINE_STRIP ,gl.LINE_LOOP ,gl.TRIANGLES ,gl.TRIANGLE_STRIP ,gl.TRIANGLE_FAN | 枚举 |
first | 指定从哪个顶点开始绘制 | 整型数 |
count | 指定绘制需要用到多少个顶点 | 整型数 |
当程序调用g1.drawArrays()时,顶点着色器将被执行count次,每次处理一个顶点。
WebGL坐标系统
<canvas>
的中心点: (0.0,0.0,0.0)<canvas>
的上边缘和下边缘: (-1.0,0.0,0.0)和(1.0,0.0,0.0)<canvas>
的左边缘和右边缘: (0.0,-1.0,0.0)和(0.0,1.0,0.0)
attribute变量和uniform变量
attribute
变量传输的是那些与顶点相关的数据,而uniform
变量传输的是那些对于所有顶点都相同(或与顶点无关)的数据。
attribute
变量是一种 GLSL ES变量,被用来从外部向顶点着色器内传输数据,只有顶点着色器能使用它。为了使用 attribute 变量,需要包含以下步骤
- 在顶点着色器中,声明attribute变量;
- 将attribute 变量赋值给gl_Position变量;
- 向 attribute 变量传输数据。
使用js设置attribute变量画一个点
这里随意点击即可绘制一个点
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position; // 设置坐标
gl_PointSize = 10.0; // 设置点的大小
}
`;
// ... 中间代码与上一版相同
// gl.getAttribLocation函数用于获取a_Position变量指定的attribute变量的存储地址
// 简单理解:让gl帮忙创建一个变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// ...
// 将数据(0.0, 0.0, 0.0)传给由a_Position参数指定的attribute 变量。
// 简单理解:让gl帮忙给变量赋值
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); // 设置顶点位置
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置清屏颜色为黑色
gl.clear(gl.COLOR_BUFFER_BIT); // 清除颜色缓冲区
gl.drawArrays(gl.POINTS, 0, 1); // 设置绘制模式为点,顶点索引从0开始,共绘制1个点
attribute vec4 a_Position
,attribute
被称为存储限定符
attribute
变量都以a_前缀开始uniform
变量都以u_前缀开始 设置vec4
变量时,当省略分量时,默认为1.0
gl.vertexAttrib3f
和gl.vertexAttrib3fv
区别在于参数的接收,3f逐个接收三个参数,3fv接受一个长度为3的数组
随机设置点的颜色
用uniform
变量将色值传递给片元着色器。
- 在片元着色器中声明一个uniform变量
- 在JavaScript中获取该变量的存储地址
- 将颜色值传递给该变量
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position; // 设置坐标
gl_PointSize = 10.0; // 设置点的大小
}
`;
const FSHADER_SOURCE = `
precision mediump float; // 设置精度
uniform vec4 u_FragColor; // 声明uniform变量
void main() {
gl_FragColor = u_FragColor; // 设置片元颜色
}
`;
function main() {
const canvas = document.getElementById('webgl');
// 获取webgl上下文
const 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 intialize shaders.');
return;
}
// 获取a_Position变量的存储地址
const 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变量的存储地址
const u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
if (!u_FragColor) {
console.log('Failed to get the storage location of u_FragColor');
return;
}
// 根据点击位置设置a_Position和u_FragColor
canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position, u_FragColor) };
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
const g_points = []; // 顶点位置数组
const g_colors = []; // 顶点颜色数组
function click(ev, gl, canvas, a_Position, u_FragColor) {
const x = ev.clientX; // 设置x坐标
const y = ev.clientY; // 设置y坐标
const 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.push([x, y]);
// 根据所在象限设置顶点颜色
if (x >= 0.0 && y >= 0.0) {
g_colors.push([1.0, 0.0, 0.0, 1.0]); // Red
} else if (x < 0.0 && y < 0.0) {
g_colors.push([0.0, 1.0, 0.0, 1.0]); // Green
} else {
g_colors.push([1.0, 1.0, 1.0, 1.0]); // White
}
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 循环设置顶点
const len = g_points.length;
for(let i = 0; i < len; i++) {
const xy = g_points[i];
const 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);
}
}
gl.getUniformLocation(program, name)
执行顶点着色器,按照 mode 参数指定的方式绘制图形。
参数 | 说明 | 类型 |
---|---|---|
program | 指定包含了着色器程序的对象 | 对象 |
name | 指定要获得存储位置的变量名 | 字符串 |
绘制和变换三角形
绘制多个点
想要一次性绘制多个点,可使用webgl提供的缓冲区对象,它可以一次性的向着色器传入多个顶点的数据。
绘制过程
获取webgl绘图上下文 -> 初始化着色器 -> 设置点的坐标 -> 设置<canvas>
背景色 -> 清空canvas -> 绘制
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position; // 设置坐标
gl_PointSize = 10.0; // 设置点的大小
}
`;
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置点的颜色
}
`;
function main() {
const canvas = document.getElementById('webgl');
// 获取webgl上下文
const 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 intialize shaders.');
return;
}
// 设置顶点的位置
const n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// 设置清屏颜色
gl.clearColor(0, 0, 0, 1);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三个点
gl.drawArrays(gl.POINTS, 0, n);
}
function initVertexBuffers(gl) {
const vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
const n = 3; // 点的个数
// 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 绑定缓冲区到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// 将缓冲区对象分配给a_Position
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position);
return n;
}
gl.createBuffer()
创建缓冲区对象,gl.deleteBuffer()
删除缓冲区对象。
gl.bindBuffer(target, buffer)
将buffer表示的缓冲区对象绑定target表示的目标上。
参数 | 说明 | 类型 |
---|---|---|
target | 指定包含了着色器程序的对象 | gl.ARRAY_BUFFER() 表示缓冲区中包含的顶点数据 gl.ELEMENT_ARRAY_BUFFER 表示缓冲区中包含的顶点索引 |
buffer | 指定之前由gl.createBuffer()返回的待绑定的缓冲区对象 如果指定为null,则禁用对target的绑定 | 对象 |
gl.bufferData(target, data, usage)
开辟存储空间,向绑定在target上的缓冲区对象中写入数据data
参数 | 说明 | 类型 |
---|---|---|
target | 指定包含了着色器程序的对象 | gl.ARRAY_BUFFER() 表示缓冲区中包含的顶点数据 gl.ELEMENT_ARRAY_BUFFER 表示缓冲区中包含的顶点索引 |
data | 写入缓冲区对象的数据 | 对象 |
usage | 表示程序将如何使用存储在缓冲区对象中的数据。 | gl.STATIC DRAW 只会向缓冲区对象中写入一次数据,但需要绘制很多次 gl.STREAM DRAW 只会向缓冲区对象中写入一次数据,然后绘制若干次 gl.DYNAMIC DRAW 会向缓冲区对象中多次写入数据,并绘制很多次 |