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);
}

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

分享到 评论