SyMind / learning

路漫漫其修远兮,吾将上下而求索。
9 stars 1 forks source link

使用 WebGL 绘制线条 #40

Open SyMind opened 2 years ago

SyMind commented 2 years ago

原文:https://blog.scottlogic.com/2019/11/18/drawing-lines-with-webgl.html

我最近有机会帮助 D3FC 在 WebGL 中实现一些功能。 D3FC 是一个 D3 库的扩展,提供常用组件,使构建交互式图表更容易。

在上一篇文章中,我讨论了在 WebGL 中绘制点所采用的方法。我们在顶点着色器中创建了一些正方形,然后在片元着色器中丢弃不需要的像素。这些形状具有相似的高度和宽度,因此效果很好 —— 片元着色器需要丢弃的像素相对较少。

然而,这种技术不适合绘制线条。线条通常又细又长,这意味着片段着色器必须在大量不需要的像素上运行。

此外,我们需要将点进行连通,这意味着 GL_POINT 不是最好的模式。

这篇文章探索一种不同的渲染方法,充分利用着色器,着眼于如何在 WebGL 中以最少的跨缓冲区传输数据来渲染线条。

利用正方形进行绘制

WebGL 中的一切都是使用三角形构建的。我们可以将两个三角形组合在一起以创建一个矩形。

我们的第一个想法可能是通过在它们之间绘制一个矩形来将每个点连接到下一个点。然而,当我们尝试这样做时,我们发现这种技术存在问题。

image

这两个矩形在顶部留下一个倾角。所以我们需要想一个更好的方法来连接各点。

利用梯形进行绘制

我们可以画梯形而不是矩形。这些将使我们能够将两个组合在一起而没有任何重叠或间隙。这种类型的接头称为斜接。

image

我们如何在定点着色器中实现。

我们可以利用 GL_TRIANGLE_STRIP。三角形带是一系列共享顶点的三角形。我们只需要定义每个顶点一次,WebGL 会将三角形连接成一条带。

对于每个点,我们需要两个顶点——一个用于线的外部连接,一个用于内部连接。

为简单起见,我们假设我们正在绘制一条包含三个点的线:A、B 和 C。B 是顶点着色器正在考虑的当前点,A 是前一个点,C 是下一个点。我们可以通过缓冲区传递这些值。

image

gl_Position.xy = gl_Position.xy + (directionToMove * distanceToMove);

方向

为了帮助我们计算斜接的方向,我们可以使用 Tom McLaughlan 开发的斜接工具。如您所见,斜接线是线 AB 和 BC 的切线的法向量。所以如果我们找到切线,我们可以用它来计算方向。

image

我们可以通过添加法线向量 AB 和 BC 来计算切线。

vec2 AB = normalize(gl_Position.xy - prev.xy);
vec2 BC = normalize(next.xy - gl_Position.xy);

gl_Position 为我们提供了当前顶点在剪辑空间中的位置。剪辑空间在两个方向上的范围从 -1 到 +1,正方形也是如此。然而,我们正在绘制的画布更可能是一个矩形。这通常不是问题,因为画布大小映射到剪辑空间。但是,这种映射的一个副作用是剪辑空间中向量的角度与我们画布上的角度不同。