• WebGL 绘制Line的bug(三)


    之前铺垫了许多,今天可以来分享点纯干货了。

    上一篇已经讲述了通过面模拟线条时候,每一个顶点的顶点数据包括:端点坐标、偏移量、前一个端点坐标、后一个端点坐标,当然如果我们通过索引的方式来绘制的话,还包括索引数组,下面的代码通过传递一组线条的端点数组来创建上述相关数据:

    bk.Line3D = function (points,colors){
         this.points = points;
         this.colors = colors;
    }
    
    bk.Line3D.prototype.computeData = function() {
          var len = this.points.length;
          var count = len * 3 * 2;    
          var position = new Float32Array(count);
          var positionPrev =  new Float32Array(count);
          var positionNext = new Float32Array(count);
          var color = new Float32Array(count);
    
          var offset = new Float32Array(len * 2);
          var indicesCount = 3 * 2 * (len - 1);
          var indices = new Uint16Array(indicesCount);
          var triangleOffset = 0,vertexOffset = 0;
          for(var i = 0; i < len; i ++){
                var i3 = i * 3 * 2;
                var point = this.points[i];
                position[i3 + 0] = point.x;
                position[i3 + 1] = point.y;
                position[i3 + 2] = point.z;
                position[i3 + 3] = point.x;
                position[i3 + 4] = point.y;
                position[i3 + 5] = point.z;
    
                var r = (i + 1) / len;
                var g = Math.random();
                var b = Math.random();
                g = r;
                b = 0;
                r =  1- r;
                color[i3 + 0] = r;
                color[i3 + 1] = g;
                color[i3 + 2] = b;
                color[i3 + 3] = r;
                color[i3 + 4] = g;
                color[i3 + 5] = b;
    
                 if (i < count - 1) {
                        var i3p = i3 + 6;
                        positionNext[i3p + 0] = point.x;
                        positionNext[i3p + 1] = point.y;
                        positionNext[i3p + 2] = point.z;
    
                        positionNext[i3p + 3] = point.x;
                        positionNext[i3p + 4] = point.y;
                        positionNext[i3p + 5] = point.z;
                    }
                 if (i > 0) {
                        var i3n = i3 - 6;
                        positionPrev[i3n + 0] = point.x;
                        positionPrev[i3n + 1] = point.y;
                        positionPrev[i3n + 2] = point.z;
    
                        positionPrev[i3n + 3] = point.x;
                        positionPrev[i3n + 4] = point.y;
                        positionPrev[i3n + 5] = point.z;
                 }
    
                 var idx = 3 * i;
    
                 var i2 = i * 2;
                 offset[i2 + 0]  = 5;
                 offset[i2 + 1]  = -5;
          }
          var end = count - 1;
          for(i = 0;i < 6 ;i ++){
              positionNext[i] = positionNext[i + 6];
              positionPrev[end - i] = positionPrev[end - i - 6];
          }
          for(i = 0;i < indicesCount ;i ++){
              if(i % 2 == 0){
                 indices[triangleOffset ++] = i;
                 indices[triangleOffset ++] = i + 1;
                 indices[triangleOffset ++] = i + 2;
              }else{
                 indices[triangleOffset ++] = i + 1;
                 indices[triangleOffset ++] = i;
                 indices[triangleOffset ++] = i + 2;
              }
          }
    
          this.position  = position;
          this.positionNext  = positionNext;
          this.positionPrev = positionPrev;
          this.color = color;
          this.offset = offset;
          this.indices = indices;
    };

    代码首先定义了一个类,该类构造函数可以传入端点数组;在该类上定义了一个方法 computeData,用来计算顶点数组,每个顶点包括上文所述的4个信息,另外增加了一个颜色信息。
    读者,可以结合第二篇的思路和上面的代码来来理解,此处不再详述 代码的细节。

    另外一个比较重要的代码是顶点着色器中,通过传入的这些顶点信息来计算最终的顶点坐标,代码如下:

    var lineVS = `
        attribute vec3 aPosition;
        attribute vec3 aPositionPre;
        attribute vec3 aPositionNext;
        attribute float aOffset;
        attribute vec3 aColor;
        varying  vec3  vColor;
     
        uniform mat4 uWorldViewProjection;
        uniform vec4 uViewport;
        uniform float uNear;
     
        uniform mat4 uViewMatrix;
          uniform mat4 uProjectMatrix;
     
        vec4 clipNear(vec4 p1,vec4 p2){
            float n = (p1.w - uNear) / (p1.w - p2.w);
            return vec4(mix(p1.xy,p2.xy,n),-uNear,uNear);
        }
     
        void main(){
     
            vec4 prevProj = uWorldViewProjection * vec4(aPositionPre, 1.0);
            vec4 currProj = uWorldViewProjection * vec4(aPosition, 1.0);
                 vec4 nextProj = uWorldViewProjection * vec4(aPositionNext, 1.0);
                 if (currProj.w < 0.0) {
               if (prevProj.w < 0.0) {
                currProj = clipNear(currProj, nextProj);
               }else {
                currProj = clipNear(currProj, prevProj);
               }
            }
            vec2 prevScreen = (prevProj.xy / abs(prevProj.w) + 1.0) * 0.5 * uViewport.zw;
            vec2 currScreen = (currProj.xy / abs(currProj.w) + 1.0) * 0.5 * uViewport.zw;
            vec2 nextScreen = (nextProj.xy / abs(nextProj.w) + 1.0) * 0.5 * uViewport.zw;
            vec2 dir;
            float len = aOffset;
            if(aPosition == aPositionPre){
                dir = normalize(nextScreen - currScreen);
            }else if(aPosition == aPositionNext){
                dir = normalize(currScreen - prevScreen);
            }else {
                vec2 dirA = normalize(currScreen - prevScreen);
                vec2 dirB = normalize(nextScreen - currScreen);
                vec2 tanget = normalize(dirA + dirB);
                float miter = 1.0 / max(dot(tanget,dirA),0.5);
                len *= miter;
                dir = tanget;
            }
            dir = vec2(-dir.y,dir.x) * len;
            currScreen += dir;
            currProj.xy = (currScreen / uViewport.zw - 0.5) * 2.0 * abs(currProj.w);
            vec4 pos = uProjectMatrix * uViewMatrix *  vec4(aPosition,1.0);
            vColor = aColor;
            gl_Position = currProj;
        }
    `; 

    计算的原理,也可以参考第二篇的论述,此处需要注意的是,为了能够计算顶点在屏幕上的最终位置,需要把canvans的尺寸大小传递给着色器(uniform 变量 uViewport),同样为了计算裁剪,需要把镜头的near值传递给着色器(uniform 变量 uNear),而变量uWorldViewProjection表示模型视图透视变换的矩阵,熟悉WebGL的同学一定清楚。

    如果你对WebGL 感兴趣,可以了解下我们用WebGL开发的3D机房项目:

    无插件纯Web 3D机房,HTML5+WebGL倾力打造

    关于这个话题就先分享到这里了,如果大家对其它方面与兴趣的,我们也可以一起来探讨。

  • 相关阅读:
    【springboot】 springboot整合quartz实现定时任务
    Map集合的四种遍历方式
    WCF自引用和循环引用导致的序列化问题
    c#反射
    小助手配置文件列表页
    WPF数据绑定(ItemTemplate和DataTemplate)
    TankMapData
    手机qq协议做的第三方qq软件
    WPF MVVM模式学习
    小助手(应用盒子之我的实现思路及示例程序)
  • 原文地址:https://www.cnblogs.com/twaver/p/7238364.html
Copyright © 2020-2023  润新知