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机房项目:
HTML5,不只是看上去很美(第二弹:打造最美3D机房)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容