WebGL-學習筆記(四)

WebGL學習筆記(四).png

最近學習構(gòu)建三維圖形的時候盾剩,深感幾何功底不夠蚁廓,一個視圖變化矩陣看了幾天也沒想過來访圃,只勉強理解原理,細節(jié)部分自己還需要加強學習

1. 視圖變換

在二維圖形繪制的時候相嵌,不用考慮z軸腿时,但是繪制的三維圖形處于一個立體空間,就要使用z軸了饭宾。由于存在z軸批糟,那么在物體觀察的時候首先就是要確定觀察坐標系

1.1 視點,觀察點看铆,正方向

觀察坐標系可以通過定義:視點徽鼎,觀察點,正方向確定

視點:是眼睛所在的位置弹惦,可以認為是觀察坐標系的原點
觀察點:是觀察對象所在位置否淤,雖然觀察對象是一個三維物體,但是物體都是有點組成棠隐,那么可以確認任何一個點作為觀察點
視線:視點和觀察點之間的連線
正方向:我的理解是觀察坐標系下y軸的正方向石抡,也就是當人觀察事物的時候,頭頂?shù)某?/p>

當確定好以上三個要素以后助泽,就可以確定一個觀察的坐標系了啰扛,為什么?
目前使用坐標系都是直角坐標系嗡贺,也就是在y軸和視線方向確定好以后隐解,y軸和視線組成了一個平面,那么x軸需要和這個平面垂直诫睬,并且經(jīng)過視點(因為默認視點為原點)厢漩,之后z軸需要和y軸還有x軸垂直,所以也就確定下來了岩臣。
在使用WebGL繪制點的時候都是使用的WebGL的坐標系溜嗜,但是現(xiàn)在的視點并不一定是WebGL坐標系中的原點,所以這個時候架谎,需要把視點轉(zhuǎn)換為WebGL坐標系中的原點炸宵,才能正確的利用WebGL繪制圖形,將視點轉(zhuǎn)換為WebGL原點的且y軸正方向和WebGL的y軸重合的變換矩陣就是視圖矩陣谷扣。
連續(xù)看了幾天也沒有特別明白這個轉(zhuǎn)換矩陣是如何運作的土全,所以這里先暫時貼出公式(代碼摘抄自WebGL編程指南)

Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX,     centerY, centerZ, upX, upY, upZ) {
  var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
  fx = centerX - eyeX;
  fy = centerY - eyeY;
  fz = centerZ - eyeZ;
  // Normalize f.
  rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz);
  fx *= rlf;
  fy *= rlf;
  fz *= rlf;
  // Calculate cross product of f and up.
  sx = fy * upZ - fz * upY;
  sy = fz * upX - fx * upZ;
  sz = fx * upY - fy * upX;
  // Normalize s.
  rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz);
  sx *= rls;
  sy *= rls;
  sz *= rls;
  // Calculate cross product of s and f.
  ux = sy * fz - sz * fy;
  uy = sz * fx - sx * fz;
  uz = sx * fy - sy * fx;
  // Set to this.
  e = this.elements;
  e[0] = sx;
  e[1] = ux;
  e[2] = -fx;
  e[3] = 0;
  e[4] = sy;
  e[5] = uy;
  e[6] = -fy;
  e[7] = 0;
  e[8] = sz;
  e[9] = uz;
  e[10] = -fz;
  e[11] = 0;
  e[12] = 0;
  e[13] = 0;
  e[14] = 0;
  e[15] = 1;
  // Translate.
  return this.translate(-eyeX, -eyeY, -eyeZ);
};
Matrix4.prototype.translate = function(x, y, z) {
  var e = this.elements;
  e[12] += e[0] * x + e[4] * y + e[8]  * z;
  e[13] += e[1] * x + e[5] * y + e[9]  * z;
  e[14] += e[2] * x + e[6] * y + e[10] * z;
  e[15] += e[3] * x + e[7] * y + e[11] * z;
  return this;
};

PS:公式主要是將點進行了反向的旋轉(zhuǎn)和平移變換

