将顶点阵列与OpenTK
本文关键字:OpenTK 阵列 顶点 | 更新日期: 2023-09-27 18:23:59
我知道使用VBO可能更好,但我有使用顶点阵列的个人原因(除了我的好奇心)
在C++中,这里使用顶点数组:
// "vertices" is an array of Vertex Struct
const char* data = reinterpret_cast<const char*>(vertices);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0));
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 8));
glColorPointer(4, GL_FLOAT, sizeof(Vertex), data + 16));
glDrawArrays(GL_QUADS, 0, vertexCount);
指针通过了,在C++中工作得很好,但是,我不能让它在带有OpenTK的C#中工作。我遵循了官方文件,最终得到了以下代码:
Vertex[] vertices = // .. Fill some vertex here
unsafe
{
fixed (Vertex* data = vertices)
{
GL.VertexPointer(2, VertexPointerType.Float, Vertex.Stride, new IntPtr(data + 0));
GL.TexCoordPointer(2, TexCoordPointerType.Float, Vertex.Stride, new IntPtr(data + 8));
GL.ColorPointer(4, ColorPointerType.Float, Vertex.Stride, new IntPtr(data + 16));
// Draw the Vertices
GL.DrawArrays(PrimitiveType.Quads,
0,
vertices.length);
GL.Finish(); // Force OpenGL to finish rendering while the arrays are still pinned.
}
}
我得到的只是空白的白色,没有任何显示。
我试图在我的VBO实现中使用相同的顶点,也使用类似的顶点指针代码,它工作正常,所以我认为它不是设置代码错误,我确信Vertex.Stride
返回了Vertex
结构的有效步长。
有什么想法吗?
unsafe
路由不起作用,因为new IntPtr(data + 8)
将指向前方8个顶点,而不是指向前方8字节。您可以使用IntPtr.Add
方法修复此问题:
fixed (Vertex* data = vertices)
{
var stride = Marshal.SizeOf<Vertex>();
GL.VertexPointer(2, VertexPointerType.Float, stride,
new IntPtr.Add(new IntPtr(data), 0));
GL.TexCoordPointer(2, TexCoordPointerType.Float, stride,
new IntPtr.Add(new IntPtr(data), 8));
GL.ColorPointer(4, ColorPointerType.Float, stride,
new IntPtr.Add(new IntPtr(data), 16));
GL.DrawArrays(PrimitiveType.Quads, 0, vertices.Length);
GL.Finish();
}
或者,您可以通过直接获取相关指针来减少出错的可能性:
fixed (void* posPtr = &vertices[0].x, texPtr = &vertices[0].u, colorPtr = &vertices[0].r)
{
var stride = Marshal.SizeOf<Vertex>();
GL.VertexPointer(2, VertexPointerType.Float, stride,
new IntPtr(posPtr));
GL.TexCoordPointer(2, TexCoordPointerType.Float, stride,
new IntPtr(texPtr))
GL.ColorPointer(4, ColorPointerType.Float, stride,
new IntPtr(colorPtr))
GL.DrawArrays(PrimitiveType.Quads, 0, vertices.Length);
GL.Finish();
}
我假设Vertex被定义为或类似于:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Vertex
{
public float x, y;
public float u, v;
public float r, g, b, a;
};
编辑:
StructLayout
LayoutKind.Sequential
属性明确地"告诉"编译器不要重新排列内存中变量的顺序。注意在这种情况下,这不是绝对必要的,因为根据MSDN,
LayoutKind.Sequential
是struct
s(值类型)的默认行为。Pack = 1
还告诉编译器将变量紧密地封装在内存中,而不留下任何间隙。默认情况下,编译器会尝试在4(或8)字节边界上对齐内存。注意同样,对于给定的
Vertex
结构,这是不必要的,因为所有变量都在4字节的边界上完全对齐,并且它们之间没有间隙。
在这种情况下,即使StructLayout
属性在没有它的情况下也能完美工作,我之所以仍然保留它,是为了清楚明确地了解Vertex
结构的预期内存布局,因为在这种情况中这很重要。
EDIT2:也适用于泛型
public struct Vertex
{
public Vector2<float> Pos;
public Vector2<float> Texcoord;
public Vector4<byte> Color;
}
public struct Vector2<T>
{
public T x, y;
}
public struct Vector3<T>
{
public T x, y, z;
}
public struct Vector4<T>
{
public T x, y, z, w;
}
获取指针:
fixed (void* posPtr = &vertices[0].Pos.x,
texPtr = &vertices[0].Texcoord.x,
colorPtr = &vertices[0].Color.x)
最后,我可以通过对Vertex
结构进行去交错来使其工作(我真的不确定它是否被称为"去交错")。然而,我认为这不是解决这个问题的最佳方案。我们将高度赞赏更好的解决方案。
这里的代码去夹层每个属性,并上传这些属性数据每帧使用顶点阵列:
Vertex[] vertices = // Fill some vertices here..
// This is workaround that i was talking about, de-interleave attributes of Vertex Struct
// it might be better to de-interleaving those attributes when the change has been made to vertices rather than de-interleaving them each frames.
List<Vector2> positions = new List<Vector2>();
foreach (Vertex v in vertices)
positions.Add(v.position);
List<Vector2> texCoords = new List<Vector2>();
foreach (Vertex v in vertices)
texCoords.Add(v.texCoords);
List<Vector4> colors = new List<Vector4>();
foreach (Vertex v in vertices)
colors.Add(v.color);
// Use attribute stride instead of Vertex stride.
GL.VertexPointer(2, VertexPointerType.Float, Vector2.SizeInBytes, positions.ToArray());
GL.TexCoordPointer(2, TexCoordPointerType.Float, Vector2.SizeInBytes, texCoords.ToArray());
GL.ColorPointer(4, ColorPointerType.Float, Vector4.SizeInBytes, colors.ToArray());
// Draw the Vertices
GL.DrawArrays(PrimitiveType.Quads,
0,
vertices.length);