2013-07-03 52 views
1

我正在嘗試使用OpenGL ES 2.0(iOS現在)製作2D遊戲引擎。我已經在Objective C中編寫了應用程序層,並在C++中包含了一個獨立的RendererGLES20。沒有GL特定的調用在渲染器之外進行。它工作完美。引擎渲染流水線:使着色器通用

但是我在使用着色器時遇到了一些設計問題。每個着色器都有自己獨特的屬性和制服,需要在主繪製調用之前設置(本例中爲glDrawArrays)。例如,爲了繪製一些幾何圖形,我會這樣做:

void RendererGLES20::render(Model * model) 
{ 
    // Set a bunch of uniforms 
    glUniformMatrix4fv(.......); 
    // Enable specific attributes, can be many 
    glEnableVertexAttribArray(......); 
    // Set a bunch of vertex attribute pointers: 
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, m->pCoords); 

    // Now actually Draw the geometry 
    glDrawArrays(GL_TRIANGLES, 0, m->vertexCount); 

    // After drawing, disable any vertex attributes: 
    glDisableVertexAttribArray(.......); 
} 

正如你所看到的,這段代碼非常僵硬。如果我要使用另一個着色器,比如漣漪效應,我需要傳遞額外的制服,頂點屬性等。換句話說,我將不得不更改RendererGLES20渲染源代碼,以便合併新的着色器。

有什麼辦法讓着色器對象完全通用?喜歡如果我只是想改變着色器對象而不擔心遊戲源重新編譯?任何方式使渲染器不受制服和屬性等的影響?即使我們需要將數據傳遞給制服,那麼最好的地方是什麼?模型類?模型類是否意識到着色器特定的制服和屬性?

繼節目演員類:

class Actor : public ISceneNode 
{ 
    ModelController * model; 
    AIController * AI; 
}; 

模擬控制器類: 類ModelController { 類IShader *着色器; int textureId; vec4 tint; float alpha; struct Vertex * vertexArray; };

Shader類只包含着色器對象,編譯和鏈接子程序等

在遊戲邏輯類我實際渲染對象:

void GameLogic::update(float dt) 
{ 
    IRenderer * renderer = g_application->GetRenderer(); 

    Actor * a = GetActor(id); 
    renderer->render(a->model); 
} 

請注意,即使演員延伸ISceneNode ,我還沒有開始實施SceneGraph。我會盡快解決此問題。

任何想法如何改善?相關設計模式等?

回答

0

當沒有屬性或制服改變時,你可以改變着色器,而無需重新編譯你的C++代碼。您可以通過使用glShaderSource加載來自文本文件的着色器代碼然後編譯它,或通過加載預編譯的二進制文件glShaderBinary來完成此操作。

要獲得某種封裝的不同渲染方法,您可以使用從RendererGLES20繼承的不同渲染器。這樣,只有渲染器需要知道着色器需要的屬性和制服。

class RendererGLES20 
{ 
public: 
    virtual void render(Model * model) = 0; 

    // ... 
} 

class RippleRenderer : public RendererGLES20 
{ 
    virtual void render(Model * model); 
    // ... 
} 

class BlinnPhongRenderer : public RendererGLES20 
{ 
    virtual void render(Model * model); 
    // ... 
} 

class CartoonRenderer : public RendererGLES20 
{ 
    virtual void render(Model * model); 
    // ... 
} 
+0

那麼在實際情況下,屬性和制服會發生變化。此外,在一個典型的遊戲中,可能會有10-15個着色器。所以這是15個擴展類,只適用於RendererGLES20。如果我想將遊戲移植到Wii(固定管道)或XNA(directX)上,該怎麼辦?您可以製作一個C++ DLL和C#接口,但是您必須重寫整個渲染器類和其他15個擴展渲染器。此外,每次添加一個新的着色器時,我都必須擴展一個新的渲染器,如果我決定放棄某些着色器,則必須刪除相關的類。不是一個好主意! – fakhir

+0

我想知道我們是否可以使渲染器完全不知道着色器的內部細節。 – fakhir

+0

好吧,就像datenwolf所說的那樣,OpengGL的新版本犧牲了程序員的便利性,以便提高性能和靈活性,所以沒有辦法使它變得通用。這只是分享一些渲染代碼和成員變量的一種方式。 – Simon

4

着色器的全部要點是特定。很長一段時間,通用API和狀態機的通貨膨脹毒化了OpenGL(請看OpenGL-1.5 glTexEnv)。現代OpenGL(v3及更高版本)的全部要點是擺脫這種通用性,以便着色器可以輕鬆地適應手頭的任務。不要認爲着色器是獨立於使用它們的渲染器代碼的東西。實際上着色器和客戶端渲染器代碼是互補的,通常是一致開發的。

