2011-07-23 23 views
4

我正在使用OpenTK編寫我自己的引擎(基本上只是C#的OpenGL綁定,gl *變成了GL。*),而且我將要存儲大量的頂點緩衝區每個頂點有數千個頂點。因此我需要自己的自定義頂點格式,因爲帶有浮點數的Vec3會佔用太多空間。 (我說的是數以百萬計的頂點位置)如何使用OpenGL製作自定義頂點格式

我想要做的就是創建自己的頂點格式用這個佈局:

Byte 0: Position X 
Byte 1: Position Y 
Byte 2: Position Z 
Byte 3: Texture Coordinate X 

Byte 4: Color R 
Byte 5: Color G 
Byte 6: Color B 
Byte 7: Texture Coordinate Y 

這裏是頂點在C#代碼:

public struct SmallBlockVertex 
{ 
    public byte PositionX; 
    public byte PositionY; 
    public byte PositionZ; 
    public byte TextureX; 

    public byte ColorR; 
    public byte ColorG; 
    public byte ColorB; 
    public byte TextureY; 
} 

作爲每個軸的位置的字節是很多,因爲我只需要32^3個獨特的位置。

我已經寫了我自己的頂點着色器,它需要兩個vec4作爲輸入,對每組字節。 我的頂點着色器是這樣的:

attribute vec4 pos_data; 
attribute vec4 col_data; 

uniform mat4 projection_mat; 
uniform mat4 view_mat; 
uniform mat4 world_mat; 

void main() 
{ 
    vec4 position = pos_data * vec4(1.0, 1.0, 1.0, 0.0); 

    gl_Position = projection_mat * view_mat * world_mat * position; 
} 

要嘗試找出問題,我已經做了我的頂點着色器儘可能簡單。 編譯着色器的代碼使用即時模式繪圖進行測試,並且它可以工作,所以它不可能如此。

這裏是我的函數,它生成,設置和填充頂點緩衝區的數據,並建立一個指向屬性的指針。

public void SetData<VertexType>(VertexType[] vertices, int vertexSize) where VertexType : struct 
    { 
     GL.GenVertexArrays(1, out ArrayID); 
     GL.BindVertexArray(ArrayID); 
     GL.GenBuffers(1, out ID); 
     GL.BindBuffer(BufferTarget.ArrayBuffer, ID); 

     GL.BufferData<VertexType>(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * vertexSize), vertices, BufferUsageHint.StaticDraw); 

     GL.VertexAttribPointer(Shaders.PositionDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 0); 
     GL.VertexAttribPointer(Shaders.ColorDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 4); 
    } 

據我瞭解,這是正確的步驟: 生成一個頂點數組對象,並將其綁定 生成頂點緩存,並將其綁定 填寫頂點緩衝帶設置屬性指針

數據

着色器*在編譯和使用着色器後,使用此代碼設置DataID。

PositionDataID = GL.GetAttribLocation(shaderProgram, "pos_data"); 
ColorDataID = GL.GetAttribLocation(shaderProgram, "col_data"); 

這是我的渲染功能:

void Render() 
{ 
    GL.UseProgram(Shaders.ChunkShaderProgram); 

    Matrix4 view = Constants.Engine_Physics.Player.ViewMatrix; 
    GL.UniformMatrix4(Shaders.ViewMatrixID, false, ref view); 

    //GL.Enable(EnableCap.DepthTest); 
    //GL.Enable(EnableCap.CullFace); 
    GL.EnableClientState(ArrayCap.VertexArray); 
    { 
      Matrix4 world = Matrix4.CreateTranslation(offset.Position); 
      GL.UniformMatrix4(Shaders.WorldMatrixID, false, ref world); 

      GL.BindVertexArray(ArrayID); 
      GL.BindBuffer(OpenTK.Graphics.OpenGL.BufferTarget.ArrayBuffer, ID); 

      GL.DrawArrays(OpenTK.Graphics.OpenGL.BeginMode.Quads, 0, Count/4); 
    } 
    //GL.Disable(EnableCap.DepthTest); 
    //GL.Disable(EnableCap.CullFace); 
    GL.DisableClientState(ArrayCap.VertexArray); 
    GL.Flush(); 
} 

誰能這麼好心給我一些指點(沒有雙關語意)?我是以錯誤的順序來做這件事還是有一些我需要調用的功能?

我在網上搜索了所有內容,但找不到一個很好的教程或指導,解釋如何實現自定義頂點。 如果您需要更多信息,請說明。

+0

那麼...當你運行它會發生什麼?無論如何,你應該做的是讓系統處於工作狀態。如果你知道如何使它與浮點數據一起工作,那就使用它。讓它工作。一旦你的東西渲染正確,那麼你可以慢慢優化數據。在每一步中,驗證它是否有效。然後,當你達到破發點時,你知道哪裏出了問題。 –

