html部分还是比较简单,引入的matrix.js是矩阵变换的一些方法,网上有很多,大家可以搜一搜(我的线性代数知识已经还给我的高数老师了,最近考虑着手捡起来)
一模一样的效果我用Three.js也做了一个,链接是这个https://segmentfault.com/a/11...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<canvas id="myCanvas" width="400" height="400"></canvas>
<script type="text/javascript" src="./matrix.js"></script>
<script type="text/javascript" src="./demo.js"></script>
</body>
</html>
下面是demo.js的代码
第一步(准备画一个静态三角形):先把顶点着色器和片元着色器和context绑定
program代表一个程序(着色程序),这个程序可以绑定顶点着色器和片元着色器,从而将顶点着色器和片元着色器加载到当前canvas要执行的程序段里来
let canvas = document.getElementById('myCanvas');
let gl = canvas.getContext('webgl');
let program = gl.createProgram();
// 先定义两个源代码
let VSHADER_SOURCE, FSHADER_SOURCE;
// 先定义两个shader
let vertexShader, fragmentShader;
function createShader(gl, sourceCode, type) {
// 创建shader
let shader = gl.createShader(type);
// 给创建的shader挂载sourceCode
gl.sourceCode(shader, sourceCode);
// 编译shader
gl.compileShader(shader);
return shader;
}
// 定义vertexShader
vertexShader = createShader(gl, VSHADER_SOURCE, gl.VERTEX_SHADER);
// 定义fragmentShader
fragmentShader = createShader(gl, FSHADER_SOURCE, gl.FRAGMENT_SHADER);
// program和shader绑定
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 给context绑定program(这里是一个,也可以定义多个shader挂载到多个program上)
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;
第二步(画出一个静态三角形):实现a_Position和对应的buffer绑定
const gl = document.getElementById('myCanvas').getContext('webgl');
const program = gl.createProgram();
// vec4 是一个四维向量,要render的顶点坐标
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
}
`;
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
function createShader(gl, sourceCode, type) {
// 创建shader
const shader = gl.createShader(type);
// 给创建的shader挂载sourceCode
gl.shaderSource(shader, sourceCode);
// 编译shader
gl.compileShader(shader);
return shader;
}
// 定义vertexShader
const vertexShader = createShader(gl, VSHADER_SOURCE, gl.VERTEX_SHADER);
// 定义fragmentShader
const fragmentShader = createShader(gl, FSHADER_SOURCE, gl.FRAGMENT_SHADER);
// program和shader绑定
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 给context绑定program(这里是一个,也可以定义多个shader挂载到多个program上)
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;
// 实现a_Position和对应的buffer绑定,从而将三角形三个顶点坐标传递到 a_Position attribute vec4中
function initVertexBuffers(gl) {
// 3个顶点坐标,坐标没有z值,默认是0
const vertices = new Float32Array([
0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
const n = 3;
// 创建buffer,利用buffer往vertexShader传递数据
const vertexBuffer = gl.createBuffer();
// 将vertexBuffer绑定到webgl上
// gl.ARRAY_BUFFER叫做顶点缓冲区,还有一种顶点索引缓冲区(存的是顶点的索引)
// 顶点缓冲区里有一些数据是重复的,比如同一个点在两个三角形或多个图形中都是顶点,多次重复出现,这种情况可以用索引来减少buffer的使用空间,对于相同的顶点坐标设为相同的顶点即可
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将data灌入buffer
// gl.STATIC_DRAW第一次对缓冲区进行render后再也不会对缓冲区的顶点数据进行修改
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取attribute a_position变量地址
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将当前buffer绑定到vertex attribute上并且带有一些特定的规则
// 传入的2代表每两个数字为一个顶点坐标
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 启用a_Position变量
gl.enableVertexAttribArray(a_Position);
return n;
}
// 顶点位置传到vertexShader
const n = initVertexBuffers(gl);
gl.clearColor(0, 0, 0, 1);
function draw() {
// 绘制,先清空画布,增加背景色
gl.clear(gl.COLOR_BUFFER_BIT);
// 以三角形的方式绘制,从buffer最开始的位置拿数据,拿n个(在上面 initVertexBuffers定义的)
gl.drawArrays(gl.TRIANGLES, 0, n);
}
draw();
第三步(画出动态三角形):让三角形动起来
/** @type {HTMLCanvasElement} */
const gl = document.getElementById('myCanvas').getContext('webgl');
const program = gl.createProgram();
// vec4 是一个四维向量,要render的顶点坐标
const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
void main() {
gl_Position = u_ModelMatrix * a_Position;
}
`;
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
function createShader(gl, sourceCode, type) {
// 创建shader
const shader = gl.createShader(type);
// 给创建的shader挂载sourceCode
gl.shaderSource(shader, sourceCode);
// 编译shader
gl.compileShader(shader);
return shader;
}
// 定义vertexShader
const vertexShader = createShader(gl, VSHADER_SOURCE, gl.VERTEX_SHADER);
// 定义fragmentShader
const fragmentShader = createShader(gl, FSHADER_SOURCE, gl.FRAGMENT_SHADER);
// program和shader绑定
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 给context绑定program(这里是一个,也可以定义多个shader挂载到多个program上)
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;
let currentAngle = 0;
// 第一次初始值
let g_last = Date.now();
function tick() {
// 更新 新的旋转角度
animate();
// 绘制
draw();
// 常用绘制方法
requestAnimationFrame(tick);
}
// 实现a_Position和对应的buffer绑定,从而将三角形三个顶点坐标传递到 a_Position attribute vec4中
function initVertexBuffers(gl) {
// 3个顶点坐标,坐标没有z值,默认是0
const vertices = new Float32Array([
0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
const n = 3;
// 创建buffer,利用buffer往vertexShader传递数据
const vertexBuffer = gl.createBuffer();
// 将vertexBuffer绑定到webgl上
// gl.ARRAY_BUFFER叫做顶点缓冲区,还有一种顶点索引缓冲区(存的是顶点的索引)
// 顶点缓冲区里有一些数据是重复的,比如同一个点在两个三角形或多个图形中都是顶点,多次重复出现,这种情况可以用索引来减少buffer的使用空间,对于相同的顶点坐标设为相同的顶点即可
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将data灌入buffer
// gl.STATIC_DRAW第一次对缓冲区进行render后再也不会对缓冲区的顶点数据进行修改
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取attribute a_position变量地址
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将当前buffer绑定到vertex attribute上并且带有一些特定的规则
// 传入的2代表每两个数字为一个顶点坐标
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 启用a_Position变量
gl.enableVertexAttribArray(a_Position);
return n;
}
// 顶点位置传到vertexShader
const n = initVertexBuffers(gl);
gl.clearColor(0, 0, 0, 1);
// 拿到gl.program里面的u_ModelMatrix(在VSHADER_SOURCE中定义的那个)
const u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
// Matrix4比较好的封装了一些矩阵变换的操作
const modelMatrix = new Matrix4();
function animate() {
// 记下时间,拿到现在的时间戳,因为每次requestAnimationFrame间隔时间是不知道的
const now = Date.now();
// 减去老的时间戳
const duration = now - g_last;
g_last = now;
// 想要requestAnimationFrame间隔时间内转180度
currentAngle = currentAngle + duration / 1000 * 180;
}
// webgl视角是沿着z轴负方向,望向圆点的,想让三角形在3D的世界里转起来,是需要z轴的
function draw() {
// 绕y轴旋转currentAngle度
modelMatrix.setRotate(currentAngle, 0, 1, 0);
// 往uniform里传matrix的api
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// 绘制,先清空画布,增加背景色
gl.clear(gl.COLOR_BUFFER_BIT);
// 以三角形的方式绘制,从buffer最开始的位置拿数据,拿n个(在上面initVertexBuffers定义的)
gl.drawArrays(gl.TRIANGLES, 0, n);
}
tick();
我在vscode上没有发现webgl的语法提示,就用了 @type {HTMLCanvasElement} 这句来做语法提示,大家有啥好用的插件推荐吗?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。