文章出處

CSharpGL(38)帶初始數據創建Vertex Buffer Object的情形匯總

開始

總的來說,OpenGL應用開發者會遇到為如下三種數據創建Vertex Buffer Object的情形:

  1. 任意一個struct類型T data;
  2. 任意一個元素類型為struct的數組T[] array;
  3. 任意一個非托管數組UnmanagedArray<T> array;

而可創建的Vertex Buffer Object也分為如下的類別:

  1. 描述頂點屬性(位置、顏色、法線等)的VertexBuffer;
  2. 描述索引的IndexBuffer;
  3. 描述其他自定義內容的各種Buffer;

本文介紹用C#如何實現上述功能。

非托管數組->VertexBuffer

最基本的功能是通過非托管數組UnmanagedArrayBase創建一個VBO,我們首先實現這個功能。

 1         public static VertexBuffer GetVertexBuffer(this UnmanagedArrayBase array, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = 0, int patchVertexes = 0)
 2         {
 3             uint[] buffers = new uint[1];
 4             glGenBuffers(1, buffers);
 5             const uint target = OpenGL.GL_ARRAY_BUFFER;
 6             glBindBuffer(target, buffers[0]);
 7             glBufferData(target, array.ByteLength, array.Header, (uint)usage);
 8             glBindBuffer(target, 0);
 9 
10             var buffer = new VertexBuffer(
11                 varNameInVertexShader, buffers[0], config, array.Length, array.ByteLength, instancedDivisor, patchVertexes);
12 
13             return buffer;
14         }

 

T[] -> VertexBuffer

很多時候,大家都是在用習慣了的托管數組(int[]、Point[]、vec3[]等)。那么能不能直接用托管數組創建VBO呢?當然可以。雖然是托管數組,但是在內存中畢竟也還是連續存放的一塊內存。我們只需找到它的地址就可以了。找地址這件事通過 Marshal.UnsafeAddrOfPinnedArrayElement(); 就可以做到。

 1         public static VertexBuffer GetVertexBuffer<T>(this T[] array, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = 0, int patchVertexes = 0) where T : struct
 2         {
 3             GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
 4             IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
 5             UnmanagedArrayBase unmanagedArray = new UnmanagedArray<T>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
 6             VertexBuffer buffer = GetVertexBuffer(unmanagedArray, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
 7             pinned.Free();
 8 
 9             return buffer;
10         }

 

T -> VertexBuffer

那么單獨的一個struct變量,如何為之創建VBO?只需用一個 var array = new T[1]{ data }; 將其封裝起來,就可以用上面的方法了。

 1         public static VertexBuffer GetVertexBuffer<T>(this T data, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = 0, int patchVertexes = 0) where T : struct
 2         {
 3             var array = new T[] { data };
 4             return GetVertexBuffer(array, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
 5             // another way to do this:
 6             //using (UnmanagedArrayBase unmanagedArray = new UnmanagedArray<T>(1))
 7             //{
 8             //    Marshal.StructureToPtr(data, unmanagedArray.Header, false);
 9             //    VertexBuffer buffer = GetVertexBufferObject(unmanagedArray, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
10             //    return buffer;
11             //}
12         }

非托管數組->IndexBuffer

非托管數組->OneIndexBuffer

從非托管數組到OneIndexBuffer的思路和上面一致。要注意的是,OneIndexBuffer能接受的元素類型只能是byte、ushort、uint三者之一。

 1         public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<byte> array, DrawMode mode, BufferUsage usage, int primCount = 1)
 2         {
 3             return GetOneIndexBuffer(array, mode, usage, IndexElementType.UByte, primCount);
 4         }
 5 
 6         public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<ushort> array, DrawMode mode, BufferUsage usage, int primCount = 1)
 7         {
 8             return GetOneIndexBuffer(array, mode, usage, IndexElementType.UShort, primCount);
 9         }