2. 可視范圍

我們觀察時候的坐標系是我們自定定義的尺度,而WebGL坐標系中坐標范圍是(-1.0,1.0)会涎,一旦超出的坐標就不會繪制了裹匙,從而導致圖形缺失。如果要讓視野所見的所有內(nèi)容都包含在坐標系中末秃,就可能需要對坐標進行縮放和平移概页,來改變可視范圍,常用的方式有兩種

2.1 正射投影

長方體的可視范圍练慕,是一種盒狀空間惰匙,用于建筑平面設(shè)計。利用近裁剪面和遠裁剪面來確定可視區(qū)域铃将,因此使用六個參數(shù)可以確定正射投影:left项鬼,right,top劲阎,bottom绘盟,near,far悯仙,具體正射投影矩陣構(gòu)建公式如下:

Matrix4.prototype.setOrtho = function(left, right, bottom, top, near, far) {
  var e, rw, rh, rd;
  if (left === right || bottom === top || near === far) {
    throw 'null frustum';
  }
  rw = 1 / (right - left);
  rh = 1 / (top - bottom);
  rd = 1 / (far - near);
  e = this.elements;
  e[0]  = 2 * rw;
  e[1]  = 0;
  e[2]  = 0;
  e[3]  = 0;
  e[4]  = 0;
  e[5]  = 2 * rh;
  e[6]  = 0;
  e[7]  = 0;
  e[8]  = 0;
  e[9]  = 0;
  e[10] = -2 * rd;
  e[11] = 0;
  e[12] = -(right + left) * rw;
  e[13] = -(top + bottom) * rh;
  e[14] = -(far + near) * rd;
  e[15] = 1;
  return this;
};

2.2 透視矩陣

透視矩陣是四棱錐的可視空間龄毡,一般用于游戲設(shè)計,符合現(xiàn)實場景(近大遠小的效果)雁比。也存在近裁剪面和遠裁剪面稚虎,需要4個參數(shù)來確定相關(guān)變換矩陣:fovy(可視空間頂面和底面的夾角),aspect(裁剪面高寬比),near偎捎,far

Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) {
  var e, rd, s, ct;
  if (near === far || aspect === 0) {
    throw 'null frustum';
  }
  if (near <= 0) {
    throw 'near <= 0';
  }
  if (far <= 0) {
    throw 'far <= 0';
  }
  fovy = Math.PI * fovy / 180 / 2;
  s = Math.sin(fovy);
  if (s === 0) {
    throw 'null frustum';
  }
  rd = 1 / (far - near);
  ct = Math.cos(fovy) / s;
  e = this.elements;
  e[0]  = ct / aspect;
  e[1]  = 0;
  e[2]  = 0;
  e[3]  = 0;
  e[4]  = 0;
  e[5]  = ct;
  e[6]  = 0;
  e[7]  = 0;
  e[8]  = 0;
  e[9]  = 0;
  e[10] = -(far + near) * rd;
  e[11] = -1;
  e[12] = 0;
  e[13] = 0;
  e[14] = -2 * near * far * rd;
  e[15] = 0;
  return this;
};

3. 深度處理

3.1 隱藏面消除

WebGL默認深度的并不會對深度進行處理蠢终,會按照我們對點/面的繪制順序進行繪制,也就最后繪制的內(nèi)容會在最前面茴她,違背了本來的意圖寻拂,不過WebGL提供了對應的方法來處理深度關(guān)系:

首先,利用gl.enable(gl.DEPTH_TEST)開啟隱藏面消除功能
然后丈牢,利用深度清理gl.clear(gl.DEPTH_BUFFER_BIT)祭钉,可以讓WebGL自己處理好深度關(guān)系

3.2 深度沖突

由于存在兩個平面處在同一個深度的情況,這個時候WebGL繪制會出現(xiàn)深度沖突己沛,表現(xiàn)為圖形繪制結(jié)果看上去表面斑駁慌核,于是WebGL提供了多邊形偏移的功能距境,讓即使深度一致的兩個表面也會發(fā)生一定深度的偏移

