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

开始

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

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

glBufferData(GL_ARRAY_BUFFER, length, NULL, GL_STATIC_DRAW);

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

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

     IntPtr pointer = buffer.MapBuffer(MapBufferAccess.WriteOnly);
var array = (vec3*)pointer.ToPointer();
for (int i = ; i < length; i++)
{
array[i] = this.model.positions[i];
}
ptr.UnmapBuffer();

来直接操作GPU上的数据即可。

使用这种方式,省去了CPU端创建大规模非托管数组并上传到GPU的步骤,直接在GPU端创建了buffer,且所需代码更少,可以说是目前我找到的最佳方式。

因此我在CSharpGL中集成并使用了这种方式。

顶点属性buffer

CSharpGL中,用于描述顶点属性数组(Vertex Buffer Object)的类型是 VertexAttributeBufferPtr 。以前,我们可以通过如下的方式创建 VertexAttributeBufferPtr 。

     // 先在CPU端创建非托管数组VertexAttributeBuffer<vec3>对象
using (var buffer = new VertexAttributeBuffer<vec3>(
varNameInShader, VertexAttributeConfig.Vec3, BufferUsage.StaticDraw))
{
buffer.Alloc(this.model.positions.Length);// 在CPU端申请内存
unsafe// 使用指针快速初始化
{
var array = (vec3*)buffer.Header.ToPointer();
for (int i = ; i < this.model.positions.Length; i++)
{
array[i] = this.model.positions[i];
}
}
// 将CPU端的VertexAttributeBuffer<vec3>上传到GPU,获得我们需要的buffer对象。
positionBufferPtr = buffer.GetBufferPtr();
}// using结束,释放VertexAttributeBuffer<vec3>申请的非托管内存。
return positionBufferPtr;

可见,这种方式实际上是按下面的步骤创建VBO的。注意其中的data不是 NULL 。

     uint[] buffers = new uint[];
glGenBuffers(, buffers);
const uint target = OpenGL.GL_ARRAY_BUFFER;
glBindBuffer(target, buffers[]);
glBufferData(target, length, data, usage);
glBindBuffer(target, );

这种方式需要先在CPU端申请一块内存,初始化数据,之后才能上传到GPU端。现在我们用新的方式创建buffer,就不需要在CPU端申请内存了。

下面是创建buffer的方法。

     /// <summary>
/// Creates a <see cref="VertexAttributeBufferPtr"/> object(actually an array) directly in server side(GPU) without initializing its value.
/// </summary>
/// <param name="elementType">element's type of this 'array'.</param>
/// <param name="length">How many elements are there?</param>
/// <param name="config">mapping to vertex shader's 'in' type.</param>
/// <param name="usage"></param>
/// <param name="varNameInVertexShader">mapping to vertex shader's 'in' name.</param>
/// <param name="instanceDivisor"></param>
/// <param name="patchVertexes"></param>
/// <returns></returns>
public static VertexAttributeBufferPtr Create(Type elementType, int length, VertexAttributeConfig config, BufferUsage usage, string varNameInVertexShader, uint instanceDivisor = , int patchVertexes = )
{
if (!elementType.IsValueType) { throw new ArgumentException(string.Format("{0} must be a value type!", elementType)); } int byteLength = Marshal.SizeOf(elementType) * length;
uint[] buffers = new uint[];
glGenBuffers(, buffers);
const uint target = OpenGL.GL_ARRAY_BUFFER;
glBindBuffer(target, buffers[]);
glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
glBindBuffer(target, ); var bufferPtr = new VertexAttributeBufferPtr(
varNameInVertexShader, buffers[], config, length, byteLength, instanceDivisor, patchVertexes); return bufferPtr;
}

使用这样的方式创建了buffer,之后在初始化数据时,就得用glMapBuffer/glUnmapBuffer了。

     int length = this.model.positions.Length;
