開(kāi)始

總的來(lái)說(shuō),OpenGL應(yīng)用開(kāi)發(fā)者會(huì)遇到為如下三種數(shù)據(jù)創(chuàng)建Vertex Buffer Object的情形:

  1. 任意一個(gè)struct類(lèi)型T data;

  2. 任意一個(gè)元素類(lèi)型為struct的數(shù)組T[] array;

  3. 任意一個(gè)非托管數(shù)組UnmanagedArray<T> array;

而可創(chuàng)建的Vertex Buffer Object也分為如下的類(lèi)別:

  1. 描述頂點(diǎn)屬性(位置、顏色、法線(xiàn)等)的VertexBuffer;

  2. 描述索引的IndexBuffer;

  3. 描述其他自定義內(nèi)容的各種Buffer;

本文介紹用C#如何實(shí)現(xiàn)上述功能。

回到頂部(go to top)

非托管數(shù)組->VertexBuffer

最基本的功能是通過(guò)非托管數(shù)組UnmanagedArrayBase創(chuàng)建一個(gè)VBO,我們首先實(shí)現(xiàn)這個(gè)功能。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

T[] -> VertexBuffer

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

T -> VertexBuffer

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

回到頂部(go to top)

非托管數(shù)組->IndexBuffer

非托管數(shù)組->OneIndexBuffer

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

T[] -> OneIndexBuffer

思路同上。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

T -> OneIndexBuffer

只有1個(gè)元素的索引數(shù)組,比較奇葩,不過(guò)也是可以實(shí)現(xiàn)的。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

ZeroIndexBuffer

這事一個(gè)特殊的Buffer,因?yàn)閷?shí)際上在OpenGL的server端并沒(méi)有真正創(chuàng)建一個(gè)Buffer。但是邏輯上把它也視作一個(gè)Buffer是方便合理的。既然沒(méi)有真正創(chuàng)建Buffer,那么也就不存在用非托管數(shù)組創(chuàng)建ZeroIndexBuffer的情形了。

回到頂部(go to top)

非托管數(shù)組->自定義Buffer

自定義Buffer都有哪些

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

1 AtomicCounterBuffer2 PixelPackBuffer3 PixelUnpackBuffer4 ShaderStorageBuffer5 TextureBuffer6 UniformBuffer

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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

非托管數(shù)組->自定義Buffer

思路同上。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

        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;
        }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

T[] –> 自定義Buffer

思路同上。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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         }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

T -> 自定義Buffer

思路同上。這個(gè)方式還是比較常見(jiàn)的一種用法。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

回到頂部(go to top)

如何使用

實(shí)現(xiàn)了上面那些看起來(lái)比較啰嗦的功能,現(xiàn)在來(lái)看看使用的時(shí)候是什么情形。

 -> VertexBuffer

最基本的功能是通過(guò)數(shù)組UnmanagedArrayBase或T[]創(chuàng)建一個(gè)VBO,我們首先使用這個(gè)功能??梢?jiàn)只需一行代碼即可實(shí)現(xiàn),且調(diào)用方式也相同。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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);

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

-> OneIndexBuffer

同上,不解釋。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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);

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 

-> 自定義Buffer

同上,不解釋。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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);

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

回到頂部(go to top)

總結(jié)

業(yè)務(wù)數(shù)據(jù)是核心,其他參數(shù)輔助,按照這一思路,就實(shí)現(xiàn)了現(xiàn)在的一行創(chuàng)建VBO的功能。

CSharpGL已經(jīng)有點(diǎn)深度,所以筆記很難寫(xiě)出讓人直接就能眼前一亮的感覺(jué)了。

目前CSharpGL中已經(jīng)涵蓋了我所知的所有OpenGL知識(shí)點(diǎn)。下一步就是精心讀書(shū),繼續(xù)深挖。