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 ...
随机推荐
- Database Replay和Consolidated Database replay
简介 在数据库的迁移和升级场景中,我们经常会遇到一个问题:在做压力测试时,如何模拟真实的业务压力,解决这个问题的方法有很多,比如:应用方开发模拟程序或者使用压力测试工具模拟,如load runner, ...
- UITextView 输入字数限制
本文介绍了UITextView对中英文还有iOS自带表情输入的字数限制,由于中文输入会有联想导致字数限制不准确所以苦恼好久,所以参考一些大神的博客终于搞定,欢迎大家参考和指正. 对于限制UITextV ...
- 调用AJAX做登陆和注册
先建立一个页面来检测一下我们建立的用户名能不能用,看一下有没有已经存在的用户名吗 可以通过ajax提示一下 $("#uid").blur(function(){ //取用户名 va ...
- Python标准模块--Iterators和Generators
1 模块简介 当你开始使用Python编程时,你或许已经使用了iterators(迭代器)和generators(生成器),你当时可能并没有意识到.在本篇博文中,我们将会学习迭代器和生成器是什么.当然 ...
- css实现单行,多行文本溢出显示省略号……
1.单行文本溢出显示省略号我们可以直接用text-overflow: ellipsis 实现方法: <style> .div_text{width: 300px; padding:10px ...
- 两个 viewports 的故事-第二部分
原文链接:A tale of two viewports — part two 译者:nzbin 在这个迷你系列中,我将解释 viewports 和各种重要元素的宽度是如何工作的,比如说 <ht ...
- HDU1671——前缀树的一点感触
题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...
- angular2系列教程(十一)路由嵌套、路由生命周期、matrix URL notation
今天我们要讲的是ng2的路由的第二部分,包括路由嵌套.路由生命周期等知识点. 例子 例子仍然是上节课的例子:
- CSS 3学习——transform 2D转换
首先声明一点,transform属性不为none的元素是它的定位子元素(绝对定位和固定定位)的包含块,而且对内创建一个新的层叠上下文. 注意:可以通过 transform-box 属性指定元素的那个盒 ...
- dedecms 后台栏目添加图片
前台调用栏目时需要显示图标,整理一下: 第一步:“系统->SQL命令工具” , 插入sql语句 alter table dede_arctype add typeimg varchar() 第二 ...