// 创建buffer
VertexAttributeBufferPtr buffer = VertexAttributeBufferPtr.Create(typeof(vec3), length, VertexAttributeConfig.Vec3, BufferUsage.StaticDraw, varNameInShader);
unsafe
{
IntPtr pointer = buffer.MapBuffer(MapBufferAccess.WriteOnly);// 获取指针
var array = (vec3*)pointer.ToPointer();// 强制类型转换
// 初始化数据
for (int i = ; i < length; i++)
{
array[i] = this.model.positions[i];
}
buffer.UnmapBuffer();// 完成,上传到GPU端。
}

对比来看,在内存上,新的方式省略了在CPU端申请非托管数组的开销;在代码上,新的方式也省去了对 VertexAttributeBuffer<vec3> 对象的使用( VertexAttributeBuffer<vec3> 类型完全可以不用了)。

索引buffer

OneIndexBufferPtr

对于使用 glDrawElements() 的索引对象,创建索引buffer就与上面雷同了。

     /// <summary>
/// Creates a <see cref="OneIndexBufferPtr"/> object directly in server side(GPU) without initializing its value.
/// </summary>
/// <param name="byteLength"></param>
/// <param name="usage"></param>
/// <param name="mode"></param>
/// <param name="type"></param>
/// <param name="length"></param>
/// <returns></returns>
public static OneIndexBufferPtr Create(int byteLength, BufferUsage usage, DrawMode mode, IndexElementType type, int length)
{
uint[] buffers = new uint[];
glGenBuffers(, buffers);
const uint target = OpenGL.GL_ELEMENT_ARRAY_BUFFER;
glBindBuffer(target, buffers[]);
glBufferData(target, byteLength, IntPtr.Zero, (uint)usage);
glBindBuffer(target, ); var bufferPtr = new OneIndexBufferPtr(
buffers[], mode, type, length, byteLength); return bufferPtr;
}

ZeroIndexBufferPtr

对于使用 glDrawArrays() 的索引,则更简单。

     /// <summary>
/// Creates a <see cref="ZeroIndexBufferPtr"/> object directly in server side(GPU) without initializing its value.
/// </summary>
/// <param name="mode"></param>
/// <param name="firstVertex"></param>
/// <param name="vertexCount"></param>
/// <returns></returns>
public static ZeroIndexBufferPtr Create(DrawMode mode, int firstVertex, int vertexCount)
{
ZeroIndexBufferPtr bufferPtr = new ZeroIndexBufferPtr(
mode, firstVertex, vertexCount); return bufferPtr;
}

使用方法也与上面雷同。

独立buffer

对于AtomicCounterBuffer、PixelPackBuffer、PixelUnpackBuffer、ShaderStorageBuffer、TextureBuffer、UniformBuffer这些,我统称为IndependentBuffer。他们当然也可以用新方法创建和使用。

     /// <summary>
