倉庫:https://gitee.com/mrxiao_com/2d_game_3
小妙招:
vscode:定位錯誤行 一頓狂按F8
重構快捷鍵:F2 重構相關的變量
回顧并為今天的內容做準備
今天的工作主要集中在渲染器的改進上,渲染器現在運行得相當不錯,得益于一些優化和組織上的改進。我們計劃在清理一些代碼方面做一些工作,尤其是整理那些我們現在已經理解和運作良好的部分。
昨天,我們意外地將渲染器恢復到了預期的工作狀態,這讓我有些驚訝。也許我做了些修改后,出現了預期的效果,我覺得這挺不尋常的,但幸運的是,我們成功了。
接下來,我們想做的事情是:我們已經把地面塊(ground chunks)重新放回來了,并且標記了這些塊的位置。接下來,我打算關閉這些標記,因為我們要處理渲染器的另一部分內容,特別是重新使用渲染器來重新合成位圖。這涉及到做一些不使用透視變換的工作。我們希望在生成這些地面塊時不引入透視變換,避免對其進行不必要的透視操作。
game.cpp: 關閉調試線
我們現在關閉了顯示黃色邊框的標記線,目的是讓場景的邊緣更清晰地顯示出來。關閉這些標記線后,我們會看到地面塊的渲染不再是無縫的,這正是我們預期的結果。問題的原因其實有些微妙,可能是大家已經很久沒有討論過地面塊的事情了,所以這也是我今天重新提起它的原因。
最初我們使用地面塊時,它能夠生成無縫的紋理,但是現在為什么渲染結果會出現這種不規則的圖案呢?這是因為我們已經在進行透視變換,而這種變換影響了地面塊的渲染,導致它們不再是無縫的。這是一個技術細節,可能之前并沒有專門處理過,直到現在才有機會來調整和修復這個問題。
打開是這樣的
黑板:為什么地面瓦片在進行透視變換后不再無縫
現在出現這種圖案的原因非常重要。之前在繪制地面塊時,我們有多個瓦片相鄰排列,比如瓦片A和瓦片B,它們是緊挨著的。當我們繪制瓦片時,我們會確保鄰近的瓦片也一起繪制出來,以保證這些瓦片的邊緣是連續的。因此,假如我們繪制了瓦片A,而瓦片B的某部分延伸到了A的邊界,我們會確保在繪制A時,也會把B的部分一同繪制出來,這樣就能保證圖案的無縫銜接。
然而,這種方式假設了一個前提,即坐標系始終是一樣的。這意味著在進行這些繪制時,無論怎樣偏移,坐標系統是統一的,而我們只是簡單地通過改變坐標的偏移量來調整瓦片位置。這個假設在我們使用正交變換(Orthogonal Transform)時是成立的。正交變換的特點是,無論物體離觀察者有多遠,投影到屏幕上的位置都不會發生變化。換句話說,正交變換下,遠近對物體在屏幕上的位置沒有影響,物體的水平和垂直位置保持不變。
然而,透視變換(Perspective Transform)則不同。透視變換的原理是,隨著物體離觀察者越來越遠,它的投影會逐漸向屏幕中心收縮。物體越遠離觀察者,它在屏幕上的位置就越靠近屏幕的中心。這意味著,在使用透視變換時,物體的距離會影響其在屏幕上的表現位置。
在當前的渲染中,問題出在我們在使用透視變換時,忽略了這種影響。由于沒有考慮到透視變換帶來的效果,導致瓦片之間的無縫連接出現了問題。雖然我們并不一定非要切換到正交變換,依然可以保持透視變換,但我們至少需要意識到透視變換的影響,并在設置渲染時進行相應的調整,確保物體在渲染時能夠正確地保持相對位置,從而解決這個問題。
黑板:使用透視變換做位移
例如,如果我們決定繼續使用透視模式,在進行位置偏移時,我們需要確定一個準確的偏移量,這個偏移量用于調整當前視圖中的中心點。為了實現這一點,我們需要計算出我們想要捕捉的區域的寬度,并按照這個寬度來進行位移。
然而,在之前的做法中,我們只是按照位圖的像素大小來進行位移,這樣做是沒有意義的。位圖的像素大小并不能告訴我們從一個位置到另一個位置需要偏移多少,因此這種做法是不合適的。
為了解決這個問題,最合理的做法是,雖然保持透視模式,但我們應該先確定一個我們希望捕捉的區域,并確保將相機放置在正確的位置,以便可以捕捉到該區域。然后再進行渲染操作。這樣的方法會更加合理,也能解決透視變換帶來的問題。
查看 FillGroundChunk 的工作方式
在填充地面塊的過程中,當前的實現存在一些問題。首先,創建了一個渲染組,并通過地面塊的高度來創建緩沖區,接著進行繪制操作。然而,在這個過程中,存在一個問題,就是沒有考慮到相機的位置以及相機視野的大小。
在代碼中,我們通過地面塊的寬度和高度來繪制,使用的是地面塊的尺寸(以米為單位)。看似這些操作會正確執行,但實際上,代碼并沒有考慮到當前相機的視野范圍。雖然我們傳遞了地面塊的偏移量和尺寸,但并沒有告訴渲染器這些值應該如何映射到屏幕上。
渲染器會根據假設的顯示器尺寸和變換來進行處理,而這些假設并沒有考慮到地面塊的實際尺寸。換句話說,渲染器只是根據默認設置假設所有東西都會對齊,但是沒有明確指定渲染區域的大小,導致在調整相機視角或位置時,渲染結果會有差異。
目前,唯一執行的操作就是依賴地面塊的寬度和高度進行渲染,但沒有實際設置渲染區域的尺寸,因此不能確保渲染結果的準確性。為了使渲染正確工作,需要明確設置渲染區域的大小,并確保與相機的位置和視野匹配。
提議根據現實世界的坐標來處理 GroundBuffers
在考慮如何處理地面塊時,未來可能需要向地面塊中插入一些元素,例如當角色在上面走動時,可能會在地面上留下腳印。為了實現這一點,需要確保能夠使用現實世界中的坐標系統來進行操作,而不僅僅是局限于像素級的坐標。
目前,渲染系統已經在一定程度上使用了這種方式,比如通過地面塊的中心來進行計算和寫入操作。為了實現更靈活的控制,應該考慮在地面塊中寫入數據時,能夠依據現實世界中的坐標來進行操作,而不是僅僅依賴于塊的局部坐標系統。這將使得插入數據(如腳印或其他元素)變得更加直觀,并且能夠與現實世界的坐標系統相對應,從而為未來的擴展和功能提供更多可能性。
遍歷 FillGroundChunk
在處理地面塊的渲染時,考慮到寬度和高度應該根據現實世界中的尺寸來設置,而不再僅僅依賴于像素或虛擬坐標系。因此,需要確保渲染緩沖區的寬度和高度是與實際的物理尺寸(如米或厘米)相對應的。這意味著,地面塊的坐標應該是相對于這些實際尺寸來設置的。
具體來說,當前的坐標應該以地面塊的中心為基準進行計算,而不需要外部干預。因此,所有的坐標和操作都應該基于現實世界的坐標系,以便在進行渲染時能夠得到正確的結果。這樣處理后,坐標將變得更加直觀,并且避免了額外的復雜性。
這也意味著,在進行渲染時,我們應該使用世界坐標系,而不是依賴于局部的坐標系,這樣可以確保渲染過程更符合預期,且方便后續的擴展和功能實現。
考慮告訴渲染器使用透視變換
在考慮如何讓渲染器正確使用坐標變換時,首先發現中心坐標不再需要單獨處理,因為默認情況下所有操作都會以中心點為基準進行。因此,實際上在渲染過程中,只需要確保所有操作都是從零點出發的,尤其是在處理當前地面塊時,中心點只用于處理其他地面塊的關系。
接下來需要確保的是,渲染中所有的縮放比例與標準地面塊在平面上的縮放比例一致。也就是說,當從一定高度看地面塊時,縮放應該與觀察到的尺寸相匹配。
目前的難點在于如何在保持世界坐標的情況下調整渲染方式。即使將坐標變換方式更改為正交投影(orthographic projection),似乎也能正常工作。因此,如何處理這些坐標變換和縮放比例是目前需要理清的一個關鍵點。
首先寫一個 API 調用給渲染器,啟用無縫地面瓦片使用正投影變換
首先,計劃是通過調用渲染器接口來設置所需的轉換方式,并將其傳遞給渲染器。這樣,在渲染器中會有一個方法來處理不同的變換情況,支持正交投影(orthographic)或其他方式。具體來說,目標是能夠設置一個正交投影模式,傳入地面塊的寬度和高度(單位為米),確保在渲染過程中,可以將元素準確地放入對應的區域。
對于渲染的具體實現,首先會將渲染器組設置為正交模式,并傳入緩沖區的像素尺寸和世界單位尺寸。這樣,所有后續的渲染操作都會基于這些參數進行調整。通過這種方式,確保渲染過程中一切都按照世界單位進行,不會發生尺寸上的錯誤或錯位。
總之,最終的目標是使渲染過程完全以世界單位為基礎來進行,從而使得渲染的對象在地面塊中準確顯示,避免任何視覺上的不一致性。
提供在透視和正投影之間切換的功能
計劃是要實現一個靈活的渲染設置,可以在正交模式和透視模式之間自由切換。首先,在渲染時,如果是使用正交模式,渲染器將會使用給定的寬度和高度(以米為單位)進行渲染。對于透視模式,渲染器將使用像素寬度和高度,并將其他必要的參數傳遞給渲染器,這些參數包括焦距、目標的距離以及MetersToPixels的轉換比例。
具體實現上,透視變換的設置將包括傳入焦距、目標高度、以及米與像素的比例,這些值將直接影響透視變換的計算方式。在渲染過程中,渲染器會使用這些數據來正確處理渲染物體的縮放和位置。對于渲染的設置,像素到米的轉換比例會被計算出來,并用來調整屏幕上顯示的物體大小和位置,確保物體的顯示符合期望的視覺效果。
此外,在渲染過程中還需要動態調整相關設置,以便在每次變換時都能夠準確地處理物理空間與屏幕空間之間的映射關系。因此,渲染器的配置也會包含一個持續跟蹤顯示尺寸(例如監視器的尺寸和顯示區域)的方法,確保無論何時變換渲染模式,渲染效果都能夠保持一致性。
總之,目標是通過清晰的設置和轉換,確保無論是正交模式還是透視模式,都能準確反映物體在世界坐標和屏幕坐標之間的映射,并且允許靈活地在這兩種模式之間切換。
game_render_group.cpp: 寫透視函數
為了實現所需的渲染設置,首先需要定義一個渲染區域,其中包括一些關鍵參數。首先是寬度和高度,以像素為單位。這些像素尺寸將決定渲染區域的大小。然后需要一個MetersToPixels的轉換比例,用來調整渲染區域的縮放。接著,還需要焦距和目標的距離等參數,這些決定了渲染的視角和深度效果。
雖然我們有這些參數,但目前有一些名稱上的混淆。例如,“分辨率像素”(resolution pixels)和"分辨率像素X"并不完全相同,可能會造成一定的困惑。因此,需要在后續考慮是否要將這些值分開指定,或者是否它們需要不同的處理方式,特別是在渲染的不同場景下。
一旦設置了透視變換,就能夠配置所有需要的渲染參數,這些設置將確保渲染區域的尺寸和視角符合預期。但同時,也需要考慮到可能需要根據實際情況調整這些參數,特別是在實現正交視角(orthogonal)時,可能需要略微調整參數以適應不同的渲染效果。
因此,當前的設置是基礎,但仍需考慮未來可能的調整和優化,確保渲染過程中能夠靈活應對不同的需求。
寫正投影函數
在處理正交渲染時,首先需要定義一些關鍵參數,包括渲染區域的像素寬度和高度以及其在實際世界中的米制寬度和高度。這些參數將用于設置渲染區域的縮放和顯示。值得注意的是,“像素到米”(PixelsToMeters)轉換實際上不再需要,因為現在的需求更多是處理“MetersToPixels”的轉換。
具體而言,"MetersToPixels"的轉換會因不同的使用情況而有所不同。例如,轉換比例的計算和應用依賴于具體的渲染需求。在渲染過程中,"MetersToPixels"轉換會影響到如何根據實際世界的尺寸調整顯示內容。
至于"PixelsToMeters"的轉換,它在多個地方都有使用。為了處理反向映射和投影,渲染過程中只需執行一個簡單的計算,這個計算基于焦距和距離的變化來進行調整。這樣,通過直接的焦距計算,可以確定從相機到目標物體的距離,從而正確地顯示渲染結果。
考慮如何讓透視和正投影通過管道流動
如果我們要全面支持三維渲染,可能需要將現有的計算轉換為齊次坐標(homogeneous coordinates)來處理這種轉換,但目前不確定是否真的需要這么做。因此,正在考慮如何在渲染管線中便捷地處理兩種模式(正交和透視)。
在這一過程中,一些參數的處理是非常簡單的。例如,屏幕中心在正交模式和透視模式下是相同的,因此不需要做額外的轉換。監視器的半尺寸(即“MonitorHalfDimInMeters”)也很簡單,因為這個參數直接由傳入的實際尺寸決定。
總的來說,雖然需要確保兩種模式之間的兼容性,但其中一些基本參數并不復雜,處理起來非常直接。
強制它們為直線型
可以考慮將正交和透視模式的處理方式分開,通過固定一些參數來避免引入扭曲現象。具體來說,可以強制要求在渲染時,傳入的像素寬度和像素高度與實際的米到像素的轉換值完全一致。這樣就避免了引入扭曲的復雜性,因為如果同時處理這兩者,就容易產生不一致的變換,導致渲染效果出現問題。
為了解決這個問題,可以讓大部分渲染設置直接沿用已有的配置,這樣就能確保渲染效果沒有問題,并且避免了復雜的修改。然后只需要考慮如何設置焦距和目標的距離,確保變換正常運行。
在填充地面塊(FillGroundChunk)時,可以將寬度和高度固定,不再允許它們產生變形。具體來說,可以將塊的維度(ChunkDim)轉化為米單位,并根據像素寬度與米單位之間的比例來確定米到像素的轉換值。這樣,就能確保渲染時處理的一致性和準確性。
清理并編譯后再繼續
目前面臨的問題是,我們需要確保在進行任何進一步的操作之前,先檢查當前的實現是否能夠正常工作。在此之前,需要確保在傳遞給渲染組時去除了多余的參數。特別是要清理掉一些不必要的傳入數據,比如分辨率的像素值,只保留實際需要的值。
為了確保沒有引入錯誤,首先要編譯并檢查代碼,確保沒有因為去除或修改這些參數而破壞現有的功能。一旦確認這些基本的修改不會造成問題,可以再進行更復雜的處理和改進。
game.cpp: 使正投影版本正常工作
現在需要確保能夠正確實現正交視圖。當前的任務是,確保傳入的“米到像素”的轉換和其他附加信息能夠順利工作,并且已經傳遞了這些信息。然而,當前的渲染使用的是透視投影,因此,接下來需要思考如何關閉透視效果,以便能夠正確切換到正交投影模式。
關鍵在于,找到一種方法來禁用透視投影的影響,從而使得正交視圖能夠正確呈現。這涉及到控制投影模式的切換,并確保所有的渲染操作都能按照正交投影的需求進行。
game_render_group.cpp: 在 entity_basis_p_result 中寫入正投影路徑
在渲染過程中,需要對原有函數進行修改,特別是函數的命名,可以改為“project”,以便更好地描述其作用。現在的函數有一些操作是我們在使用正交投影時不需要的,比如與實體相關的部分。對于正交投影,我們希望做的是簡化操作,比如:
- 結果縮放:正交投影下的結果縮放始終為1,因為正交投影不會進行任何縮放變化。
- 結果有效性:由于正交投影沒有變換,因此結果的有效性始終為真,不會受到其他因素的影響。
- 坐標變換:對于正交投影,坐標變換僅僅是將傳入的坐標值從米轉換為像素,具體來說就是對原始坐標進行米到像素的轉換。因此,正交投影下的結果坐標將直接等于原始坐標(在米到像素轉換后)。
對于透視投影,則需要其他的處理,比如應用焦距與深度距離的計算。
為了適應正交投影,必須保證原始坐標(x, y)在變換過程中保持不變,因此,在正交投影下,不會進行任何額外的坐標變換。為了保證這一點,需要引入一些新的計算參數,確保能夠正常處理坐標系的轉換。
此外,問題還涉及到如何處理縮放因子。在正交投影下,不需要復雜的縮放計算,而透視投影需要關注焦距與目標距離的比例。所以,在處理不同投影模式時,需要考慮這些因素,確保每種模式都能夠正確渲染。
最終,正交投影下,直接使用傳入的原始坐標進行簡單的米到像素轉換,避免了不必要的復雜變換,而透視投影則保留原有的焦距和深度計算,確保兩種投影方式能夠正確切換。
game_render_group.h: 引入 bool32 Orthographic
在處理正交投影和透視投影的情況下,可以通過引入一個條件判斷來區分兩者的行為。相比于引入系數或進行仿射變換,條件判斷顯得更加簡單直接。通過這種方式,可以在處理渲染時區分投影類型,從而簡化計算。
具體來說,正交投影的設置中會將“orthographic”設置為true
,而透視投影則將其設置為false
。這樣,渲染時可以根據“orthographic”的值來決定使用哪種投影類型,正交投影時只需要做米到像素的轉換,透視投影則繼續使用焦距和深度等計算。
關于焦距和目標距離的問題,正交投影不需要焦距和目標距離的具體值,因為它不涉及透視效果。而對于透視投影,焦距和目標距離是必須的。雖然可以將焦距和目標距離設置為非常大的值,但要注意確保Z剪裁平面不會造成任何物體被剪裁掉。為此,可能需要調整這些參數,以避免出現物體被不必要地裁剪的情況。
最后,在代碼實現中,需要注意正交和透視投影的相關處理。特別是在設置“orthographic”值時,確保正確區分不同的渲染模式,以便在渲染時正確應用相應的轉換和計算。如果遇到錯誤或問題,可以通過調試和修改相關參數來解決問題。
查看游戲內效果
現在需要做的就是確保一切正常工作,盡管目前看到的結果只是一個很小的塊,但是接下來只需要解決具體的細節問題,應該就能順利完成。
game_render_group.cpp: 遍歷 entity_basis_p_result
我們需要檢查渲染組的內容,特別是渲染基準的部分。在進行正交投影的變換時,目標是獲取屏幕中心的坐標,并將其與米到像素的轉換系數相乘,將傳入的x、y值擴展到相應的像素范圍。此時不需要對任何內容進行縮放,所有的東西應該保持其實際的米尺度。因此,計算時要確保所使用的尺度符合實際的米制單位。
更正 OriginalP 為 P
發現了一個問題,原本的計算是錯誤的。應當使用 P
而不是 OriginalP
。雖然這個錯誤可能對問題影響不大,但還是需要糾正。這一修改應該會給我們帶來正確的變換結果,盡管當前依然存在一些問題。做出這個修正后,預期結果應該會符合要求。
game.cpp: 考慮 PushBitmap 在 FillGroundChunk 中的作用
在進行操作時,遇到了一些問題,主要是在處理位圖時。當前傳遞的高度值似乎是以米為單位的,這讓我對每個“splat”在地面上的實際寬度產生了疑問。原本以為每個“splat”可能是5米寬,這顯得過大,因此推測它們的尺寸應該更小,可能更接近半米。雖然仍然不清楚這些“splat”應有的準確尺寸,但合理的假設是它們應該相當小,接近地面上的草塊尺寸。
不過,仍然存在一些問題,盡管嘗試做了一些調整,輸出的結果依然不太對,可能還需要進一步的修改。
game_render_group.cpp: 確保 MetersToPixels 計算正確
在檢查當前設置時,確認了“米到像素”(MetersToPixels)轉換是正確的。重點是確保縮放比例設置得當。對于正交視圖來說,“米到像素”和“像素到米”是互為反轉的關系。同時,顯示器的半尺寸(MonitorHalfDimInMeters)也被正確計算。因此,當前的設置應該沒有問題。
對于“米到像素”的轉換,它應該是一個相對較大的值,因為這是在進行縮放時的關鍵因素。
領悟時刻:比例不應該是 1.0f
在調試過程中,發現了問題的根源。原本預期的比例應該不是 1.0f,而是與“米到像素”(meters to pixels)轉換值相關的比例。因為原本的設置忽視了這一點,導致了比例計算錯誤,導致尺寸異常。經過調整,應該將比例值設置為正確的“縮放值”,而不是固定為 1.0f。
盡管找到了問題所在,但現在依然看不到任何渲染結果,說明還有其他問題存在,需要進一步檢查和解決。
game.cpp: 更正正射中的像素/米計算
在分析代碼后,發現了一個簡單的錯誤:在傳遞轉換時,實際上傳遞的是“像素到米”(pixels to meters),而不是應該傳遞的“米到像素”(meters to pixels)。這個錯誤導致了問題的發生,邏輯上應該是米轉換為像素,而不是反過來。因此,一旦修正了這個錯誤,問題應該就能得到解決。
離遠一點方便查看
地面有縫查看一下填充的位置
把乘以寬高去掉
查看游戲內效果
目前,盡管距離預期的效果已經越來越近,但問題仍然沒有完全解決。在渲染場景時,仍然可以看到一些明顯的問題,盡管部分場景看起來已經接近正確。對于這些渲染出來的地面補丁,有些場景似乎比較接近預期,而其他場景則明顯不對。
一些渲染的區域似乎出現了較大的錯誤,而有些區域則稍微接近正確,可能只是偶然的結果。問題可能出現在尺寸設置上,雖然這還不確定,可能是轉換設置的錯誤。要確認這個問題,必須進一步調試,檢查每個地面塊的渲染方式,確定轉換邏輯是否正確。
最終目標是確保地面補丁能夠正確生成并顯示,盡管目前的問題還沒完全解決,正在接近最終的正確狀態。
返回到全瓦片濺射并調整大小
目前的情況是,渲染出的大小似乎大致是合理的,但還不確定是否完全正確。對于某些物體的尺寸,可能存在一些疑慮。例如,如果我們將物體的大小設置為半米,感覺它們可能會稍顯不合理,甚至1米的長度也似乎有些過大,盡管從渲染效果來看,物體的尺寸有點比預期的小。
有時,這些尺寸可能比我們常用的標準尺寸要小得多,因此對于設置為2米的物體,可能顯得過于龐大。而且,實際使用中的尺寸可能比我們想象的要小一些,或者需要根據實際效果調整。整體來說,當前渲染結果看起來有些怪異,需要進一步檢查和調整這些尺寸,以確保渲染出的物體與預期一致。
草地 和草 高度分別設置為2.0f和0.1f 比較合理
創建棋盤格模式以突出顯示接縫
目前的思路是通過改變物體的顏色來幫助調試和查看效果。可以考慮通過某種方式,例如使用棋盤格模式來檢查不同區域的顯示。具體來說,可以在代碼中根據地塊(chunk)的坐標來設置不同的顏色。例如,當某個地塊的X坐標和Y坐標滿足特定條件時,給它們指定不同的顏色,如紅色和藍色,這樣就能更清晰地看到不同區域的渲染效果。
接下來,在渲染位圖時,需要將這些顏色應用到相應的區域,以便能直觀地了解渲染是否符合預期。這個過程中可能會使用到一些調試工具,幫助檢查問題所在,確保渲染結果能夠正確顯示出想要的效果。
雖然這只是一個調試思路,但通過這種方式,可以更容易識別渲染中的問題,找到需要修正的部分。
看到了接縫
目前的調試結果有些奇怪,雖然已經可以看到不同區域的渲染效果,但沒有預期中的那么明顯。不過,至少能在一些地方看到渲染的差異,雖然不如預期那么突出。問題雖然不大,但還是需要進一步調整。
接下來計劃是繼續修復那些不一致的部分,尤其是接縫(seams)問題,確保所有的渲染都能正常顯示,避免出現明顯的錯誤。現在的情況是,整體的工作進展還是比較順利的,除了少數地方的輕微錯誤,其他部分看起來已經接近完成。
由于時間問題,需要暫停一下,等明天再繼續完善。
改為從外面把參數傳進去
build.bat: 切換到 -O2 并環游世界
目前,渲染的速度已經足夠快,即使還沒有實現多線程渲染,性能也沒有明顯的卡頓。觀察到在重新創建地面區塊時,只有很小的暫停,所以現在的實現已經相當平穩了。這表明,一旦將渲染過程放到獨立線程中,地面區塊的系統應該會變得非常穩定,幾乎不會有卡頓現象。
總的來說,渲染過程已經接近理想狀態,性能方面也在不斷改進,下一步就是實現多線程,進一步提高效率。
我們什么時候會讓所有內容硬件加速,利用旋轉的強大能力?
我們目前已經具備了旋轉的能力,只是目前并沒有實際的需求去使用它。可能之前沒跟上進度的朋友沒有注意到,我們已經實現了旋轉功能,只是現在還沒有真正應用到項目中。如果以后有需要旋轉的場景,我們完全可以啟用這個功能,已經具備了相應的支持。
game_render_group.cpp: 演示旋轉
在渲染過程中,如果需要的話,可以隨時改變繪制的位圖。例如,我們可以修改位圖的坐標軸,使得渲染時所有的位圖都被旋轉。這意味著,不需要依賴硬件加速,也能實現旋轉效果。只需調整位圖的坐標變換,就可以達到預期的旋轉效果。如果需要的話,甚至可以讓不同線程上的渲染做不同的旋轉,盡管這種方法會有些奇怪并且可能造成一些不可預見的效果。不過,這完全是可以實現的。總之,旋轉功能已經可以在沒有硬件加速的情況下實現,完全由軟件完成。如果以后不需要這個旋轉效果,只需要恢復正常的坐標軸就可以了。
不同線程旋轉的不一樣
我們會看到如何找到每幀之間“昂貴”的函數嗎?目前我們只在像素渲染器上使用了 SIMD,因為我們知道
在調試和優化代碼時,尋找每幀之間調用的開銷較大的函數是非常重要的。當前,我們主要專注于調試視圖,已經有了一些基本的工具來幫助我們識別性能瓶頸。接下來,我們計劃添加一些額外的功能,比如一個簡單的分析器,這樣就可以更方便地識別那些在每一幀中被頻繁調用的昂貴函數。
對于一個較大的代碼庫,尤其是當你對這個代碼庫不太熟悉時,通常會使用采樣型的分析工具來幫助找出性能瓶頸。例如,可以使用像 tune 這樣的工具來進行采樣分析。如果你沒有直接控制代碼(比如你在一個別人寫的項目中工作),你可能需要借助這些外部工具來獲得分析數據。
而如果你是自己編寫所有代碼并且對代碼庫非常熟悉,就可以直接通過一些內置的工具來監控性能,并且進行優化。在這種情況下,性能分析工具的使用方法也會有所不同,可以選擇在編寫代碼時直接集成一些性能監控代碼,來幫助自己跟蹤和分析函數的執行情況。
總結起來,尋找性能瓶頸的技巧有很多種,既可以依賴內建的調試功能,也可以借助外部的工具。如果在自己的代碼中沒有合適的工具,采樣型分析工具將是非常有用的幫手。
你能簡要說明一下我們在開始實現游戲玩法代碼之前,計劃對引擎做哪些改進嗎?
在實現游戲玩法代碼之前,還有一些事情需要完成。大部分內容已經列在待辦事項中,所以接下來要做的主要是完成渲染器的工作。具體來說,想要加入一個粒子系統,并且引入一些光照效果。此外,我們在渲染器方面已經做了一些工作,現在需要完善這些內容。雖然有一些不確定因素,但基本思路已經有了。
關于資源流的管理,雖然實現起來比較簡單,但我們需要實際去編寫代碼,因為現在我們是在啟動時加載所有資源,接下來我們需要確保能夠在后臺異步加載資源。
調試代碼方面也有一些簡單的工作要做,比如處理字體、日志記錄、圖形化調試界面以及一些調整功能。這些工作雖然比較直接,但非常重要,因為它們能夠幫助我們更好地調試和開發。預計這些任務會花費幾周時間,工作量不小,但它們是必不可少的基礎工作。
音頻方面,計劃實現一個基礎的音頻系統,預計大約花費一周時間。我們會展示如何簡單地寫入音頻緩沖區,這是一個非常基礎的實現,但也是實現音頻功能的必要步驟。
總體來說,還有一些工作需要完成,雖然不復雜,但也是非常重要的。
這些將全部是非常游戲代碼的內容
接下來的工作主要是進入游戲開發階段。一旦完成了之前的基礎工作,就需要重新審視一些已經完成的內容,確保它們適合游戲的實際需求。這一階段將會更加關注游戲相關的內容,并且是游戲化的核心部分。
整個過程將會分為兩個階段。第一個階段是架構展示階段,這時會先處理一些關鍵的、準備好進入生產階段的內容,比如實體系統的搭建、動畫以及路徑規劃等。這個階段的目標是確保一些核心系統能夠穩定運行,為后續的開發打下基礎。
第二個階段則是生產階段,主要是在確保了核心內容穩定的前提下,開始大量構建其他功能和內容。盡管這兩個階段有不同的目標和側重點,但它們之間的關系是密切的,許多工作會相互交織在一起。例如,實體系統的開發可能會和其他系統同時進行,在不斷調整和完善中推動整體進展。因此,這兩個階段并非完全分離,而是相互影響,動態調整。
現在,為了從渲染器中獲得更多性能,你是否已經多線程化以在塊中渲染屏幕?GPU 是按設計并行的,那這兩者有什么不同?
目前為了提高渲染性能,采用了多線程渲染技術,將屏幕劃分為多個塊來并行處理。這種做法的目的是通過并行計算來加速渲染過程,從而提高整體的渲染效率。
關于問題中提到的“并行設計”和“多線程渲染”的不同,實際上它們是緊密相關的,但也有所區別。并行設計(parallel by design)通常指的是在設計系統時就考慮到如何通過并行處理來提高效率。它強調的是從系統架構上考慮任務分配和處理方式,使得多個任務可以同時進行。而多線程渲染則是并行設計的一種具體實現方式,通過使用多個線程同時處理渲染任務的不同部分(如渲染不同的屏幕區域),從而提高渲染的效率和性能。總的來說,前者是系統設計層面的概念,后者是具體的實現方式。
黑板:瓦片式與非瓦片式 GPU
有兩種不同類型的GPU使用方式,一種是“分塊(tiled)”方式,另一種是“非分塊(non-tiled)”方式。非分塊的方式與我們所用的方式基本不同,但也有一些相似之處。我們處理像素的方式與GPU的處理方式非常相似,唯一的區別在于我們通常一次處理4個像素,而GPU通常一次處理16個像素,且處理的塊通常更大一些。比如,在我們所用的硬件上,處理的通常是4個像素,而在一些常見的GPU上,它們會以16個像素的塊進行處理。這種處理方式與我們所使用的方式是相似的,但由于硬件的不同,處理的塊大小有所不同。
非分塊的GPU架構通常是通過處理三角形來進行像素操作,這種方式的內存控制與我們的方式沒有太大關系。而分塊架構的GPU與我們的處理方式非常類似,它們也會將屏幕劃分成若干個塊,然后獨立地處理每個塊。GPU通常會將這些塊的處理任務放入隊列中,然后使用多個獨立線程并行處理不同的塊。當一個塊處理完成后,GPU會將其從隊列中移除,并取出新的塊進行處理。這種方式與我們處理圖像的方式非常相似,尤其是對于移動端的GPU,它們通常采用這種分塊架構。與此不同的是,桌面級的GPU通常不是分塊架構的,它們的工作方式可能與我們所描述的有所不同,但這些具體的實現細節不太了解,因為并沒有深入接觸過桌面GPU的硬件設計或編程。
總體來說,分塊架構的GPU常見于移動設備,而非分塊架構的GPU則更多出現在桌面設備中。
如果沒有更好的問題,是否內部將對象鏈接成任意數量的鏈表會對性能有影響?還是我錯過了什么智能解決方案?
關于在任意數量的鏈表中內部鏈接對象的問題,通常來說,內部鏈接對象到多個鏈表并不會帶來性能上的問題。但實際上,通常不會將對象內部鏈接到任意數量的鏈表中。原因是,當需要將對象鏈接到多個鏈表時,通常會選擇外部鏈接。
如果你有一個已知數量的鏈表,那么可以在內部實現鏈表鏈接。也就是說,內部鏈接適用于鏈表數量固定的情況,而如果鏈表的數量是動態的或不確定的,通常會選擇外部管理這些鏈表。因此,問題的關鍵在于鏈表的數量是否是已知的,已知數量的鏈表可以使用內部鏈接來管理對象。
我感覺地面塊比樹木在同一平面上移動得更快
感覺像是地面部隊的移動速度比平面上的樹木還要快。可能是這樣,因為實際上并不確定地面部隊是否和樹木在同一位置上。到目前為止,甚至還沒有確定這一點。因此,接下來需要去處理這個問題,清理和調整相關的部分。
游戲行業太難進入了。為什么潛在的程序員浪費時間?
進入游戲行業確實很困難,可能潛在的程序員會浪費時間在這個領域上,我不太清楚具體原因,也許是因為他們想要編程游戲吧。對于這個問題,我也不太了解,現在的游戲行業和很多年前的情況完全不同了。
我即將開始學習計算機科學學位。我喜歡游戲,創建游戲的概念很有趣,盡管我在數學方面不太擅長。鑒于此,你會建議我遠離游戲開發,轉向我原本的計劃——軟件工程嗎?
如果你不擅長數學,但對編程感興趣,特別是游戲開發,建議不要完全放棄游戲開發的方向。不過,游戲開發確實涉及大量數學,如果你對數學本身沒有興趣,可能不太適合從事游戲編程。數學在游戲開發中扮演著非常重要的角色,比如圖形學、物理模擬等方面。至于如果你僅僅是在高中時數學不好,但對編程有興趣,仍然可以嘗試,因為實際的編程工作并不完全依賴于高中的數學成績。
我有 Ruby 和 JS 背景,我想知道你們是否通常在游戲開發中做單元測試/行為測試,還是因為有視覺元素,做起來太難了?
在游戲開發中,確實可以進行單元測試,尤其是對于一些核心組件,比如內存分配器等。對于這些復雜的、獨立的系統,單元測試是有意義的,可以幫助發現一些潛在的bug。然而,對于整個游戲本身,進行有效的單元測試是非常困難的。游戲的交互性和視覺效果使得構造能夠有效發現bug的單元測試變得非常復雜且成本高昂。因此,游戲開發中通常采取的是更隨機化的測試方法。通過這種方式進行一些自動化測試,可以幫助發現一些問題,但這并不是完全依賴單元測試的解決方案。
我們目前知道每幀游戲邏輯的毫秒數嗎?目前我們推的是 60 FPS,雖然是硬鎖定的,可能我們已經有一些空閑的空間來處理游戲邏輯了?
目前游戲邏輯每幀的時間應該是相當充裕的。雖然游戲的幀率設定為每秒60幀,并且它是鎖定的,但在游戲邏輯的計算上應該還有很多空間。主要原因是屏幕上的像素數與游戲中的實體數量差異巨大。舉個例子,假設屏幕分辨率是1920x1080,意味著有約200萬個像素,而游戲中顯示的實體最多只有100個左右。相比之下,游戲邏輯的計算量遠低于圖形渲染的計算量。因此,游戲邏輯應該不會成為瓶頸。同時,已經為世界其他地方的背景模擬做好了準備,這有助于確保即使在大規模模擬時,也不需要過度擔心性能問題。
所有動畫都是程序化完成的,還是會由藝術家來做?還是兩者都有?
希望一切動畫都能通過程序化的方式實現,而不是由藝術家手工制作。這樣可以更靈活地控制動畫效果,并且更容易進行調整和擴展。
你能展示一段你實現哈希表的代碼嗎?
這個哈希表的實現方法是,我們首先通過計算世界中某個區域的x、y、z坐標來生成哈希值。然后,我們通過掩碼(masking)操作,從哈希值中去除掉不需要的部分,確保哈希值適合存入哈希表。
在哈希表的存儲結構中,我們采用了外部鏈式法(External Chaining),也就是說,在哈希表的每個槽中存儲的是一個鏈表,多個值可能會映射到同一個槽,我們就通過遍歷這些鏈表來查找所需要的元素。簡而言之,這就是一個使用鏈表處理哈希沖突的哈希表。
此外,在代碼中,我們通常會看到一個“更好的哈希函數”的笑話,這表明這個哈希函數可能還不是最優的,可能以后還會進行改進。
如果 Vulkan 在你硬件加速渲染器時已經發布,我們可以使用 Vulkan 嗎?
如果使用 Vulkan 渲染的話,理論上是可行的。盡管現在渲染速度已經非常快,可能暫時不需要深入探索渲染的優化問題。我們很可能會在最后準備發布前才進行這方面的優化。
你認為微軟為什么決定在 Xbox One 上省略 1080i 分辨率選項?你認為他們很難包括它嗎?
關于微軟決定在 Xbox One 上省略 1080p 分辨率選項的問題,這個話題和當前討論并不相關,而且也不清楚微軟做出這樣的決策背后的具體原因。因此,很難給出準確的解釋。
今天很多游戲都有延遲,真是太不可思議了。感謝你為這些問題(糟糕的編程和/或語言)提供了一些見解
今天的計算機硬件已經非常強大,尤其是圖形處理單元(GPU)和中央處理單元(CPU)都非常快速,因此出現許多存在延遲的游戲顯得有些不可理解。盡管有時候可能是操作系統或者顯卡驅動的問題,導致游戲出現卡頓、延遲等現象,但很多時候問題并非出在硬件上,而是因為游戲開發者在編程時沒有采取優化措施。
在許多情況下,游戲開發者可能因為不懂或者懶得投入時間去寫高效的代碼,導致游戲出現延遲或者低幀率等問題。雖然這種現象并不一定完全歸咎于開發者,但游戲的性能,特別是流暢度,應該是一個重要的開發目標。確保游戲在高性能硬件上也能提供流暢的體驗應該是開發者的首要任務。
然而,現實中很多開發者更注重選擇自己熟悉的工具,可能會選擇像C#這樣的語言,但這些語言在性能方面可能會有所限制,導致最終的游戲體驗無法達到理想的流暢度。雖然使用這些語言開發游戲可能方便,但如果忽視了性能問題,最終可能會導致游戲中的卡頓、長時間加載等問題,這會影響玩家的游戲體驗。
因此,開發者應該始終把良好的性能作為開發游戲的基本要求。只有當確保了良好的性能之后,才能再考慮如何讓開發過程更加便捷。為了整個行業的進步,我們應該堅持這一點,只有玩家要求更高的游戲表現,開發工具和引擎才會不斷改進。最終,游戲開發者的職責是確保玩家能夠獲得流暢、穩定的體驗,因為計算機硬件本身就有足夠的能力支持這一點。
不安全的內存是否是現代游戲高性能優化所需的關鍵?我聽說這是像 Rust 和 Haskell 這樣的內存安全語言不能接管低延遲應用或驅動程序的關鍵原因,但我對這個領域了解不夠
關于現代游戲和高性能應用程序中是否需要不安全內存操作的問題,觀點認為內存安全可能與高性能存在沖突,但這種看法并不完全成立。實際上,內存安全本身并不必然導致性能問題。問題的關鍵在于目前的語言,它們在提供內存安全的同時,可能并不適用于高性能的需求。
未來的編程語言有可能在保證內存安全的同時,仍能提供高性能的能力。當前技術尚未達到這一水平。值得注意的是,內存安全和垃圾回收并不完全等同,垃圾回收需要在運行時分析指針并自動釋放內存,這種做法會影響性能,因此難以滿足高性能需求。然而,內存安全本身并不需要依賴于垃圾回收機制,未來可能會出現不依賴這些機制、依舊保證內存安全的編程語言。
目前來看,內存安全和性能之間的矛盾是由現有的編程語言和技術所造成的,但這并不意味著這兩者不能共存。隨著技術的發展,未來可能會發現,內存安全和高性能實際上是可以和諧共存的。此時,一種高性能的語言可能也能夠提供內存安全,因為這兩者可能是相輔相成的。
總的來說,當前我們還處于編程技術發展的初期階段,很多解決方案尚未成熟。在未來的二十年內,隨著技術的不斷進步,我們有可能會找到既能保證內存安全又能提供高性能的語言和技術。