rotate 3d基礎


基礎

  看了岑安大大的教程學習了3d基礎,之前寫了篇總結,覺得寫的太散廢話太多,重寫一篇。

  本文需要實現的效果如下:3d球

  岑安的兩篇教程寫的很棒,但我感覺改變下順序或許會更好理解。

  我們把畫布(此文所講所見所得均基於canvas)的中心當做是一個空間三維系的中心,畫布的x和y軸正方向分別當做三維系的x和y軸的正方向,把垂直畫布向內當做z軸正方向,那么三維系大致如下圖:

  我們假設空間中有個點在圍繞y軸轉動,那么轉動的軌跡就是個圓;如果畫面感強的畫,可以想象出就像地球上的某個點由於地球的自轉做的向心運動。那么問題來了,如果三維系里的某個點圍繞y軸轉動確定的角度后,能計算出轉動后的三維坐標嗎?

  答案是肯定的,空間上的某個點(已知三維坐標)圍繞x軸y軸或者z軸轉動一定角度后,都能計算出相應的三維坐標。

  過程貌似是個線性代數里的矩陣變換,數學渣渣跪...

  直接把它寫在矢量類里了:

// 矢量旋轉
Vector3.prototype.rotateX = function(angleX) {
  var cosx = Math.cos(angleX);
  var sinx = Math.sin(angleX);
  var y1 = this.y * cosx - this.z * sinx;
  var z1 = this.y * sinx + this.z * cosx;
  this.y = y1;
  this.z = z1;
};

Vector3.prototype.rotateY = function(angleY) {
  var cosy = Math.cos(angleY);
  var siny = Math.sin(angleY);
  var x1 = this.z * siny + this.x * cosy;
  var z1 = this.z * cosy - this.x * siny;
  this.x = x1;
  this.z = z1;
};

  更近一步,空間上的某個點圍繞任意軸轉動一定角度,都能計算出轉動后的三維坐標。有興趣的可以參考【自己給自己題目做】:如何在Canvas上實現魔方效果

  那么就好辦了,我們在三維空間初始化一些點(當做球心),隨意賦給他們xyz坐標值,隨意設定旋轉角度,那么旋轉后的xyz值也能很輕松地得到了!當然有了這些還只是成功了一半,我們的canvas只支持2d,如果支持3d問題就解決了,我們還得把三維降到二維上。我們知道,如果一個球在三維系上圍繞y軸轉動,球的大小是不會變化的,但是如果體現在二維上,z值越大時,球體在視覺上的體現就是越小(跟z值有關);在三維上,y值是不會變化的,但是在二維視覺上的體現不應該是這樣,y值是應該變化的(跟z值有關)。另一方面,球體的任意角度視圖都是一個圓形。

  於是我們需要“把z方向扁平化”,即把z軸數值的大小體現在x和y上:

// focalLength 表示當前焦距,一般可設為一個常量
var focalLength = 250;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

  z方向扁平化使得z方向的值體現在x、y和r的大小以及球的透明度等變量上,一方面由於球體的旋轉我們需要時刻調整球體在三維系上的坐標,另一方面在draw的時候需要計算二維上的坐標。

  所以簡單的來說:

    1. 初始化球心在三維系上的坐標以及球的顏色半徑等屬性(三維系原點的設定)
    2. 將三維降到二維,繪制
    3. 根據旋轉角度重新計算三維坐標
    4. 將三維降到二維,繪制
    5. 反復重復

  這里還有個注意點就是每幀重繪時,根據小球在三維系的z值排個序,因為后面的會被前面的擋住,所以繪制應該有個順序。

 

3d標簽雲

  有了以上的基礎,可以動手做個3d標簽雲。理論上3d標簽雲中的標簽應該是個a標簽,是可以點擊跳轉的,這里為了方便,不添加點擊跳轉功能了(直接在canvas上)。

  思路應該很簡單,在一個球面上初始化一些點,把球換成文字就可以了。

  怎樣構造球體獲得坐標?充分認識到了數學的必要性...

  不懂數學,試了下覺得角度的取值有兩種方法:

  • (0 <= θ <= PI && 0 <= Φ <= 2 * PI)
  • (0 <= θ <= 2 * PI && 0 <= Φ <= PI)

  可以驗證x*x+y*y+z*z確實等於r*r。

  有了公式,我們可以枚舉角度獲得坐標。

  如果有n個點,我需要平均分配在球面上,怎么做?我們引入第二個公式:

  初始化坐標:

var all = 100;  // 100個
for(var i = 1; i <= all; i++) {
  var a1 = Math.acos(1 - (2 * i) / all);
  var a2 = a1 * Math.sqrt(all * Math.PI);
  var x = 150 * Math.sin(a1) * Math.cos(a2);  // r = 150
  var y = 150 * Math.sin(a1) * Math.sin(a2);
  var z = 150 * Math.cos(a1);
  garden.createBall(x, y, z);
}
 
  其實利用這個球面公式還能做很多好看的效果,例如: 3D旋轉球(W·Axes)
 

點-線-面

  有了點,根據點-線-面的原理,線和面也呼之欲出了。

  稍微動點腦筋搞點創意,就能搞點看起來炫酷(實際上沒花頭)的小東東。

  比如下面這樣:

  或者這樣: 
  再添點東西加點文字音效,表個白什么的還是不錯的。
  更多demo:
  不得不感嘆數學的奇妙,有時改變一個數值就能出現完全不同的效果。
  更多數學公式請參考: Famous Curves Index
 

總結

  學習3d主要是為了做粒子特效。

  這只是很基礎的入門,我也只會這么點,有更進一步興趣的可以參考岑安當耐特W·Axes或者miloyip的博客。

  岑安的JCanvas也可以看看,添加了很多事件代理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM