美文网首页
2023-02-03 webgl绘制粗线

2023-02-03 webgl绘制粗线

作者: MrSwilder | 来源:发表于2023-02-02 14:41 被阅读0次

一、原文

Drawing Lines is Hard (svbtle.com)

二、翻译

绘制线条听起来可能不像火箭科学那么高科技,但在OpenGL中很难做到,尤其是WebGL。在这里,我探索了一些不同的2D和3D线渲染技术,并附带了一个小画布演示。

源码和示例戳这儿:https://github.com/mattdesl/webgl-lines

2.1、线图元

https://mattdesl.github.io/webgl-lines/native/index.html

WebGL包括对具有gl.LINES、gl.LINE_STRIP和gl.LINE_LOOP的行的支持。听起来很棒,对吧?不是真的。这里有几个问题:

  • 驱动程序可能会以稍微不同的方式实现渲染/过滤,并且您可能无法在设备或浏览器之间获得一致的渲染
  • 最大线宽取决于驱动程序。例如,运行ANGLE的用户将获得最大值1.0,这是非常无用的。在我的新Yosemite机器上,线宽最大值约为10。
  • 无法控制线连接或端帽样式
  • 并非所有设备都支持MSAA,大多数浏览器也不支持它用于离屏缓冲区。最后可能会出现锯齿状线条
  • 对于虚线/点线,glLineStipple已被弃用,并且在WebGL中不存在

在一些演示中,比如上面的演示,gl.LINES可能是可以接受的,但在大多数情况下,它不适合生产质量的线渲染。

2.2 三角形线

https://mattdesl.github.io/webgl-lines/triangles/index.html

一种常见的替代方法是将一条线分成三角形或三角形条带,然后将其渲染为规则几何体。这样可以最大程度地控制线条,允许端点封口、特定连接、并集(用于重叠的透明区域)等。这也可以导致一些更具创意和有趣的线条渲染,如上面的演示中所示。

实现这一点的一种典型方法是沿路径获取每个点的法线,并向外扩展两边厚度的一半。有关实现,请参见polyline-normals.
。这里讨论斜接背后的数学。

更高级的网格可能需要为端盖、斜角连接、羽化等发出新的几何图形。处理这些边缘情况会变得相当复杂,正如您在Vaser C/C++源代码中看到的那样。

对于抗锯齿,您有几个选项:

  • 希望支持MSAA,并且永远不需要将行渲染到屏幕外缓冲区
  • 为笔划的羽毛边添加更多三角形(如下图所示)
  • 使用纹理查找来渐变alpha;很容易,但规模不大
  • 在片段着色器中,根据屏幕空间中笔划的投影比例计算抗锯齿
  • 将预过滤的gl.LINES渲染为第二遍,围绕笔划边缘

注意:斜接线的一个缺点是边缘锋利。当连接两个线段的角度非常尖锐时,斜接长度会朝着无穷大的方向呈指数增长,并在渲染中造成巨大的瑕疵。在某些应用中,这可能不是问题,在其他应用中,当角度过大时,您可能希望限制斜接或回退到另一个连接(即斜面)。

上面的三角形演示使用了挤出多段线,这是一个正在进行的小模块,用于从二维多段线构建三角形网格。最终,它旨在支持圆形连接/封口和适当的斜接限制。

2.3、在顶点着色器中扩展线

https://mattdesl.github.io/webgl-lines/expanded/index.html

三角剖分会给代码增加相当多的复杂性,当笔划和连接样式更改时,需要重新构建网格。如果你只想在WebGL中使用简单的粗线条,那就有点过分了。

上面的演示扩展了顶点着色器中的笔划,其中厚度是均匀的。我们为路径中的每个点提交两个顶点,并将线法线和斜接长度作为顶点属性传递。每对都有一个法线(或斜接)翻转,这样两个点就被推离中心,形成一条粗线。

attribute vec2 position;
attribute vec2 normal;
attribute float miter;
uniform mat4 projection;

void main() {
    //push the point along its normal by half thickness
    vec2 p = position.xy + vec2(normal * thickness/2.0 * miter);
    gl_Position = projection * vec4(p, 0.0, 1.0);
}

左侧的内部笔划效果(单击画布以设置其动画)是使用与中心的带符号距离在片段着色器中创建的。我们还可以通过传递distance LongPath作为另一个顶点属性来实现虚线、渐变、光晕和其他效果。

2.4 屏幕空间投影线

前面的演示适用于2D(正交)线,但可能不适合您在3D空间中的设计需求。为了使线具有恒定的厚度而不管3D视图如何,我们需要在将线投影到屏幕空间后展开线。
https://mattdesl.github.io/webgl-lines/projected/index.html

与上一个演示一样,我们需要将每个点提交两次,并使用镜像方向,以便它们彼此远离。然而,我们在顶点着色器中进行计算,而不是计算法线和斜接长度CPU侧。为此,我们需要沿路径发送下一个和上一个位置的顶点属性。

