绘制线段(鼠标点击)
绘制线段
线段图元的分类
线段图元分为三种:
- LINES:基本线段。
- LINE_STRIP:带状线段。
- LINE_LOOP:环状线段。
LINES 图元
LINES 图元称为基本线段图元,绘制每一条线段都需要明确指定构成线段的两个端点。
我们还是通过每次点击产生一个点,并将点击位置坐标放进 positions 数组中。
注意,我们的坐标还是相对于屏幕坐标系,顶点着色器中会将屏幕坐标系转换到裁剪坐标系,也就是坐标区间在[-1, 1]之间。
点击逻辑
canvas.addEventListener('click', e => {
const x = e.offsetX
const y = e.offsetY
// 两个坐标为一组
positions.push(x, y)
if (positions.length > 0) {
/**
* 向当前缓冲区写入数据
*/
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
/**
* 设置颜色
*/
gl.uniform4f(u_Color, 255, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
/**
* 绘制线段,这里的图元我们设置为 gl.LINES
*/
gl.drawArrays(gl.LINES, 0, positions.length / 2)
}
})
每点击两次就会绘制一条线段,但是线段是互相独立的。
如果我们想绘制线段带(gl.LINE_STRIP)或者线段环(gl.LINE_LOOP)可以设置对应的图元。
完整代码
import { getWebGLContext, createShader, createProgram } from '@3dgl/utils'
/**
* 获取 canvas 元素
*/
const canvas = document.getElementById('canvas')
const gl = getWebGLContext(canvas)
/**
* 定义顶点着色器
*/
const VERTEX_SHADER_SOURCE = `
precision mediump float;
attribute vec2 a_Position;
attribute vec2 a_Screen_Size;
void main() {
vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
position = position * vec2(1.0, -1.0);
gl_Position = vec4(position, 0.0, 1.0);
}
`
/**
* 定义片元着色器
*/
const FRAG_SHADER_SOURCE = `
precision mediump float;
uniform vec4 u_Color;
void main() {
gl_FragColor = u_Color / vec4(255, 255, 255, 1);
}
`
/**
* 初始化着色器
*/
const vertexShader = createShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER_SOURCE)
const fragShader = createShader(gl, gl.FRAGMENT_SHADER, FRAG_SHADER_SOURCE)
/**
* 初始化程序
*/
const { program } = createProgram(gl, vertexShader, fragShader)
/**
* 使用 program
*/
gl.useProgram(program)
const a_Position = gl.getAttribLocation(program, 'a_Position')
const a_Screen_Size = gl.getAttribLocation(program, 'a_Screen_Size')
const u_Color = gl.getUniformLocation(program, 'u_Color')
/**
* 为顶点着色器中的 a_Screen_Size 传递 canvas 的宽高信息
*/
gl.vertexAttrib2f(a_Screen_Size, canvas.width, canvas.height)
/**
* 创建缓冲区
*/
const buffer = gl.createBuffer()
/**
* 绑定缓冲区
*/
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
// 每次取两个数据
const size = 2
// 每个数据的类型是32位浮点型
const type = gl.FLOAT
// 不需要归一化数据
const normalize = false
// 每次迭代运行需要移动数据数 * 每个数据所占内存 到下一个数据开始点。
const stride = 0
// 从缓冲起始位置开始读取
const offset = 0
// 将 a_Position 变量获取数据的缓冲区指向当前绑定的 buffer。
gl.vertexAttribPointer(a_Position, size, type, normalize, stride, offset)
/**
* 开启 attribute 变量
*/
gl.enableVertexAttribArray(a_Position)
/**
* 清空绘图区
*/
gl.clearColor(0.0, 0.0, 0.0, 0.1)
gl.clear(gl.COLOR_BUFFER_BIT)
/**
* 点击 canvas 动态绘制线段
*/
canvas.addEventListener('click', e => {
const x = e.offsetX
const y = e.offsetY
// 两个坐标为一组
positions.push(x, y)
if (positions.length > 0) {
/**
* 向当前缓冲区写入数据
*/
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
/**
* 设置颜色
*/
gl.uniform4f(u_Color, 255, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
/**
* 绘制线段,试着将 gl.LINES 改成 gl.LINE_STRIP 和 gl.LINE_LOOP
*/
gl.drawArrays(gl.LINES, 0, positions.length / 2)
}
})