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

开始

近日在OpenGL红宝书上看到这样的讲解。

其核心意思是,在创建VBO时用

  1. glBufferData(GL_ARRAY_BUFFER, length, NULL, GL_STATIC_DRAW);

来初始化buffer占用的内存(此内存在GPU端),其中的 NULL 表示并未初始化数据(即此buffer中的数据是随机值,类似在C语言中刚刚创建一个数组 int x[]; 的情况)。

这样,就不必在CPU端申请大量内存了。接下来需要初始化buffer数据时,用

  1. IntPtr pointer = buffer.MapBuffer(MapBufferAccess.WriteOnly);
  2. var array = (vec3*)pointer.ToPointer();
  3. for (int i = ; 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 = ; 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[];
  2. glGenBuffers(, buffers);
  3. const uint target = OpenGL.GL_ARRAY_BUFFER;
  4. glBindBuffer(target, buffers[]);
  5. glBufferData(target, length, data, usage);
  6. glBindBuffer(target, );

这种方式需要先在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 = , int patchVertexes = )
  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[];
  18. glGenBuffers(, buffers);
  19. const uint target = OpenGL.GL_ARRAY_BUFFER;
  20. glBindBuffer(target, buffers[]);
  21. glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
  22. glBindBuffer(target, );
  23.  
  24. var bufferPtr = new VertexAttributeBufferPtr(
  25. varNameInVertexShader, buffers[], 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 = ; 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[];
  13. glGenBuffers(, buffers);
  14. const uint target = OpenGL.GL_ELEMENT_ARRAY_BUFFER;
  15. glBindBuffer(target, buffers[]);
  16. glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
  17. glBindBuffer(target, );
  18.  
  19. var bufferPtr = new OneIndexBufferPtr(
  20. buffers[], 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 = ;
  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[];
  43. glGenBuffers(, buffers);
  44. glBindBuffer(bufferTarget, buffers[]);
  45. glBufferData(bufferTarget, byteLength, IntPtr.Zero, (uint)usage);
  46. glBindBuffer(bufferTarget, );
  47.  
  48. IndependentBufferPtr bufferPtr;
  49. switch (target)
  50. {
  51. case IndependentBufferTarget.AtomicCounterBuffer:
  52. bufferPtr = new AtomicCounterBufferPtr(buffers[], length, byteLength);
  53. break;
  54.  
  55. case IndependentBufferTarget.PixelPackBuffer:
  56. bufferPtr = new PixelPackBufferPtr(buffers[], length, byteLength);
  57. break;
  58.  
  59. case IndependentBufferTarget.PixelUnpackBuffer:
  60. bufferPtr = new PixelUnpackBufferPtr(buffers[], length, byteLength);
  61. break;
  62.  
  63. case IndependentBufferTarget.ShaderStorageBuffer:
  64. bufferPtr = new ShaderStorageBufferPtr(buffers[], length, byteLength);
  65. break;
  66.  
  67. case IndependentBufferTarget.TextureBuffer:
  68. bufferPtr = new TextureBufferPtr(buffers[], length, byteLength);
  69. break;
  70.  
  71. case IndependentBufferTarget.UniformBuffer:
  72. bufferPtr = new UniformBufferPtr(buffers[], 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知识点。下一步就是精心读书,继续深挖。

CSharpGL(37)创建和使用VBO的最佳方式的更多相关文章

  1. CSharpGL(7)对VAO和VBO的封装

    CSharpGL(7)对VAO和VBO的封装 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合入门参考 ...

  2. Oracle创建新undo表空间最佳实践(包含段检查)

    在处理一则ORA-600 [4194]案例时,参考MOS文档:Step by step to resolve ORA-600 4194 4193 4197 on database crash (文档 ...

  3. 4.3.6 对象的界定通过编写接口来访问带这类命名结构的表会出问题。如前所述,SQL Server的灵活性不应用作编写错误代码或创建问题对象的借口。 注意在使用Management Studio的脚本工具时,SQL Server会界定所有的对象。这不是因为这么做是必须的,也不是编写代码的最佳方式,而是因为在界定符中封装所有的对象,比编写脚本引擎来查找需要界定的对象更容易。

    如前所述,在创建对象时,最好避免使用内嵌的空格或保留字作为对象名,但设计人员可能并没有遵守这个最佳实践原则.例如,我当前使用的数据库中有一个审核表名为Transaction,但是Transaction ...

  4. js创建对象的最佳方式

    1.对象的定义 ECMAScript中,对象是一个无序属性集,这里的“属性”可以是基本值.对象或者函数 2.数据属性与访问器属性 数据属性即有值的属性,可以设置属性只读.不可删除.不可枚举等等 访问器 ...

  5. vue父子组件状态同步的最佳方式续章(v-model篇)

    大家好!我是木瓜太香!一名前端工程师,之前写过一篇<vue父子组件状态同步的最佳方式>,这篇文章描述了大多数情况下的父子组件同步的最佳方式,也是被开源中国官方推荐了,在这里表示感谢! 这次 ...

  6. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

  7. Java反射机制(创建Class对象的三种方式)

    1:了解什么是反射机制? 在通常情况下,如果有一个类,可以通过类创建对象:但是反射就是要求通过一个对象找到一个类的名称:   2:在反射操作中,握住一个核心概念: 一切操作都将使用Object完成,类 ...

  8. JDBC 创建连接对象的三种方式 、 properties文件的建立、编辑和信息获取

    创建连接对象的三种方式 //第一种方式 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ ...

  9. python中逐行读取文件的最佳方式_Drupal_新浪博客

    python中逐行读取文件的最佳方式_Drupal_新浪博客 python中逐行读取文件的最佳方式    (2010-08-18 15:59:28)    转载▼    标签:    python   ...

随机推荐

  1. 【每日一linux命令3】参数(或称选项)顺序

    一般除了特殊情况,参数是没有顺序的.举例而言,输入"–a –v"与输入"–v –a"以及"–av" 的执行效果是相同的.但若该参数后指定了要 ...

  2. [C#] 进阶 - LINQ 标准查询操作概述

    LINQ 标准查询操作概述 序 “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法.大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了IEnumerable<T> ...

  3. Mac OS 使用 Vagrant 管理虚拟机(VirtualBox)

    Vagrant(官网.github)是一款构建虚拟开发环境的工具,支持 Window,Linux,Mac OS,Vagrant 中的 Boxes 概念类似于 Docker(实质是不同的),你可以把它看 ...

  4. 代码的坏味道(18)——依恋情结(Feature Envy)

    坏味道--依恋情结(Feature Envy) 特征 一个函数访问其它对象的数据比访问自己的数据更多. 问题原因 这种气味可能发生在字段移动到数据类之后.如果是这种情况,你可能想将数据类的操作移动到这 ...

  5. PHP设计模式(八)桥接模式(Bridge For PHP)

    一.概述 桥接模式:将两个原本不相关的类结合在一起,然后利用两个类中的方法和属性,输出一份新的结果. 二.案例 1.模拟毛笔(转) 需求:现在需要准备三种粗细(大中小),并且有五种颜色的比 如果使用蜡 ...

  6. Unity C#最佳实践(上)

    本文为<effective c#>的读书笔记,此书类似于大名鼎鼎的<effective c++>,是入门后提高水平的进阶读物,此书提出了50个改进c#代码的原则,但是由于主要针 ...

  7. Apache2.4:AH01630 client denied by server configuration

    问题说明:Apache服务总共有4个,是为了防止单点故障和负载均衡,负载均衡控制由局方的F5提供. 访问的内容在NAS存储上,现象是直接访问每个apache的服务内容都是没有问题,但是从负载地址过来的 ...

  8. CacheManager:–个通用缓存接口抽象类库

    CacheManager是–个缓存通用接口抽象类库,它支持各种高速缓存提供者,例如Memcache,Redis,并且有许多先进的功能特性.具体可以访问官方网站  http://cachemanager ...

  9. Team Leader 你不再只是编码, 来炖一锅石头汤吧

    h3{ color: #000; padding: 5px; margin-bottom: 10px; font-weight: bolder; background-color: #ccc; } h ...

  10. Android开发学习之路-机器学习库(图像识别)、百度翻译

    对于机器学习也不是了解的很深入,今天无意中在GitHub看到一个star的比较多的库,就用着试一试,效果也还行.比是可能比不上TensorFlow的,但是在Android上用起来比较简单,毕竟Tens ...