文章出處

CSharpGL(37)創建和使用VBO的最佳方式

開始

近日在OpenGL紅寶書上看到這樣的講解。

 

其核心意思是,在創建VBO時用

glBufferData(GL_ARRAY_BUFFER, length, NULL, GL_STATIC_DRAW);

來初始化buffer占用的內存(此內存在GPU端),其中的 NULL 表示并未初始化數據(即此buffer中的數據是隨機值,類似在C語言中剛剛創建一個數組 int x[10]; 的情況)。

這樣,就不必在CPU端申請大量內存了。接下來需要初始化buffer數據時,用

1     IntPtr pointer = buffer.MapBuffer(MapBufferAccess.WriteOnly);
2     var array = (vec3*)pointer.ToPointer();
3     for (int i = 0; i < length; i++)
4     {
5         array[i] = this.model.positions[i];
6     }
7     ptr.UnmapBuffer();

來直接操作GPU上的數據即可。

使用這種方式,省去了CPU端創建大規模非托管數組并上傳到GPU的步驟,直接在GPU端創建了buffer,且所需代碼更少,可以說是目前我找到的最佳方式。

因此我在CSharpGL中集成并使用了這種方式。

頂點屬性buffer

CSharpGL中,用于描述頂點屬性數組(Vertex Buffer Object)的類型是 VertexAttributeBufferPtr 。以前,我們可以通過如下的方式創建 VertexAttributeBufferPtr 。

 1     // 先在CPU端創建非托管數組VertexAttributeBuffer<vec3>對象
 2 using (var buffer = new VertexAttributeBuffer<vec3>(
 3         varNameInShader, VertexAttributeConfig.Vec3, BufferUsage.StaticDraw))
 4     {
 5         buffer.Alloc(this.model.positions.Length);// 在CPU端申請內存
 6         unsafe// 使用指針快速初始化
 7         {
 8             var array = (vec3*)buffer.Header.ToPointer();
 9             for (int i = 0; i < this.model.positions.Length; i++)
10             {
11                 array[i] = this.model.positions[i];
12             }
13         }
14         // 將CPU端的VertexAttributeBuffer<vec3>上傳到GPU,獲得我們需要的buffer對象。
15         positionBufferPtr = buffer.GetBufferPtr();
16     }// using結束,釋放VertexAttributeBuffer<vec3>申請的非托管內存。
17     return positionBufferPtr;

可見,這種方式實際上是按下面的步驟創建VBO的。注意其中的data不是 NULL 。

1     uint[] buffers = new uint[1];
2     glGenBuffers(1, buffers);
3     const uint target = OpenGL.GL_ARRAY_BUFFER;
4     glBindBuffer(target, buffers[0]);
5     glBufferData(target, length, data, usage);
6     glBindBuffer(target, 0);

這種方式需要先在CPU端申請一塊內存,初始化數據,之后才能上傳到GPU端。現在我們用新的方式創建buffer,就不需要在CPU端申請內存了。

下面是創建buffer的方法。

 1     /// <summary>
 2     /// Creates a <see cref="VertexAttributeBufferPtr"/> object(actually an array) directly in server side(GPU) without initializing its value.
 3     /// </summary>
 4     /// <param name="elementType">element's type of this 'array'.</param>
 5     /// <param name="length">How many elements are there?</param>
 6     /// <param name="config">mapping to vertex shader's 'in' type.</param>
 7     /// <param name="usage"></param>
 8     /// <param name="varNameInVertexShader">mapping to vertex shader's 'in' name.</param>
 9     /// <param name="instanceDivisor"></param>
10     /// <param name="patchVertexes"></param>
11     /// <returns></returns>
12     public static VertexAttributeBufferPtr Create(Type elementType, int length, VertexAttributeConfig config, BufferUsage usage, string varNameInVertexShader, uint instanceDivisor = 0, int patchVertexes = 0)
13     {
14         if (!elementType.IsValueType) { throw new ArgumentException(string.Format("{0} must be a value type!", elementType)); }
15 
16         int byteLength = Marshal.SizeOf(elementType) * length;
17         uint[] buffers = new uint[1];
18         glGenBuffers(1, buffers);
19         const uint target = OpenGL.GL_ARRAY_BUFFER;
20         glBindBuffer(target, buffers[0]);
21         glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
22         glBindBuffer(target, 0);
23 
24         var bufferPtr = new VertexAttributeBufferPtr(
25             varNameInVertexShader, buffers[0], config, length, byteLength, instanceDivisor, patchVertexes);
26 
27         return bufferPtr;
28     }

使用這樣的方式創建了buffer,之后在初始化數據時,就得用glMapBuffer/glUnmapBuffer了。

 1     int length = this.model.positions.Length;
 2     // 創建buffer
 3     VertexAttributeBufferPtr buffer = VertexAttributeBufferPtr.Create(typeof(vec3), length, VertexAttributeConfig.Vec3, BufferUsage.StaticDraw, varNameInShader);
 4     unsafe
 5     {
 6         IntPtr pointer = buffer.MapBuffer(MapBufferAccess.WriteOnly);// 獲取指針
 7         var array = (vec3*)pointer.ToPointer();// 強制類型轉換
 8         // 初始化數據
 9         for (int i = 0; i < length; i++)
10         {
11             array[i] = this.model.positions[i];
12         }
13         buffer.UnmapBuffer();// 完成,上傳到GPU端。
14     }

對比來看,在內存上,新的方式省略了在CPU端申請非托管數組的開銷;在代碼上,新的方式也省去了對 VertexAttributeBuffer<vec3> 對象的使用( VertexAttributeBuffer<vec3> 類型完全可以不用了)。

索引buffer

OneIndexBufferPtr

