Рисование большого количества одинаковых моделей с использованием буферов вершин?
Я сталкиваюсь с проблемой, что многие разработчики, вероятно, нашли решение. У меня есть небольшой проект с полом с маленькими кубиками (100X100). Если я превышаю этот лимит, моя игра сильно пострадала, и Lague!
Вот как я рисую свой пол:
//Function to draw my ground ( 100 X 100 X 1)
public void DrawGround(GameTime gameTime)
{
// Copy any parent transforms.
Matrix[] transforms = new Matrix[this.model.Bones.Count];
this.model.CopyAbsoluteBoneTransformsTo(transforms);
//loop 1 cube high
for (int a = 0; a < 1; a++)
{
//loop 100 along cube
for (int b = 0; b < 100; b++)
{
//loop 100 cubic wide
for (int c = 0; c < 100; c++)
{
// Draw the model. A model can have multiple meshes, so loop.
foreach (ModelMesh mesh in this.model.Meshes)
{
// This is where the mesh orientation is set, as well
// as our camera and projection.
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateTranslation(this.position);
effect.View = this.view;
effect.Projection = this.projection;
}
// Draw the mesh, using the effects set above.
mesh.Draw();
}
}
}
}
}
Я думаю, что лучше использовать [VertexBuffer] "память о видеокарте", но я не нашел учебник для этого я хочу сделать...
Можете ли вы дать мне пример использования [VertexBuffer] в моей функции "DrawGround"?Большое спасибо!
1 ответ
ТЛ; др;
Используйте аппаратное инстансинг для рисования кубов. При желании используйте View Frustum Culling, чтобы избежать рисования невидимых экземпляров. Начните работу с вершинными и индексными буферами, прочитав Учебник Римера.
Instancing
Идея состоит в том, что буферы вершин и индексов привязываются к устройству один раз, а затем отрисовываются несколько раз за один вызов отрисовки. Экземпляр - это небольшой пакет данных, который содержит только информацию, уникальную для экземпляра модели. Эти данные экземпляра записываются в дополнительный вершинный буфер и связываются с устройством вместе с буферами, содержащими фактические вершины и индексы.
Как вы можете воспользоваться этим?
В вашем случае у вас будет следующая настройка:
- IndexBuffer, содержащий индексы сетки куба
- VertexBuffer, содержащий вершины сетки куба
- второй VertexBuffer, содержащий преобразование всех кубов, которые вы хотите нарисовать
- специальный макет ввода, который учитывает данные экземпляра
Поскольку ваши кубы различаются только по положению, вам даже не нужно отправлять полные матрицы преобразования в шейдер. Просто отправьте позицию экземпляра и добавьте ее в позицию вершины. Вы также можете отправить другие данные, как цвет для каждого экземпляра.
Как вы это реализуете?
Быстрый поиск в Google дал мне массу результатов для xna instancing, так что вам стоит начать. Вот два случайных результата, которые кажутся многообещающими:
Отбор Frustum
В любое время может быть видна только часть сетки в сцене. Объект может быть закрыт другим объектом или находится полностью вне поля зрения игрока. Процесс удаления этих невидимых сеток перед их рисованием называется отбраковкой. Просмотр Frustum Culling адресует второй случай:
Все тома, которые не пересекаются с громкостью камеры, невидимы.
Каков объем камеры? Ортографическая проекция имеет ограничивающий объем в форме коробки. С перспективной проекцией эта коробка имеет коническую форму, пирамиду, которая обрезается в ближней и дальней плоскостях; отсюда вид усеченного.
Как вы можете воспользоваться этим?
Используйте усеченный конус, чтобы идентифицировать экземпляры, невидимые в текущем кадре. Нарисуйте только те кубы, которые пересекаются с усечением вашей камеры. Ваша нагрузка рендеринга может быть уменьшена до 100%, когда игрок смотрит в небо.;-)
Вы можете объединить его с пространственной иерархией ( квадри или дерево октав), чтобы уменьшить количество вычислений пересечения коробчатого усеченного конуса.
Как вы это реализуете?
Xna предоставляет структуру BoundingBox, а также класс BoundingFrustum. Все, что вам нужно сделать, это использовать их.
Крошечный маленький недостаток
Объединение View Frustum Culling и Hardware Instancing может быть сложным. Удаление экземпляров означает, что вам также придется удалить их из экземпляра buffer =>, что означает воссоздание всего этого. Возможно, рассмотрите возможность вычисления видимости куба и обновления буфера экземпляра только каждые несколько кадров или когда камера перемещается быстро. Как и в какой степени вы, наконец, реализуете эти методы, зависит от вас и зависит от ваших требований.
Я только что понял, что вы хотите знать, как работают буферы вершин:
Vertex Buffers
Прежде чем треугольник можно будет нарисовать на экране, графическое устройство должно быть подготовлено. Это включает в себя связывание шейдеров, сэмплеров, текстур и, конечно, связывание ваших данных геометрии. Когда вызывается отрисовка, графическое устройство запускает шейдеры и записывает вывод в цель рендеринга.
В настоящее время вы используете встроенные абстракции XNA, которые упрощают процесс (и берут на себя весь контроль). Связывание шейдеров, констант, входного макета и т. Д. В настоящее время все скрыто в вашем классе Effect, а класс Mesh заботится о геометрической стороне вещей.
Что делает mesh.Draw()?
При создании сетки создаются два буфера, VertexBuffer и IndexBuffer. Создание этих буферов стоит дорого, поэтому хорошо, что это делается только один раз. Когда вы вызываете метод draw, буферы привязываются к устройству, а затем выполняется вызов draw. Сначала необходимо установить состояния эффекта, как вы правильно прокомментировали в своем коде.
Но вы также можете создавать свои собственные буферы вершин и индексов:
// Instantiate the VertexBuffer
var vertexBuffer = new VertexBuffer(
device,
VertexPositionColorNormal.VertexDeclaration,
vertices.Length,
BufferUsage.WriteOnly
);
// Write data to the buffer
vertexBuffer.SetData(vertices);
// Instantiate the IndexBuffer
var indexBuffer = new IndexBuffer(
device,
typeof(int),
indices.Length,
BufferUsage.WriteOnly
);
// Write data to the buffer
indexBuffer.SetData(indices);
... и привязать их вручную к устройству:
device.Indices = myIndexBuffer;
device.SetVertexBuffer(myVertexBuffer);
Этот код был взят непосредственно из учебников по XNA Римера:
Посмотрите сайт, он мне очень помог, когда я впервые начал программировать графику.