上一篇文章简单介绍了WebGL绘制Line的bug,不少朋友给我发了私信,看来这个问题大家都遇上过哈。今天这篇文章会讲述解决这个问题的work around。

基本思路

上一篇文章结尾简单提了下解决的思路,就是通过三角形来模拟线条。

以两个端点组成的线段为例,绘制line的时候只用指定两个端点,如果通过三角形来模拟一条线段,则至少需要两个三角形,如下图:

这是两个三角形模拟的线段。

因此要绘制一条线段,则需要六个顶点,两个三角形;当时从上图中,可以看出有些顶点是共享,实际上只需要四个顶点,然后通过索引的方式绘制两个三角形,相信熟悉WebGL的同学都理解这种通过索引来绘制的方式,此处不详细说明。

如果要绘制两条相连的线段呢,则需要增加两个顶点,也就是6个顶点,绘制四个三角形,依次类推,绘制三条相连的线段需要8个顶点,绘制6个三角形;由此可以得出一个结论,绘制有n个端点的Line,需要:2 n 个顶点, (n-1) 2个三角形。

对于一条线段而言,控制的参数实际上只有两个端点的坐标和线的宽度。

从上面的分析,我们知道了给定一系列点(n个)和线的宽度,绘制一条线段需要的顶点数是n * 2.

如何计算顶点

两个端点的情况

当时 n个顶点数据应该如何计算得到呢? 先举个简单的2维绘图的例子,现在假设给定了两个端点:

(-50,0)和(50,0),要绘制一条宽度为2的线条,那么总共是四个顶点,第一个顶点是从第一个端点 + 线宽造成的偏移量,线宽为2,所以偏移量的基数应该是2 / 2 = 1;

第二个顶点是从第一个端点 + 线宽造成的偏移量 (-1),同样线宽为2,所以偏移量的基数应该是2 /2 (-1) = -1;

依次类推,那么应该如何偏移呢? 这个与线段的走向有关,示例中 线段的走向可以用第二个端点 - 第一个端点计算出来:

(50,0) - (-50,0) = (100,0) ,归一化之后就是(1,0),此为线段的方向向量,表示的线段的走向的是沿x轴正方向,对于第一顶点,偏移的方向应该是(1,0)逆时针旋转90度,即和线段走向垂直的方向(与线段垂直的方向有两个,此处基于右手法则,选择逆时针旋转90度的一个),旋转90度之后,向量编程了(0,-1)

从图形学里面的数学知识可以得知,向量(x,y)逆时针旋转90度变成(-y,x);

对于第二个顶点,偏移的方向应该是(1,0)顺时针旋转90度,但是前面,我们已经把偏移的基数变成-1了,所以可以认为偏移的方向还是(1,0)逆时针旋转90度,如图:

基于线段方向计算顶点偏移方向
由此,可以得出第一个顶点的位置是:

(-50,0) - (0,-1)* 1 = (-50,-1),
第二个顶点的位置是:

(-50,0)-(0,-1) * 1 = (-50,1)
对于第三,第四个顶点的计算也是类似的。

多个端点的情况

上面讨论的是只有两个端点的情况,事实上,如果是多个端点,以上讨论的情况只适合多个端点中第一个端点和最后一个端点的情况,对于中间的端点,偏移的方向要综合考虑这个端点连接的两条线段的情况,同样举例说明:

假设三个端点的情况,三个端点 分别是 (-50,0),(0,0),(0,50),现在要计算第二个端点(0,0)对应的两个顶点(第三、第四个),如图:

此时要计算中间的端点的两个顶点位置,则需要考虑改端点连接的两天线段的方向:

计算的大致思路,通过该端点的和前一个端点相减 计算出第一条线段的方向:
(0,0) - (-50,0) = (50,0) = (1,0)(归一化)

在通过该该端点的下一个端点减去该端点计算出第二条线段的方向:
(0,50) - (0,0) = (0,50) = (0,1)(归一化)

然后两个方向向量相加,在旋转逆时针旋转90度,可以得到偏移的方向:
(1,0) + (0,1) = (1,1) = (0.707,0.707)(归一化)

旋转之后,偏移方向编程了(-0.707,0.707),

