/** * @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, 0.5, 0, 0.7, 0 ]; 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); }