使用三角形產生 3D 特效Flash Player 10 以及更新的版本,Adobe AIR 1.5 以及更新的版本 在 ActionScript 中,您必須使用 Graphics.drawTriangles() 方法執行點陣圖變形,因為 3D 模型都是以空間中的一組三角形表示 (不過,由於 Flash Player 和 AIR 都不支援深度緩衝區,因此顯示物件本質上仍是以平面 (或 2D) 呈現。您可以在瞭解 Flash Player 和 AIR 執行階段的 3D 功能中找到相關說明)。Graphics.drawTriangles() 方法與 Graphics.drawPath() 方法類似,前者會使用一組座標來繪製三角形路徑。 若要熟悉 Graphics.drawPath() 的用法,請參閱繪製路徑。 Graphics.drawTriangles() 方法會使用 Vector.<Number>,指定三角形路徑的座標點位置: drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void drawTriangles() 的第一個參數 vertices 是唯一必要的參數。這個參數是數字的向量,可定義繪製三角形時所需的各個座標。每三組座標 (共六個數字) 即可表示一個三角形路徑。如果不使用 indices 參數,向量的長度就一定要是六的因數,因為每個三角形都需要三組座標 (三組 x/y 值)。例如: graphics.beginFill(0xFF8000); graphics.drawTriangles( Vector.<Number>([ 10,10, 100,10, 10,100, 110,10, 110,100, 20,100])); 上述三角形都不會共用任何座標點,但是如果有的話,drawTriangles() 的第二個參數 indices 就可以針對多個三角形,用來重複使用 vertices 向量中的值。 使用 indices 參數時,請注意 indices 值都是座標點索引,而非 vertices 陣列元素的直接相對索引。換句話說,如 indices 所定義,vertices 向量中的索引實際上就是真實的索引除以 2。例如,對 vertices 向量的第三個座標點而言,即使該點的第一個數值是從向量索引 4 開始,您仍然是使用 2 做為 indices 的值。 例如,以使用 indices 參數合併兩個三角形來共用對角斜邊為例: graphics.beginFill(0xFF8000); graphics.drawTriangles( Vector.<Number>([10,10, 100,10, 10,100, 100,100]), Vector.<int>([0,1,2, 1,3,2])); 請注意,雖然已使用兩個三角形繪製了一個正方形,但是 vertices 向量中僅指定了四個座標點。使用 indices 時,每個三角形都會重複用到這兩個三角形所共用的兩個點。如此即可將所有頂點數目從 6 個 (12 個數字) 減少為 4 個 (8 個數字): 使用 vertices 參數合併兩個三角形所繪製的正方形 如果是較大的三角形網格,而且其中有多個三角形都共用大多數座標點時,這項技巧就很有用。 您可以對三角形套用所有填色。這些填色能套用至所產生的三角形網格,就像套用至其它任何形狀一樣。 對點陣圖執行變形點陣圖變形可讓人產生透視的錯覺,或對三維物件產生具有「紋理」的錯覺。具體來說,您可以使點陣圖朝消失點的方向扭曲,讓影像朝著消失點移動時,看起來就像是縮小了一樣。或者,您也可以使用二維點陣圖為三維物件建立表面,讓人產生三維物件具有紋理或「折合」的錯覺。 使用消失點的二維表面以及使用點陣圖折合的三維物件 UV 對應開始使用材質之後,您會希望使用 drawTriangles() 的 uvtData 參數。這個參數可以讓您設定用於點陣圖填色的 UV 對應。 UV 對應是一種為物件加上材質的方法,所仰賴的兩個值為 U 水平 (x) 值和 V 垂直 (y) 值 (以百分比為基礎,而非像素值)。0 U 和 0 V 是影像的左上角,而 1 U 和 1 V 則是影像的右下角: 點陣圖上 UV 0 和 1 的位置 您可以指定 UV 座標當做三角形的向量,將這些向量與影像上對應的位置產生關聯: 點陣圖影像上三角區域的 UV 座標 三角形各點的 UV 值都會保持一致: 三角形的各頂點會移動且點陣圖會扭曲,如此各點的 UV 值都保持不變 將 ActionScript 3D 變形套用至與點陣圖有關聯的三角形時,會根據 UV 值將點陣圖影像套用至三角形。因此,您不需要使用矩陣計算,只要設定或調整 UV 值,即可建立三維特效。 Graphics.drawTriangles() 方法也能接受三維變形的部分選擇性資訊:T 值。uvtData 中的 T 值表示 3D 透視,更具體來說,就是相關聯頂點的縮放比例係數。UVT 對應會將透視更正加入至 UV 對應。例如,如果將物件放在 3D 空間中遠離檢視者的位置,讓它看起來是原始大小的 50%,那麼,因為繪製三角形的目的是要表示 3D 空間中的物件,所以物件在 z 軸上的位置就會決定物件的 T 值。也就是說,物件的 T 值就是 0.5。決定 T 值的方程式如下: T = focalLength/(focalLength + z); 在這個方程式中,focalLength 表示焦距或經過計算的「螢幕」位置,這個位置會規定檢視中所提供的透視量。
焦距和 z 值
T 的值是用於縮放基本的形狀,讓它們看起來似乎在遠方,離自己有一段距離。這個值通常用於將 3D 座標點轉換成 2D 座標點。如果是 UVT 資料,則通常會用於縮放具有透視特效之三角形內座標點間的點陣圖。 當您定義 UVT 值時,T 值會直接接在針對頂點所定義的 UV 值後面。得出 T 值之後,uvtData 參數中的每三個值 (U、V 和 T) 都會符合 vertices 參數中的每兩個值 (x 和 y)。只有 UV 值時,uvtData.length == vertices.length。得出 T 值之後,uvtData.length = 1.5*vertices.length。 下列範例會顯示使用 UVT 資料在 3D 空間中旋轉的平面。在範例中,會使用「helper」類別 ImageLoader 載入名為 ocean.jpg 的影像,以便將影像指定給 BitmapData 物件。 以下為 ImageLoader 類別的原始碼 (請儲存下列程式碼,並命名為 ImageLoader.as): package { import flash.display.* import flash.events.*; import flash.net.URLRequest; public class ImageLoader extends Sprite { public var url:String; public var bitmap:Bitmap; public function ImageLoader(loc:String = null) { if (loc != null){ url = loc; loadImage(); } } public function loadImage():void{ if (url != null){ var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIoError); var req:URLRequest = new URLRequest(url); loader.load(req); } } private function onComplete(event:Event):void { var loader:Loader = Loader(event.target.loader); var info:LoaderInfo = LoaderInfo(loader.contentLoaderInfo); this.bitmap = info.content as Bitmap; this.dispatchEvent(new Event(Event.COMPLETE)); } private function onIoError(event:IOErrorEvent):void { trace("onIoError: " + event); } } } 以下為 ActionScript,其中使用了三角形、UV 對應和 T 值,使影像看起來就像一邊旋轉並且朝著消失點縮小。請儲存下列程式碼,並命名為 Spinning3dOcean.as: package { import flash.display.* import flash.events.*; import flash.utils.getTimer; public class Spinning3dOcean extends Sprite { // plane vertex coordinates (and t values) var x1:Number = -100, y1:Number = -100, z1:Number = 0, t1:Number = 0; var x2:Number = 100, y2:Number = -100, z2:Number = 0, t2:Number = 0; var x3:Number = 100, y3:Number = 100, z3:Number = 0, t3:Number = 0; var x4:Number = -100, y4:Number = 100, z4:Number = 0, t4:Number = 0; var focalLength:Number = 200; // 2 triangles for 1 plane, indices will always be the same var indices:Vector.<int>; var container:Sprite; var bitmapData:BitmapData; // texture var imageLoader:ImageLoader; public function Spinning3dOcean():void { indices = new Vector.<int>(); indices.push(0,1,3, 1,2,3); container = new Sprite(); // container to draw triangles in container.x = 200; container.y = 200; addChild(container); imageLoader = new ImageLoader("ocean.jpg"); imageLoader.addEventListener(Event.COMPLETE, onImageLoaded); } function onImageLoaded(event:Event):void { bitmapData = imageLoader.bitmap.bitmapData; // animate every frame addEventListener(Event.ENTER_FRAME, rotatePlane); } function rotatePlane(event:Event):void { // rotate vertices over time var ticker = getTimer()/400; z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker)); x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker)); // calculate t values t1 = focalLength/(focalLength + z1); t2 = focalLength/(focalLength + z2); t3 = focalLength/(focalLength + z3); t4 = focalLength/(focalLength + z4); // determine triangle vertices based on t values var vertices:Vector.<Number> = new Vector.<Number>(); vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4); // set T values allowing perspective to change // as each vertex moves around in z space var uvtData:Vector.<Number> = new Vector.<Number>(); uvtData.push(0,0,t1, 1,0,t2, 1,1,t3, 0,1,t4); // draw container.graphics.clear(); container.graphics.beginBitmapFill(bitmapData); container.graphics.drawTriangles(vertices, indices, uvtData); } } } 若要測試這個範例,請將這兩個類別檔案儲存於「ocean.jpg」影像所在的目錄。您可以看到原始點陣圖的變形方式,看起來就像在 3D 空間中一邊旋轉,一邊朝著遠方消失。 剔除剔除是一項程序,可決定描繪器不應該顯示三維物件的哪些表面,因為從目前的視點無法看到這些隱藏的表面。在 3D 空間中,從視點看不到三維物件「背後」的表面:
從視點無法看到 3D 物件的背面。
所有三角形在本質上都一定會顯示,無論其大小、形狀或位置為何都一樣。剔除可確保 Flash Player 或 AIR 能正確顯示您的 3D 物件。此外,為了在顯示週期期間節省效能,有時您會希望描繪器略過某些三角形。以在空間中旋轉的立方體為例。無論在任何時候,您都不會看到立方體呈現三個以上的表面,因為看不到的那些表面都在看得到的表面的相反方向。由於會看不到這些表面,因此描繪器就不需要加以繪製。如果不使用剔除,Flash Player 或 AIR 便會一併顯示前後表面。 立方體,其中幾個表面是從目前視點看不到的 因此,Graphics.drawTriangles() 方法便具有第四個參數,以建立剔除值。 public function drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void culling 參數具有衍生自 TriangleCulling 列舉類別的值:TriangleCulling.NONE、TriangleCulling.POSITIVE 和 TriangleCulling.NEGATIVE。這些值與定義物件表面之三角形路徑的方向有關。用於決定剔除的 ActionScript API 會假設 3D 形狀的所有外顯三角形都應以相同的路徑方向繪製。一旦某個三角形表面翻轉之後,路徑方向也會變更。此時,就能剔除 (不再顯示) 該三角形。 如此一來,POSITIVE 的 TriangleCulling 值便會移除具有正向路徑方向 (順時針) 的三角形;而 NEGATIVE 的 TriangleCulling 值則會移除具有負向路徑方向 (逆時針) 的三角形。如果是立方體,那麼外顯的表面便具有正向路徑方向,而內藏的表面則具有負向路徑方向: 「展開」的立方體,顯示路徑方向。在「折合」時,內藏表面的路徑方向則相反。 若要瞭解剔除的運作方式,請從先前UV 對應的範例開始,將 drawTriangles() 方法的 culling 參數設定為 TriangleCulling.NEGATIVE: container.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE); 請注意,當物件旋轉時,不會顯示影像的「背面」。 |
|