Создание геометрии дорог и кривых

PlayCanvas предоставляет мощные инструменты для разработки 3D-графики и создания интерактивных приложений в браузере с использованием WebGL. Одной из важнейших задач при создании игр и приложений является моделирование геометрии окружающего мира, в том числе дорог, трасс и кривых. В этой главе рассмотрим, как создать геометрию дорог и кривых с использованием библиотеки PlayCanvas, начиная с простых примитивов и заканчивая более сложными методами.

Основные понятия

Геометрия дороги или кривой в 3D-пространстве обычно состоит из последовательности точек, соединённых линиями, или из кривых, которые могут быть выражены с помощью различных математических формул, например, сплайнов. В PlayCanvas можно работать с такими объектами с помощью классов для создания объектов Mesh и обработки геометрии на основе точек и кривых.

Примитивы и Mesh

Основной элемент геометрии в PlayCanvas — это Mesh, который представляет собой набор вершин и треугольников. Для создания дороги или кривой нужно либо использовать примитивы (например, плоскости, кубы или цилиндры), либо генерировать их вручную, с помощью кода.

Создание простой дороги с использованием примитивов

Для создания дороги можно использовать ряд примитивов, таких как прямоугольные плоскости, которые будут соединяться друг с другом, имитируя дорогу. В PlayCanvas можно использовать Entity и ModelComponent для добавления примитивов на сцену.

var roadEntity = new pc.Entity();
roadEntity.addComponent('model', {
    type: 'box'
});
roadEntity.setLocalScale(10, 0.1, 100); // Дорога с длиной 100 и шириной 10
app.root.addChild(roadEntity);

Этот код создаёт прямоугольную дорогу в виде плоской коробки с масштабом 10x0.1x100. Примитивы можно комбинировать для создания более сложных дорог, но в реальных проектах часто требуется создание кривых, чтобы дорога имела плавные изгибы.

Использование кривых для создания дорог

Для создания более сложной геометрии, например, извилистых дорог или трасс, можно использовать кривые. В PlayCanvas для работы с кривыми удобно использовать математические функции, такие как Bézier-сплайны или Catmull-Rom сплайны, которые позволяют генерировать плавные кривые через заданные точки.

Создание кривой с помощью Catmull-Rom сплайна

Catmull-Rom сплайн представляет собой гладкую кривую, проходящую через все указанные точки. Он часто используется для создания дорог, потому что позволяет плавно изменять направление, сохраняя естественные изгибы.

Для реализации этого сплайна нужно создать набор точек и вычислить промежуточные точки на основе этих данных. Пример кода, создающего такую кривую, представлен ниже:

function catmullRom(points, t) {
    var p0 = points[0];
    var p1 = points[1];
    var p2 = points[2];
    var p3 = points[3];

    var t2 = t * t;
    var t3 = t2 * t;

    var x = 0.5 * ((2 * p1.x) + (-p0.x + p2.x) * t + (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 + (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
    var y = 0.5 * ((2 * p1.y) + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
    var z = 0.5 * ((2 * p1.z) + (-p0.z + p2.z) * t + (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 + (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);

    return new pc.Vec3(x, y, z);
}

// Пример использования
var points = [
    new pc.Vec3(0, 0, 0),
    new pc.Vec3(10, 0, 10),
    new pc.Vec3(20, 0, -10),
    new pc.Vec3(30, 0, 0)
];

var curvePoints = [];
for (var t = 0; t < 1; t += 0.1) {
    curvePoints.push(catmullRom(points, t));
}

Этот код генерирует кривую, проходящую через четыре точки, с шагом 0.1 вдоль кривой. Результатом будет массив точек, которые можно использовать для создания геометрии дороги.

Создание Mesh для дороги по кривой

Для того чтобы дорога была более интересной, можно создать её не как просто плоский прямоугольник, а как объект, который будет следовать вдоль кривой. Для этого можно создать серию сегментов, которые будут выстраиваться вдоль вычисленных точек.

var roadEntity = new pc.Entity();
roadEntity.addComponent('model', {
    type: 'box'
});

var segments = [];
for (var i = 0; i < curvePoints.length - 1; i++) {
    var segment = new pc.Entity();
    segment.addComponent('model', {
        type: 'box'
    });
    segment.setLocalScale(10, 0.1, curvePoints[i].distance(curvePoints[i + 1]));
    segment.setPosition(curvePoints[i].x, curvePoints[i].y, curvePoints[i].z);

    var direction = curvePoints[i + 1].clone().sub(curvePoints[i]).normalize();
    segment.lookAt(segment.getPosition().add(direction));
    segments.push(segment);
    app.root.addChild(segment);
}

Этот код создаёт серию прямоугольных сегментов, которые следуют вдоль кривой. Каждый сегмент ориентируется в направлении следующей точки кривой, создавая тем самым ощущение плавного изгиба.

Работа с текстурами и материалами

Для более правдоподобного вида дороги, можно добавить текстуры. Например, дорогу можно покрыть асфальтом или грунтом, применив соответствующие материалы.

var material = new pc.Material();
material.diffuse = new pc.Color(0.2, 0.2, 0.2); // Цвет асфальта
material.update();

roadEntity.model.model.meshInstances[0].material = material;

Этот код создаёт новый материал и назначает его модели дороги. В реальных проектах для создания текстурных карт используется более сложная обработка с наложением текстур и карты нормалей.

Оптимизация

При создании дорог и кривых важно помнить о производительности. Чем больше сегментов и точек используется для построения дороги, тем выше нагрузка на систему. Чтобы снизить нагрузку, можно использовать методы объединения Mesh-ов, использование оптимизированных сплайнов и системы LOD (Level of Detail), где удалённые от камеры части дороги могут быть представлены с меньшим количеством деталей.

Заключение

Создание геометрии дорог и кривых в PlayCanvas требует комплексного подхода. Использование простых примитивов, таких как кубы и цилиндры, подходит для базовых моделей, однако для более сложных объектов, например, извилистых дорог, лучше использовать математические кривые, такие как Catmull-Rom сплайны. Важно учитывать как визуальные аспекты, так и производительность, чтобы создавать эффективные и визуально привлекательные сцены.