CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总

开始

总的来说,OpenGL应用开发者会遇到为如下三种数据创建Vertex Buffer Object的情形:

  1. 任意一个struct类型T data;
  2. 任意一个元素类型为struct的数组T[] array;
  3. 任意一个非托管数组UnmanagedArray<T> array;

而可创建的Vertex Buffer Object也分为如下的类别:

  1. 描述顶点属性(位置、颜色、法线等)的VertexBuffer;
  2. 描述索引的IndexBuffer;
  3. 描述其他自定义内容的各种Buffer;

本文介绍用C#如何实现上述功能。

非托管数组->VertexBuffer

最基本的功能是通过非托管数组UnmanagedArrayBase创建一个VBO,我们首先实现这个功能。

         public static VertexBuffer GetVertexBuffer(this UnmanagedArrayBase array, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = , int patchVertexes = )
{
uint[] buffers = new uint[];
glGenBuffers(, buffers);
const uint target = OpenGL.GL_ARRAY_BUFFER;
glBindBuffer(target, buffers[]);
glBufferData(target, array.ByteLength, array.Header, (uint)usage);
glBindBuffer(target, ); var buffer = new VertexBuffer(
varNameInVertexShader, buffers[], config, array.Length, array.ByteLength, instancedDivisor, patchVertexes); return buffer;
}

T[] -> VertexBuffer

很多时候,大家都是在用习惯了的托管数组(int[]、Point[]、vec3[]等)。那么能不能直接用托管数组创建VBO呢?当然可以。虽然是托管数组,但是在内存中毕竟也还是连续存放的一块内存。我们只需找到它的地址就可以了。找地址这件事通过 Marshal.UnsafeAddrOfPinnedArrayElement(); 就可以做到。

         public static VertexBuffer GetVertexBuffer<T>(this T[] array, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = , int patchVertexes = ) where T : struct
{
GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, );
UnmanagedArrayBase unmanagedArray = new UnmanagedArray<T>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
VertexBuffer buffer = GetVertexBuffer(unmanagedArray, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
pinned.Free(); return buffer;
}

T -> VertexBuffer

那么单独的一个struct变量,如何为之创建VBO?只需用一个 var array = new T[]{ data }; 将其封装起来,就可以用上面的方法了。

         public static VertexBuffer GetVertexBuffer<T>(this T data, VBOConfig config, string varNameInVertexShader, BufferUsage usage, uint instancedDivisor = , int patchVertexes = ) where T : struct
{
var array = new T[] { data };
return GetVertexBuffer(array, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
// another way to do this:
//using (UnmanagedArrayBase unmanagedArray = new UnmanagedArray<T>(1))
//{
// Marshal.StructureToPtr(data, unmanagedArray.Header, false);
// VertexBuffer buffer = GetVertexBufferObject(unmanagedArray, config, varNameInVertexShader, usage, instancedDivisor, patchVertexes);
// return buffer;
//}
}

非托管数组->IndexBuffer

非托管数组->OneIndexBuffer

从非托管数组到OneIndexBuffer的思路和上面一致。要注意的是,OneIndexBuffer能接受的元素类型只能是byte、ushort、uint三者之一。

         public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<byte> array, DrawMode mode, BufferUsage usage, int primCount = )
{
return GetOneIndexBuffer(array, mode, usage, IndexElementType.UByte, primCount);
} public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<ushort> array, DrawMode mode, BufferUsage usage, int primCount = )
{
return GetOneIndexBuffer(array, mode, usage, IndexElementType.UShort, primCount);
} public static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArray<uint> array, DrawMode mode, BufferUsage usage, int primCount = )
{
return GetOneIndexBuffer(array, mode, usage, IndexElementType.UInt, primCount);
} private static OneIndexBuffer GetOneIndexBuffer(this UnmanagedArrayBase array, DrawMode mode, BufferUsage usage, IndexElementType elementType, int primCount = )
{
if (glGenBuffers == null)
{
InitFunctions();
} uint[] buffers = new uint[];
glGenBuffers(, buffers);
const uint target = OpenGL.GL_ELEMENT_ARRAY_BUFFER;
glBindBuffer(target, buffers[]);
glBufferData(target, array.ByteLength, array.Header, (uint)usage);
glBindBuffer(target, ); var buffer = new OneIndexBuffer(buffers[], mode, elementType, array.Length, array.ByteLength, primCount); return buffer;
}

