星期二, 10月 31, 2017

電腦圖學之透視投影

畫家在繪畫時會使用透視 (Perspective) 的方法,將三維空間的形象表現在二維平面的畫布上,此法是設定畫面上有一條位在無限遠處的視平線,我們的視線焦點就落在視平線上某處,互相平行的線條也會靠攏交集在視平線上的某一點,如此形成近大遠小的視覺現象,讓我們在觀賞時會覺得平面畫面裡的景物之間拉出距離而變得立體了。
運用透視法的《最後的晚餐》




工程圖學有投影作圖方法,是假想有一透明平面 (投影面) 置於物體與觀察者之間或在物體後方,以投影方法將物體輪廓頂點投射至投影面上描繪成圖。若觀察者位於有限距離內觀看投影也會形成透視效果,此即所謂透視投影 (Perspective Projection)。

曾在過往的 PC/DOS 時代寫過兩支 C 程式,嘗試以透視投影的基礎在 2D 畫面上描繪簡單的 3D 立方體線框,並沿三軸做旋轉。今日已有許多極其方便的程式庫可供電腦程式作 3D 繪圖用,不似以往得多了解一些像三角幾何的數學知識才能解題。此次重施故技用 HTML5 與 Javascript 在瀏覽器上重溫這些基礎概念,但不再畫線框而是填色立方體的六個面。

幾個運用到數學基礎,如以下:
一、等長的線段在透視法裡表現出近大遠小的效果,是因為「平行線截比例線段性質」,如圖:
假設 3D 空間座標原點 O 位於視線正前方,且 Z 軸朝向視點與視線重疊,投影面位於視點與點 P(x,y,z) 之間且與 Z 軸垂直,則 P 點在投影面上投射點 P' 的計算如以下程式:
function Pt3D(x,y,z){
 this.x = x;
 this.y = y;
 this.z = z;
 this.p = function(ctx){
 // ctx.dz = [投影面至視點距離,O至投影面距離]
 // ctx.o = 3D座標原點在電腦螢幕的座標
  var _x = this.x * (ctx.dz[1]/(ctx.dz[0]+ctx.dz[1]+this.z)) + ctx.o[0];
  var _y = this.y * (ctx.dz[1]/(ctx.dz[0]+ctx.dz[1]+this.z)) + ctx.o[1];
  return [_x,_y];
 };
}


二、計算線框之 3D 座標繞三個軸旋轉後的新座標,使用了「三角函數和差角公式」:
sin(α + β) = sin(α)cos(β) + cos(α)sin(β)
sin(α – β) = sin(α)cos(β) – cos(α)sin(β)
cos(α + β) = cos(α)cos(β) – sin(α)sin(β)
cos(α – β) = cos(α)cos(β) + sin(α)sin(β)
若只繞 Z 軸旋轉,則 z 座標不變而旋轉後的 x, y 座標計算如以下程式:
 function(rad){
  var _x = this.x*Math.cos(rad) - this.y*Math.sin(rad);
  var _y = this.x*Math.sin(rad) + this.y*Math.cos(rad);
  this.x = _x; this.y = _y;
 };


三、繪圖時為避免繪出隱藏面 (視點看不見的面) 要判斷各面的方向,這需要計算各面的「法線向量」。這部分涉及向量的運算有些複雜,可參考這篇平面法線判定法有詳細的說明。
三維平面的法線是垂直於該平面的三維向量

至此,不得不說,別認為程式語言就能解決問題,決戰還得靠數學!


參考資料:

沒有留言:

張貼留言