案例中使用到了一个库glMatrix。这个库的下载链接在下面。在项目下放一个glMatrix.js,然后在html标签中引入即可。(webgl glMatrix-0.9.6.js 未混淆版_glmatrix-0.9.6.min.js-CSDN博客)
项目的运行效果如下
下面是完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>webgl时钟</title>
<script src="./glMatrix.js "></script>
</head>
<body>
<canvas id="canvas" width="900" height="900"></canvas>
</body>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
// var mat4=mat4.create();
var textureLoc = 0;
var attribOutUV = 0;
var uniformTexture0 = 0;
var uniformTexture1 = 0;
var texture0 = null;
var texture1 = null;
var count = 0;
var clock = {
HOUR_HAND: "HOUR_HAND",
MINUTE_HAND: "MINUTE_HAND",
SECOND_HAND: "SECOND_HAND",
};
var vsString = `
attribute vec4 a_position;
attribute vec2 outUV;
varying vec2 inUV;
uniform mat4 u_formMatrix;
void main(void){
gl_Position= u_formMatrix * a_position;
gl_PointSize=10.0;
inUV = outUV;
}
`;
var fsString = `
precision mediump float;
uniform float flag;
uniform sampler2D texture0;
varying vec4 v_Color;
varying vec2 inUV;
void main(){
if(flag==1.){
gl_FragColor=vec4(1.,0.,0.,1.);
}else if(flag==2.){
gl_FragColor=vec4(0.,0.,0.,1);
}else if(flag==3.){
gl_FragColor=vec4(.4824,.4863,.2078,1.);
}
else{
vec4 color0=texture2D(texture0,inUV);
gl_FragColor=color0;
}
}
`;
// 入口函数
async function main() {
initWebgl();
initShader();
await drawColckBackground();
clear();
draw();
// 此处的绘制必须异步的,等待纹理加载完毕后,才进行绘制。否则会出现部分纹理不显示的情况
// draw();
// window.requestAnimationFrame(animation);
setInterval(() => {
//清除上一帧的画面
clear();
// 绘制表盘的中心点
drawCenter();
// 绘制秒针
drawHand(clock.SECOND_HAND);
// 绘制分针
drawHand(clock.MINUTE_HAND);
// 绘制时针
drawHand(clock.HOUR_HAND);
// 绘制表盘纹理
drawColckBackground();
count++;
}, 1000);
}
main();
// webgl初始化函数
function initWebgl() {
// 确定可视域范围
gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);
}
// shader初始化函数
function initShader() {
const vsShader = gl.createShader(gl.VERTEX_SHADER);
const fsShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vsShader, vsString);
gl.shaderSource(fsShader, fsString);
gl.compileShader(vsShader);
if (!gl.getShaderParameter(vsShader, gl.COMPILE_STATUS)) {
throw Error("顶点着色器错误:" + gl.getShaderInfoLog(vsShader));
}
gl.compileShader(fsShader);
if (!gl.getShaderParameter(fsShader, gl.COMPILE_STATUS)) {
throw Error("片元着色器错误:" + gl.getShaderInfoLog(fsShader));
}
const program = gl.createProgram();
gl.attachShader(program, vsShader);
gl.attachShader(program, fsShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw Error("program连接错误:" + gl.getShaderInfoLog(vsShader));
}
gl.useProgram(program);
gl.program = program;
}
// 数据缓存区的初始化函数
async function drawColckBackground() {
const pointPosition = new Float32Array([
-1, -1, 0, 1, 0, 0,
1, 1, 0, 1, 1, 1,
-1, 1, 0, 1, 0, 1,
1, -1, 0, 1, 1, 0,
]);
const index=new Uint8Array([
2,0,1,
1,0,3
])
// 顶点坐标缓存的创建与传入数据
const aPosition = gl.getAttribLocation(gl.program, "a_position");
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 4, gl.FLOAT, false, 6 * 4, 0);
// 纹理坐标缓存的创建与传入数据
attribOutUV = gl.getAttribLocation(gl.program, "outUV");
gl.enableVertexAttribArray(attribOutUV);
gl.vertexAttribPointer(attribOutUV, 2, gl.FLOAT, false, 6 * 4, 4 * 4);
// 索引缓存
const indexBuffer=gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,index,gl.STATIC_DRAW)
uniformTexture0 = gl.getUniformLocation(gl.program, "texture0");
const flag=gl.getUniformLocation(gl.program,'flag')
gl.uniform1f(flag,0.5)
let modelView1 = mat4.create();
// 设置为单位矩阵
mat4.identity(modelView1);
let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
if(!texture0){
texture0 = await initTexture("http://localhost:3000/clock-bg.png");//时钟表盘背景图
}
draw()
}
// webgl绘制函数
function draw() {
// 初始化画布
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture0);
gl.uniform1i(uniformTexture0, 0);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE,0);
}
async function initTexture(src) {
return new Promise((reslove, reject) => {
const image = new Image();
//解决读取远程图片链接的跨域问题
image.crossOrigin = "Anonymous";
image.src = src;
image.onload = function () {
console.log('图片加载完毕')
reslove(handleLoadTexture(image));
};
image.onerror = reject;
});
}
function handleLoadTexture(img) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
//将图片坐标系转化为纹理坐标系。图标坐标系与纹理坐标系x轴方向相同,y轴相反。这个方法就是把y坐标取负值
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,666);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
return texture;
}
function drawHand(type) {
let arr = [];
let angle = 0;
let handType=2.0
if (type == clock.HOUR_HAND) {
arr = [
-0.1, -0.025, 0, 1,
-0.1, 0.025, 0, 1,
0.3, 0, 0, 1,
];
angle = ((-Math.PI / 1800) * count * 5) / 60;
} else if (type == clock.MINUTE_HAND) {
arr = [
-0.1, -0.025, 0, 1,
-0.1, 0.025, 0, 1,
0.5, 0, 0, 1,
];
angle = (-Math.PI / 1800) * count;
} else {
arr = [
-0.1, -0.025, 0, 1,
-0.1, 0.025, 0, 1,
0.7, 0, 0, 1,
];
angle = (-Math.PI / 30) * count;
handType=1.0
}
let modelView1 = mat4.create();
mat4.identity(modelView1);
mat4.scale(modelView1, [1, 1, 1])
mat4.rotate(modelView1, angle, [0, 0, 1]);
let pointPosition = new Float32Array(arr);
let aPsotion = gl.getAttribLocation(gl.program, "a_position");
let triangleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
gl.enableVertexAttribArray(aPsotion);
gl.vertexAttribPointer(aPsotion, 4, gl.FLOAT, false, 4 * 4, 0);
let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
const flag=gl.getUniformLocation(gl.program,'flag')
gl.uniform1f(flag,handType)
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
//清屏
function clear(){
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
}
function drawCenter(){
let pointPosition = new Float32Array([0,0,0,1]);
let aPsotion = gl.getAttribLocation(gl.program, "a_position");
let triangleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
gl.enableVertexAttribArray(aPsotion);
gl.vertexAttribPointer(aPsotion, 4, gl.FLOAT, false, 4 * 4, 0);
let modelView1 = mat4.create();
mat4.identity(modelView1);
let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
const flag=gl.getUniformLocation(gl.program,'flag')
gl.uniform1f(flag,3.0)
gl.drawArrays(gl.POINTS, 0, 1);
}
</script>
</html>