WebGL 测试2 - 简单的有粗细的线条

示例可以在这里查看。


续上,这次我们添加一个小小的改进,给线条指定宽度。

我们知道,由于 ANGLE 的限制,在 Windows 平台上 gl.lineWidth() 函数并不能用。那么要画宽度不是1的线条怎么办呢?按照上次我提出的“用矩形模拟”的方法是一种思路。

修改 main() 函数,这里我们动态加入线条。

function main() {
    if (!initShaders(glc, "vshader-color", "fshader-color")) {
        console.log("Failed to intialize shaders.");
        return;
    }

    // 这里添加三条线
    verticesAndColorsArray = verticesAndColorsArray
        .concat(calculateLineVertices(0, 40, -40, 0, 5, 1, 0, 0))
        .concat(calculateLineVertices(-40, 0, 40, 0, 3, 0, 1, 0))
        .concat(calculateLineVertices(40, 0, 0, 40, 1, 0, 0, 1));

    // 不使用 WebGL 的线宽设置(保持默认)
    //glc.lineWidth(5);

    var u_ProjMatrix = glc.getUniformLocation(glc.program, "u_ProjMatrix");
    if (!u_ProjMatrix) {
        console.log("Failed to get the storage location of u_ProjMatrix.");
        return;
    }
    var projMatrix = new Matrix4();
    projMatrix.setOrtho(-canvas.width / 2, canvas.width / 2, -canvas.height / 2, canvas.height / 2, 0, 1000);
    glc.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

    drawLines(glc);
}

同时,由于我们使用矩形来模拟,也就是要画三角形,那么就要保证顶点数组的排列符合预期。这里我们选用 gl.TRIANGLES

为什么不选用 gl.TRIANGLE_STRIP 或者 gl.TRIANGLE_FAN 加上索引-顶点呢?嘛,这是个简单的示例而已……索引+顶点到时候很可能要用的(将顶点 6N 的数据量压成了 2N+2 啊),不过考虑到画线时的相接情况,TRIANGLE_STRIPTRIANGLE_FAN 在处理上会麻烦一些。

/**
 * @param {WebGLRenderingContext} glc
 */
function drawLines(glc) {
    /**
     * @type {Number}
     */
    var n;
    if ((n = initVertexBuffers(glc)) <= 0) {
        console.log("Failed to initialize vertex buffers.");
        return;
    }
    clearCanvas(glc);
    // 这次改用 gl.TRIANGLES 绘制
    glc.drawArrays(gl.TRIANGLES, 0, n);
}

顶点数组中每个顶点的格式不变,我们写一个函数生成需要的格式数组。其实就是一个简单的平面几何而已,最多不过 tan2(x) 的变换。

注意 WebGL 使用的坐标系(源自 OpenGL)和数学上的是一致的(正方向为右、上),和一般操作系统的坐标系(正方向为右、下)关系是相互为 Y 轴翻转。

/**
 * @param {Number} x1
 * @param {Number} y1
 * @param {Number} x2
 * @param {Number} y2
 * @param {Number} width
 * @param {Number} r
 * @param {Number} g
 * @param {Number} b
 * @returns {Number[]}
 */
function calculateLineVertices(x1, y1, x2, y2, width, r, g, b) {
    if (width < 0) {
        return [];
    }
    var halfWidth = width / 2;
    var vert1 = [0, 0, r, g, b], vert2 = vert1.slice(), vert3 = vert2.slice(), vert4 = vert3.slice();
    if (x1 == x2) {
        vert1[0] = x1 - halfWidth;
        vert1[1] = y1;
        vert2[0] = x1 + halfWidth;
        vert2[1] = y1;
        vert3[0] = x2 - halfWidth;
        vert3[1] = y2;
        vert4[0] = x2 + halfWidth;
        vert4[1] = y2;
    } else {
        // a little tricky
        /**
         * @type {Number}
         */
        var slope = (y2 - y1) / (x2 - x1);
        /**
         * @type {Number}
         */
        var ct = 1 / Math.sqrt(1 + slope * slope);
        /**
         * @type {Number}
         */
        var st = Math.sqrt(1 - ct * ct);

        /**
         * @type {Number}
         */
        var dx = halfWidth * st;
        /**
         * @type {Number}
         */
        var dy = halfWidth * ct;
        if (slope >= 0) {
            vert1[0] = x1 - dx;
            vert1[1] = y1 + dy;
            vert2[0] = x1 + dx;
            vert2[1] = y1 - dy;
            vert3[0] = x2 - dx;
            vert3[1] = y2 + dy;
            vert4[0] = x2 + dx;
            vert4[1] = y2 - dy;
        }else {
            vert1[0] = x1 - dx;
            vert1[1] = y1 - dy;
            vert2[0] = x1 + dx;
            vert2[1] = y1 + dy;
            vert3[0] = x2 - dx;
            vert3[1] = y2 - dy;
            vert4[0] = x2 + dx;
            vert4[1] = y2 + dy;
        }
    }

    return vert1.concat(vert2).concat(vert3).concat(vert2).concat(vert3).concat(vert4);
}

看看示例,是三种不同粗细的线条吧?

分享到 评论