學計算機圖學時,自己寫的軟渲染器代碼結構是否應模仿OpenGL的狀態機模式?
最近自己在嘗試寫一個軟渲染器,寫了一些基本的頂點轉換、貼圖後開始遇到接下來的功能不知如何組織進代碼,感覺每新增一個程式結構就會大改。現在在考慮是否應該模仿API的調用方法和OpenGL的狀態機模式。主要是對這整個渲染器腦中勾勒不出一個完整架構,所以需要對象來讓我參考。而小弟覺得狀態機模式個人覺得不直覺,所以各位有什麼想法或是有什麼開源軟渲染器的架構可以提供給小弟知道(不過太龐大的小弟嗑不下阿...)?感激不盡!
瀉 @Milo Yip 葯。
不應該專門選用狀態機式的API。對於渲染器來說,渲染的屬性有這些特點:- 渲染狀態(Render State)可以Draw來組織,RS和Draw之間是一對多的關係。
- RS要有一定的完整性,不能任意缺少屬性。
- Rendering API一般要求是非同步工作的,前一個Draw沒有畫完,就需要準備後一個Draw的數據。這意味著不同Draw之間的RS是相互獨立的。
- RS的內部屬性之間,有約束和關聯。所以儘管渲染狀態的組合非常多,但是有效組合比較少。
- RS的復用性好。一個渲染狀態往往會針對多個對象、在多幀的時候同時有效。考慮到RS的完整性,在復用的時候可以不再做檢查,這可以節省很多的時間。
如果把狀態機和每次Draw都使用獨立的RS Object相比較,那麼前者的優點是:
- 對於使用渲染器的用戶程序來說,如果前一個RS和後一個RS只有一兩個屬性的差異,那麼狀態機可以顯著減少代碼量。
- 渲染器可以實現渲染狀態的增量更新。對於硬體來說可以減少匯流排上數據的傳輸量。
缺點是:
- 以狀態機的形式存在的API,會暴露大量無用、且錯誤的中間狀態。大大增加了用戶使用API錯誤的可能性。並且因為屬性檢查要到繪製前才能進行,所以出錯位置和代碼不在同一處,甚至可能相距甚遠,很影響Debug。
- 渲染順序之間是隱式依賴的。顛倒了渲染順序就會獲得完全不一樣的結果,而且這種結果上的改變經常難以察知。
- 如果不增加保存可以 Save/Store 中間結果的API,RS 將難以復用。
- RS Object是Immutable的。一旦創建後不可更改;
- 每個Draw依賴且僅依賴一個RS Object;
- 允許RS Object的Clone,並在Clone時,提供RS Object內部屬性細粒度的訪問和修改(類似於RS Object內的狀態機)。
- 如果RS Object切換時需要拷貝(例如Renderer和Application在不同的進程),並且拷貝成本非常高,那麼在RS Object切換時,需要檢查屬性變化,進行傳輸優化。
對於GL的API來說,有另外一個嚴重的問題,就是有很多Binding,都是完全不必要的,因為根本不存在那些中間的Binding Point,比如Texture。GL有著大量人為增加的,毫無必要的複雜度,這種設計的愚蠢程度已經超出對狀態機API的討論範圍了。
--------------------- 以下是安利時間 -----------------------
對於SALVIA來說,
Render Core 是真正的渲染器實現,以Render State Object來組織的。
wuye9036 / SALVIA/ source / salviar / include / render_core.h
Renderer 是類似於 DX9 的API,介乎於狀態機和Object-based API之間。
wuye9036 / SALVIA
/ source / salviar / include / renderer.h
/ source / salviar / src / async_renderer.cpp
不允許glTranslate, glRotate, glScale,以及glPushMatrix, glPopMatrix這幾個函數,把所有圖元的坐標全部轉換到world坐標系下面。然後繪製的時候設置view matrix 和 projection matrix.這樣應該思路要清晰一些。
鼓勵題主按照自己的思路堅持寫下去,總結出不舒服的地方,帶著問題去讀mesa3d。
推薦閱讀:
※為什麼CryENGINE3引擎號稱最強大、最真實的引擎,但在孤島危機2/3當中的畫面仍然有一種光澤度過強的塑料質感?
※Unity中DrawCall和openGL、光柵化等有何內在聯繫,為什麼說DC降低有助於渲染性能優化?
※unity走圖形學好,還是轉行做架構?