zoukankan      html  css  js  c++  java
  • CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总

    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知识点。下一步就是精心读书,继续深挖。

  • 相关阅读:
    好久没来园子里转了,最近在学ssh,有个小问题提出来
    ClearType使用的问题
    Metro中访问特定设备的方法
    UMDF驱动程序快速上手
    关于GPS使用上的一个怪异问题
    一个不能创建WINCE6.0工程的问题
    Metro开发小记
    在WINPE中添加驱动
    DOS命令活用
    METRO开发中的多语言处理
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-38-create-VBO-with-initial-data.html
Copyright © 2011-2022 走看看