首先,利用gl.enable(gl.POLYGON_OFFSET_FILL)開啟多邊形偏移功能
然后垮卓,利用gl.polygonOffset(1.0, 1.0)來指定計算偏移量的參數(shù)

4. 繪制立方體

要繪制立方體垫桂,仍然可以使用gl.drawArrays(),利用緩沖區(qū)數(shù)據(jù)來繪制表面粟按,除此之外诬滩,為了高效利用圖形中的坐標信息,可以使用gl.drawElements()配合將序列放到gl.ELEMENT_BUFFER_ARRAY來繪制表面灭将。
具體做法如下:

function initPoint(gl, a_Position, a_Color) {
    let pointData = new Float32Array([
        0.0, 0.5, 0.0, 1.0, 0.0, 0.0,
        -0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
        0.5, -0.5, 0.5, 1.0, 0.0, 0.0,

        0.0, 0.5, 0.0, 0.0, 1.0, 0.0,
        0.5, -0.5, 0.5, 0.0, 1.0, 0.0,
        0.0, -0.5, -0.5, 0.0, 1.0, 0.0,

        0.0, 0.5, 0.0, 0.0, 0.0, 1.0,
        0.0, -0.5, -0.5, 0.0, 0.0, 1.0,
        -0.5, -0.5, 0.5 ,0.0, 0.0, 1.0,

        -0.5, -0.5, 0.5, 1.0, 0.0, 1.0,
        0.0, -0.5, -0.5, 1.0, 0.0, 1.0,
        0.5, -0.5, 0.5, 1.0, 0.0, 1.0
    ]);
    // 利用的坐標序列
    let indexData = new Uint8Array([
        0, 1, 2,
        3, 4, 5,
        6, 7, 8,
        9, 10, 11
    ])
    let FSIZE = pointData.BYTES_PER_ELEMENT;
    let buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, pointData, gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 6*FSIZE, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6*FSIZE, 3*FSIZE);
    gl.enableVertexAttribArray(a_Color);
    // 將坐標序列信息存儲到ELEMENT_ARRAY_BUFFER
    let indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
}

5. 總結(jié)

構(gòu)建三維圖形相比二維圖形來說疼鸟,需要使用視圖轉(zhuǎn)換和可視范圍的矩陣信息,因此坐標信息就變?yōu)榱?gl_Position = 可視矩陣 X 視圖矩陣 X 變換矩陣 X 原始坐標庙曙,同時要利用深度規(guī)則消除深度影響空镜,最后可以利用gl.drawElements(),定義序列矾利,復用坐標信息

6. 參考

《WebGL編程指南》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姑裂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子男旗,更是在濱河造成了極大的恐慌舶斧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察皇,死亡現(xiàn)場離奇詭異茴厉,居然都是意外死亡,警方通過查閱死者的電腦和手機什荣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門矾缓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稻爬,你說我怎么就攤上這事嗜闻。” “怎么了桅锄?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵琉雳,是天一觀的道長。 經(jīng)常有香客問我友瘤,道長翠肘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任辫秧,我火速辦了婚禮束倍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己绪妹,他們只是感情好甥桂,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著邮旷,像睡著了一般格嘁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上廊移,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音探入,去河邊找鬼狡孔。 笑死,一個胖子當著我的面吹牛蜂嗽,可吹牛的內(nèi)容都是我干的苗膝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼植旧,長吁一口氣:“原來是場噩夢啊……” “哼辱揭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起病附,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤问窃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后完沪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體域庇,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年覆积,在試婚紗的時候發(fā)現(xiàn)自己被綠了听皿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宽档,死狀恐怖尉姨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吗冤,我是刑警寧澤又厉,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站欣孤,受9級特大地震影響馋没,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜降传,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一篷朵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦声旺、人聲如沸笔链。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鉴扫。三九已至,卻和暖如春澈缺,著一層夾襖步出監(jiān)牢的瞬間坪创,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工姐赡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莱预,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓项滑,卻偏偏與公主長得像依沮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枪狂,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容