Как создать призму в Three.js?

Я использую Three.js и пытаюсь сделать пирамиду, но с отрезанной вершиной, основанием является прямоугольником, а вершина тоже прямоугольником, но не в тех же пропорциях.

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

Я думаю, что это слово — «Призмоид», за исключением того, что в моем случае основанием будет не квадрат, а прямоугольник. Еще я хочу сделать с этим верх не по центру основания, что позволит мне создавать такие вещи, как параллелепипед.

Я рассмотрел CylinderGeometry и PolyhedronGeometry, но они оба требуют, чтобы все стороны основания были одинаковой длины.

Какой самый эффективный способ создания этих фигур?

Возможно, вершинный шейдер применен к кубу? Или, может быть, модификатор Taper из этой библиотеки

2pha 15.05.2024 01:15
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
175
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если по какой-то причине вы не хотите создавать такую ​​фигуру, например. в Blender (мне кажется, это самое простое решение). Вы можете просто изменить грани и вершины вручную. Просто поиграйте с вершинами, чтобы получить нужную форму. Раскомментируйте вершины, и вы поймете, что я имею в виду...

const vertices = [
    new THREE.Vector3(-1.5, -1, -1),
    new THREE.Vector3(1.5, -1, -1),
    new THREE.Vector3(1.5, -1, 1),
    new THREE.Vector3(-1.5, -1, 1),
    new THREE.Vector3(-1, 1, -0.5),

    // Top rectangle 
    new THREE.Vector3(1, 1, -0.5),
    new THREE.Vector3(1, 1, 0.5),
    new THREE.Vector3(-1, 1, 0.5)
];

// for example:

// vertices[1].y += 0.2;
// vertices[6].y += 0.2;
// vertices[4].y -= 0.2;
// vertices[7].y -= 0.2;

const faces = [
    [0, 1, 4],
    [1, 2, 5],
    [2, 3, 6],
    [3, 0, 7],
    [1, 0, 3],
    [3, 2, 1],
    [5, 2, 1],
    [5, 6, 2],
    [7, 6, 3],
    [7, 3, 0],
    [4, 5, 6],
    [4, 6, 7]
];

const geometry = new THREE.BufferGeometry();
const verticesArray = [];
vertices.forEach(function(vertex) {
    verticesArray.push(vertex.x, vertex.y, vertex.z);
});
geometry.setAttribute('position', new THREE.Float32BufferAttribute(verticesArray, 3));

const indices = [];
faces.forEach(function(face) {
    indices.push(face[0], face[1], face[2]);
});
geometry.setIndex(indices);

const material = new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true });
const pyramid = new THREE.Mesh(geometry, material);
scene.add(pyramid);

спасибо за ответ чувак! это не решило мою проблему полностью, но дало мне фору, мне нужно было, чтобы что-то работало

shieldgenerator7 21.05.2024 22:22
Ответ принят как подходящий

Это был долгий и сложный процесс, чтобы во всем этом разобраться, но я это сделал! Спасибо @Łukasz D. Mastalerz за помощь!

вот код:

