摘要:那是因为,线条的光栅化过程和多边形的光栅化过程并不是完全一致的。这将会导致一些本该被隐藏的线段,未被遮挡。原理我们知道,一般对象都是由三角形组成的。其中涉及到和的相关介绍,笔者将会在后续的文章中介绍。
如果要把一个对象的线框绘制出来,一般的方法是先绘制实体对象,然后通过gl.LINES的模式再绘制一遍模型,此时模型的线框就会被绘制出来。
gl.LINES的问题此方法需要绘制两遍对象,因此会造成性能的损失。
使用此种方式绘制线框的时候,深度值偏移是必要的。那是因为,线条的光栅化过程和多边形的光栅化过程并不是完全一致的。这就导致绘制一个多边形的边和绘制多边形本身,相同位置的片元,其深度值可能并不一样。
线段和多边形的光栅化不完全一致,为了避免z-fighting,也需要一个深度偏移。
但是,添加一个偏移并不能完美的解决问题。 这将会导致一些本该被隐藏的线段,未被遮挡。
使用gl.LINES的另外一个问题是,在WebGL中,存在一个bug,就是线宽只能设置为1。请参考以下文章:
https://www.jianshu.com/p/cee... 绘制Line的bug(一)
因此本文将会介绍一种方法,可以在一个pass内绘制对象及其线框。
原理我们知道,一般对象都是由三角形组成的。而要显示的线框,正好是三角形的边,如果在绘制的时候,给三角形的边一个不同的颜色,便可以实现在对象上面绘制线框的效果。
那么现在的问题是,如何确定三角形的边呢?
要确定三角形的变,可以使用重心坐标系。有关重心坐标的的说明,可以参考:
https://zh.wikipedia.org/wiki...
对于三角形而言,重心坐标可以这样定义:
三角形所在平面上的任意一点P(笛卡尔坐标),可以通过三角形的三个顶点A、B、C(笛卡尔坐标)来表示:
P = Ax + By + C * Z,其中(x、y、z)便是重心坐标。由此可以看出P点其实是A、B、C点加权之和。
如下图所示,A点的重心坐标是(1,0,0),B点的重心坐标是(0,1,0),C点的重心坐标是(0,0,1)
由上面的讲解 和图片展示可以得知,重心坐标(x,y,z)中任何一个值为0的点,都在三角形的边上。不过在实际的图形渲染中,边的宽度不可能是0,而应该是一个大于0的值,所以一般可以指定一个要绘制的线宽width,如果任何一个点的重心坐标(x,y,z)中的人一个分量的值小于这个线宽width,可以认为在边上。
代码实现基于上面说的原理,首先需要定义顶点的重心坐标,对于一个三角形来说,可以把三个顶点的中心坐标分别指定为(1,0,0)、(0,1,0)、(0,1,0)即可。而对于一个四边形,有四个顶点 0,1,2,3,而绘制的时候使用索引 0,1,2, 2,1,3来绘制,此时可以把重心坐标定义如下:
var barycentric = [ 1,0,0, 0,1,0, 0,0, 1, 1,0,0, ];
然后,在着顶点色器中定义对应的attribute变量,由于重心坐标最终需要传递到片元着色器中,所以还需要对应的varying变量:
attribute vec3 aBarycentric; ... varying vec3 vBarycentric; void main(){ ... vBarycentric = aBarycentric; }
然后,在片元着色器中判断,如果vBarycentric的任意一个分量的值小于指定的宽度值,则颜色为指定的线框颜色:
if(any(lessThan(vBarycentric, vec3(0.02)))){ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } else{ gl_FragColor = color; }
通过上面代码,最终绘制的立方体效果如下:
从上面的立方体绘制的效果图可以看出,线框的锯齿很严重,而且线的宽度不是一致的。这是因为,之前的判断是基于三角形表面的,通过光栅化之后,由于线条的角度等原因,最终在屏幕上面的宽度就变得不一致了。
使用fwidth方法要线宽的判断基于屏幕,需要使用到一个方法fwidth。该方法需要WebGL 引入一个扩展之后才能使用。该扩展是:OES_standard_derivatives。
首先使用WebGL的getExtension方法获取该扩展,代码如下:
gl.getExtension("OES_standard_derivatives");
然后在shader中启用这个扩展,代码如下:
#extension GL_OES_standard_derivatives : enable
然后通过fwidth函数,把vBarycentric的值缩放到 vBarycentric在屏幕变化的范围之内,代码如下:
vec3 d = fwidth(vBarycentric); vec3 a3 = smoothstep(vec3(0.0), d*2.0, vBarycentric);
fwidth表示一个变量在屏幕空间的x轴变化dFdx + y轴变化dFdx之和。 其中涉及到dFdx、dFdy和fwidth的相关介绍,笔者将会在后续的文章中介绍。
在获取了基于屏幕像素空间的的重心坐标a3之后,变可以通过通过该变量来进行判断,并绘制出指定宽度的线框:
gl_FragColor.rgb = mix(vec3(0.0,0.0,0.0), vec3(1.0), min(min(a3.x, a3.y), a3.z));
通过改良之后的绘制效果如下,可以看出明细效果改进了很多:
前面我们看到的都是三角形的线框,有的时候,我们希望获取四边形的线框,应该怎么处理呢? 其实此时,只需要调整下顶点的重心坐标即可,在前文中,一个四边形的四个顶点的重心坐标如下:
var barycentric = [ 1,0,0, 0,1,0, 0,0, 1, 1,0,0, ];
如果把四边形的四个顶点的重心坐标调整如下:
var barycentric = [ 1,0,0, 1,1,0, 0,0, 1, 0,0,0, //前 ];
便可以达到效果,最终绘制的效果如下图所示:
英伟达也提出过绘制线框的解决方案,参考下面链接:
https://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf
不过该方案是基于Geometry Shader技术来实现的,而该技术在WebGL并未实现,所以该方案不能很好的移植到到WebGL。
如果获取完整源代码,请关注公众号。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97119.html
摘要:偏导数函数可以用于片元着色器中的任何变量。偏导数和用于计算纹理的一些列的子图,每个子图都比前一个的尺寸缩小了倍。在纹理取样过程中使用偏导数来选择最佳的级数。 本文适合对webgl、计算机图形学、前端可视化感兴趣的读者。 偏导数函数(HLSL中的ddx和ddy,GLSL中的dFdx和dFdy)是片元着色器中的一个用于计算任何变量基于屏幕空间坐标的变化率的指令(函数)。在WebGL中,使用...
摘要:一般说来,对于制图建模软通常使正交投影,这样不会因为投影而改变物体比例而对于其他大多数应用,通常使用透视投影,因为这更接近人眼的观察效果。 showImg(https://segmentfault.com/img/remote/1460000012581680?w=1920&h=1080); 1. 概述 1.1 什么是WebGL? WebGL是在浏览器中实现三维效果的一套规范 想要使用...
摘要:导言立体视觉技术前几年研究极为广泛,研究生期间也以此为课题,主要学习并研究特征提取和图像匹配方向目前火遍全球的虚拟现实技术,也属于立体视觉领域的研究范畴。 导言 立体视觉技术前几年研究极为广泛,研究生期间也以此为课题,主要学习并研究特征提取和图像匹配方向;目前火遍全球的虚拟现实技术,也属于立体视觉领域的研究范畴。 立体视觉技术可研究内容很多,实现一整套立体视觉呈现步骤如下: 摄像机标...
阅读 2708·2021-11-22 15:22
阅读 1608·2021-11-22 14:56
阅读 3593·2021-09-22 15:12
阅读 2383·2021-09-02 15:41
阅读 2102·2021-08-27 16:26
阅读 1089·2019-08-30 15:55
阅读 2116·2019-08-29 17:30
阅读 629·2019-08-29 16:26