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. wepack+sass+vue 入门教程(一)

    一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...

  2. 轻量级“集合”迭代器-Generator

    Generator是PHP 5.5加入的新语言特性.但是,它似乎并没有被很多PHP开发者广泛采用.因此,在我们了解PHP 7对Generator的改进之前,我们先通过一个简单却显而易见的例子来了解下G ...

  3. WCF学习之旅—第三个示例之四(三十)

           上接WCF学习之旅—第三个示例之一(二十七)               WCF学习之旅—第三个示例之二(二十八)              WCF学习之旅—第三个示例之三(二十九)   ...

  4. HTTP常用状态码分析

    不管是面试还是工作中,经常会碰到需要通过HTTP状态码去判断问题的情况,比如对于后台RD,给到前端FE的一个接口,出现502或者504 error错误,FE就会说接口存在问题,如果没有知识储备,那就只 ...

  5. MySQL数据库和InnoDB存储引擎文件

    参数文件 当MySQL示例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数,这些参数通常定义了某种内存结构有多大等.在默认情况下,MySQL实例会按照一定 ...

  6. Android学习路线总结,绝对干货

    title: Android学习路线总结,绝对干货 tags: Android学习路线,Android学习资料,怎么学习android grammar_cjkRuby: true --- 一.前言 不 ...

  7. WebApi接口 - 响应输出xml和json

    格式化数据这东西,主要看需要的运用场景,今天和大家分享的是webapi格式化数据,这里面的例子主要是输出json和xml的格式数据,测试用例很接近实际常用情况:希望大家喜欢,也希望各位多多扫码支持和点 ...

  8. 算法与数据结构(七) AOV网的拓扑排序

    今天博客的内容依然与图有关,今天博客的主题是关于拓扑排序的.拓扑排序是基于AOV网的,关于AOV网的概念,我想引用下方这句话来介绍: AOV网:在现代化管理中,人们常用有向图来描述和分析一项工程的计划 ...

  9. C#数组,List,Dictionary的相互转换

    本篇文章会向大家实例讲述以下内容: 将数组转换为List 将List转换为数组 将数组转换为Dictionary 将Dictionary 转换为数组 将List转换为Dictionary 将Dicti ...

  10. 数据图表插件Echarts(一)

    一.引言 最近做一个智慧城市项目,项目中需要图表和报表进行数据分析,从网上找了很多,最后找到了百度开放的echarts,一个很强大的插件. 二.介绍 ECharts,缩写来自Enterprise Ch ...