CSharpGL(37)创建和使用VBO的最佳方式
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的最佳方式的更多相关文章
- CSharpGL(7)对VAO和VBO的封装
CSharpGL(7)对VAO和VBO的封装 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合入门参考 ...
- Oracle创建新undo表空间最佳实践(包含段检查)
在处理一则ORA-600 [4194]案例时,参考MOS文档:Step by step to resolve ORA-600 4194 4193 4197 on database crash (文档 ...
- 4.3.6 对象的界定通过编写接口来访问带这类命名结构的表会出问题。如前所述,SQL Server的灵活性不应用作编写错误代码或创建问题对象的借口。 注意在使用Management Studio的脚本工具时,SQL Server会界定所有的对象。这不是因为这么做是必须的,也不是编写代码的最佳方式,而是因为在界定符中封装所有的对象,比编写脚本引擎来查找需要界定的对象更容易。
如前所述,在创建对象时,最好避免使用内嵌的空格或保留字作为对象名,但设计人员可能并没有遵守这个最佳实践原则.例如,我当前使用的数据库中有一个审核表名为Transaction,但是Transaction ...
- js创建对象的最佳方式
1.对象的定义 ECMAScript中,对象是一个无序属性集,这里的“属性”可以是基本值.对象或者函数 2.数据属性与访问器属性 数据属性即有值的属性,可以设置属性只读.不可删除.不可枚举等等 访问器 ...
- vue父子组件状态同步的最佳方式续章(v-model篇)
大家好!我是木瓜太香!一名前端工程师,之前写过一篇<vue父子组件状态同步的最佳方式>,这篇文章描述了大多数情况下的父子组件同步的最佳方式,也是被开源中国官方推荐了,在这里表示感谢! 这次 ...
- java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...
- Java反射机制(创建Class对象的三种方式)
1:了解什么是反射机制? 在通常情况下,如果有一个类,可以通过类创建对象:但是反射就是要求通过一个对象找到一个类的名称: 2:在反射操作中,握住一个核心概念: 一切操作都将使用Object完成,类 ...
- JDBC 创建连接对象的三种方式 、 properties文件的建立、编辑和信息获取
创建连接对象的三种方式 //第一种方式 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ ...
- python中逐行读取文件的最佳方式_Drupal_新浪博客
python中逐行读取文件的最佳方式_Drupal_新浪博客 python中逐行读取文件的最佳方式 (2010-08-18 15:59:28) 转载▼ 标签: python ...
随机推荐
- npm 私有模块的管理使用
你可以使用 NPM 命令行工具来管理你在 NPM 仓库的私有模块代码,这使得在项目中使用公共模块变的更加方便. 开始前的工作 你需要一个 2.7.0 以上版本的 npm ,并且需要有一个可以登陆 np ...
- ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑
前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...
- 用html5的canvas和JavaScript创建一个绘图程序
本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...
- Android探索之AIDL实现进程间通信
前言: 前面总结了程序间共享数据,可以使用ContentProvider也可以使用SharedPreference,那么进程间怎么共享内存呢?Android系统中的进程之间不能共享内存,因此,需要提供 ...
- pt-mext
pt-mext实现的功能比较简单,就是将mysqladmin输出的多次迭代的相同status变量值放到同一行输出. 参数很少,除了--help和--version外,只有一个--relative参数 ...
- 集合(set)-Python3
set 的 remove() 和 discard() 方法介绍. 函数/方法名 等价操作符 说明 所有集合类型 len(s) 集合基数:集合s中元素个数 set([obj]) 可变集合工 ...
- BPM始终服务于人,落脚于人
数字经济时代下,云计算.大数据.移动互联已经成为当下企业必须采取的武装力量.随着互联网+.中国制造2025.工业4.0等国家战略的引导与支持,无数的企业在这场数字化浪潮中使尽浑身解数,想要抓住机遇奋力 ...
- iOS之开发中常用的颜色及其对应的RGB值
R G B 值 R G B 值 R G B 值 黑色 0 0 0 #000000 黄色 255 255 0 #FFFF00 浅灰蓝色 176 224 230 #B0E0E6 象牙黑 41 ...
- SqlServer之数据库三大范式
分析: 数据库设计应遵循三大范式分别为: 第一范式:确保表中每列的原子性(不可拆分): 第二范式:确保表中每列与主键相关,而不能只与主键的某部分相关(主要针对联合主键),主键列与非主键列遵循完全函数依 ...
- uboot环境配置
uboot环境配置 通过配置uboot让它在启动过程中从tftp获取内核和设备树,并从在加载内核之后把通过启动参数将"从nfs挂载根文件系统"传入内核.这个配置主要是通过uboot ...