WebGL 单通道wireframe渲染

简介: WebGL 单通道wireframe渲染

如果要把一个对象的线框绘制出来,一般的方法是先绘制实体对象,然后通过gl.LINES的模式再绘制一遍模型,此时模型的线框就会被绘制出来。


gl.LINES的问题


  1. 此方法需要绘制两遍对象,因此会造成性能的损失。


  1. 使用此种方式绘制线框的时候,深度值偏移是必要的。那是因为,线条的光栅化过程和多边形的光栅化过程并不是完全一致的。这就导致绘制一个多边形的边和绘制多边形本身,相同位置的片元,其深度值可能并不一样。


线段和多边形的光栅化不完全一致,为了避免z-fighting,也需要一个深度偏移。

但是,添加一个偏移并不能完美的解决问题。 这将会导致一些本该被隐藏的线段,未被遮挡。


  1. 使用gl.LINES的另外一个问题是,在WebGL中,存在一个bug,就是线宽只能设置为1。请参考以下文章:


https://www.jianshu.com/p/cee4ce496e78WebGL 绘制Line的bug(一)

因此本文将会介绍一种方法,可以在一个pass内绘制对象及其线框。


原理


我们知道,一般对象都是由三角形组成的。而要显示的线框,正好是三角形的边,如果在绘制的时候,给三角形的边一个不同的颜色,便可以实现在对象上面绘制线框的效果。

那么现在的问题是,如何确定三角形的边呢?


重心坐标系


要确定三角形的变,可以使用重心坐标系。有关重心坐标的的说明,可以参考:


https://zh.wikipedia.org/wiki/%E9%87%8D%E5%BF%83%E5%9D%90%E6%A0%87


对于三角形而言,重心坐标可以这样定义:


三角形所在平面上的任意一点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://github.com/netcy/solidwireframe

相关文章
|
6月前
three.js的3D模型渲染主要构成
three.js的3D模型渲染主要构成
105 0
|
存储 前端开发 JavaScript
three.js 纹理
纹理是添加到材料中的图像或颜色,以提供更多细节或美感。纹理是Three.js中必不可少的主题。在本节中,我们将了解如何将基本纹理应用于我们的材质。
104 0
|
前端开发
Threejs - 加载视频纹理渲染 实现一个3D视频播放器
Threejs - 加载视频纹理渲染 实现一个3D视频播放器
2122 0
Threejs - 加载视频纹理渲染 实现一个3D视频播放器
|
存储 编解码 缓存
webgl系列之抗锯齿和深度缓存
前言 大家好我是Fly 哥, 这是今年webgl 系列的第三篇文章, 如果你之前的两篇文章没看的话,建议先看一下,然后再来看这一篇文章 Webgl 系列之buffer的使用 webgl系列之对光栅化的理解 上一篇文章,任何虚拟3维世界的转换到二维屏幕中通过「采样」 也就判断屏幕上的每个像素中心点是不是在三角形内部的得到了 下面这幅图: 图片 走样之前 这时候有同学问, 这不像三角形哇, 这个其实用个专业的词—— 「锯齿」 , 我的理解 一个三角形经过光栅化后, 得到屏幕上每一个像素点 组成的像素点的集合。那到底是经过什么样的处理得到下面这张图: 图片 final 反走样 其实出现上面
webgl系列之抗锯齿和深度缓存
|
3月前
|
存储 缓存 数据可视化
Cesium渲染一帧中用到的图形技术
Cesium渲染一帧中用到的图形技术
97 0
Cesium渲染一帧中用到的图形技术
|
3月前
|
缓存 图形学
Unity3D学习笔记12——渲染纹理
Unity3D学习笔记12——渲染纹理
39 2
|
6月前
|
前端开发
canvas详解06-合成
canvas详解06-合成
62 2
【Redshift渲染器渲染出图片有色差(红移渲染器)】
【Redshift渲染器渲染出图片有色差(红移渲染器)】
197 0
【Redshift渲染器渲染出图片有色差(红移渲染器)】
|
前端开发
WebGL光照、贴图和帧缓冲
WebGL光照、贴图和帧缓冲
|
算法 定位技术
WebGL开发:加载图片配准
WebGL开发:加载图片配准
177 0