10 
11         public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<uint> array, DrawMode mode, BufferUsage usage, int primCount = 1)
12         {
13             return GetOneIndexBuffer(array, mode, usage, IndexElementType.UInt, primCount);
14         }
15 
16         private static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArrayBase array, DrawMode mode, BufferUsage usage, IndexElementType elementType, int primCount = 1)
17         {
18             if (glGenBuffers == null)
19             {
20                 InitFunctions();
21             }
22 
23             uint[] buffers = new uint[1];
24             glGenBuffers(1, buffers);
25             const uint target = OpenGL.GL_ELEMENT_ARRAY_BUFFER;
26             glBindBuffer(target, buffers[0]);
27             glBufferData(target, array.ByteLength, array.Header, (uint)usage);
28             glBindBuffer(target, 0);
29 
30             var buffer = new OneIndexBuffer(buffers[0], mode, elementType, array.Length, array.ByteLength, primCount);
31 
32             return buffer;
33         }

 

T[] -> OneIndexBuffer

思路同上。

 1         public static OneIndexBuffer GetOneIndexBuffer(this byte[] array, DrawMode mode, BufferUsage usage, int primCount = 1)
 2         {
 3             GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
 4             IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
 5             var unmanagedArray = new UnmanagedArray<byte>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
 6             OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UByte, primCount);
 7             pinned.Free();
 8 
 9             return buffer;
10         }
11 
12         public static OneIndexBuffer GetOneIndexBuffer(this ushort[] array, DrawMode mode, BufferUsage usage, int primCount = 1)
13         {
14             GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
15             IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
16             var unmanagedArray = new UnmanagedArray<ushort>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
17             OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UShort, primCount);
18             pinned.Free();
19 
20             return buffer;
21         }
22 
23         public static OneIndexBuffer GetOneIndexBuffer(this uint[] array, DrawMode mode, BufferUsage usage, int primCount = 1)
24         {
25             GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
26             IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
27             var unmanagedArray = new UnmanagedArray<uint>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
28             OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UInt, primCount);
29             pinned.Free();
30 
31             return buffer;
32         }

 

T -> OneIndexBuffer

只有1個元素的索引數組,比較奇葩,不過也是可以實現的。

 1         public static OneIndexBuffer GetOneIndexBuffer(this byte data, DrawMode mode, BufferUsage usage, int primCount = 1)
 2         {
 3             var array = new byte[] { data };
 4             return GetOneIndexBuffer(array, mode, usage, primCount);
 5         }
 6 
 7         public static OneIndexBuffer GetOneIndexBuffer(this ushort data, DrawMode mode, BufferUsage usage, int primCount = 1)
 8         {
 9             var array = new ushort[] { data };
10             return GetOneIndexBuffer(array, mode, usage, primCount);
11         }
12 
13         public static OneIndexBuffer GetOneIndexBuffer(this uint data, DrawMode mode, BufferUsage usage, int primCount = 1)
14         {
15             var array = new uint[] { data };
16             return GetOneIndexBuffer(array, mode, usage, primCount);
17         }

 

ZeroIndexBuffer

這事一個特殊的Buffer,因為實際上在OpenGL的server端并沒有真正創建一個Buffer。但是邏輯上把它也視作一個Buffer是方便合理的。既然沒有真正創建Buffer,那么也就不存在用非托管數組創建ZeroIndexBuffer的情形了。

非托管數組->自定義Buffer

自定義Buffer都有哪些

所謂自定義Buffer,是那些用途各異的特殊Buffer,目前CSharpGL包含了:

1 AtomicCounterBuffer
2 PixelPackBuffer
3 PixelUnpackBuffer
4 ShaderStorageBuffer
5 TextureBuffer
6 UniformBuffer

下面以 AtomicCounterBuffer 為例,其他雷同。

非托管數組->自定義Buffer