+0

在上面的渲染器代碼中,如何在不更改渲染器源**的情況下將其與另一個着色器**交換?例如,我需要將默認着色器更改爲將着色器渲染10秒,然後將具有可能不同制服和屬性的投影着色器應用於接下來的20秒,然後返回默認着色器? – fakhir

+2

@ user2370784:呃,你沒有。無論如何,bloom着色器需要不同的渲染器(源代碼)。在你的想法中獲得這個:GLSL着色器源代碼是你的渲染器源代碼的一部分。如果你改變GLSL着色器,你需要一個不同的渲染器。 GLSL着色器不是你應該隨意丟棄的模塊。例如,一個漣漪着色器需要完全不同的輸入比一個綻放着色器;不僅僅是不同的制服,還有不同的頂點屬性。GLSL和渲染器代碼齊頭並進。 – datenwolf

+0

嗯,這很有趣。我認爲着色器是可拆卸的組件,比如紋理(或多或少)。那麼下面@Simon給出的解決方案是最好的?根據特定的着色器繼承一堆渲染器? – fakhir

0

正如datenwolf所說,着色器不應該是通用的。

也就是說,理論上,你可以換出着色器的代碼,你想,只要它採用全同uniforminout參數什麼都不會真的能分辨出來。也就是說,這可能不是一個好主意。

如果你想要這樣的靈活性,你可能想看看介紹一些排序中介腳本層。您的引擎可以爲您提供所需的所有信息,腳本層可以提供此腳本層,並且可以負責制定要設置的制服以及設置的內容。這不是一件微不足道的事情,但是你所要做的並不是那麼簡單。

+0

嗯,我不是在談論着色器代碼本身的概括(即:用ifs/defs,switch/cases等來擴大它)。我正在着眼於使着色器對象層(實際上管理着色器)更加高效和抽象。如果我想要將bloom着色器加載5秒然後返回默認着色器,該怎麼辦?在不改變渲染器代碼的情況下,我無法在上述源中做到這一點。 – fakhir

+0

從它的聲音中,你並不完全理解你將如何管理着色器。它們在一個程序中被鏈接在一起,所以如果你想使用一個不同的程序,你只需要'使用'它,然後'使用'下一個。如果你想改變一個程序中的一個着色器,你將不得不重新鏈接整個程序。這只是OpenGL工作方式的一個難以「限制」。 – thecoshman

+0

我意識到這一點。考慮以下內容:默認着色器將MVP矩陣與紋理採樣器一起統一。如果我想說出漣漪效應,我不僅要通過MVP和採樣器,還要通過delta時間(或其他變量),以便它可以正確計算漣漪。我也想把漣漪的直徑作爲制服發送。現在這些額外的制服在默認着色器中無法使用。所以你不能簡單地在上面提到的情況下做「glUseProgram」!你將不得不改變通過uniforms/attribs到着色器的代碼。 – fakhir

0

我不自稱是專家(我在學習使用OpenGL作爲你完全相同的階段),但這裏是我想嘗試:

也許你可以做一個抽象RenderEffect類並將其中的一個(列表)傳遞給RendererGLES20 :: render()。從中你可以派生類如RippleEffect和BloomEffect。 RenderEffect對象將包含渲染器對着色器進行必要的ogl狀態調整所需的所有信息。 這將基本上是一個容器類的:

  • 包含統一的名稱/ ID「UniformInfo」對象的列表,轉置和布爾(指針)的值。可以查詢類型和元件數量,因此不需要將其存儲在此處。
  • 與「UniformInfo」一樣,包含glVertexAttribPointer *()的所有必要信息的'VertexAttribInfo'列表。同樣,可以查詢組件數等一些內容。

派生類可以聲明相匹配所需的特定效果的信息是,並將其存儲在通過他們的父類中定義的列表參數的構造函數。所以RippleEffect的構造函數可能有delta t和直徑的參數,當它被調用時,它會爲每個對象創建和存儲一個UniformInfo對象。

最後,您可以製作所有這些數據驅動的並指定文本文件中的所有信息。這只不過是創建一個類似於所提出的思科系統的腳本系統而已。

PS: UniformInfo對象和VertexAttribInfo對象將存儲或引用不同類型的值。爲了解決這個問題,你可以爲每種類型創建不同的類,但我建議只存儲一個void *或類似的東西。另一個選擇是使用模板,但我不知道任何有關objective-c的內容,所以我不知道這是否可行。