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. Java 线程

    线程:线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程.线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不能拥有系统资源.它与父进程的其他线程共享该进程的所有资 ...

  2. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  3. .NET 提升教育 第一期:VIP 付费课程培训通知!

    为响应 @当年在远方 同学的建议,在年前尝试进行一次付费的VIP培训. 培训的课件:点击下载培训周期:10个课程左右,每晚1个半小时培训价格:1000元/人.报名方式:有意向的请加QQ群:路过秋天.N ...

  4. JAVA语言中的修饰符

    JAVA语言中的修饰符 -----------------------------------------------01--------------------------------------- ...

  5. 神经网络、logistic回归等分类算法简单实现

    最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...

  6. 免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)

    在生活中有一种东西几乎已经快要成为我们的另一个电子”身份证“,那就是二维码.无论是在软件开发的过程中,还是在普通用户的日常中,几乎都离不开二维码.二维码 (dimensional barcode) , ...

  7. android 事件分发机制详解(OnTouchListener,OnClick)

    昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制.探究如下: 首 ...

  8. 使用gulp解决RequireJS项目前端缓存问题(二)

    1.前言 这一节,我们主要解决在上一节<使用gulp解决RequireJSs项目前端缓存问题(一)>末尾提到的几个问题: 对通过require-config.js引入的js文件修改后,没有 ...

  9. sqlServer去除字符串空格

    说起去除字符串首尾空格大家肯定第一个想到trim()函数,不过在sqlserver中是没有这个函数的,却而代之的是ltrim()和rtrim()两个函数.看到名字所有人都 知道做什么用的了,ltrim ...

  10. Set up VLAN (802.1q) tagging on a network interface?

    SOLUTION VERIFIED October 13 2015 KB39674 KB741413 environment Red Hat Enterprise Linux 4 Red Hat En ...