本教程的目的是解釋如何在沒有WebGL的情況下為Web構建簡單的3D引擎。我們將首先看到如何存儲3D形狀。然後,我們將在兩個不同的視圖中看到如何顯示這些形狀。
>鑰匙要點
>僅利用JavaScript,就可以創建一個簡單的3D引擎,而無需諸如WebGl之類的高級庫,使其適合呈現基本形狀,例如立方體。 在該發動機中,多面體在3D建模中至關重要,在該發動機中,使用扁平面近似複雜的形狀,類似於2D中的多邊形。 通過存儲對頂點而不是複制它們來實現有效的內存管理,從而大大降低了內存使用情況,因為每個頂點都在多個面上共享。
猜猜如何存儲多面體,我們必須記住如何在數學中識別這種東西。在您的學年期間,您肯定已經做了一些基本的幾何形狀。例如,要識別一個正方形,您將其稱為ABCD,用A,B,C和D稱為構成正方形每個角落的頂點。 對於我們的3D發動機,
將是相同的。我們將首先存儲形狀的每個頂點。然後,此形狀將列出其面孔,每個面都會列出其頂點。為代表頂點,我們需要正確的結構。在這裡,我們創建一個以存儲頂點的坐標的類
現在可以像任何其他對像一樣創建頂點:
><span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>
接下來,我們創建一個代表多面體的類。讓我們以立方體為例。班級的定義在下面,並在此之後進行解釋。
><span>var A = new Vertex(10, 20, 0.5); </span>
使用此類,我們可以通過指示其中心及其邊緣長度來創建一個虛擬立方體。
<span>var <span>Cube</span> = function(center<span>, size</span>) { </span> <span>// Generate the vertices </span> <span>var d = size / 2; </span> <span>this.vertices = [ </span> <span>new Vertex(center.x - d, center.y - d, center.z + d), </span> <span>new Vertex(center.x - d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z + d) </span> <span>]; </span> <span>// Generate the faces </span> <span>this.faces = [ </span> <span>[this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3]], </span> <span>[this.vertices[3], this.vertices[2], this.vertices[5], this.vertices[4]], </span> <span>[this.vertices[4], this.vertices[5], this.vertices[6], this.vertices[7]], </span> <span>[this.vertices[7], this.vertices[6], this.vertices[1], this.vertices[0]], </span> <span>[this.vertices[7], this.vertices[0], this.vertices[3], this.vertices[4]], </span> <span>[this.vertices[1], this.vertices[6], this.vertices[5], this.vertices[2]] </span> <span>]; </span><span>}; </span>
>立方體類的構造函數首先從指示中心的位置生成立方體的頂點。模式將更清晰,因此請參閱下面我們生成的八個頂點的位置:
<span>var cube = new Cube(new Vertex(0, 0, 0), 200); </span>
然後,我們列出了面孔。每張臉是一個正方形,因此我們需要為每個臉部指出四個頂點。在這裡,我選擇代錶帶有數組的面孔,但是,如果您需要的話,您可以為此創建一個專門的類。
>創建面部時,我們會使用四個頂點。我們不需要再次表示它們的位置,因為它們存儲在the.vertices [i]對像中。這是實用的,但是還有另一個原因。 >
默認情況下,JavaScript試圖使用最少的內存數量。為了實現這一目標,它不會復制通過函數參數傳遞的對象,甚至存儲在數組中。就我們的情況而言,這是完美的行為。
實際上,每個頂點包含三個數字(它們的坐標),以及如果需要添加幾種方法。如果對於每張臉,我們存儲了頂點的副本,我們將使用很多內存,這是沒有用的。在這裡,我們只有參考文獻:坐標(和其他方法)一次存儲一次,只有一次。由於每個頂點由三個不同的面都使用,而不是存儲參考而不是副本,因此我們將所需的內存除以三個(或多或少)!
>!我們需要三角形嗎?
如果您已經玩過3D(例如,使用Blender之類的軟件,或WebGl等庫),也許您聽說我們應該使用三角形。在這裡,我選擇不使用三角形。>
這個選擇背後的原因是本文是對該主題的介紹,我們將顯示類似立方體的基本形狀。在我們的情況下,使用三角形展示正方形比其他任何事物都更像是並發症。
> 但是,如果您打算構建一個更完整的渲染器,那麼通常需要知道,三角形是首選的。這樣的主要原因有兩個:
>
>在這裡,我們會做同樣的事情:我們不會碰到面孔。我們在每個頂點上應用所需的操作,我們完成了。當面部使用參考時,面部的坐標會自動更新。例如,查看我們如何翻譯我們先前創建的立方體:
渲染圖像
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>我們知道如何存儲3D對像以及如何對它們作用。現在是時候看看如何查看它們了!但是,首先,我們需要該理論的少量背景,以了解我們將要做什麼。
>目前,我們存儲3D坐標。但是,屏幕只能顯示2D坐標,因此我們需要一種將3D坐標轉換為2D的方法:這就是我們所謂的數學投影。 3D到2D投影是由稱為虛擬相機的新對象進行的抽像操作。該攝像機將3D對象帶入2D對象,並將其發送到渲染器,以將其顯示在屏幕上。我們將在這裡假設我們的相機位於3D空間的起源(因此其坐標為(0,0,0))。自本文開頭以來,我們已經討論了坐標,由三個數字表示:x,y和z。但是要定義坐標,我們需要一個基礎:z是垂直坐標嗎?它是到頂部還是底部?沒有通用的答案,也沒有慣例,因為您可以選擇自己想要的任何東西。您唯一要記住的是,當您對3D對象採取行動時,您必須保持一致,因為公式會根據它而改變。在本文中,我選擇了您可以在上面的立方體模式中看到的基礎:x從左至右,y從後到正面,從下到頂部。 >
如何渲染我們的場景 >數組可以包含幾個以渲染的對象。這些對象必須尊重一件事:擁有一個名為“面孔”的公共屬性,該屬性是一個數組列出對象的所有面(如我們先前創建的Cube)。這些面孔可以是任何東西(如果需要的話,正方形,三角形,甚至是十二桿):它們只需要列出其頂點的數組。 讓我們看一下功能的代碼,然後說明: 這個功能值得一些解釋。更確切地說,我們需要解釋該項目()函數是什麼,這些DX和DY參數是什麼。其餘的基本上除了列出對象,然後繪製每個臉。
>而不是命名坐標x和z我在這裡選擇將z坐標命名為y,而是要維護我們經常在2D幾何形狀中找到的經典慣例,但是如果您喜歡的話,可以保留z。
>
最後,最後一個細節:為什麼我們使用-y而不是直接使用y?答案是我們選擇的基礎:Z軸針對頂部。然後,在我們的場景中,帶有正面Z坐標的Vertice將上升。但是,在畫布上,Y軸定向到底部:帶正Y坐標的佛特(Vertice)將向下移動。這就是為什麼我們需要在畫布上定義畫布的y坐標為我們場景的z坐標的逆轉。 >現在呈渲染函數很明顯,是時候查看project()。
>您現在可以測試自本文開頭以來我們編寫的所有代碼:它有效!恭喜,您剛剛在平面上顯示了一個3D對象! 請參見codepen上的sitepoint(@sitepoint)的筆3D拼字圖。 >
。
,但是,在現實生活中,我們的眼睛更像以下模式。 基本上,我們有兩個步驟: >
現在,如果您從側面看同一場景,則獲得了類似的模式,允許您獲得Z'的值,這要歸功於Z,Y和D:Z'= D / Y *Z。 我們現在可以使用透視視圖編寫project()函數: 可以在下面的實時示例中測試此功能。再一次,您可以與Cube進行交互。
其他事情可以更改。我們將相機放置在空間的原點,但是您可以移動它(在投射頂點之前需要更改基礎)。另外,放置在相機後面的頂點在這裡繪製,這不是我們想要的。剪裁平面可以解決這個問題(易於理解,易於實現)。
>!
>向JavaScript 3D引擎添加照明涉及創建光源並實現陰影模型。光源可以是定向,點或聚光燈。確定表面如何響應光的陰影模型可以很簡單(例如蘭伯特陰影)或複雜的(例如基於物理的渲染)。 >如何將動畫添加到我的javascript 3D引擎? >調試JavaScript 3D引擎可能會很具有挑戰性3D圖形的複雜性。但是,WebGL檢查員和Chrome DevTools之類的工具可能非常有幫助。這些工具允許您檢查WebGL上下文的狀態,查看緩衝區和紋理的內容,並跨過著色器。>在投影我們的對象之前,讓我們編寫顯示它們的功能。此函數接受為參數,列出了渲染對象的數組,必須用於顯示對象的畫布的上下文,以及在正確位置繪製對象所需的其他詳細信息。
>
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
<span>var A = new Vertex(10, 20, 0.5);
</span>
>實際上,project()函數返回虛擬2D平面上的坐標,但具有相同的原點,而不是我們為3D空間定義的坐標。但是,我們希望原點位於畫布的中心,這就是為什麼我們翻譯坐標的原因:頂點(0,0)不在畫布的中心,但是(0 dx,0 dy)是,如果我們是選擇DX並明智地選擇DY 。正如我們想要的(DX,DY)位於畫布的中心一樣,我們實際上沒有選擇,我們定義dx = canvas.width / 2和dy = canvas.height / 2. >
我們有三個坐標,我們只需要兩個。在這種情況下,最簡單的事情是什麼?刪除其中一種坐標。這就是我們在拼字法中所做的。我們將刪除表示深度的坐標:y坐標。 <span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
此功能在下面的實時示例中實現,您可以通過與鼠標旋轉來與立方體進行交互。
>
>與拼字法視圖相反,這裡的飛機的確切位置很重要:如果將飛機放在遠離相機的位置,則不會獲得與將其放置在接近的情況下相同的效果。在這裡,我們將其放在距相機的距離D。 >猜測我們將如何計算這些坐標,讓我們採取另一個觀點並查看與上面相同的模式,但從頂部看。
>我們可以識別攔截定理中使用的配置。在上面的架構上,我們知道一些值:x,y和d等。我們要計算X',以便應用截距定理並獲得此方程:x'= d / y * x。
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
請參閱codepen上的sitepoint(@sitepoint)的筆3D透視圖。 >
>另外,我們沒有談論紋理。在這裡,我們所有的形狀都具有相同的顏色。您可以通過例如在對像中添加顏色屬性來更改它,以了解如何繪製它們。您甚至可以選擇每張臉部的一種顏色,而無需更改很多事情。您也可以嘗試在臉上顯示圖像。但是,更加困難,並且詳細說明如何做這樣的事情將花費整篇文章。
>在JavaScript中構建3D引擎的先決條件是什麼?熟悉HTML和CSS也是有益的。 3D數學的知識,包括媒介,矩陣和四元素,至關重要。了解計算機圖形的基礎知識,例如渲染管道,著色器和紋理映射,也將很有幫助。
我如何開始學習3D數學用於JavaScript 3D引擎開發?在線可用的幾種資源可以學習3D數學。像汗學院這樣的網站提供了有關線性代數和矢量微積分的課程,這些課程對於理解3D數學至關重要。諸如“圖形和遊戲開發的3D數學底漆”之類的書也可能會有所幫助。 >在JavaScript中構建3D引擎的最佳圖書館是什麼?在JavaScript中構建3D引擎的兩個最受歡迎的庫。這兩個庫都為WebGL提供了高級接口,從而更容易創建複雜的3D場景。他們還提供了廣泛的文檔和社區支持。 >如何優化我的JavaScript 3D引擎以提高性能? >
>有幾種方法可以優化您的3D引擎以提高性能。一種方法是通過使用諸如實例和批處理之類的技術來最大程度地減少呼叫的數量。另一種方法是使用壓縮紋理和幾何形狀減少發送到GPU的數據量。您還可以優化著色器以獲得更好的性能。 >如何處理JavaScript 3D引擎中的用戶輸入? >在JavaScript 3D引擎中處理用戶輸入通常涉及聆聽鍵盤和鼠標事件。您可以使用瀏覽器提供的“ AddEventListener”方法來聆聽這些事件。然後,您可以使用事件數據來控制3D場景中的相機或其他元素。
>如何將照明添加到我的JavaScript 3D引擎?
>將紋理添加到JavaScript 3D引擎中涉及加載圖像文件,創建紋理對象並將它們映射到幾何形狀上。您可以使用瀏覽器提供的“圖像”對象來加載圖像。然後,您可以使用WebGl提供的“ gl.CreateTexture”方法創建紋理對象。
以上是使用JavaScript構建3D引擎的詳細內容。更多資訊請關注PHP中文網其他相關文章!