需要注意的是,此时的的偏移基数其实也是发生了变化的,拐角处的偏移量此时应该变成大了,即有了一个放大因子。 可以通过 1 / 偏移方向 点乘 第一条线段的方向 来获取这个放大因子,不过如果两条线段夹角很小,点乘的值也很小,放大因子很大,为了拐角处的尖角不显得是否大,我们一般限定放大因子不超过2. 因此公式可以变成:

1 / max(偏移方向 .  第一条线段的方向,0.5)

如何组织顶点数据

上面大量篇幅讲述了如何计算顶点坐标,事实上,前面文字所述的一切计算方法都是发生在顶点着色器中的,而且也只能在着色器中计算,因为最终显示到屏幕上的顶点与镜头相关,上文中只是简单的用了2维的情况模拟,如果在js端计算,将极大消耗性能。 (那你不是瞎扯吗,我们都还没搞清楚如何计算出要传递给顶点着色器的数据呢),其实不是瞎扯,因为只有搞清楚了在着色器中如何计算最终的顶点,才知道如何向顶点着色器中组织数据,

以上文中“多个端点的情况”的为例,我们可以总结出计算出一个顶点需要哪些数据:

端点坐标,偏移量,前一个端点坐标,后一个端点坐标

因此在着色器中需要定义四个attribute变量 position,offset,positionPrev,positionNext,分别用来接收端点坐标,偏移量,前一个端点坐标,后一个端点坐标。

对于前面两顶点,其端点没有前一个端点,此时前一个端点就取端点坐标,然后在着色器中判断 如果前一个端点点坐标 == 端点坐标,则表明是第一个端点;使用两个端点的情况计算。

低于后面两个顶点,其端点没有后一个端点,此时后一个端点就取端点坐标,然后在着色器中判断 如果后一个端点点坐标 == 端点坐标,则表明是最后一个端点;使用两个端点的情况计算。

对于中间的顶点,既存在端点坐标,也存在前一个端点的坐标,和后一个端点的坐标,就使用前面多个端点的情况计算。

还是以之前三个端点的例子为例,三个端点的(50,0,0),(0,0,0),(0,50,0),线宽为2(注意此时已经是三维坐标了,之前模拟的情况是用屏幕上的2维坐标来模拟顶点在着色器中通过透视变换变成了二维坐标的情况)

那么第一个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(50,0,0),2/2,       (50,0,0)                  (0,0,0)

第二个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(50,0,0),-2/2,       (50,0,0)                  (0,0,0)

第三个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(0,0,0),2/2,       (50,0,0)                  (0,50,0)

第四个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(0,0,0),-2/2,       (50,0,0)                  (0,50,0)

第五个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(0,50,0),2/2,       (0,0,0)                  (0,50,0)

第六个顶点的四个变量的数据分别是:

端点坐标,      偏移量,  前一个端点坐标,后一个端点坐标

(0,50,0),-2/2,       (0,0,0)                  (0,50,0)

到此为止,我们知道了如何组织绘制需要的顶点的数据。

下一篇将贴上相关代码说明。

如果你对WebGL 感兴趣,可以了解下我们用WebGL开发的3D机房项目:

无插件纯Web 3D机房,HTML5+WebGL倾力打造