+0

如果我運行它,沒有任何反應。也就是說,它是_runs_,但沒有顯示。我也會嘗試使用浮點數據並從那裏開發,但問題是我不知道我應該怎麼做才能獲得我自己的頂點格式(因此標題)。這是一個重大的關鍵步驟(可能不能分成更小的步驟),因爲很多事情都可能出錯。所以這基本上只是試驗和錯誤。如果你知道如何,你能發現我的代碼中有任何錯誤嗎? – Azzi777

回答

8

製作自己的頂點格式沒有太多。這一切都在glVertexAttribPointer調用中完成。首先,你使用4作爲步長參數,但是你的頂點結構是8個字節寬,所以從一個頂點到下一個頂點有8個字節,所以步幅必須是8(在兩次調用中,當然)。偏移量是正確的,但是你應該爲顏色設置歸一化標誌爲真,因爲你一定希望它們處於[0,1]範圍內(我不知道頂點位置是否也是這種情況) )。

接下來,在着色器中使用自定義頂點屬性時,不要啓用棄用的固定函數數組(gl...ClienState)。相反,您必須使用

GL.EnableVertexAttribArray(Shaders.PositionDataID); 
GL.EnableVertexAttribArray(Shaders.ColorDataID); 

和相應的​​調用。

什麼是count/4glDrawArrays調用中的含義。請記住,最後一個參數指定了頂點的數量,而不是基元(在你的情況下是四邊形)。但也許它是用這種方式。

除了這些真實的錯誤之外,您不應該使用您必須在着色器中自己解碼它的副本頂點格式。這就是glVertexAttribPointer的步幅和偏移量參數。例如重新定義你的頂點數據位:

public struct SmallBlockVertex 
{ 
    public byte PositionX; 
    public byte PositionY; 
    public byte PositionZ; 
    public byte ColorR; 
    public byte ColorG; 
    public byte ColorB; 
    public byte TextureX; 
    public byte TextureY; 
} 

,然後你可以使用

GL.VertexAttribPointer(Shaders.PositionDataID, 3, VertexAttribPointerType.UnsignedByte, false, 8, 0); 
GL.VertexAttribPointer(Shaders.ColorDataID, 3, VertexAttribPointerType.UnsignedByte, true, 8, 3); 
GL.VertexAttribPointer(Shaders.TexCoordDataID, 2, VertexAttribPointerType.UnsignedByte, true, 8, 6); 

而在着色器,你有

attribute vec3 pos_data; 
attribute vec3 col_data; 
attribute vec2 tex_data; 

,你不必提取紋理座標從自己的位置和顏色。

如果您的空間需求確實需要使用頂點位置的字節,您應該真的考慮一下,因爲這極大地限制了位置數據的精度。也許短褲或半精準浮標將是一個很好的折衷。

而且也應該不會neccessary調用的渲染方法glBindBuffer,因爲這僅僅需要glVertexAttribPointer,並保存在得到由glBindVertexArray激活VAO。當緩衝區交換時(假設您使用雙緩衝),您通常也不應該調用glFlush,因爲操作系統無論如何都要這樣做。

最後但並非最不重要的一點是,確保您的硬件也支持您使用的所有功能(如VBO和VAO)。

編輯:其實陣列的啓用標誌也存儲在VAO,這樣就可以調用

GL.EnableVertexAttribArray(Shaders.PositionDataID); 
GL.EnableVertexAttribArray(Shaders.ColorDataID); 
SetData方法

(創建和綁定VAO後,當然)和然後在渲染函數中通過glBindVertexArray綁定VAO時,它們會啓用。哦,我剛看到另一個錯誤。在渲染函數中綁定VAO時,屬性數組的啓用標誌將被來自VAO的狀態覆蓋,因爲您在創建VAO之後未啓用它們,它們仍處於禁用狀態。所以你必須像上面說的那樣去做,在SetData方法中啓用數組。實際上,在你的情況下,你可能會很幸運,當你在渲染功能中啓用陣列時,VAO仍然是綁定的(因爲你沒有調用glBindVertexArray(0)),但你不應該指望這一點。

+0

感謝您提供非常詳細的回覆。這解釋了_lot_!我明天會看看並報告。我在每個位置座標軸中只需要一個字節的原因是因爲每個頂點都是大小爲32 * 32 * 32的塊的一部分,我只需要顯示整數位置。然後我用一個平移矩陣來抵消頂點。 – Azzi777

+0

@ Azzi777好的,在這種情況下字節可能是個好主意。 –

+0

@ Azzi777更新了我的答案,發現了另一個可能的問題。 –

相關問題