“可以画个世界地图的边框么”——设计
“可以,没问题”——开发
“你这边框能再粗点么,都看不清”——设计
“……”——开发
在传统的canvas绘图技术中,画一条有粗细的线,并非难事,但是对于WebGL来说,这简直难死宝宝了。
WebGL的绘制支持三种模式,分别是点、线、面。然而,在绘制线条的时候,在某些浏览器中(比如chrome)线的宽度只能设置为1,在http://alteredqualia.com/tmp/webgl-linewidth-test/这个地址中,可以测试你当前的浏览器是否支持线的粗细。
这是chrome一直以来的bug,从2010年被提起,至今,没!有!被!解!决!在chrome的bug issue中(https://bugs.chromium.org/p/chromium/issues/detail?id=60124),nicolasc*的一句“别再想粗线了,数学意义上根本没有这玩意”,终结了所有人的幻想。同时提出应该用Polygon来实现粗线。
既然Chrome自己也不想解决这个问题,我们也说服不了自己的产品和设计,那么,我们就只能自己想办法通过用面的方式,来模拟一根线。
那么如何通过面来模拟一根线条呢?
以一条线段为例,描述一条线段只需要两2个端点,如果用面来描述,则需要4个点;绘制2条线段,需要6个点,以此类推,每多画一条线段,就要多2个点。因此,如果想用面去表示一条n个端点的线,就需要2n个端点去描述它。
那么如何确定面上点的位置呢?
以上图为例,我们取每个线段端点的法线方向为a1方向,取法线负方向为a2方向,分别乘以宽度的一半,就有了a1和a2的位置,b点同理。
如何得到a和b的法线方向呢?
以下图为例,用last、current、next三点,算出current点的切线方向,切线方向乘以复数i,逆时针旋转90就为法线方向,乘以复数-i,顺时针旋转90度为负法线方向,则有了Normal和-Normal。
vec2 dir2=normalize(currentV2-prevV2);
vec2 dir=normalize(dir1+dir2);
vec2 normal=vec2(-dir.y,dir.x);
现在有了面线,但这只是一个二维方法,但是转个角度看,就又变成了一根线。
我们想要的是无论相机怎么看,线都是一样粗的,就好像面是一个三维的面一样,那怎么办呢?我们将这一步计算留在着色器里,对乘完投影矩阵后的二维坐标进行线的“拉宽”计算。
mat4 pvm=projectionMatrix*modelViewMatrix;
vec4 currentV4=pvm*vec4(position,1.0);
vec4 prevV4=pvm*vec4(prevPositions,1.0);
vec4 nextV4=pvm*vec4(nextPositions,1.0);
vec2 currentV2=currentV4.xy/currentV4.w;
currentV2.x*=aspect;
vec2 prevV2=prevV4.xy/prevV4.w;
prevV2.x*=aspect;
vec2 nextV2=nextV4.xy/nextV4.w;
nextV2.x*=aspect;
这样就得到一个可以任意旋转看,都是有粗度的“面线”了。
https://codepen.io/mysisi/pen/WJerrB?editors=1010
就这样解决了问题。。。
参考:
[1] https://www.cnblogs.com/twaver/p/7228687.html
[2] https://blog.mapbox.com/drawing-antialiased-lines-with-opengl-8766f34192dc
[3] http://codeflow.org/entries/2012/aug/05/webgl-rendering-of-solid-trails/
好啦,今天的分享就到这里啦~
也欢迎留言与我们讨论互动喔!
◆◆◆
请大家持续关注我们的公众号
我们会不断地分享更多有趣的干货~
笔芯~
领取专属 10元无门槛券
私享最新 技术干货