bx2eq6ulg18.feishu.cn/docx/I4hTdO...(青牛老师的入门教程文档)
基础封装:
scss
const initShader = (gl, vShader, fShader) => {
/**
* gl:webgl上下文对象
* vShader:顶点着色器源码
* fShader:片元着色器源码
*/
// 创建着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 为着色器对象赋值
gl.shaderSource(vertexShader, vShader);
gl.shaderSource(fragmentShader, fShader);
// 编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 创建程序对象
const program = gl.createProgram();
// 挂载着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 链接程序
gl.linkProgram(program);
// 使用程序
gl.useProgram(program);
// 返回程序对象
return program;
}
export default initShader;
图元--点:
ini
<template>
<div>
<canvas class="canvas" width="500" height="500"></canvas>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import initShader from "@/hooks/initShader";
const initCnavas = () => {
const canvas = document.querySelector(".canvas");
//获取webgl三维上下文对象
const gl = canvas.getContext("webgl");
//定义顶点着色器
const vertexShader = `
attribute vec4 a_Position;
void main() {
gl_Position =a_Position;
gl_PointSize = 10.0;
}`;
//定义片元着色器
const fragmentShader = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;
//创建程序对象
const program = initShader(gl, vertexShader, fragmentShader);
//创建缓冲区对象
const buffer = gl.createBuffer();
//绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//定义顶点数据
const points = new Float32Array([0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5]);
//向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
//查询a_Position变量的索引位置
const a_Position = gl.getAttribLocation(program, "a_Position");
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//启用a_Position变量
gl.enableVertexAttribArray(a_Position);
//绘制图元--点
gl.drawArrays(gl.POINTS, 0, 4);
};
onMounted(() => {
initCnavas();
});
</script>
<style scoped>
.canvas {
border: 1px solid #000;
}
</style>

图元--线:
xml
<template>
<div>
<canvas class="canvas" width="500" height="500"></canvas>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import initShader from "@/hooks/initShader";
const initCnavas = () => {
const canvas = document.querySelector(".canvas");
//获取webgl三维上下文对象
const gl = canvas.getContext("webgl");
//定义顶点着色器
const vertexShader = `
attribute vec4 a_Position;
void main() {
gl_Position =a_Position;
gl_PointSize = 10.0;
}`;
//定义片元着色器
const fragmentShader = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;
//创建程序对象
const program = initShader(gl, vertexShader, fragmentShader);
//创建缓冲区对象
const buffer = gl.createBuffer();
//绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//定义顶点数据
const points = new Float32Array([0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5]);
//向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
//查询a_Position变量的索引位置
const a_Position = gl.getAttribLocation(program, "a_Position");
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//启用a_Position变量
gl.enableVertexAttribArray(a_Position);
//绘制图元--不连接的线段
gl.drawArrays(gl.LINES, 0, 4);
//绘制图元--连接的线段
// gl.drawArrays(gl.LINE_STRIP, 0, 4);
//绘制图元--闭环线段
// gl.drawArrays(gl.LINE_LOOP, 0, 4);
};
onMounted(() => {
initCnavas();
});
</script>
<style scoped>
.canvas {
border: 1px solid #000;
}
</style>



图元--三角
ini
<template>
<div>
<canvas class="canvas" width="500" height="500"></canvas>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import initShader from "@/hooks/initShader";
const initCnavas = () => {
const canvas = document.querySelector(".canvas");
//获取webgl三维上下文对象
const gl = canvas.getContext("webgl");
//定义顶点着色器
const vertexShader = `
attribute vec4 a_Position;
void main() {
gl_Position =a_Position;
gl_PointSize = 10.0;
}`;
//定义片元着色器
const fragmentShader = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;
//创建程序对象
const program = initShader(gl, vertexShader, fragmentShader);
//创建缓冲区对象
const buffer = gl.createBuffer();
//绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//定义顶点数据
const points = new Float32Array([
0.5, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
]);
//向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
//查询a_Position变量的索引位置
const a_Position = gl.getAttribLocation(program, "a_Position");
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//启用a_Position变量
gl.enableVertexAttribArray(a_Position);
//绘制图元--三角
gl.drawArrays(gl.TRIANGLES, 0, 3);
};
onMounted(() => {
initCnavas();
});
</script>
<style scoped>
.canvas {
border: 1px solid #000;
}
</style>

