Tells the story of a paper on analog lines by plane, each vertex of the data including: endpoint coordinates, offset, an endpoint coordinates before and after an endpoint coordinates, of course, if we to paint by means of index, also including the index array, the following code by passing the endpoint of a set of line array to create the relevant data:

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;
};Copy the code

The code begins by defining a class whose constructor can be passed in an array of endpoints. A method, computeData, is defined on this class to compute an array of vertices, each containing the four pieces of information described above, plus a color information. Readers can combine the ideas in the second article with the above code to understand, the code details are not detailed here.

Another important code is the vertex shader, through the passing of these vertex information to calculate the final vertex coordinates, the code is as follows:

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;
    }
`;Copy the code

In order to calculate the final position of vertices on the screen, you need to pass the Size of Canvans to the uniform variable uViewport. In order to calculate clipping, The uniform variable uNear needs to pass the near value of the lens to the shader. The variable uWorldViewProjection represents the matrix of model view perspective transformation.

If you are interested in WebGL, you can take a look at the 3D room project we developed with WebGL: HTML5, which is not only beautiful to look at.