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的同學一定清楚。

請關注公衆號。ITman彪叔公衆號
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章