将顶点阵列与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结构的有效步长。

有什么想法吗?

将顶点阵列与OpenTK

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;
};

编辑:

  1. StructLayout LayoutKind.Sequential属性明确地"告诉"编译器不要重新排列内存中变量的顺序。

    注意在这种情况下,这不是绝对必要的,因为根据MSDN,LayoutKind.Sequentialstruct s(值类型)的默认行为。

  2. 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);