用于游戏开发和其他目的的光线投射教程
用于游戲開發和其他目的的光線投射教程
- 前言
- 介紹
- 一個簡短的歷史
- 什么是光線投射
- 用于游戲開發的光線投射與光線追蹤
- 光線投射的局限性
- 光線投射第 1 步:創造一個世界
- 光線投射第 2 步:定義投影屬性
- 尋找到投影平面的距離
- 光線投射第 3 步:尋找墻壁
- 尋找墻壁的光線
- 這條射線在 A、B、C、D、E 和 F 點與網格相交
- 尋找到墻壁的距離
- 查找到墻壁切片的距離
- 畫墻
- 紋理貼圖墻
- 繪制地板
- 繪制天花板
- 可變高度的墻
- 水平運動 // 讓玩家移動
- A. 向前和向后移動。
- B、轉向。
- 上下看
- A. 上下看。
- 飛行和蹲伏
- 綜合效果
- 陰影
- 其他參考內容、參考文獻和注釋
前言
本文檔探討了光線投射背后的基本理論,這是一種在 90 年代游戲開發領域非常流行的偽 3 維渲染技術。通常,本文檔不會涉及實現和編碼細節。討論將主要關于概念,實現取決于讀者。對于普通讀者,假設了解勾股定理和高中數學知識。
本文檔寫于 1996 年,盡管光線投射已被更新、更強大的技術(和硬件!)所取代,但讀者仍有望從該技術中受益。
介紹
過去幾年,個人電腦市場出現爆炸式增長*。這種增長部分是由對多媒體標題的興奮和好奇產生的。本項目是嘗試在多媒體標題開發過程中獲得一些知識和經驗。具體來說,我們將仔細研究與3D多媒體游戲開發相關的問題。
(*本文檔寫于 1996 年)
一個簡短的歷史
1992 年,隨著一款游戲《德軍總部 3D》(iD Software)的發布,光線投射的轟動開始了。在 Wolfenstein 3D 中,玩家被放置在一個 3D 迷宮般的環境中,他/她必須在與多個對手作戰時找到出口。Wolfenstein 3D 因其快速流暢的動畫而成為即時經典。使這種動畫成為可能的是一種稱為“光線投射”的創新 3 維渲染方法。
Wolfenstein 3D 由 Id Software 開發和創建。從今以后,Id 的程序員 John Carmack 很可能是發起光線投射感覺的人(Myers 5)。
什么是光線投射
光線投射是一種技術,它通過將光線從視點追蹤到觀看體 (LaMothe 942)來將有限形式的數據(非常簡化的地圖或平面圖)轉換為 3D 投影。例如,光線投射變換在下圖中。
請注意,這不是光線投射的唯一應用。光線投射也可用于渲染地形圖,例如圖 2(下圖)。要記住的重點是光線投射“從觀察者的眼睛向后追蹤光線到物體”。
用于游戲開發的光線投射與光線追蹤
與光線投射一樣,光線追蹤“通過追蹤從觀察者的眼睛到場景中物體的假想光線來確定表面的可見性”(Foley 701)。
從這兩個定義來看,光線投射和光線追蹤似乎是一樣的。事實上,有些書籍交替使用這兩個術語。然而,從游戲程序員的角度來看,光線投射被視為光線追蹤的一種特殊實現(子類)。
之所以做出這種區分,是因為一般而言,光線投射比光線追蹤快。這是可能的,因為光線投射利用一些幾何約束來加速渲染過程。例如:墻壁總是與地板垂直(您可以在 Doom 或 Wolfenstein 3D 等游戲中看到這一點)。如果沒有這樣的限制,光線投射將是不可行的。例如,我們不想對任意樣條進行光線投射,因為很難找到對此類形狀的幾何約束。
表 1是光線投射和光線追蹤之間的一般比較。要記住的要點是,由于某些“幾何約束”,在光線投射中要跟蹤的“光線數量較少”。或者,也可以說光線投射是光線追蹤的一種特殊用途的實現。
表 1:光線投射和光線追蹤之間的比較
(游戲程序員/游戲開發者的觀點)
光線投射的局限性
光線投射很快,因為它利用了一些幾何約束。在大多數情況下,墻壁始終與地板成 90 度角。(請注意,我們不是在談論墻壁和另一面墻壁之間的角度,而是墻壁和地板之間的角度。)
因此,光線投射游戲幾乎存在的一個限制是視點不能沿 Z 軸旋轉(圖 5)。如果允許這樣做,則墻壁可能會傾斜,從而失去繪制垂直切片的好處。這種無法沿 Z 軸旋轉是光線投射環境不被視為真正的 3D 環境的原因之一。
在光線投射環境中,玩家可以向前、向后、向左或向右轉;但不能繞Z軸旋轉/擺動(這種Z軸旋轉稱為傾斜)。
光線投射第 1 步:創造一個世界
為了說明光線投射的過程,我們將創建一個具有以下幾何約束的迷宮世界:
出于我們的目的,每個立方體的大小為 64x64x64 單位。(選擇 64 是任意的,但選擇 2 的倍數會很有用;因為我們可以對這樣的數字執行一些算術移位運算(移位運算比乘法或除法快)。大小越大立方體,世界看起來會更塊,但較小的立方體會使渲染速度變慢。)
這樣的世界如 下圖 所示。
在繼續之前,我們將定義我們的坐標系,以免混淆。我們使用的坐標系如下:
注意:任何類型的笛卡爾坐標系都可以正常工作。但是,您必須保持一致(不要對一件事使用自上而下的坐標系,而對其他事使用自上而下的坐標)。如果你這樣做,你可能會讓自己感到困惑——我做到了。
光線投射第 2 步:定義投影屬性
現在我們有了世界,我們需要定義一些屬性,然后才能投影和渲染世界。具體來說,我們需要知道這些屬性:
玩家應該能夠看到他/她面前的東西。為此,我們需要定義一個視野 (FOV)。FOV 決定玩家看到他/她面前的世界的寬度(見下圖)。大多數人的 FOV 為 90 度或更多。然而,這個角度的 FOV 在屏幕上看起來并不好。因此,我們通過試驗和實驗(關于它在屏幕上看起來有多好)將 FOV 定義為 60 度。玩家的高度被定義為 32 個單位,因為考慮到墻壁(立方體)高 64 個單位,這是一個合理的假設。
要將玩家置于世界中,我們需要定義玩家的 X 坐標、玩家的 Y 坐標以及玩家面對的角度。這三個屬性構成了玩家的“觀點”。
假設玩家以相對于世界的 45 度視角放置在網格坐標(1,2)中間的某個位置,那么玩家的視角和 FOV 將如下圖 所示。(一個網格組成是 64 x 64 個單位。因此,我們也可以說玩家在單位坐標(96,160)中)。
播放器位于網格坐標(1,2)或單位坐標(96,160)中間,視角為 45 度,視野為 60 度。
我們需要定義一個投影平面,以便我們可以將玩家看到的東西投影到投影平面中。320 單位寬和 200 單位高的投影平面是一個不錯的選擇,因為這是大多數 VGA 視頻卡的分辨率。(視頻分辨率通常以像素為單位,因此可以將 1 像素視為等于 1 個單位。)
當玩家的視角被投影到投影平面時,世界應該看起來像下面中的場景。
尋找到投影平面的距離
通過知道視野(FOV)和投影平面的尺寸,我們可以計算出后續光線之間的角度以及玩家與投影平面之間的距離。
這是我們可以計算的(其中大部分是高中數學,如果你不明白,我建議你復習三角學/勾股定理):
所以現在我們知道:
- 投影平面的尺寸 = 320 x 200 個單位
- 投影平面的中心 = (160,100)
- 到投影平面的距離 = 277 個單位
- 后續光線之間的角度 = 60/320 度
(我們偶爾會將“后續光線之間的角度”稱為“后續列之間的角度”。稍后,這個角度將用于從一列到另一列循環。玩家到投影平面之間的距離將用于縮放。)
光線投射第 3 步:尋找墻壁
可以將墻視為 320 條垂直線(或 320 條墻切片)的集合。
這正是一種適用于光線投射的幾何約束形式。我們可以只跟蹤屏幕的每個垂直列,而不是為屏幕上的每個像素跟蹤一條光線。FOV 最左側的光線將投影到投影平面的第 0 列,最右側的光線將投影到投影平面的第 319 列。
尋找墻壁的光線
因此,為了渲染這樣的場景,我們可以簡單地從左到右追蹤 320 條光線。這可以在循環中完成。以下說明了這些步驟:
A. 投一縷。(術語“施法”有點令人困惑。想象一下玩家是一個可以“施放”射線而不是法術的巫師。射線只是從玩家延伸出來的“假想”線。)
B. 跟蹤光線,直到它碰到墻壁。
步驟 2A的技巧是,我們只需檢查每個網格,而不是檢查每個像素。這是因為墻只能出現在網格邊界上。考慮如下面所示跟蹤的光線。要檢查這條射線是否撞到了墻壁,只需檢查 A、B、C、D、E 和 F 處的網格交點即可。
這條射線在 A、B、C、D、E 和 F 點與網格相交
要找到墻壁,我們需要檢查射線遇到的任何網格交點;并查看網格上是否有墻。最好的方法是分別檢查水平和垂直交叉點。當垂直或水平交叉口上有墻時,檢查停止。然后比較到兩個交點的距離,并選擇更近的距離。以下兩幅圖說明了此過程。
找水平網格線交點的步驟:
例如,以下是您如何獲得 A 點:
注意:記住笛卡爾坐標向下增加,任何小數值都將向下舍入。
(可以看到,一旦我們有了Xa和Ya的值,這個過程就很簡單了,我們只要把舊的值與Xa和Ya不斷相加,并進行移位操作,就可以找出下一個的網格坐標被射線擊中的點。)
尋找垂直網格線交點的步驟:
如果光線朝左 Bx = rounded_down(Px/64) * (64) – 1. Ay = Py + (Px-Ax)*tan(ALPHA);
在圖中,首先,光線擊中點 B。網格 (2,2) 被選中。(2,2) 上沒有墻,所以射線延伸到 E。網格 (3,0) 被檢查。那里有一堵墻,所以我們停下來計算距離。
在此示例中,點 D 比 E 更近。因此將繪制 D(不是 E)處的墻切片。
尋找到墻壁的距離
有幾種方法可以找到從視點(玩家)到墻壁切片的距離。它們如下圖所示。
查找到墻壁切片的距離
正弦或余弦函數的實現成本更低,因為它們可以預先計算并放入表格中。這是可以做到的,因為 ALPHA(玩家的 POV)必須在 0 到 360 度之間,所以可能性的數量是有限的(平方根方法對 x 和 y 的可能值幾乎沒有限制)。
在畫墻之前,有一個問題是必須要解決的。這個問題被稱為“魚缸效應”。魚缸效應的發生是因為光線投射實現將極坐標和笛卡爾坐標混合在一起。因此,在不直接位于觀察者面前的墻壁切片上使用上述公式將提供更長的距離。這不是我們想要的,因為它會導致如下圖所示的觀看失真。
因此,為了消除觀看失真,從上圖 中的方程獲得的最終距離必須乘以 cos(BETA);其中 BETA 是投射的光線相對于視角的角度。在上圖中,視角 (ALPHA) 為 90 度,因為玩家面朝上。因為我們有 60 度的視野,所以最左邊的光線的 BETA 是 30 度,最右邊的光線是 -30 度。
畫墻
在前面的步驟中,投射了 320 條光線,當每條光線擊中墻壁時,計算到該墻壁的距離。知道距離,然后可以將墻切片投影到投影平面上。為此,需要找到投影墻切片的高度。事實證明,這可以通過一個簡單的公式來完成:
實際切片高度 投影切片高度 = --------------------- * 到投影平面的距離到切片的距離下面的圖 解釋了該公式背后的邏輯
墻壁縮放背后的數學
我們的世界由立方體組成,其中每個立方體的尺寸為 64x64x64 單位, 所以墻高是64個單位。我們也已經知道玩家到投影平面的距離(277)。因此,方程可以簡化為:
在實際實現中,可以考慮以下幾點:
-
例如,可以預先計算 64/277,因為這將是一個常數值。一旦計算出來,就可以在屏幕上繪制墻切片。這可以通過簡單地在投影平面(屏幕)上的相應列上繪制一條垂直線來完成。
-
還記得數字 277 是從哪里來的嗎?這個數字實際上可以稍微偏離一點,而不會造成太大的影響。事實上,使用255的值會節省時間,因為程序員可以使用移位運算符來節省計算時間(右移3進行乘法,左移進行除法)。
例如,假設第 200 列的光線在 330 個單位的距離處撞擊墻壁切片。切片的投影將為 64 / 330 * 277 = 54(向上取整)。
由于投影平面的中心被定義為 100。墻切片的中間應該出現在這一點上。因此,應繪制墻切片的頂部位置是 100-27=73。(其中 27 是 54 的二分之一)。最后,切片的投影將類似于下圖。
紋理貼圖墻
為了使墻壁更具吸引力,可以使用稱為紋理映射的技術在墻壁上繪制紋理(位圖)。(紋理映射通常是指在表面上繪制位圖/紋理的技術。)對于立方體世界,我們使用大小為 64 x 64 像素的位圖。選擇這個大小是因為 64 x 64 也是我們在我們的世界中使用的立方體面的大小。可以使用不同大小的位圖,但使用相同大小會簡化紋理映射過程。
如果我們要將紋理映射到任意多邊形上,紋理映射過程會很復雜。幸運的是,在我們正在創建的光線投射世界中,紋理映射只是縮放位圖切片(一列)的問題(參見下面的圖)。
當光線尋找墻壁交點時,可以很容易地找到偏移量(光線相對于網格的位置)。然后可以使用該偏移量來確定位圖的哪一列將被繪制為墻切片。下圖說明了查找偏移量的過程。
繪制地板
要繪制地板,我們可以進行地板澆鑄(floor-casting 是指渲染地板的一種技術)。但是請注意,在沒有紋理映射或著色的情況下執行地板鑄造將是浪費的。換句話說,如果地板沒有紋理或陰影(陰影將在后面探討),那么我們可以簡單地用純色繪制地板,我們就完成了。牢記這一點,讓我們探索進行地板鑄造所需的條件。
有幾種方法可以進行地板澆鑄。但是,它們都使用類似的技術。該技術解釋如下。
請注意,沒有必要繪制所有樓層。我們應該只繪制未被墻壁覆蓋的地板。出于這個原因,我們應該從壁切片的底部開始鑄造。從切片的底部,我們然后向下掃描投影平面上的每個像素(即:隨后向下投射光線)。然而,這一次,光線不是尋找與墻壁的交點,而是尋找與地板的交點。
請記住,我們不需要投射超出投影平面。(即:從墻片底部開始,逐行向下投射;到達投影平面底部時停止。)
下面的圖解釋了地板澆鑄背后的數學原理。
重申一下,請在閱讀以下步驟時查看插圖:
- 從墻切片的底部開始。
- 重復 1-5 直到到達屏幕底部。
繪制天花板
要繪制天花板,可以顛倒地板澆筑過程。而不是從跟蹤光線的底部在一個壁切片的向下方向,從追蹤射線頂壁在向上方向。一旦掌握了地板鑄造背后的理論,這實際上非常簡單。
稍后,我們將解釋如何模擬仰視、俯視、飛行和蹲伏的錯覺。如果程序員不想模擬這些,可以同時繪制地板和天花板。這是因為玩家的眼睛到地板和天花板的距離相等/對稱。(地板和天花板是對稱的,因為玩家的眼睛正好在地板和天花板的中點。)
可變高度的墻
到目前為止,我們世界上所有的墻都具有相同的高度。通過一些創新,我們實際上可以使用不同高度的墻壁。這使世界變得更有趣,如下圖所示。
將可變高度墻概念化的最簡單方法是將墻視為地板。也就是說,將墻壁想象成升高的地板。我們需要一個數組來保存每個地板網格的高度來完成這項工作。渲染場景的基本方法是這樣的:
為了闡明這個過程,請考慮渲染下面圖中的場景的過程。
首先跟蹤擊中 A 點(投影平面的最底行)的光線。當光線沿投影平面向上移動時,B點的壁被擊中,因此繪制了切片BC。知道A點到B點沒有高度變化,就繪制了A點到B點的地板。然后射線被延伸。它檢測到 D 點的高度變化。因此,繪制了切片 DE。知道C點到D點沒有高度變化,繪制C點到D點的地板。然后射線再次延伸。在 F 點,到達地圖的邊緣。由于不能再有高度變化,所以繪制了從 F 點到 E 點的地板,并重復該過程,直到渲染整個屏幕。
使用可變高度墻的主要缺點是渲染過程會相當慢。這是因為當最近的墻壁被擊中時,光線不再停止。加快此過程的一種方法是設置可見距離,然后忽略超出該距離的任何內容。
可變高度墻背后的數學計算。
水平運動 // 讓玩家移動
玩家至少應該能夠以三種方式移動:向前、向后和轉身。玩家的位置由坐標和視角定義。為了允許運動,還需要兩個屬性。它們是玩家的移動速度,以及玩家的轉彎速度。玩家的移動速度定義了玩家向前或向后移動時應該移動多少個單位。玩家的轉彎速度(以角度衡量)定義了玩家轉彎時要增加或減少的角度。我們將討論我們如何使用這些屬性來允許運動。
A. 向前和向后移動。
我們將玩家的移動速度定義為 10 個單位。(通常,這可以是任何數字,但數字越大,運動就越不平滑。) 查找 x 和 y 位移的過程如下圖所示。如果玩家向前移動,我們將 XDisplacement 添加到當前玩家的 X 坐標;并將 Ydisplacement 添加到當前玩家的 Y 坐標。如果玩家向后移動,我們將 XDisplacement 減去當前玩家的 X 坐標;并將 Ydisplacement 減去當前玩家的 Y 坐標。(始終檢查世界/墻壁邊界,以免玩家走出地圖或穿過墻壁。)
根據玩家的速度查找位移(在本例中,玩家的速度以 10 個單位表示)。
B、轉向。
車削過程實施起來非常簡單。我們需要做的就是在當前玩家的視角上增加或減少一個角度增量 (aI)(每當轉彎變成一個完整的圓圈時就環繞)。同樣,較大的角度增量會導致運動看起來不太平滑。
上下看
可以模擬在光線投射環境中向上和向下看以及飛行和蹲伏的錯覺。然而,請注意——這很重要——這里將要解釋的技巧并不總是遵循正確的三維投影理論。即:這些是技巧,它們不是進行“真實”模擬的正確方法。
A. 上下看。
回想一下,投影平面是 200 個單位高。并且到此為止,我們始終將投影平面的垂直中心設置為正好在中間(即點 y=100)。因此,任何墻切片的中點都將在投影點 y=100 處繪制。事實證明,只需更改此值即可模擬向上或向下查找的效果。
也就是說,為了模擬仰視,我們不是將垂直切片的中心放在 y=100 處,而是將其放在 y>100 的點上(這類似于向上移動投影平面)。
類似地,為了模擬向下看,我們不是將垂直切片的中心放在 y=100 處,而是將其放在 y<100 處(這類似于向下移動投影平面)。
為什么這個技巧有效?希望下面的插圖可以解釋它。
如果你感到困惑,想象一下站在你身后拿著一面墻的鏡子,同時站直。當鏡子向上或向下移動時,會顯示墻壁的不同部分。鏡子是投影平面。(在繼續之前花點時間想象一下。)
飛行和蹲伏
回想一下,玩家的高度設置為 32 個單位。這意味著玩家的眼睛(假設玩家的眼睛正好在玩家的頭頂上)正直視點 32 處的墻壁。由于 32 是墻壁高度的一半,因此玩家的高度為 32 會使玩家的眼睛位于地板和天花板之間的中間(見下圖)。
如果我們改變這個值怎么辦?令人驚訝的是(或可能不會),墻壁會根據玩家的身高是增加還是減少而向上或向下移動。
因此,為了讓玩家仿佛在飛行(或跳躍),我們可以簡單地增加玩家的高度。同樣,為了讓玩家好像她/他在蹲伏,我們可以降低玩家的高度。高度不應小于 0 或大于墻壁的高度,因為這樣做會使玩家越過天花板或沉入地板。
下圖顯示了此方法為何有效。
如果您感到困惑,我們再次使用鏡像方法來闡明其工作原理。想象一下,你站直了,在一個小房間里拿著一面鏡子。背對墻壁站立。將鏡子放在眼睛前面(即:您不必轉頭就能看到鏡子)。現在,想象一下如果你蹲下看看鏡子里的東西會發生什么。在鏡子里,你應該看到墻壁的不同部分和更多的地板面積…就像本頁的第二張圖片… 希望你明白了。
鏡子是投影平面,眼睛位置是玩家的高度。
這種垂直運動方法有一個反直覺的方面,那就是:
投影平面必須始終與玩家的眼睛垂直。(也就是說:投影平面必須始終與墻壁平行——它們不能以任何方式傾斜。)對此進行概念化的最佳方法是想象一個人通過相機鏡頭“瞄準”。人總是以 90 度角向前瞄準;即使他/她蹲伏或站在桌子上。
這樣做的原因是使用這種方法時,我們不能像下圖那樣傾斜投影平面;因為如果我們旋轉投影平面以遵循“正常”的眼睛方向,那么墻壁將傾斜(不再與投影平面平行);然后渲染過程必須考慮到這一點。這意味著將需要更復雜的計算,并且渲染過程將變得非常緩慢。
更真實的俯視應該是這樣的:
當你往下看時,你不僅會移動眼睛,還會移動頭部,因此視角不同(與前6張圖片相比)。本頁描述的技術使用之前的技巧來“模擬”這一點。
綜合效果
上面解釋的效果可以結合起來創建更有趣的動作,如下圖所示。
陰影
陰影
當一個物體離觀察者更遠時,該物體應該顯得更暗/更亮。為了實現這一點,需要陰影效果。但首先,我們需要了解顏色是如何表示的。
標準的 256 色 VGA 模式寄存器包含調色板中每種顏色的 0 到 63 之間的三個數字,稱為 RGB (RedGreenBlue) 值。例如,全紅色具有 (63,0,0) 的 RGB 分量;全綠色有 (0,63,0); 全藍有 (0,0,63)。諸如全黃之類的顏色,可以通過混合全紅和全綠使(63,63,0)為黃色來獲得。
要更改顏色的紅色、綠色或藍色分量的亮度,必須增加或減少表示顏色分量的數字。例如,要將具有 RGB 分量 (50,10,10) 的顏色的強度降低一半,請將每個分量乘以 0.5。生成的顏色將為 (25,5,5)。
這很簡單,但是我們如何知道在什么距離上使用什么強度?第一個選項是使用一個精確的光強度公式,它是這樣的:
強度 = (kI/(d+do))(NL)
從游戲程序員的角度來看,這個公式太復雜了,而且速度會非常慢,所以我們甚至不會去理會它。我們的主要目標將是制作看起來正確(或至少合理)的陰影效果。我們并不特別關心我們使用的公式是否是正確的教科書公式。
(旁注:對于游戲編程,我傾向于同意這個原則:
最好是擁有快速且看起來合理正確的東西;擁有完全正確但緩慢的東西。)
因此,改為使用以下公式(Lampton 406)。
強度 = 物體強度/距離 * 乘數
這里,Object Intensity是程序員希望使用的強度(它應該在 0 和 1 之間)。這實際上在概念上非常簡單。它基本上是說隨著物體變遠,物體的強度變小。乘數是一個數字,以防止強度隨距離下降到快速。這種實時計算仍然很昂貴,因此可以使用如下表所示的距離表:
| 到物體的距離 | 強度 |
| 0 到 500 | 1 |
| 501 至 1000 | 0.75 |
| 1001 至 1500 | 0.50 |
光線投射過程在這里非常有用,因為當我們投射光線時,我們還獲得了到要渲染的對象的距離。在實際實現中,我們還需要考慮可用顏色的數量。由于大多數游戲只能使用 256 種顏色,因此需要進行一些操作以確保調色板包含正確的顏色范圍。一個可能的解決方案是使用顏色匹配算法并將結果映射到強度表中。渲染時,我們只需從相應的表中獲取正確的顏色值。(這非常快,因為一個特定的墻切片對于它的所有像素都具有相同的強度。所以我們只需要在墻切片之間切換表格。)
| 到物體的距離 | 強度 | 調色板映射表索引 |
| 0 到 500 | 1 | 1 |
| 501 至 1000 | 0.75 | 2 |
| 1001 至 1500 | 0.50 | 3 |
| … | … | … |
通常,當物體的強度接近零時,物體會顯得更暗。然而,情況并非總是如此。我們可以通過改變“目標顏色”來創建有趣的效果,例如霧或水下效果。例如,要創建霧效果,我們可以使調色板收斂為白色。
其他參考內容、參考文獻和注釋
https://dev.opera.com/articles/3d-games-with-canvas-and-raycasting-part-1/
總結
以上是生活随笔為你收集整理的用于游戏开发和其他目的的光线投射教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电磁场与电磁波(5)——静电场基本方程、
- 下一篇: 汉字区位码查询