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


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

  • 相关阅读:
    写的一个split函数
    从头開始写项目Makefile(十):make内嵌函数及make命令显示
    MySQL 中文乱码解决
    查看DNS主机名解析的主机IP并向DNSserver进行DNS域名解析
    【iOS开发-90】CALayer图层:自己定义图层,图层的一些动画
    iconv 中文截断问题的解决方法
    linux有用技巧:使用ntfs-3g挂载ntfs设备
    SQL Sever——远程过程调用失败(0x800706be)
    USACO ariprog 暴力枚举+剪枝
    Shell编程(脚本)的经常使用命令和语句
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-37-best-practice-creating-VBO.html
Copyright © 2020-2023  润新知