2015-11-24 3 views
1

Я знаю, что это, вероятно, лучше использовать VBO вместо этого, но у меня есть личные причины, чтобы использовать вершинные массивы (кроме моего любопытства)Использование вершинных массивов с OpenTK

В 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++, однако я не могу заставить его работать на C# с OpenTK. Я последовал за official documentation и в конечном итоге с этим кодами:

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 struct.

Любые идеи?

+0

Учитывая ваш доказанный C++ навыки, это, возможно, проще сделать это в C++/CLI (включая WinForm) и использовать OpenGL как раз в том случае, если есть что-то напуганное с opentk. В частности, когда вы используете _pointers_ – MickyD

+0

К сожалению, мне нужно придерживаться C# в этой ситуации. – SirusDoma

+0

Попробуйте придерживаться примера грамматики: 1: 'Вершина * data = vertices' 2:' GL.VertexPoi ... stride, data) '' GL.ColorPoi ... stride, data + 16); ' –

ответ

1

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

EDIT:

  1. StructLayoutLayoutKind.Sequential атрибут явно сообщает компилятору НЕ переупорядочить порядок переменных в памяти.

    NB. В этом случае это не является абсолютно необходимым, так как согласно MSDN LayoutKind.Sequential используется поведение по умолчанию для struct s (типы значений).

  2. Pack = 1 дополнительно сообщает компилятору, что он должен аккуратно упаковать переменные в памяти, не оставляя никаких пробелов. По умолчанию компилятор пытается выровнять память на 4 (или 8) байтовой границе.

    NB. Опять же, это не обязательно для данной структуры 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) 
+0

Спасибо за ваш ответ, у меня есть некоторые вопросы относительно вашего ответа: 1. Нужен ли флаг' StructLayout'? что оно делает? Я не уверен в этом 2. Моя 'Vertex' состоит из нескольких структур, позиция, определенная с помощью структуры Vector2, имеет свойства X и Y (float), то же самое происходит с TexCoords. Для Color я определяю 'Vector4b', это структура, состоящая из 4 цветовых свойств (r g b a) с типом данных байта. Это нормально? – SirusDoma

+1

@SirusDoma Если все они являются структурами, это должно работать (даже без явного атрибута StructLayout). Особенно, используя второй способ получения точного адреса первого компонента. Проверьте обновленный ответ. – JBeurer

+0

Я только что проверил код, но он дал мне ошибку времени компиляции. Я получил две ошибки: один из них говорит: «Не могу взять адрес, получить размер или объявить указатель на управляемый тип (« Вершина »)», а другой: «Указатели и буферы фиксированного размера могут быть только используется в небезопасном контексте. Я забыл упомянуть, что я использую .NET 2.0, все в порядке? также кажется, что код просят помещать в «небезопасный» блок. – SirusDoma

1

Наконец-то, я могу заставить его работать, де-чередуя структуру Vertex (я не уверен, называется ли это «de-interleave»). Однако, я считаю, что это не лучшее решение этой проблемы. Лучшие решения будут высоко оценены.

Здесь коды де-interlave каждый атрибуты и загружать эти атрибуты данных каждого кадра с помощью вершинных массивов:

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

 Смежные вопросы

  • Нет связанных вопросов^_^