279 lines
8.2 KiB
JavaScript
279 lines
8.2 KiB
JavaScript
/**
|
||
* @description 创建着色器对象
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @param {'VERTEX_SHADER' | 'FRAGMENT_SHADER'} type
|
||
* @param {string} source
|
||
* @return { WebGLShader | undefined }
|
||
*/
|
||
function makeShader(gl, type, source) {
|
||
// 创建 shader
|
||
const shader = gl.createShader(gl[type]);
|
||
// 给 shader 提供 glsl 代码
|
||
gl.shaderSource(shader, source);
|
||
// 编译 glsl 生成 shader
|
||
gl.compileShader(shader);
|
||
const ok = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
|
||
// 如果成功,则返回 shader,否则返回 undefined
|
||
return ok ? shader : gl.deleteShader(shader);
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* @param {string} path
|
||
* @return {string}
|
||
*/
|
||
async function getSource(path) {
|
||
const response = await fetch(path);
|
||
const source = await response.text();
|
||
return source;
|
||
}
|
||
|
||
|
||
window.onload = async () => {
|
||
const canvas = document.getElementById('canvas');
|
||
const gl = canvas.getContext('webgl2');
|
||
|
||
if (!(gl instanceof WebGL2RenderingContext)) {
|
||
console.log('浏览器不支持 webgl2');
|
||
return;
|
||
}
|
||
|
||
const vertexSource = await getSource('./vertex.shader.glsl');
|
||
const fragementSource = await getSource('./fragment.shader.glsl');
|
||
const vertexShader = makeShader(gl, 'VERTEX_SHADER', vertexSource);
|
||
const fragmentShader = makeShader(gl, 'FRAGMENT_SHADER', fragementSource);
|
||
if (!vertexShader || !fragmentShader) {
|
||
console.log('着色器创建失败');
|
||
return;
|
||
}
|
||
|
||
const program = createProgram(gl, vertexShader, fragmentShader);
|
||
if (!program) {
|
||
console.log('着色程序创建失败');
|
||
return;
|
||
}
|
||
|
||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||
gl.clearColor(0, 0, 0, 0);
|
||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||
|
||
const points = [
|
||
0, -0.5,
|
||
0.5, -0.5,
|
||
];
|
||
|
||
drawMask(gl, program, points, { width: 0.06 });
|
||
drawLine2D(gl, program, points, { width: 0.06 });
|
||
}
|
||
|
||
function onTheSameLine(x0, y0, x1, y1, x2, y2) {
|
||
const slope1 = (x1 - x0) !== 0 ? (y1 - y0) / (x1 - x0) : Infinity;
|
||
const slope2 = (x2 - x0) !== 0 ? (y2 - y0) / (x2 - x0) : Infinity;
|
||
|
||
return slope1 === slope2;
|
||
}
|
||
|
||
function sgn(value) {
|
||
if (value === 0) return 0;
|
||
return value > 0 ? 1 : -1;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @param {WebGLProgram} program
|
||
* @param {number[]} points
|
||
* @param {{
|
||
* width: number
|
||
* color: string
|
||
* }} config
|
||
*/
|
||
function drawLine2D(gl, program, points, config) {
|
||
const pointNum = points.length >> 1;
|
||
const w = config.width;
|
||
const shift = w / 2;
|
||
const positions = [];
|
||
for (let i = 0; i < pointNum; ++ i) {
|
||
const x1 = points[(i << 1)];
|
||
const y1 = points[(i << 1) + 1];
|
||
|
||
if (i === 0) {
|
||
const x2 = points[((i + 1) << 1)];
|
||
const y2 = points[((i + 1) << 1) + 1];
|
||
if (y1 === y2) {
|
||
// console.log('enter i = 0');
|
||
positions.push(
|
||
x1, y1 + shift, // v0
|
||
x1, y1 - shift // v1
|
||
);
|
||
} else if (x1 === x2) {
|
||
positions.push(
|
||
x1 + shift, y1, // v1
|
||
x1 - shift, y1, // v0
|
||
);
|
||
}
|
||
} else if (i === pointNum - 1) {
|
||
const x0 = points[((i - 1) << 1)];
|
||
const y0 = points[((i - 1) << 1) + 1];
|
||
if (y1 === y0) {
|
||
// console.log('enter i = pointNum - 1');
|
||
positions.push(
|
||
x1, y1 + shift, // v0
|
||
x1, y1 - shift // v1
|
||
);
|
||
} else if (x1 === x0) {
|
||
positions.push(
|
||
x1 + shift, y1, // v1
|
||
x1 - shift, y1, // v0
|
||
);
|
||
}
|
||
} else {
|
||
const x0 = points[((i - 1) << 1)];
|
||
const y0 = points[((i - 1) << 1) + 1];
|
||
const x2 = points[((i + 1) << 1)];
|
||
const y2 = points[((i + 1) << 1) + 1];
|
||
|
||
if (x0 !== x1 && y0 === y1 && x1 === x2 && y1 !== y2) {
|
||
const d = sgn(y2 - y1);
|
||
if (y2 > y1) {
|
||
positions.push(
|
||
x1 - shift, y1 + shift,
|
||
x1 + shift, y1 - shift
|
||
);
|
||
} else {
|
||
positions.push(
|
||
x1 + shift, y1 + shift,
|
||
x1 - shift, y1 - shift,
|
||
);
|
||
}
|
||
|
||
} else if (x0 === x1 && y0 !== y1 && x1 !== x2 && y1 === y2) {
|
||
const d = sgn(y1 - y0);
|
||
if (y1 > y0) {
|
||
positions.push(
|
||
x1 - shift, y1 + shift,
|
||
x1 + shift, y1 - shift,
|
||
);
|
||
} else {
|
||
positions.push(
|
||
x1 + shift, y1 + shift,
|
||
x1 - shift, y1 - shift,
|
||
);
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
const positionFloat32 = new Float32Array(positions);
|
||
const buffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||
gl.bufferData(gl.ARRAY_BUFFER, positionFloat32, gl.STATIC_DRAW);
|
||
gl.useProgram(program);
|
||
const positionLocation = gl.getAttribLocation(program, 'a_position');
|
||
const colorLocation = gl.getUniformLocation(program, 'a_color');
|
||
gl.uniform4f(colorLocation, 0.4, 0.0, 1.0, 1.0);
|
||
gl.enableVertexAttribArray(positionLocation);
|
||
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, positionFloat32.length >> 1);
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
*
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @param {WebGLProgram} program
|
||
* @param {number[]} times
|
||
* @param {{
|
||
* width: number
|
||
* color: string
|
||
* }} config
|
||
* @param {number} yshift
|
||
*/
|
||
function drawBlock(gl, program, times, config, yshift) {
|
||
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @param {WebGLProgram} program
|
||
* @param {number[]} points
|
||
* @param {{
|
||
* width: number
|
||
* color: string
|
||
* }} config
|
||
*/
|
||
function drawMask(gl, program, points, config) {
|
||
const positions = [];
|
||
const pointNum = points.length >> 1;
|
||
const w = config.width;
|
||
const shift = w / 2;
|
||
for (let i = 0; i < pointNum; ++ i) {
|
||
const x1 = points[(i << 1)];
|
||
const y1 = points[(i << 1) + 1];
|
||
if (i + 1 < pointNum) {
|
||
const x2 = points[((i + 1) << 1)];
|
||
const y2 = points[((i + 1) << 1) + 1];
|
||
if (y2 > y1 && i + 2 < pointNum) {
|
||
const x3 = points[((i + 2) << 1)];
|
||
const y3 = points[((i + 2) << 1) + 1];
|
||
const p1 = [x1, y1 - shift];
|
||
const p2 = [x2, y2 - shift];
|
||
const p3 = [x3, y3 - shift];
|
||
const p4 = [x3, y1 - shift];
|
||
|
||
positions.push(
|
||
...p1,
|
||
...p2,
|
||
...p3,
|
||
...p1,
|
||
...p3,
|
||
...p4
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
const positionFloat32 = new Float32Array(positions);
|
||
const buffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||
gl.bufferData(gl.ARRAY_BUFFER, positionFloat32, gl.STATIC_DRAW);
|
||
gl.useProgram(program);
|
||
|
||
const aPostionLocation = gl.getAttribLocation(program, 'a_position');
|
||
const colorLocation = gl.getUniformLocation(program, 'a_color');
|
||
gl.uniform4f(colorLocation, 0.4, 0.0, 1.0, 0.2);
|
||
|
||
gl.enable(gl.BLEND);
|
||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||
|
||
gl.enableVertexAttribArray(aPostionLocation);
|
||
gl.vertexAttribPointer(aPostionLocation, 2, gl.FLOAT, false, 0, 0);
|
||
gl.drawArrays(gl.TRIANGLES, 0, positionFloat32.length >> 1);
|
||
}
|
||
|
||
|
||
/**
|
||
* @description 创建着色器
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @param {WebGLShader} vertexShader
|
||
* @param {WebGLShader} fragmentShader
|
||
* @return { WebGLProgram | undefined }
|
||
*/
|
||
function createProgram(gl, vertexShader, fragmentShader) {
|
||
// 创建着色程序
|
||
const program = gl.createProgram();
|
||
// 加载着色器到着色程序
|
||
gl.attachShader(program, vertexShader);
|
||
gl.attachShader(program, fragmentShader);
|
||
// 连接着色程序
|
||
gl.linkProgram(program);
|
||
const ok = gl.getProgramParameter(program, gl.LINK_STATUS);
|
||
|
||
return ok ? program : gl.deleteProgram(program);
|
||
}
|