在顶点着色器中,我们计算屏幕空间中的连接和拉伸,以确保恒定的厚度。为了在屏幕空间中工作,我们需要使用虚幻的同质组件W。也称为“透视分割”。这为我们提供了归一化设备坐标(NDC),其范围为[-1,1]。然后,我们在扩展线之前修正纵横比。我们也对路径上的上一个和下一个位置执行相同的操作:

mat4 projViewModel = projection * view * model;

//into clip space
vec4 currentProjected = projViewModel * vec4(position, 1.0);

//into NDC space [-1 .. 1]
vec2 currentScreen = currentProjected.xy / currentProjected.w;

//correct for aspect ratio (screenWidth / screenHeight)
currentScreen.x *= aspect;

对于路径中的第一个点和最后一个点,需要处理一些边缘情况,否则,一个简单的线段可能如下所示:

//normal of line (B - A)
vec2 dir = normalize(nextScreen - currentScreen);
vec2 normal = vec2(-dir.y, dir.x);

//extrude from center & correct aspect ratio
normal *= thickness/2.0;
normal.x /= aspect;

//offset by the direction of this point in the pair (-1 or 1)
vec4 offset = vec4(normal * direction, 0.0, 0.0);
gl_Position = currentProjected + offset;

请注意,这里没有尝试连接两个线段。这种方法有时比斜接更可取,因为它不处理尖锐边缘的问题。上述演示中的扭曲圆没有使用任何斜接。

另一方面,演示中的沙漏形状在没有斜接的情况下看起来会被挤压变形。为此,顶点着色器实现了基本的斜接,没有任何限制。

我们可以对数学做一些细微的修改,以实现不同的设计。例如,使用NDC的Z分量在线条深入场景时缩放线条的厚度。这将有助于提高深度感。

For a ThreeJS implementation of this approach, see THREE.MeshLine by @thespite.

其他相关的

与WebGL中的大多数内容一样,有十几种方法可以剥猫皮。上面的演示是用相当低级的抽象实现的,因此您可以了解正在发生的事情,并自行决定下一个应用程序最合适的方法。其他一些可行的方法:

  • Stencil Geometry
    使用模板缓冲区创建复杂多边形而无需三角剖分的一个很酷的技巧。然而,它根本没有收到任何MSAA。[1][2][3]

  • Loop-Blinn Curve Rendering
    分辨率无关的三次样条曲线渲染,非常适合字体字形。

  • Rasterized strokes
    光栅化笔划

  • Single Pass Wireframe Rendering
    类似于投影线演示,但更适合3D线框[1]

  • Geometry Shaders
    这将允许投影线发出各种封口/连接。但是,WebGL不支持几何体着色器。

  • Analytic Distance Fields
    在片段着色器中使用单个四边形和距离场也可以实现2D和3D中的粗抗锯齿线。这不是很实用,可能表现不佳,但也可以实现一些有趣的效果(如运动模糊)。

示例使用的模块

进阶阅读

相关文章

  • TWaver3D直线、曲线、曲面的绘制

    1. WebGL原生线 WebGL支持绘制点、线、三角;绘制线的方法比较简单,给定顶点,设置绘制方式即可; 假设给...

  • WebGL 绘制Line的bug(一)

    熟悉WebGL的同学都知道,WebGL绘制模式有点、线、面三种;通过点的绘制可以实现粒子系统等,通过线可以绘制一些...

  • webgl入门(一)

    webgl是基于 canvas 进行绘制,通过 getWebGLContext()来获取 webgl 的绘图上下文...

  • 用线段绘制球体(three.js webgl_lines_spe

    用线段绘制球体(three.js webgl_lines_spere例子) Three.js中的webgl_lin...

  • Threejs in autonomous driving -(

    绘制各种几何体是webgl的强项,相反各种异性几何体就非常麻烦。比如圆角矩形来说在webgl中绘制就相对比较麻烦。...

  • WebGL入门

    初识WebGL 01-手动绘制一个WebGL图形 实现的步骤: 添加一个画布元素 获取到画布元素的基于webgl上...

  • WebGL学习(1) — 浏览器支持测试

    什么是WebGL? WebGL是一项用来在网页上绘制和渲染三维图形并允许用户与之交互的技术。同时,WebGL(we...

  • webgl 入门(二)

    webgl 参数传递 webgl是基于着色器进行绘制的.着色器本身无法与 JavaScript 进行通讯以及数据的...

  • 《WebGL编程指南》1&2 WebGL入门

    什么是WebGL? WebGL是一种可以在网页上绘制渲染3D图形,并允许用户与之交互的技术。浏览器内置了WebGL...

  • webgl多点绘制

    采用JavaScript循环的方式。使用二维数组的方式定义点的位置。然后通过for循环以此绘制点。 vertexA...

网友评论

      本文标题:2023-02-03 webgl绘制粗线

      本文链接:https://www.haomeiwen.com/subject/cfowhdtx.html