衍生:
镂空立方体:
由图元线--段衍生,两个闭合线段搭配四个不连接线段拼接形成
ini
<template>
<div><canvas width="500" height="500" class="canvas"></canvas></div>
</template>
<script setup>
import { onMounted } from "vue";
import initShader from "@/hooks/initShader";
const initCanvas = () => {
const canvas = document.querySelector(".canvas");
const gl = canvas.getContext("webgl");
/**
* radin:旋转角度
* cos:旋转角度的余弦值
* sin:旋转角度的正弦值
* mx:绕X轴旋转矩阵
* my:绕Y轴旋转矩阵
*/
const vertexShader = `
attribute vec4 a_Position;
void main(){
float radin = radians(10.0);
float cos = cos(radin);
float sin = sin(radin);
mat4 mx = mat4(
1.0,0.0,0.0,0.0,
0.0,cos,sin,0.0,
0.0,-sin,cos,0.0,
0.0,0.0,0.0,1.0);
mat4 my = mat4(
cos,0.0,-sin,0.0,
0.0,1.0,0.0,0.0,
sin,0.0,cos,0.0,
0.0,0.0,0.0,1.0);
gl_Position = my*mx*a_Position;
gl_PointSize = 10.0;
}
`;
const fragmentShader = `
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;
const program = initShader(gl, vertexShader, fragmentShader);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
const points = new Float32Array([
//第一面的四个点
0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5,
//第二面的四个点
0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5,
//连接两个面的八个点
0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5,
0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
]);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(program, "a_Position");
gl.vertexAttribPointer(
a_Position,
3,
gl.FLOAT,
false,
3 * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.enableVertexAttribArray(a_Position);
//绘制第一个面的四个点和连线
gl.drawArrays(gl.POINTS, 0, 4);
gl.drawArrays(gl.LINE_LOOP, 0, 4);
//绘制第二个面的四个点和连线
gl.drawArrays(gl.POINTS, 4, 4);
gl.drawArrays(gl.LINE_LOOP, 4, 4);
//绘制两个面的连接线
gl.drawArrays(gl.LINES, 8, 8);
};
onMounted(() => {
initCanvas();
});
</script>

带颜色立方体:
由图元线--三角衍生,12个三角拼接而成
ini
<template>
<div>
<canvas class="canvas" width="500" height="500"></canvas>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import initShader from "@/hooks/initShader";
//搭建一个每个面不同颜色的立方体
const initCanvas = () => {
const canvas = document.querySelector(".canvas");
const gl = canvas.getContext("webgl");
const vertexShader = `
attribute vec4 a_Position;
uniform mat4 u_x_ModelMatrix;
uniform mat4 u_y_ModelMatrix;
void main(){
gl_Position = u_y_ModelMatrix*u_x_ModelMatrix*a_Position;
gl_PointSize = 10.0;
}`;
const fragmentShader = `
precision mediump float;
uniform vec4 u_FragColor;
void main(){
gl_FragColor = u_FragColor;
}`;
const program = initShader(gl, vertexShader, fragmentShader);
const u_FragColor = gl.getUniformLocation(program, "u_FragColor");
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//每个面的四个点
const points = new Float32Array([
0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
-0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5,
]);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(program, "a_Position");
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
//每个面的两个三角形(索引缓冲区)
const indexData = new Uint16Array([
0, 1, 2, 0, 3, 2, 4, 5, 6, 4, 7, 6, 0, 1, 5, 0, 4, 5, 3, 2, 6, 3, 7, 6, 0,
3, 7, 0, 4, 7, 1, 2, 6, 1, 5, 6,
]);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
gl.enable(gl.DEPTH_TEST); //开启深度测试
/**
* deg:旋转角度
* radin:旋转角度的弧度值
* cos:旋转角度的余弦值
* sin:旋转角度的正弦值
* x_ModelMatrixValue:绕X轴旋转矩阵
* y_ModelMatrixValue:绕Y轴旋转矩阵
* requestAnimationFrame:请求动画帧
* 为了能更好的看到每个面的颜色,使用requestAnimationFrame来实现旋转动画效果
*/
let deg = 30.0;
function render() {
deg += 0.1;
const radin = (Math.PI / 180) * deg;
const cos = Math.cos(radin);
const sin = Math.sin(radin);
const u_x_ModelMatrix = gl.getUniformLocation(program, "u_x_ModelMatrix");
const u_y_ModelMatrix = gl.getUniformLocation(program, "u_y_ModelMatrix");
const x_ModelMatrixValue = new Float32Array([
1.0,
0.0,
0.0,
0.0,
0.0,
cos,
-sin,
0.0,
0.0,
sin,
cos,
0.0,
0.0,
0.0,
0.0,
1.0,
]);
const y_ModelMatrixValue = new Float32Array([
cos,
0.0,
-sin,
0.0,
0.0,
1.0,
0.0,
0.0,
sin,
0.0,
cos,
0.0,
0.0,
0.0,
0.0,
1.0,
]);
gl.uniformMatrix4fv(u_x_ModelMatrix, false, x_ModelMatrixValue);
gl.uniformMatrix4fv(u_y_ModelMatrix, false, y_ModelMatrixValue);
//fragmentListColor:每个面的颜色
const fragmentListColor = [
[0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 1.0, 1.0],
[1.0, 0.0, 0.0, 1.0],
[1.0, 1.0, 0.0, 1.0],
[1.0, 0.5, 0.0, 1.0],
[1.0, 0.0, 0.5, 1.0],
];
for (let i = 0; i < 6; i++) {
gl.uniform4fv(u_FragColor, new Float32Array(fragmentListColor[i]));
gl.drawElements(
gl.TRIANGLES,
6,
gl.UNSIGNED_SHORT,
i * 6 * Uint16Array.BYTES_PER_ELEMENT
);
}
window.requestAnimationFrame(render);
}
window.requestAnimationFrame(render);
};
onMounted(() => {
initCanvas();
});
</script>
<style lang="scss" scoped>
</style>
