WebGL绘制有端头的线

  关于WebGL绘制线原理不明白的小伙伴,可以看看我之前的文章WebGL绘制有宽度的线。这一篇我们主要来介绍端头的绘制,先看效果图。

  端头一般被称为lineCap,主要有以下三种形式:

  butt最简单等于没有端头,square一般是多出lineWidth/2的长度,round是一个以lineWidth/2为半径的圆。一般情况下绘制lineCap的思路都是添加额外的三角形,如一些开元库或者mapbox的方法,一般来说mapbox的方法已经可以了,但是我还是感觉顶点太多,甚至对square的情况都不愿意在增加两个顶点。

  方法总是自己去探索的,在绘制宽度线中,我已经总结了自己的一套理论,将线距离映射成uv坐标的思路来绘制一些线的效果,那么在这里仍然沿着这种思路去思考。

  对于square来说只要在绘制顶点的时候,在线的开头和结尾分别做一定的偏移即可。对于round我们可以在suqare的基础上,在片元着色器中做一些判断,距离中心点距离大于lineWidth/2的像素全部抛弃掉。

  

  所以对于线起点和终点处顶点的位置在上一篇文章的基础上,做了沿着线方向的偏移

 

  这样的话我们就能绘制出square类型的lineCap。但是对于round仅仅这样做还不行,还需要在片元着色器中根据中心点做一次剔除。那么问题来了,经过偏移后如何知道圆心点的位置,在来根据像素距离来进行剔除。这时候就轮到上篇文章利用纹理坐标来表示线长度的思路了,上一篇中我们把路线长度映射成从0-1的uv坐标,那么对于端头来说,我们可以把超出的那一半线宽的像素映射成纹理坐标,可以想象这部分长度对于起点来说等于负的线长度,对应的纹理坐标就是负的纹理坐标;对于终点来说多出的这部分像素等于增加了的线长度,那么对应纹理坐标就是超过1的部分。那么接下来的问题就是如何把线上的像素长度对应于线的长度等于把像素与3d世界中的单位进行映射。

  如果对投影矩阵不是很了解的同学,最好看看我的这篇文章webgl开发第一道坎——矩阵与坐标变换,这里我们需要用到投影矩阵的中的元素

  resolution.x代表canvas显示元素的宽度,这里恐怕有些地方不太好理解。n其实是代表相机的近平面,我们先假设为1;那么pixelWidthRatio表示像素与3d单位的一个比值的一半。后面我们先暂时也假设finalPosition.w的值也为一,那么最终vPixelWidth的值表示每像素代表多少3d单位。但是由于视锥体的投影变换并不是线性的,所以这样得到的vPixelWidth并不适用视锥体中的所有地方。这时候我们回来看pixelWidthRatio有一个分母n代表近平面,finalPosition.w代表投影后的点距离相机坐标中的z值距离。w/n这里是想用线性来补充一部分非线性变换带来的影响,让vPixelWidth表示每像素代表多少3d单位这个结果尽量的准确。实际效果也确实可以达到预期。

 

  现在我们可以将端头多出的一半线宽的像素距离转化成线的距离,同时在转化成纹理单位。

 

  接下来我们可以在片元着色器中进行剔除工作。这里我们需要做几步工作:

  1. 纹理坐标转换成线的距离长度
  2. 线的距离长度转换成像素单位
  3. 对大于lineWidth/2长度的像素进行剔除
 

  这里我们需要用到varying变量对vPixelWidth进行差值。最终我们绘制出round的lineCap效果。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebGL 绘制宽度的曲线可以通过使用顶点着色器和片段着色器来实现。具体步骤如下: 1. 定义曲线的顶点:可以使用一个数组来存储曲线的顶点坐标和宽度,例如对于一个二次贝塞尔曲线,可以按照步长(例如 0.01)来计算曲线上的点,并计算出每个点的法向量,然后将顶点坐标和法向量乘以宽度得到具体的顶点坐标。 2. 创建顶点着色器:顶点着色器的作用是将顶点的坐标和法向量传递给片段着色器,并计算出对应的屏幕坐标和法向量。在传递顶点数据时,需要使用 attribute 变量来声明变量类型和名称,例如: ``` attribute vec3 aVertexPosition; attribute vec3 aNormal; ``` 在顶点着色器中,需要使用 varying 变量来声明传递到片段着色器的变量类型和名称,例如: ``` varying vec3 vPosition; varying vec3 vNormal; ``` 然后在顶点着色器中,可以使用 gl_Position 变量来计算出屏幕坐标,例如: ``` void main() { vPosition = aVertexPosition; vNormal = aNormal; gl_Position = projectionMatrix * modelViewMatrix * vec4(aVertexPosition, 1.0); } ``` 3. 创建片段着色器:片段着色器的作用是根据顶点数据计算出每个像素的颜色。在传递顶点数据时,需要使用 varying 变量来声明变量类型和名称,例如: ``` varying vec3 vPosition; varying vec3 vNormal; ``` 然后在片段着色器中,可以使用这些变量来计算出每个像素的颜色,例如: ``` void main() { vec3 color = vec3(1.0, 0.0, 0.0); // 设置曲线的颜色 float width = 0.1; // 设置曲线的宽度 vec3 normal = normalize(vNormal); vec2 offset = vec2(normal.y, -normal.x) * width; // 计算法向量的偏移量 vec2 position = (gl_FragCoord.xy + offset) / resolution; // 计算屏幕坐标 gl_FragColor = vec4(color, 1.0); // 设置像素的颜色 } ``` 4. 绘制线:在绘制线前,需要启用深度测试和混合功能,可以使用以下代码: ``` gl.enable(gl.DEPTH_TEST); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); ``` 然后使用 gl.drawArrays 函数来绘制线,例如: ``` gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount); ``` 其中,vertexCount 表示顶点数量。 需要注意的是,绘制宽度的曲线需要使用深度测试和混合来实现遮挡关系和半透明效果,可以使用 gl.depthFunc 和 gl.blendFunc 函数来设置相应的参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值