WebGL 绘制Line的bug(二)的更多相关文章

  1. WebGL 绘制Line的bug(一)

    今天说点跟WebGL相关的事儿,不知道大家有没有碰到过类似的烦恼. 熟悉WebGL的同学都知道,WebGL绘制模式有点.线.面三种:通过点的绘制可以实现粒子系统等,通过线可以绘制一些连线关系:面就强大 ...

  2. WebGL 绘制Line的bug(三)

    之前铺垫了许多,今天可以来分享点纯干货了. 上一篇已经讲述了通过面模拟线条时候,每一个顶点的顶点数据包括:端点坐标.偏移量.前一个端点坐标.后一个端点坐标,当然如果我们通过索引的方式来绘制的话,还包括 ...

  3. WebGL绘制有宽度的线

    WebGL中有宽度的线一直都是初学者的一道门槛,因为在windows系统中底层的渲染接口都是D3D提供的,所以无论你的lineWidth设置为多少,最终绘制出来的只有一像素.即使在移动端可以设置有宽度 ...

  4. 利用javascript和WebGL绘制地球 【翻译】

    利用javascript和WebGL绘制地球 [翻译] 原翻译:利用javascript和WebGL绘制地球 [翻译] 在我们所有已知的HTML5API中,WebGL可能是最有意思的一个,利用这个AP ...

  5. WebGL绘制有端头的线

    关于WebGL绘制线原理不明白的小伙伴,可以看看我之前的文章WebGL绘制有宽度的线.这一篇我们主要来介绍端头的绘制,先看效果图. 端头一般被称为lineCap,主要有以下三种形式: butt最简单等 ...

  6. 一篇文章理清WebGL绘制流程

    转自:https://www.jianshu.com/p/e3d8a244f3d9 目录 初始化WebGL环境 顶点着色器(Vertex Shader)与片元着色器(Fragment Shader) ...

  7. WPF GDI+字符串绘制成图片(二)

    原文:WPF GDI+字符串绘制成图片(二) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/BYH371256/article/details/83 ...

  8. WebGL入门教程(二)-webgl绘制三角形

    前面已经介绍过了webgl,WebGL入门教程(一)-初识webgl(http://www.cnblogs.com/bsman/p/6128447.html),也知道了如何绘制一个点,接下来就用web ...

  9. FireFox下Canvas使用图像合成绘制SVG的Bug

    本文适合适合对canvas绘制.图形学.前端可视化感兴趣的读者阅读. 楔子 所有的事情都会有一个起因.最近产品上需要做一个这样的功能:给一些图形进行染色处理.想想这还不是顺手拈来的事情,早就研究过图形 ...

随机推荐

  1. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 24. Logging

    常用的诊断中间件: UseDeveloperExceptionPage UseStatusCodePages:返回 400~600 的状态码 UseExceptionHandler 自定义异常的处理器 ...

  2. codforces 1C Ancient Berland Circus(几何)

    题意 给出正多边形上三个点的坐标,求正多边形的最小面积 分析 先用三边长求出外接圆半径(海伦公式),再求出三边长对应的角度,再求出三个角度的gcd,最后答案即为\(S*2π/gcd\),S为gcd对应 ...

  3. poj3669【bfs】

    题意: 有个**要看流星雨,可是流星雨会砸死他的... 给出n个流星雨下落的坐标,时间,如果那个**在下落坐标或是上下左右就会gg,问你他最短跑到流星雨打不到的地方的时间. 思路: ①:预处理出一个矩 ...

  4. 2016 Multi-University Training Contest 1 GCD【RMQ+二分】

    因为那时候没怎么补所以就分到了未搞分组里!!!然后因为标题如此之屌吧= =点击量很高,然后写的是无思路,23333,估计看题人真的是觉得博主就是个撒缺.废话不多说了,补题... update////2 ...

  5. python 元类 type metaclass

    python中一切皆对象,类对象创建实例对象,元类创建类对象,元类创建元类. 元类创建类对象有2中方式: 一.type方法 type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性 ...

  6. 【EXCEL终极总结分享】基于NPOI扩展封装的简易操作工具类库(简单灵活易用,支持导出、导入、上传等常见操作)

    对于EXCEL的导入.导出,我之前已分享过多次,比如: 第一种方案:<我写的一个ExcelHelper通用类,可用于读取或生成数据>这个主要是利用把EXCEL当成一个DB来进行获取数据,导 ...

  7. 你不知道的meta标签

    前言 meta标签可以用来做seo优化.指定移动端viewport的展现形式.设置http请求.告诉浏览器缓存静态资源的模式等等.今天整理一下使用meta标签实用的,常见的场景. meta标签的组成 ...

  8. 《Redis开发与运维》快速笔记(一)

    1.前言&基本介绍 在原始的系统架构中,我们都由程序直接连接DB,随着业务的进一步开展,DB的压力越来越大,为了缓解DB的这一压力,我们引入了缓存,在程序连接DB中加入缓存层, 从而减轻数据库 ...

  9. 第一个 swift 项目

    今天 学习了 一丢丢 swift,特此记录一下 ! 原来创建的时候 ,只要把 语言 由以前的Object-C改为Swift,变创建好了自己的swift工程 第一个简单的swift demo 上代码 i ...

  10. SQL异常为"当IDENTITY_INSERT设置为OFF时" 的解决

    误删数据库时,可以利用insert插入删除的数据,但是有时表可能有自增字段如id.这是插入数据如果包含自增字段就会出现错误,提示"IDENTITY_INSERT设置为OFF,插入失败&quo ...