基本概念
绘制多个点------缓冲区
1、建立顶点数据,两个浮点数构成一个顶点,分别代表 x、y值
javascript
const vertices = new Float32Array([
// x y
0.0, 0.1,
-0.1, -0.1,
0.1, -0.1
])
现在上面的这些顶点数据是存储在 js 缓存里的,着色器拿不到,所以需要建立一个着色器和 js 都能进入的公共区
2、建立缓冲对象
javascript
const vertexBuffer = gl.createBuffer();
现在上面的这个缓冲区是独立存在的,它知识一个空着的仓库,和谁都没有关系,接下来需要让其和着色器建立连接
3、绑定缓冲对象
javascript
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bindBuffer(target, buffer) 绑定缓冲区
target 要把缓冲区放在 webgl 系统中的什么位置
buffer 缓冲区
着色器对象在执行 初始化着色器的时候,已经被写入 webgl 上下文对象 gl 中了。
当缓冲区和着色器建立了绑定关系,我们就可以往这块空间写入数据了
4、往缓冲区对象中写入数据
javascript
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
bufferData(target, data, usage) 将数据写入缓冲区
target 要把缓冲区放在 webgl系统中的什么位置
data 数据
usage 向缓冲区写入数据的方式,gl.STATIC_DRAW 是向缓冲区一次性写入数据,着色器绘制多次
现在着色器虽然绑定了缓冲区,可以访问里面的数据了,但是我们还得让着色器知道这个仓库是给哪个变量的,比如咱们这里用于控制点位的 attribute 变量。这样做是为了提高绘图效率。
5、将缓冲区对象分配给 attribute 变量
javascript
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
gl.vertexAttrubPointer(local, size, type, normalized, stride, offset) 将缓冲区对象分配给 attribute变量
local attribute变量
size 顶点分量的个数,比如我们的 vertices 数组中,两个数据表示一个顶点,那咱们就写2
type 数据类型,比如 gl.FLOAT 浮点型
normalize 是否将顶点数据归一
stride 相邻两个顶点间的字节数,例子中写 0, 就是顶点之间是紧挨着的
offset 从缓冲区的什么位置开始存储变量,例子中写 0, 就是从头开始存储变量
到了这,着色器就能知道缓冲区的数据是给谁了,因为缓冲区里的顶点数据是数组,里面有多个顶点,所以得开启一个着色器批量处理顶点得属性,默认着色器指挥一个一个得接收顶点数据,然后一个一个绘制顶点
6、开启顶点数据得批处理功能
javascript
gl.enableVertexAttribArray(a_Position);
gl.enableVertexAttribArray(location);
location attribute变量
7、绘制图形
javascript
gl.drawArrays(gl.POINTS, 0, 3);
gl.drawArrays(mode, first, count) 方法可以绘制以下图形:
POINTS 可视的点
LINES 单独线段
LINE_STRIP 线条
LINE_LOOP 闭合线条
TRIANGLES 单独三角形
TRIANGLE_STRIP 三角带
TRIANGLE_FAN 三角扇
面的绘制
对于面的绘制,需要知道一个原理:
面有正反面;
面向我们的面,如果是正面,那它必然是逆时针绘制的;
面向我们的面,如果是反面,那它必然是顺时针绘制的。
三角带的绘制规律:
第一个三角形: V0>V1>V2
第偶数个三角形:以上一个三角形的第二条边+下一个点为基础,以和第二条边相反的方向绘制三角形
第奇数个三角形:以上一个三角形的第三条边+下一个点为基础,以和第二条边相反的方向绘制三角形
(V0, V1, V2),(V2, V3, V4),(V4, V3, V5)
三角扇的绘制规律:
第一个三角形: V0>V1>V2
第偶数个三角形:以上一个三角形的第三条边+下一个点为基础,以和第三条边相反的顺序,绘制三角形
第奇数个三角形:以上一个三角形的第三条边+下一个点为基础,以和第三条边相反的顺序,绘制三角形
(V0, V1, V2),(V0, V2, V3),(V0, V3, V4),(V0, V4, V5)
绘制矩形面
可以用 TRIANGLE_STRIP 三角带拼矩形
两个三角形分别为: V0>V1>Va2, V2>V1>V3
代码实现:
1、建立顶点数据
javascript
const vertices = new Float32Array([
-0.2, 0.2,
-0.2, -0.2,
0.2, -0.2,
0.2, -0.2
])
上面两个浮点代表一个顶点,依次是 V0, V1, V2, V3
2、绘图
javascript
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
上面参数的意思分别是:三角带,从第0个顶点开始画,画4个
异步绘制多点
在项目实战的时候,用户交互事件是必不可少的,因为事件是异步的,所以在绘图的时候,必须要考虑异步绘图。
异步绘制线段
1、先画一个点
2、一秒后,在左下角画一个点
3、两秒后,再画一条线段
代码实现:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制星星</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
// 一个属性值,将会从缓冲区中获取数据
attribute vec4 a_Position;
attribute float a_PointSize;
// 所有着色器都有一个 main 方法
void main() {
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_FragColor;
void main() {
gl_FragColor = u_FragColor;
}
</script>
<script type="module">
import { getPosByMouse, getInnerText, initShaderProgram } from "./utils/index.js"
const canvas = document.querySelector('#canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');
gl.clearColor(0, 0, 0, 1);
const vertexStr = getInnerText("vertexShader");
const fragmentStr = getInnerText("fragmentShader");
const program = initShaderProgram(gl, vertexStr, fragmentStr )
let a_Position = gl.getAttribLocation(program, 'a_Position');
let a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
gl.useProgram(program);
let vertices = [0.0, 0.1]
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position, 1, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.vertexAttrib1f(a_PointSize, 5);
gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
gl.drawArrays(gl.POINTS, 0, 1);
setTimeout(() =>{
vertices.push(...[0.2, 0.3])
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
gl.drawArrays(gl.POINTS, 0, 2);
}, 1000)
setTimeout(() => {
gl.drawArrays(gl.POINTS, 0, 2);
gl.drawArrays(gl.LINES, 0, 2);
}, 2000)
</script>
</body>
</html>
./utils/index.js
javascript
export function getPosByMouse(event, canvas) {
const { offsetX, offsetY } = event;
const { width, height } = canvas;
console.log(offsetX, offsetY, width, height, "当前数据");
return {
x: (offsetX * 2) / width - 1,
y: 1 - (offsetY * 2) / height,
};
}
export function getInnerText(id) {
return document.getElementById(id).innerText;
}
export function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(
`Unable to initialize the shader program: ${gl.getProgramInfoLog(
shaderProgram,
)}`,
);
return null;
}
return shaderProgram;
}
export function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(
`An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`,
);
gl.deleteShader(shader);
return null;
}
return shader;
}
封装多边形对象
javascript
const defAttr = () => ({
gl: null,
vertices: [],
geoData: [],
size: 2,
attrName: 'a_Position',
count: 0,
types: ['POINTS'],
program: null,
})
export default class Poly {
constructor(atrr) {
Object.assign(this, defAttr(), attr);
this.init();
}
init() {
const {attrName, size, gl, program} = this;
if(!gl) {
return;
}
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
this.updateBuffer(gl);
const a_Position = gl.getAttribLocation(program, attrName);
gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
}
addVertice(...params) {
this.vertices.push(...params);
this.updateBuffer();
}
popVertice() {
const {vertices, size} = this;
const len = vertices.length;
vertices.splice(len - size, len);
this.updateCount();
}
setVertice(ind, ...params) {
const {vertices, size} = this;
const i = ind * size;
params.forEach((param, paramId) => {
vertices[i + paramId] = param;
});
}
updateBuffer() {
const {gl, vertices} = this;
this.updateCount();
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}
updateCount() {
this.count = this.vertices.length / this.size;
}
updateVertices(params) {
const {geoData} = this;
const vertices = [];
geoData.forEach(data =>{
params.forEach(key => {
vertices.push(data[key]);
});
});
this.vertices = vertices;
}
draw(types = this.types){
const {gl, count} = this;
for(let type of types) {
gl.drawArrays(gl[type], 0, count);
}
}
}
属性:
gl:webgl上下文对象
vertices: 顶点数据集合,再被赋值的时候会做两件事
更新count 顶点数量,数据运算尽量不放渲染方法里
向缓冲区内写入顶点数据
geoData:模型数据,对象数组,可解析出 vertices 顶点数据
size:顶点分量的数目
positionName:代表顶点位置的 attribute 变量名
count: 顶点数量
types: 绘图方式,可以用多种方式绘图
方法:
init(): 初始化方法,建立缓冲对象,并将其绑定到webgl上下文对象上,然后向其中写入顶点数据。将缓冲对象交给 attribute变量,并开启 attribute变量的批处理功能
addVertice: 添加顶点
popVertice:删除最后一个顶点
setVertice: 根据索引位置设置顶点
updateBuffer:更新缓冲区数据,同时更新顶点数量
updateCount:更新顶点数量
updateVertices:基于 geoData解析出 vertices 数据
draw:绘图方法
封装多边形代码实例化
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制多边形</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
// 一个属性值,将会从缓冲区中获取数据
attribute vec4 a_Position;
attribute float a_PointSize;
// 所有着色器都有一个 main 方法
void main() {
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_FragColor;
void main() {
gl_FragColor = u_FragColor;
}
</script>
<script type="module">
import Poly from "./utils/Poly.js"
import { getPosByMouse, getInnerText, initShaderProgram } from "./utils/index.js"
const canvas = document.querySelector('#canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');
const vertexStr = getInnerText("vertexShader");
const fragmentStr = getInnerText("fragmentShader");
const program = initShaderProgram(gl, vertexStr, fragmentStr )
let a_Position = gl.getAttribLocation(program, 'a_Position');
let a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
gl.useProgram(program);
const drawPoly = new Poly({
gl,
program,
vertices: [0.0, 0.1],
})
gl.vertexAttrib1f(a_PointSize, 5);
canvas.addEventListener('click', function(event) {
const {x, y} = getPosByMouse(event, canvas);
drawPoly.addVertice(x, y);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
drawPoly.draw(['POINTS', 'LINES', 'LINE_STRIP'])
})
</script>
</body>
</html>
绘制多线
绘制多线,需要有个容器来承载它们,这样方便管理
建立容器对象
建立一个 Sky 对象,作为承载多边形的容器
javascript
export default class Sky{
constructor(gl) {
this.gl = gl;
this.children = [];
}
add(obj) {
obj.gl = this.gl;
this.children.push(obj);
}
updateVertices(params) {
this.children.forEach(el =>{
el.updateVertices(params)
})
}
draw() {
this.children.forEach(el => {
el.init();
el.draw();
})
}
}
属性:
gl:webgl 上下文对象
children:子级
方法:
add:添加子级
updateVertices:更新子级对象的顶点数据
draw:遍历子对象绘图,每个子对象对应一个buffer对象绘图之前要先初始化
需求
鼠标点击画布,绘制多边形路径
鼠标右击,取消绘制
鼠标再次点击,绘制新的多边形
javascript
// 夜空
const sky = new Sky(gl);
// 当前正在绘制的多边形
let poly = null;
// 取消右击提示
canvas.oncontextmenu = () => {
return false;
}
// 鼠标点击事件
canvas.addEventListener('mousedown', (event) => {
if(event.button === 2) {
popVertice()
} else {
const {x, y} = getPosByMouse(event, canvas);
if(poly) {
poly.addVertice(x, y);
} else {
createPoly(x, y);
}
}
render();
})
// 鼠标移动
canvas.addEventListener('mousemove', (event) => {
if(poly) {
const {x, y} = getPosByMouse(event, canvas);
poly.setVertice(poly.count - 1, x, y);
render();
}
})
// 新建多边形对象
function createPoly(x, y) {
poly = new Poly({
gl,
program,
vertices: [x, y, x, y],
types: ['POINTS', 'LINE_STRP']
})
sky.add(poly)
}
// 删除最后一个顶点
function popVertice() {
poly.popVertice();
poly = null;
}
// 渲染
function render() {
gl.clear(gl.COLOR_BUFFER_BIT);
sky.draw()
}
代码实现绘制多线
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制多线</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
// 一个属性值,将会从缓冲区中获取数据
attribute vec4 a_Position;
attribute float a_PointSize;
// 所有着色器都有一个 main 方法
void main() {
// gl_Position 是一个顶点着色器主要设置的变量
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_FragColor;
void main() {
gl_FragColor = u_FragColor;
}
</script>
<script type="module">
import Poly from "./utils/Poly.js"
import Sky from './utils/Sky.js'
import { getPosByMouse, getInnerText, initShaderProgram } from "./utils/index.js"
const canvas = document.querySelector('#canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');
const vertexStr = getInnerText("vertexShader");
const fragmentStr = getInnerText("fragmentShader");
const program = initShaderProgram(gl, vertexStr, fragmentStr )
let a_Position = gl.getAttribLocation(program, 'a_Position');
let a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
gl.useProgram(program);
gl.vertexAttrib1f(a_PointSize, 5);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
let sky = new Sky(gl), poly = null;
canvas.oncontextmenu = () => {
return false;
}
// 鼠标点击事件
canvas.addEventListener('mousedown', (event) => {
if(event.button === 2) {
popVertice()
} else {
const {x, y} = getPosByMouse(event, canvas);
if(poly) {
poly.addVertice(x, y);
} else {
createPoly(x, y);
}
}
render();
})
// 鼠标移动
canvas.addEventListener('mousemove', (event) => {
if(poly) {
const {x, y} = getPosByMouse(event, canvas);
poly.setVertice(poly.count - 1, x, y);
render();
}
})
function createPoly(x, y) {
poly = new Poly({
gl,
program,
vertices: [x, y, x, y],
types: ['POINTS', 'LINE_STRIP']
})
sky.add(poly)
}
// 删除最后一个顶点
function popVertice() {
poly.popVertice();
poly = null;
}
// 渲染
function render() {
gl.clear(gl.COLOR_BUFFER_BIT);
sky.draw()
}
</script>
</body>
</html>
狮子座图案绘制
1、基本绘图需求
1、鼠标第一次点击画布时:
创建多边形
绘制2个点
2、鼠标移动时:
当前多边形最后一个顶点随鼠标移动
3、鼠标接下来点击画布时:
新建一个点
4、鼠标右击时:
删除最后一个随鼠标移动的点
2、优化需求
1、顶点要有闪烁动画
2、建立顶点的时候,如果鼠标点击了其他顶点,就不要再显示新的顶点
代码实现
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制多线</title>
<style>
* {
margin: 0;
padding: 0;
}
#canvas {
background: url("../images/lionsky.png");
background-size: cover;
background-position: right bottom;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 a_Attr;
varying float v_Alpha;
void main(){
gl_Position = vec4(a_Attr.x, a_Attr.y, 0, 1);
gl_PointSize = a_Attr.z;
v_Alpha = a_Attr.w;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying float v_Alpha;
void main(){
float dist = distance(gl_PointCoord, vec2(0.5, 0.5));
if(dist < 0.5) {
gl_FragColor = vec4(0.87, 0.91, 1, v_Alpha);
} else {
discard;
}
}
</script>
<script type="module">
import Poly from "../utils/Poly.js";
import Sky from '../utils/Sky.js';
import Compose from "../utils/Compose.js";
import Track from "../utils/Track.js";
import { getPosByMouse, getInnerText, initShaderProgram, glToCssPos } from "../utils/index.js";
const canvas = document.querySelector('#canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = canvas.getContext('webgl');
gl.clearColor(0, 0, 0, 0);
const vertexStr = getInnerText("vertexShader");
const fragmentStr = getInnerText("fragmentShader");
const program = initShaderProgram(gl, vertexStr, fragmentStr );
gl.enable(gl.BLEND);
// 设置混合公式(正常透明模式)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(program);
gl.clearColor(0, 0, 0, 0);
let sky = new Sky(gl),
poly = null,
point = null,
compose = new Compose();
canvas.oncontextmenu = () => {
return false;
}
// 鼠标点击事件
canvas.addEventListener('mousedown', (event) => {
if(event.button === 2) {
popVertice()
} else {
const {x, y} = getPosByMouse(event, canvas);
if(poly) {
addVertice(x, y);
} else {
createPoly(x, y);
}
}
render();
})
// 鼠标移动
/**
* 鼠标移动:基于鼠标是否划上顶点,设置鼠标的状态
* 设置正在绘制的多边形的最后一个顶点点位
*/
canvas.addEventListener('mousemove', (event) => {
const {x, y} = getPosByMouse(event, canvas);
point = hoverPoint(x, y);
canvas.style.cursor = point ? 'pointer' : 'default';
if(poly) {
const obj = poly.geoData[poly.geoData.length - 1]
obj.x = x;
obj.y = y;
}
})
/**
* 遍历 sky中的所有顶点数据
* 忽略绘图时随鼠标移动的点
* 获取鼠标和顶点的像素距离
* 若此距离小于10像素,返回此点,否则返回null
*
*/
function hoverPoint(mx, my) {
for(let {geoData} of sky.children) {
for(let obj of geoData) {
if(poly && obj === poly.geoData[poly.geoData.length - 1]) {
continue;
}
const delta = {
x: mx - obj.x,
y: my - obj.y
}
const {x, y} = glToCssPos(delta, canvas), dist = x * x + y * y;
if(dist < 100) {
return obj;
}
}
}
return null;
}
function addVertice(x, y) {
let {geoData} = poly;
if(point) {
geoData[geoData.length - 1] = point;
}
let obj = {x, y, pointSize: getRandom(), alpha: 1};
geoData.push(obj);
createTrack(obj);
}
function createPoly(x, y) {
let o1 = point ? point: {x, y, pointSize: getRandom(), alpha: 1},
o2 = {x, y, pointSize: getRandom(), alpha: 1};
poly = new Poly({
gl,
program,
size: 4,
attrName: 'a_Attr',
geoData: [o1, o2],
types: ['POINTS', 'LINE_STRIP']
})
sky.add(poly);
createTrack(o1);
createTrack(o2);
}
function createTrack(obj) {
const {pointSize} = obj;
const track = new Track(obj);
track.start = Date.now();
track.keyMap = new Map([
[
'pointSize',
[
[500, pointSize],
[1000, 0],
[1500, pointSize]
]
],
[
'alpha',
[
[500, 1],
[1000, 0],
[1500, 1],
]
]
])
track.timeLen = 2000;
track.loop = true;
compose.add(track);
}
function getRandom() {
return Math.random()* 8 + 3;
}
// 删除最后一个顶点
function popVertice() {
poly?.geoData?.pop();
compose.children.pop();
poly = null;
}
// 渲染
function render() {
gl.clear(gl.COLOR_BUFFER_BIT);
sky.draw()
}
!(function ani(){
compose.update(Date.now());
sky.updateVertices(['x', 'y', 'pointSize', 'alpha'])
render();
requestAnimationFrame(ani);
})()
</script>
</body>
</html>
.../utils/index.js
javascript
export function getPosByMouse(event, canvas) {
const { offsetX, offsetY } = event;
const { width, height } = canvas;
return {
x: (offsetX * 2) / width - 1,
y: 1 - (offsetY * 2) / height,
};
}
/**
* 将 gl 的坐标位置转换成像素位置,精确度更高
* @param {*} param0
* @param {*} canvas
* @returns
*/
export function glToCssPos({ x, y }, canvas) {
const { width, height } = canvas;
return {
x: ((x + 1) * width) / 2,
y: 1 - (y * height) / 2,
};
}
export function getInnerText(id) {
return document.getElementById(id).innerText;
}
export function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(
`Unable to initialize the shader program: ${gl.getProgramInfoLog(
shaderProgram,
)}`,
);
return null;
}
return shaderProgram;
}
export function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(
`An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`,
);
gl.deleteShader(shader);
return null;
}
return shader;
}