T[] -> OneIndexBuffer

思路同上。

         public static OneIndexBuffer GetOneIndexBuffer(this byte[] array, DrawMode mode, BufferUsage usage, int primCount = )
{
GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, );
var unmanagedArray = new UnmanagedArray<byte>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UByte, primCount);
pinned.Free(); return buffer;
} public static OneIndexBuffer GetOneIndexBuffer(this ushort[] array, DrawMode mode, BufferUsage usage, int primCount = )
{
GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, );
var unmanagedArray = new UnmanagedArray<ushort>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UShort, primCount);
pinned.Free(); return buffer;
} public static OneIndexBuffer GetOneIndexBuffer(this uint[] array, DrawMode mode, BufferUsage usage, int primCount = )
{
GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, );
var unmanagedArray = new UnmanagedArray<uint>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
OneIndexBuffer buffer = GetOneIndexBuffer(unmanagedArray, mode, usage, IndexElementType.UInt, primCount);
pinned.Free(); return buffer;
}

T -> OneIndexBuffer

只有1个元素的索引数组,比较奇葩,不过也是可以实现的。

         public static OneIndexBuffer GetOneIndexBuffer(this byte data, DrawMode mode, BufferUsage usage, int primCount = )
{
var array = new byte[] { data };
return GetOneIndexBuffer(array, mode, usage, primCount);
} public static OneIndexBuffer GetOneIndexBuffer(this ushort data, DrawMode mode, BufferUsage usage, int primCount = )
{
var array = new ushort[] { data };
return GetOneIndexBuffer(array, mode, usage, primCount);
} public static OneIndexBuffer GetOneIndexBuffer(this uint data, DrawMode mode, BufferUsage usage, int primCount = )
{
var array = new uint[] { data };
return GetOneIndexBuffer(array, mode, usage, primCount);
}

ZeroIndexBuffer

这事一个特殊的Buffer,因为实际上在OpenGL的server端并没有真正创建一个Buffer。但是逻辑上把它也视作一个Buffer是方便合理的。既然没有真正创建Buffer,那么也就不存在用非托管数组创建ZeroIndexBuffer的情形了。

非托管数组->自定义Buffer

自定义Buffer都有哪些

所谓自定义Buffer,是那些用途各异的特殊Buffer,目前CSharpGL包含了:

 AtomicCounterBuffer
PixelPackBuffer
PixelUnpackBuffer
ShaderStorageBuffer
TextureBuffer
UniformBuffer

下面以 AtomicCounterBuffer 为例,其他雷同。

非托管数组->自定义Buffer

思路同上。

        public static AtomicCounterBuffer GetAtomicCounterBuffer(this UnmanagedArrayBase array, BufferUsage usage)
{
return GetIndependentBuffer(array, IndependentBufferTarget.AtomicCounterBuffer, usage) as AtomicCounterBuffer;
} private static Buffer GetIndependentBuffer(this UnmanagedArrayBase array, IndependentBufferTarget bufferTarget, BufferUsage usage)
{
uint[] buffers = new uint[];
glGenBuffers(, buffers);
var target = (uint)bufferTarget;
glBindBuffer(target, buffers[]);
glBufferData(target, array.ByteLength, array.Header, (uint)usage);
glBindBuffer(target, ); Buffer buffer = null;
switch (bufferTarget)
{
case IndependentBufferTarget.AtomicCounterBuffer:
buffer = new AtomicCounterBuffer(buffers[], array.Length, array.ByteLength);
break; case IndependentBufferTarget.PixelPackBuffer:
buffer = new PixelPackBuffer(buffers[], array.Length, array.ByteLength);
break; case IndependentBufferTarget.PixelUnpackBuffer:
buffer = new PixelUnpackBuffer(buffers[], array.Length, array.ByteLength);
break; case IndependentBufferTarget.ShaderStorageBuffer:
buffer = new ShaderStorageBuffer(buffers[], array.Length, array.ByteLength);
break; case IndependentBufferTarget.TextureBuffer:
buffer = new TextureBuffer(buffers[], array.Length, array.ByteLength);
break; case IndependentBufferTarget.UniformBuffer:
buffer = new UniformBuffer(buffers[], array.Length, array.ByteLength);
break; default:
throw new NotImplementedException();
} return buffer;
}

T[] –> 自定义Buffer

