WebGL 繪製Line的bug(三)
來自專欄計算機圖形學 前端可視化 WebGL
上一篇已經講述了通過面模擬線條時候,每一個頂點的頂點數據包括:端點坐標、偏移量、前一個端點坐標、後一個端點坐標,當然如果我們通過索引的方式來繪製的話,還包括索引數組,下面的代碼通過傳遞一組線條的端點數組來創建上述相關數據:
```
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彪叔。
ITman彪叔公眾號推薦閱讀:
※Mantis 搭建及自定義心得
※解Bug之路-Druid的Bug
※《羅伯特議事規則》也有bug?
※Bug追蹤管理簡史
※Weekly Beta Apps 第5期