three.js: матрица вращения для размещения THREE.group вдоль новой оси

(Пожалуйста, обратитесь также к моей иллюстрации проблемы: http://i.stack.imgur.com/SfwwP.png)

описание проблемы и идеи

Я создаю несколько объектов в стандартной системе координат XYZ. Они добавляются в THREE.group.

Пожалуйста, подумайте о группе как о стене с несколькими рамами и изображением, висящим на ней. Я хочу создать свои объекты кадра, например. размер (40, 20, 0,5). Таким образом, я получаю довольно плоскую рамку / произведение искусства в ландшафтном формате. Я создаю и размещаю несколько таких. Потом добавляю их в группу, которую хотел свободно вращать в мире по двум векторам start и end.

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

До сих пор я пытался решить это с помощью THREE.Matrix4().lookAt :

var group = new THREE.Group();
startVec = new THREE.Vector3( 100, 0, -100 );
endVec = new THREE.Vector3( -200, 0, 200 );
matrix = new THREE.Matrix4().lookAt(startVec, endVec, new THREE.Vector3( 0, 1, 0 ));
group.matrixAutoUpdate = false;

var object1 = new THREE.Mesh(new THREE.BoxGeometry(0.5, 20, 40), mat);
// etc. -> notice the swapping of X and Z coordinates I have to do.
group.add(object1);

group.applyMatrix(matrix);

Вы можете увидеть пример на jsfiddle: http://jsfiddle.net/y6b9Lumw/1/

Если вы откроете jsfiddle, вы увидите, что объекты не размещаются вдоль линии от start до end, хотя я размещаю их вдоль внутренней оси X групп, например: addBox(new THREE.Vector3( i * 30, 0 , 0 ));

Полный код:

<html>
<head>
<title>testing a rotation matrix</title>
 <style>body { margin: 0; } canvas { width: 100%; height: 100% } </style>
</head> <body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
<script>

var scene, camera, renderer, light, matrix;
var startVec, endVec;
var boxes;


function addBox(v) {
    var boxmesh;
    var boxgeom = new THREE.BoxGeometry( 15, 5, 1 );
    var boxmaterial = new THREE.MeshLambertMaterial( {color: 0xdd2222} );
    boxmesh = new THREE.Mesh( boxgeom, boxmaterial );

    //boxmesh.matrix.makeRotationY(Math.PI / 2);
    boxmesh.matrix.setPosition(v);

    boxmesh.matrixAutoUpdate = false;

    boxes.add(boxmesh);
}
function init() {
    renderer = new THREE.WebGLRenderer();
                    renderer.setClearColor( 0x222222 );
                    renderer.setPixelRatio( window.devicePixelRatio );
                    renderer.setSize( window.innerWidth, window.innerHeight );
                    document.body.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
    camera.position.set( -20, 30, 300 );

    var light = new THREE.PointLight (0xCCCCCC, 0.5 );
    scene.add(light);

    startVec = new THREE.Vector3( 100, 0, -100 );
    endVec = new THREE.Vector3( -200, 0, 200 );
    matrix = new THREE.Matrix4().lookAt(startVec, endVec, new THREE.Vector3( 0, 1, 0 ));

    boxes   = new THREE.Group();


    for (var i = -100; i <  100; i++) {
        addBox(new THREE.Vector3( i * 30, 0 , 0 ));
    }

    boxes.matrixAutoUpdate = false;
    boxes.applyMatrix(matrix);
    scene.add(boxes);



    var linegeometry = new THREE.Geometry();
    linegeometry.vertices.push( startVec, endVec);
    var line = new THREE.Line(linegeometry, new THREE.LineBasicMaterial({color: 0x33eeef}));
    scene.add(line);


    render();
}

function render(){

  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
init();



</script>
</body> </html>

Это хорошо работает только до некоторой степени. Поскольку вектор взгляда обычно ориентирован по оси Z (я думаю, что это (0,0,1)). Так что, к сожалению, объекты внутри группы также вращаются. На самом деле это то, что вы ожидаете от вращательного преобразования lookAt(). Это не то, что я хотел бы иметь, так как это помещает всех дочерних элементов в группу на их ось Z, а не их ось X.

Чтобы все выглядело правильно, мне пришлось инициализировать дочерние группы, поменяв местами X и Z. Вместо:

var object1 = new THREE.BoxGeometry( 40, 20, 0.5 );

Я должен сделать:

var object1 = new THREE.BoxGeometry( 0.5, 20, 40 );

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

мой вопрос: как моя матрица должна быть построена/выглядеть, чтобы выполнить то, что я хочу: обычно создавать объекты, а затем размещать их ось X вдоль вектора start и вектора end, например, размещать произведения искусства на стене, что может быть двигался? Я подумал о создании матрицы, ось X которой равна end.sub(start), поэтому вектор от начала до конца может быть тем, что мне нужно сделать? Если да, то как бы я его построил?

иллюстрация проблемы с изображением

Я попытался проиллюстрировать свою позицию двумя изображениями. Одна из них — стена, другая — стена внутри мира, с теми же объектами, прикрепленными к стене (см. начало поста).

На первом рисунке вы видите локальную систему координат группы с двумя добавленными дочерними элементами, один из которых перенесен по X. На втором рисунке вы можете увидеть ту же локальную систему внутри мира, как мне бы хотелось быть. Зеленые оси — это мировые оси. Также показаны векторы start и end. Вы можете видеть, что оба прямоугольника правильно расположены вдоль этой линии.


person nylki    schedule 22.02.2015    source источник
comment
Вам не хватает фундаментальной концепции о том, как работает three.js. Вместо непосредственного управления матрицей объектов вы устанавливаете объект position, rotation или quaternion и scale. Средство визуализации вычисляет матрицу для вас. Вот скрипт, который поможет вам начать работу: jsfiddle.net/1wfvL1bt.   -  person WestLangley    schedule 23.02.2015
comment
@WestLangley Обычно я использую position, rotation и т. д. вместо того, чтобы возиться с матрицей. Я почему-то подумал, что лучше в этом случае создать матрицу вращения. Я исправляюсь. С помощью вашего вдохновения в коде и подсказки использовать стандартные методы three.js я вскоре отвечу на свой вопрос.   -  person nylki    schedule 24.02.2015


Ответы (1)


Я хотел бы ответить на свой вопрос, не принимая во внимание идею манипулирования матрицей самостоятельно. Спасибо @WestLangley. Я адаптировал свою идею к следующему, установив группы quaternion через .setFromUnitVectors. Таким образом, поворот получается из поворота от оси X к вектору направления начала и конца, как описано в файле three.js. документация:

«Устанавливает этот кватернион в значение поворота, необходимого для поворота вектора направления vFrom в вектор направления vTo». (http://threejs.org/docs/#Reference/Math/Quaternion.setFromUnitVectors< /а>)

Ниже приведена соответствующая часть моего решения:

// define the starting and ending vector of the wall
start = new THREE.Vector3( -130, -40, 300 );
end = new THREE.Vector3( 60, 20, -100 );

// dir is the direction from start to end, normalized
var dir = new THREE.Vector3().copy(end).sub(start).normalize();

// position wall in the middle of start and end
var middle = new THREE.Vector3().copy(start).lerp(end, 0.5);
wall.position.copy(middle);

// rotate wall by applying rotation from X-Axis to dir
wall.quaternion.setFromUnitVectors( new THREE.Vector3(1, 0, 0), dir );

Результат можно увидеть здесь: http://jsfiddle.net/L9dmqqvy/1/

person nylki    schedule 23.02.2015