思路同上。

        public static AtomicCounterBuffer GetAtomicCounterBuffer(this UnmanagedArrayBase array, BufferUsage usage)
        {
            return GetIndependentBuffer(array, IndependentBufferTarget.AtomicCounterBuffer, usage) as AtomicCounterBuffer;
        }

        private static Buffer GetIndependentBuffer(this UnmanagedArrayBase array, IndependentBufferTarget bufferTarget, BufferUsage usage)
        {
            uint[] buffers = new uint[1];
            glGenBuffers(1, buffers);
            var target = (uint)bufferTarget;
            glBindBuffer(target, buffers[0]);
            glBufferData(target, array.ByteLength, array.Header, (uint)usage);
            glBindBuffer(target, 0);

            Buffer buffer = null;
            switch (bufferTarget)
            {
                case IndependentBufferTarget.AtomicCounterBuffer:
                    buffer = new AtomicCounterBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                case IndependentBufferTarget.PixelPackBuffer:
                    buffer = new PixelPackBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                case IndependentBufferTarget.PixelUnpackBuffer:
                    buffer = new PixelUnpackBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                case IndependentBufferTarget.ShaderStorageBuffer:
                    buffer = new ShaderStorageBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                case IndependentBufferTarget.TextureBuffer:
                    buffer = new TextureBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                case IndependentBufferTarget.UniformBuffer:
                    buffer = new UniformBuffer(buffers[0], array.Length, array.ByteLength);
                    break;

                default:
                    throw new NotImplementedException();
            }

            return buffer;
        }

 

T[] –> 自定義Buffer

思路同上。

 1         public static AtomicCounterBuffer GetAtomicCounterBuffer<T>(this T[] array, BufferUsage usage) where T : struct
 2         {
 3             GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
 4             IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
 5             var unmanagedArray = new UnmanagedArray<T>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
 6             AtomicCounterBuffer buffer = GetIndependentBuffer(unmanagedArray, IndependentBufferTarget.AtomicCounterBuffer, usage) as AtomicCounterBuffer;
 7             pinned.Free();
 8 
 9             return buffer;
10         }

T -> 自定義Buffer

思路同上。這個方式還是比較常見的一種用法。

1         public static AtomicCounterBuffer GetAtomicCounterBuffer<T>(this T data, BufferUsage usage) where T : struct
2         {
3             var array = new T[] { data };
4             return GetAtomicCounterBuffer(array, usage);
5         }

如何使用

實現了上面那些看起來比較啰嗦的功能,現在來看看使用的時候是什么情形。

 -> VertexBuffer

最基本的功能是通過數組UnmanagedArrayBase或T[]創建一個VBO,我們首先使用這個功能。可見只需一行代碼即可實現,且調用方式也相同。

1     vec3 position = GetPositions();
2     VertexBuffer buffer = position.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);
3     //
4     vec3[] positions = GetPositions();
5     VertexBuffer buffer = positions.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);
6     //
7     UnmanagedArray<vec3> positions = GetPositions();
8     VertexBuffer buffer = positions.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);

 

-> OneIndexBuffer

同上,不解釋。

1     uint position = GetIndexes();
2     VertexBuffer buffer = position.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);
3     //
4     uint[] positions = GetIndexes();
5     VertexBuffer buffer = positions.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);
6     //
7     UnmanagedArray<uint> positions = GetIndexes();
8     VertexBuffer buffer = positions.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);

 

-> 自定義Buffer

同上,不解釋。

1     SomeStruct data = GetIndexes();
2     VertexBuffer buffer = position.GetUniformBuffer(BufferUsage.StaticDraw);
3     //
4     SomeStruct[] data = GetIndexes();
5     VertexBuffer buffer = data.GetUniformBuffer(BufferUsage.StaticDraw);
6     //
7     UnmanagedArray<SomeStruct> data = GetIndexes();
8     VertexBuffer buffer = data.GetUniformBuffer(BufferUsage.StaticDraw);

總結

業務數據是核心,其他參數輔助,按照這一思路,就實現了現在的一行創建VBO的功能。

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

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

 


文章列表


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

    IT工程師數位筆記本

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