/// Creates a sub-type of <see cref="IndependentBufferPtr"/> object directly in server side(GPU) without initializing its value.
/// </summary>
/// <param name="target"></param>
/// <param name="byteLength"></param>
/// <param name="usage"></param>
/// <param name="length"></param>
/// <returns></returns>
public static IndependentBufferPtr Create(IndependentBufferTarget target, int byteLength, BufferUsage usage, int length)
{
uint bufferTarget = ;
switch (target)
{
case IndependentBufferTarget.AtomicCounterBuffer:
bufferTarget = OpenGL.GL_ATOMIC_COUNTER_BUFFER;
break; case IndependentBufferTarget.PixelPackBuffer:
bufferTarget = OpenGL.GL_PIXEL_PACK_BUFFER;
break; case IndependentBufferTarget.PixelUnpackBuffer:
bufferTarget = OpenGL.GL_PIXEL_UNPACK_BUFFER;
break; case IndependentBufferTarget.ShaderStorageBuffer:
bufferTarget = OpenGL.GL_SHADER_STORAGE_BUFFER;
break; case IndependentBufferTarget.TextureBuffer:
bufferTarget = OpenGL.GL_TEXTURE_BUFFER;
break; case IndependentBufferTarget.UniformBuffer:
bufferTarget = OpenGL.GL_UNIFORM_BUFFER;
break; default:
throw new NotImplementedException();
} uint[] buffers = new uint[];
glGenBuffers(, buffers);
glBindBuffer(bufferTarget, buffers[]);
glBufferData(bufferTarget, byteLength, IntPtr.Zero, (uint)usage);
glBindBuffer(bufferTarget, ); IndependentBufferPtr bufferPtr;
switch (target)
{
case IndependentBufferTarget.AtomicCounterBuffer:
bufferPtr = new AtomicCounterBufferPtr(buffers[], length, byteLength);
break; case IndependentBufferTarget.PixelPackBuffer:
bufferPtr = new PixelPackBufferPtr(buffers[], length, byteLength);
break; case IndependentBufferTarget.PixelUnpackBuffer:
bufferPtr = new PixelUnpackBufferPtr(buffers[], length, byteLength);
break; case IndependentBufferTarget.ShaderStorageBuffer:
bufferPtr = new ShaderStorageBufferPtr(buffers[], length, byteLength);
break; case IndependentBufferTarget.TextureBuffer:
bufferPtr = new TextureBufferPtr(buffers[], length, byteLength);
break; case IndependentBufferTarget.UniformBuffer:
bufferPtr = new UniformBufferPtr(buffers[], length, byteLength);
break; default:
throw new NotImplementedException();
} return bufferPtr;
}

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. npm 私有模块的管理使用

    你可以使用 NPM 命令行工具来管理你在 NPM 仓库的私有模块代码,这使得在项目中使用公共模块变的更加方便. 开始前的工作 你需要一个 2.7.0 以上版本的 npm ,并且需要有一个可以登陆 np ...

  2. ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑

    前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...

  3. 用html5的canvas和JavaScript创建一个绘图程序

    本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...

  4. Android探索之AIDL实现进程间通信

    前言: 前面总结了程序间共享数据,可以使用ContentProvider也可以使用SharedPreference,那么进程间怎么共享内存呢?Android系统中的进程之间不能共享内存,因此,需要提供 ...

  5. pt-mext

    pt-mext实现的功能比较简单,就是将mysqladmin输出的多次迭代的相同status变量值放到同一行输出. 参数很少,除了--help和--version外,只有一个--relative参数 ...

  6. 集合(set)-Python3

    set 的 remove() 和 discard()  方法介绍. 函数/方法名   等价操作符 说明 所有集合类型 len(s)   集合基数:集合s中元素个数 set([obj])   可变集合工 ...

  7. BPM始终服务于人,落脚于人

    数字经济时代下,云计算.大数据.移动互联已经成为当下企业必须采取的武装力量.随着互联网+.中国制造2025.工业4.0等国家战略的引导与支持,无数的企业在这场数字化浪潮中使尽浑身解数,想要抓住机遇奋力 ...

  8. iOS之开发中常用的颜色及其对应的RGB值

      R G B 值   R G B 值   R G B 值 黑色 0 0 0 #000000 黄色 255 255 0 #FFFF00 浅灰蓝色 176 224 230 #B0E0E6 象牙黑 41 ...

  9. SqlServer之数据库三大范式

    分析: 数据库设计应遵循三大范式分别为: 第一范式:确保表中每列的原子性(不可拆分): 第二范式:确保表中每列与主键相关,而不能只与主键的某部分相关(主要针对联合主键),主键列与非主键列遵循完全函数依 ...

  10. uboot环境配置

    uboot环境配置 通过配置uboot让它在启动过程中从tftp获取内核和设备树,并从在加载内核之后把通过启动参数将"从nfs挂载根文件系统"传入内核.这个配置主要是通过uboot ...