插入广告(长期有效)MONO需要在武汉招聘JavaScript工程师。几个要求:前端技术(JavasScript、HTML、CSS)、可视化技术(Canvas、WebGL)基础不好可以培养浓厚的兴趣,基础好可以谋大事。如果您有兴趣,给我发邮件:hr@servasoft.com--------------------------------------------------------------文本分割线--------------------------------------------------------------上一篇简单介绍WebGL画线的bug。很多朋友私信给我。这个问题好像大家都遇到过。今天的文章将描述解决此问题的变通方法。基本思路上一篇文章的最后简单提到了解决思路,就是通过三角形来模拟直线。以由两个端点组成的线段为例。绘制直线时,只需指定两个端点。如果用三角形模拟一条线段,至少需要两个三角形,如下图:这是用两个三角形模拟的线段。因此,画一条线段需要六个顶点和两个三角形;从上图可以看出,有些顶点是共享的,其实只需要四个顶点,然后通过索引绘制两个三角形,相信熟悉WebGL的同学都明白这种通过索引绘制的方式,就不做了这里就不细说了。如果要绘制两条相连的线段,则需要添加两个顶点,即6个顶点,绘制四个三角形,以此类推,绘制三个相连的线段需要8个顶点,绘制6个三角形;由此我们可以得出一个结论,画一条有n个端点的直线需要:2n个顶点,(n-1)2个三角形。对于一条线段,控制参数其实只是两个端点的坐标和线的宽度。从上面的分析我们知道,给定一系列点(n)和线的宽度,绘制一条线段需要的顶点数为n*2。如何计算顶点的两个端点应该如何n个顶点数据到时候算出来了吗?让我们以一个简单的二维绘图为例。现在假设给出了两个端点:(-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),0)并逆时针旋转90度,如图:根据线段方向计算顶点偏移方向由此可得第一个顶点的位置为:(-50,0)-(0,-1)*1=(-50,-1),第二个顶点的位置为:(-50,0)-(0,-1)*1=(-50,1)第三个和第四个顶点类似。多个端点的情况上面的讨论是只有两个端点的情况。实际上,如果有多个端点,上面讨论的情况只适用于多个端点中的第一个端点和最后一个端点。对于中间端点,偏移的方向要综合考虑这个端点连接的两条线段的情况,还举个例子:假设三个端点的情况,三个端点分别是(-50,0),(0,0),(0,50),现在需要计算第二个端点(0,0)对应的两个顶点(第三和第四),如图:此时,要计算位置中间端点的两个顶点,需要考虑改变端点连接的两个天线段的方向:计算的大致思路,第一个线段的方向是用前面的减去终点计算出来的endpoint:(0,0)-(-50,0)=(50,0)=(1,0)(normalized)通过这个端点减去下一个端点计算第二条线段的方向:(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)如何组织顶点数据上面用了很多篇幅介绍了如何计算顶点坐标,其实前文讲的所有计算方法都是在vertexshader中发生的,而且只能在shader中计算,因为顶点最终屏幕上显示的都是和镜头有关的,上面只是简单的用一个2维的模拟来进行,如果在js端计算的话,会非常消耗性能。(那你就不废话了,我们还没想好怎么计算传递给vertexshader的数据),其实也不是废话,因为只有弄清楚shader中最终的vertex是怎么计算的,我们知道如何发送数据是在顶点着色器中组织的。以上面的“多端点”为例,我们可以总结出计算一个顶点需要哪些数据:端点坐标、偏移量、前一个端点坐标、后一个端点坐标。因此,在Shader中需要定义四个属性变量position、offset、positionPrev、positionNext,分别用来接收端点坐标、offset、上一个端点坐标、下一个端点坐标。对于前面两个顶点,端点没有前面的端点。这时候前面的端点取端点坐标,然后在shader中判断,如果前面的端点坐标==端点坐标,说明是第一个端点;使用两个端点案例计算。它低于最后两个顶点,并且它的端点没有后一个端点。此时后一个端点取端点坐标,然后在shader中判断,如果后一个端点坐标==端点坐标,说明是最后一个端点;为每个端点使用两个计算值。对于中间的顶点,不仅有端点的坐标,还有上一个端点的坐标,还有下一个端点的坐标,都是用前面的多个端点计算出来的。还是以前面三个端点为例,三个端点(50,0,0),(0,0,0),(0,50,0),线宽为2(注意是已经是三维坐标,前面的模拟情况是用屏幕上的二维坐标来模拟顶点在shader中通过透视变换变成二维坐标的情况)那么第一个顶点的四个变量的数据分别是:endpointcoordinates,Offset,Coordinatesofthepreviousendpoint,Coordinatesofthenextendpoint(50,0,0),2/2,(50,0,0)(0,0,0)四个变量的数据第二个顶点分别是:端点坐标,偏移量,前一个端点坐标,下一个端点坐标(50,0,0),-2/2,(50,0,0)(0,0,0)第三个顶点的数据四个变量分别是:endpointcoordinates,offset,previousendpointcoordinates,nextendpointcoordinates(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机房项目:HTML5,不仅好看(下篇:打造最美3D机房)