思路同上。

         public static AtomicCounterBuffer GetAtomicCounterBuffer<T>(this T[] array, BufferUsage usage) where T : struct
{
GCHandle pinned = GCHandle.Alloc(array, GCHandleType.Pinned);
IntPtr header = Marshal.UnsafeAddrOfPinnedArrayElement(array, );
var unmanagedArray = new UnmanagedArray<T>(header, array.Length);// It's not neecessary to call Dispose() for this unmanaged array.
AtomicCounterBuffer buffer = GetIndependentBuffer(unmanagedArray, IndependentBufferTarget.AtomicCounterBuffer, usage) as AtomicCounterBuffer;
pinned.Free(); return buffer;
}

T -> 自定义Buffer

思路同上。这个方式还是比较常见的一种用法。

         public static AtomicCounterBuffer GetAtomicCounterBuffer<T>(this T data, BufferUsage usage) where T : struct
{
var array = new T[] { data };
return GetAtomicCounterBuffer(array, usage);
}

如何使用

实现了上面那些看起来比较啰嗦的功能,现在来看看使用的时候是什么情形。

-> VertexBuffer

最基本的功能是通过数组UnmanagedArrayBase或T[]创建一个VBO,我们首先使用这个功能。可见只需一行代码即可实现,且调用方式也相同。

     vec3 position = GetPositions();
VertexBuffer buffer = position.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);
//
vec3[] positions = GetPositions();
VertexBuffer buffer = positions.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);
//
UnmanagedArray<vec3> positions = GetPositions();
VertexBuffer buffer = positions.GetVertexBuffer(VBOConfig.Vec3, varNameInShader, BufferUsage.StaticDraw);

-> OneIndexBuffer

同上,不解释。

     uint position = GetIndexes();
VertexBuffer buffer = position.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);
//
uint[] positions = GetIndexes();
VertexBuffer buffer = positions.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);
//
UnmanagedArray<uint> positions = GetIndexes();
VertexBuffer buffer = positions.GetOneIndexBuffer(DrawMode.Triangles, BufferUsage.StaticDraw);

-> 自定义Buffer

同上,不解释。

     SomeStruct data = GetIndexes();
VertexBuffer buffer = position.GetUniformBuffer(BufferUsage.StaticDraw);
//
SomeStruct[] data = GetIndexes();
VertexBuffer buffer = data.GetUniformBuffer(BufferUsage.StaticDraw);
//
UnmanagedArray<SomeStruct> data = GetIndexes();
VertexBuffer buffer = data.GetUniformBuffer(BufferUsage.StaticDraw);

总结

业务数据是核心,其他参数辅助,按照这一思路,就实现了现在的一行创建VBO的功能。

CSharpGL已经有点深度,所以笔记很难写出让人直接就能眼前一亮的感觉了。

目前CSharpGL中已经涵盖了我所知的所有OpenGL知识点。下一步就是精心读书,继续深挖。

最新文章

  1. Java的JDBC操作
  2. C++复数类对除法运算符 / 的重载
  3. Java项目相关监控与调优
  4. 正确使用 Volatile 变量——Brian Goetz
  5. So easy Webservice 5.WSDL 文件说明
  6. JavaScript之菱形打印
  7. java常用集合类:Deque,ArrayList,HashMap,HashSet
  8. 【USACO 2.4.2】穿越栅栏
  9. Renting Boats
  10. 利用扩展双屏技术及Chrome浏览器,高速剖析优秀网页Div及CSS构成,并高效实现原型创作
  11. 第一次作业:扑通扑通 我的IT
  12. 51nod 1201 整数划分 dp
  13. learning websocket protocol
  14. c# 文件笔记
  15. shell升级
  16. 【Win10】一些零碎不好归档的小总结(原谅我这个该死的标题吧)
  17. SpringMVC 面试题
  18. vivado与modelsim的联合仿真
  19. BZOJ 4326 NOIP2015 运输计划 (二分+树上差分)
  20. 软件定义网络(SDN)研究进展

热门文章

  1. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理
  2. MVVM设计模式和WPF中的实现(四)事件绑定
  3. C# 中参数验证方式的演变
  4. MySQL碎碎念
  5. Mysql存储引擎及选择方法
  6. javascript 判断参数类型大全
  7. java中易错点(一)
  8. java中的内部类
  9. 彻底搞懂Javascript的“==”
  10. 太多选择——企业如何选择合适的BI工具?