Я использую Three.js и пытаюсь сделать пирамиду, но с отрезанной вершиной, основанием является прямоугольником, а вершина тоже прямоугольником, но не в тех же пропорциях.
Вот картинка из Википедии, но я хочу, чтобы она позволяла использовать прямоугольные основы.
Я думаю, что это слово — «Призмоид», за исключением того, что в моем случае основанием будет не квадрат, а прямоугольник. Еще я хочу сделать с этим верх не по центру основания, что позволит мне создавать такие вещи, как параллелепипед.
Я рассмотрел CylinderGeometry и PolyhedronGeometry, но они оба требуют, чтобы все стороны основания были одинаковой длины.
Какой самый эффективный способ создания этих фигур?
Если по какой-то причине вы не хотите создавать такую фигуру, например. в 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);
спасибо за ответ чувак! это не решило мою проблему полностью, но дало мне фору, мне нужно было, чтобы что-то работало
Это был долгий и сложный процесс, чтобы во всем этом разобраться, но я это сделал! Спасибо @Ł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;
}
vertices
).indices
). обязательно используйте правило правой руки при объявлении очков. В этом методе я объявил точки в одном и том же порядке на каждой грани, чтобы упростить программирование алгоритмов в дальнейшем.uvs
). это была самая сложная часть. для каждого треугольника и для каждой точки поместите 2 числа с плавающей запятой от 0
до 1
в массиве uvs
. Проблема в том, что до сих пор мы устанавливали параметры с помощью индексации, но uvs требует от нас не использовать индексацию (по крайней мере, в моем случае). Итак, чтобы оно совпадало, нам нужно вызвать toNonIndexed()
, прежде чем устанавливать UV.
В объявлении массива uvs
много кода, но все они возвращают число между 0
и 1
. Я вставил туда специальный код, чтобы он выглядел естественно, когда верх не по центру и его размер отличается от основания.После этого скомпилируйте все это вместе, и вы получите геометрию призмы, которая также может обрабатывать параллелепипеды.
Возможно, вершинный шейдер применен к кубу? Или, может быть, модификатор Taper из этой библиотеки