我的博客

用WebGL在浏览器上画一个点

效果如上图所示,H5标准提供了 canvas标签,需要浏览器提供对应的支持,WebGL的效果就展示在这个元素上。

JavaScript代码如下

let vShaderSource = `
void main() {
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
    gl_PointSize = 10.0;
}
`
let fShaderSource = `
void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

`
function main() {
    let canvas = document.getElementById("webgl")

    let ctx = getWebGlContext(canvas)
    if (!initShader(ctx, vShaderSource, fShaderSource)) {
        console.error('init shader fail')
    }
    ctx.clearColor(0.0, 0.0, 0.0, 1.0)
    ctx.clear(ctx.COLOR_BUFFER_BIT)
    ctx.drawArrays(ctx.POINTS, 0, 1)
}

main方法是入口方法,首先通过canvas得到WebGL的上下文,我们使用了一个辅助函数

/**
 * get webgl context from canvas dom element
 * @param canvas dom canvas element
 * @param attributes
 * @returns {*}
 */
function getWebGlContext(canvas, attributes={alpha:false}) {
    return canvas.getContext('webgl') || canvas.getContext("experimental-webgl")
}

不同浏览器取得WebGL上下文的方法不一样,笔者的chrome浏览器是支持这样的。

WebGL的运作流程同OpenGL类似,先通过JavaScript脚本准备好数据,通过着色器渲染到画布上

就像OpenGL一样,有两种着色器,顶点着色器(vertex shader)和像素着色器(fragment shader/ pixel shader),他们需要在JavaScript中编译好,因此需要是一个JavaScript字符串。

在顶点着色器中,有两个内置的变量 gl_Position和 gl_PointSize,gl_Position会把对应的位置当作一个顶点,然后光栅化。我们要画的点的位置定在画布中心,画布的中心也是WebGL坐标系的中心。

在像素着色器中,gl_FragColor内置变量表示顶点的颜色

准备好这两个shader,我们需要将shader编译后加载到WebGL的上下文中,这里用到了另一个辅助函数

function initShader(ctx, vs, fs) {
    let program = createProgram(ctx, vs, fs);
    if (!program) {
        console.error("fail to create program")
        return false
    }
    ctx.useProgram(program)
    return true
}

这里的 program保存了 shader编译后的信息,createProgram细节如下

function createProgram(ctx, vs, fs) {
    let vshader = createVertexShader(ctx, vs);
    let fshader = createFragmentShader(ctx, fs);
    let program = ctx.createProgram();
    if (!program) {
        console.error("Fail to create program")
        return
    }
    ctx.attachShader(program, vshader);
    ctx.attachShader(program, fshader);
    ctx.linkProgram(program);
    // check if linked successful
    let linkedStatus = ctx.getProgramParameter(program, ctx.LINK_STATUS);
    if (!linkedStatus) {
        let errMsg = ctx.getProgramInfoLog(program);
        console.error("fail to link program")
        ctx.deleteProgram(program)
        ctx.deleteShader(vshader)
        ctx.deleteShader(fshader)
        return
    }
    return program;
}

在 createVertexShader和 createFragmentShader中,我们已经将shader编译好,得到了shaderObject,代码细节如下

function createVertexShader(ctx, shaderSource) {
    return createShaderObj(ctx, ctx.VERTEX_SHADER, shaderSource);
}

function createFragmentShader(ctx, shaderSource) {
    return createShaderObj(ctx, ctx.FRAGMENT_SHADER, shaderSource);
}

function createShaderObj(ctx, type, shaderSource) {
    let shader = ctx.createShader(type);
    if (!shader) {
        console.error("fail to create shader object");
        return;
    }
    ctx.shaderSource(shader, shaderSource);
    ctx.compileShader(shader);
    let compiledRes = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
    if (!compiledRes) {
        let log = ctx.getShaderInfoLog(shader);
        console.error(`Fail to compile shader :${log}`);
        return;
    }
    return shader;
}

通过 attachShader和 linkProgram,我们已经准备好了运行渲染函数。渲染的数据存储在WebGL的COLOR_BUFFER_BIT中,每次绘图完成,我们都需要重置这个BUFFER,重置后我们运行 ctx.drawArrays(ctx.POINTS, 0, 1),表示,我们要画一个点,从第一个顶点开始,运行一次shader,我们的shader中只有一个点,最终的结果就是将这个点画出来。

最近更新: 2026/3/15 14:17
Contributors: Keyang Li