function createGeometry(width, depth, height, widthTop, depthTop, posTop = new THREE.Vector3(0,0,0)) {

    //create geometry
    let bufferGeometry = new BufferGeometry();

    //create base points
    const w2 = width / 2;
    const d2 = depth / 2;
    let v0 = new Vector3(-w2, 0, -d2);
    let v1 = new Vector3(w2, 0, -d2);
    let v2 = new Vector3(w2, 0, d2);
    let v3 = new Vector3(-w2, 0, d2);
    //create top points
    const wt2 = widthTop / 2;
    const dt2 = depthTop / 2;
    let v4 = new Vector3(-wt2, height, -dt2).add(posTop);
    let v5 = new Vector3(wt2, height, -dt2).add(posTop);
    let v6 = new Vector3(wt2, height, dt2).add(posTop);
    let v7 = new Vector3(-wt2, height, dt2).add(posTop);
    // points
    let points = [
        v0, v1, v2, v3,
        v4, v5, v6, v7,
    ];

    //vertices
    const vertices = new Float32Array(
        points
            .map(p => [p.x, p.y, p.z])
            .flat(Infinity)
    );

    //indices
    const indices = new Uint32Array([

        //right
        0, 3, 7,
        7, 4, 0,

        //left
        2, 1, 5,
        5, 6, 2,

        //top
        7, 6, 5,
        5, 4, 7,

        //bottom
        0, 1, 2,
        2, 3, 0,

        //back
        1, 0, 4,
        4, 5, 1,

        //front
        3, 2, 6,
        6, 7, 3,

    ]);

    //width uv constants
    const widthOffset = posTop.x;
    const maxWidth = Math.max(width, w2 + wt2 + Math.abs(widthOffset));
    const widthDiffLeft = w2 - wt2 + widthOffset;
    const widthDiffRight = width - (w2 + wt2 + widthOffset);

    //depth uv constants
    const depthOffset = posTop.z;
    const maxDepth = Math.max(depth, d2 + dt2 + Math.abs(depthOffset));
    const depthDiffLeft = d2 - dt2 + depthOffset;
    const depthDiffRight = depth - (d2 + dt2 + depthOffset);

    //uv
    const uvs = new Float32Array([

        //right
        Math.max(0, (0 - depthDiffLeft) / maxDepth), 0,//BL
        Math.min(1, (maxDepth + depthDiffRight) / maxDepth), 0,//BR
        Math.min(1, (maxDepth - depthDiffRight) / maxDepth), 1,//TR
        Math.min(1, (maxDepth - depthDiffRight) / maxDepth), 1,//TR
        Math.max(0, (0 + depthDiffLeft) / maxDepth), 1,//TL
        Math.max(0, (0 - depthDiffLeft) / maxDepth), 0,//BL

        //left
        Math.max(0, (0 - depthDiffRight) / maxDepth), 0,//BL
        Math.min(1, (maxDepth + depthDiffLeft) / maxDepth), 0,//BR
        Math.min(1, (maxDepth - depthDiffLeft) / maxDepth), 1,//TR
        Math.min(1, (maxDepth - depthDiffLeft) / maxDepth), 1,//TR
        Math.max(0, (0 + depthDiffRight) / maxDepth), 1,//TL
        Math.max(0, (0 - depthDiffRight) / maxDepth), 0,//BL

        //top
        0, 0,
        1, 0,
        1, 1,
        1, 1,
        0, 1,
        0, 0,

        //bottom
        0, 0,
        1, 0,
        1, 1,
        1, 1,
        0, 1,
        0, 0,

        //back
        Math.max(0, (0 - widthDiffRight) / maxWidth), 0,//BL
        Math.min(1, (maxWidth + widthDiffLeft) / maxWidth), 0,//BR
        Math.min(1, (maxWidth - widthDiffLeft) / maxWidth), 1,//TR
        Math.min(1, (maxWidth - widthDiffLeft) / maxWidth), 1,//TR
        Math.max(0, (0 + widthDiffRight) / maxWidth), 1,//TL
        Math.max(0, (0 - widthDiffRight) / maxWidth), 0,//BL

        //front
        Math.max(0, (0 - widthDiffLeft) / maxWidth), 0,//BL
        Math.min(1, (maxWidth + widthDiffRight) / maxWidth), 0,//BR
        Math.min(1, (maxWidth - widthDiffRight) / maxWidth), 1,//TR
        Math.min(1, (maxWidth - widthDiffRight) / maxWidth), 1,//TR
        Math.max(0, (0 + widthDiffLeft) / maxWidth), 1,//TL
        Math.max(0, (0 - widthDiffLeft) / maxWidth), 0,//BL

    ]);

    //compile it together
    bufferGeometry.setIndex(new BufferAttribute(indices, 1));
    bufferGeometry.setAttribute('position', new BufferAttribute(vertices, 3));

    const sideCount = 6;
    for (let i = 0; i < sideCount; i++) {
        bufferGeometry.addGroup(i * sideCount, sideCount, i);
    }
    bufferGeometry.attributes.position.needsUpdate = true;

    bufferGeometry = bufferGeometry.toNonIndexed();

    bufferGeometry.setAttribute('uv', new BufferAttribute(uvs, 2));
    bufferGeometry.attributes.uv.needsUpdate = true;

    //return
    return bufferGeometry;
}
  1. сначала вам нужно определить, куда будут идти вершины (массив vertices).
  2. затем вам нужно определить лица (массив indices). обязательно используйте правило правой руки при объявлении очков. В этом методе я объявил точки в одном и том же порядке на каждой грани, чтобы упростить программирование алгоритмов в дальнейшем.
  3. после этого идут ультрафиолетовые лучи (массив uvs). это была самая сложная часть. для каждого треугольника и для каждой точки поместите 2 числа с плавающей запятой от 0 до 1 в массиве uvs. Проблема в том, что до сих пор мы устанавливали параметры с помощью индексации, но uvs требует от нас не использовать индексацию (по крайней мере, в моем случае). Итак, чтобы оно совпадало, нам нужно вызвать toNonIndexed(), прежде чем устанавливать UV. В объявлении массива uvs много кода, но все они возвращают число между 0 и 1. Я вставил туда специальный код, чтобы он выглядел естественно, когда верх не по центру и его размер отличается от основания.

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

Другие вопросы по теме