對于使用 glDrawElements() 的索引對象,創建索引buffer就與上面雷同了。

 1     /// <summary>
 2     /// Creates a <see cref="OneIndexBufferPtr"/> object directly in server side(GPU) without initializing its value.
 3     /// </summary>
 4     /// <param name="byteLength"></param>
 5     /// <param name="usage"></param>
 6     /// <param name="mode"></param>
 7     /// <param name="type"></param>
 8     /// <param name="length"></param>
 9     /// <returns></returns>
10     public static OneIndexBufferPtr Create(int byteLength, BufferUsage usage, DrawMode mode, IndexElementType type, int length)
11     {
12         uint[] buffers = new uint[1];
13         glGenBuffers(1, buffers);
14         const uint target = OpenGL.GL_ELEMENT_ARRAY_BUFFER;
15         glBindBuffer(target, buffers[0]);
16         glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
17         glBindBuffer(target, 0);
18 
19         var bufferPtr = new OneIndexBufferPtr(
20              buffers[0], mode, type, length, byteLength);
21 
22         return bufferPtr;
23     }

ZeroIndexBufferPtr

對于使用 glDrawArrays() 的索引,則更簡單。

 1     /// <summary>
 2     /// Creates a <see cref="ZeroIndexBufferPtr"/> object directly in server side(GPU) without initializing its value.
 3     /// </summary>
 4     /// <param name="mode"></param>
 5     /// <param name="firstVertex"></param>
 6     /// <param name="vertexCount"></param>
 7     /// <returns></returns>
 8     public static ZeroIndexBufferPtr Create(DrawMode mode, int firstVertex, int vertexCount)
 9     {
10         ZeroIndexBufferPtr bufferPtr = new ZeroIndexBufferPtr(
11          mode, firstVertex, vertexCount);
12 
13         return bufferPtr;
14     }

使用方法也與上面雷同。

獨立buffer

對于AtomicCounterBuffer、PixelPackBuffer、PixelUnpackBuffer、ShaderStorageBuffer、TextureBuffer、UniformBuffer這些,我統稱為IndependentBuffer。他們當然也可以用新方法創建和使用。

 1     /// <summary>
 2     /// Creates a sub-type of <see cref="IndependentBufferPtr"/> object directly in server side(GPU) without initializing its value.
 3     /// </summary>
 4     /// <param name="target"></param>
 5     /// <param name="byteLength"></param>
 6     /// <param name="usage"></param>
 7     /// <param name="length"></param>
 8     /// <returns></returns>
 9     public static IndependentBufferPtr Create(IndependentBufferTarget target, int byteLength, BufferUsage usage, int length)
10     {
11         uint bufferTarget = 0;
12         switch (target)
13         {
14             case IndependentBufferTarget.AtomicCounterBuffer:
15                 bufferTarget = OpenGL.GL_ATOMIC_COUNTER_BUFFER;
16                 break;
17 
18             case IndependentBufferTarget.PixelPackBuffer:
19                 bufferTarget = OpenGL.GL_PIXEL_PACK_BUFFER;
20                 break;
21 
22             case IndependentBufferTarget.PixelUnpackBuffer:
23                 bufferTarget = OpenGL.GL_PIXEL_UNPACK_BUFFER;
24                 break;
25 
26             case IndependentBufferTarget.ShaderStorageBuffer:
27                 bufferTarget = OpenGL.GL_SHADER_STORAGE_BUFFER;
28                 break;
29 
30             case IndependentBufferTarget.TextureBuffer:
31                 bufferTarget = OpenGL.GL_TEXTURE_BUFFER;
32                 break;
33 
34             case IndependentBufferTarget.UniformBuffer:
35                 bufferTarget = OpenGL.GL_UNIFORM_BUFFER;
36                 break;
37 
38             default:
39                 throw new NotImplementedException();
40         }
41 
42         uint[] buffers = new uint[1];
43         glGenBuffers(1, buffers);
44         glBindBuffer(bufferTarget, buffers[0]);
45         glBufferData(bufferTarget, byteLength, IntPtr.Zero, (uint)usage);
46         glBindBuffer(bufferTarget, 0);
47 
48         IndependentBufferPtr bufferPtr;
49         switch (target)
50         {
51             case IndependentBufferTarget.AtomicCounterBuffer:
52                 bufferPtr = new AtomicCounterBufferPtr(buffers[0], length, byteLength);
53                 break;
54 
55             case IndependentBufferTarget.PixelPackBuffer:
56                 bufferPtr = new PixelPackBufferPtr(buffers[0], length, byteLength);
57                 break;
58 
59             case IndependentBufferTarget.PixelUnpackBuffer:
60                 bufferPtr = new PixelUnpackBufferPtr(buffers[0], length, byteLength);
61                 break;
62 
63             case IndependentBufferTarget.ShaderStorageBuffer:
64                 bufferPtr = new ShaderStorageBufferPtr(buffers[0], length, byteLength);
65                 break;
66 
67             case IndependentBufferTarget.TextureBuffer:
68                 bufferPtr = new TextureBufferPtr(buffers[0], length, byteLength);
69                 break;
70 
71             case IndependentBufferTarget.UniformBuffer:
72                 bufferPtr = new UniformBufferPtr(buffers[0], length, byteLength);
73                 break;
74 
75             default:
76                 throw new NotImplementedException();
77         }
78 
79         return bufferPtr;
80     }
public static IndependentBufferPtr Create(IndependentBufferTarget target, int byteLength, BufferUsage usage, int length)

總結

現在CSharpGL已經有點深度,所以筆記很難寫出讓人直接就能眼前一亮的感覺了。

目前CSharpGL中已經涵蓋了我所知的所有OpenGL知識點。下一步就是精心讀書,繼續深挖。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()