Что с ним не так - или как найти правильные настройки THREE.PerspectiveCamera
У меня есть простая сцена THREE.Scene, где основным содержимым является сетка THREE.Line, которая визуализирует основанный на ключевом кадре путь, по которому будет следовать камера для некоторой скриптовой анимации. Затем существует одна сетка на основе THREE.SphereGeometry, которая всегда перемещается в текущее местоположение камеры.
Текущий НЕПРАВИЛЬНЫЙ результат выглядит следующим образом (фрактальный фон визуализируется независимо, но с использованием одного и того же ключевого кадра - и в конечном итоге идея заключается в том, что визуализация "пути камеры" заканчивается в том же масштабе / проекции, что и соответствующий фрактальный фон...):
Основой является массив ключевых кадров, каждый из которых представляет собой modelViewMatrix для конкретной позиции / ориентации камеры и непосредственно используется для управления вершинным шейдером для фона, например:
varying vec3 eye, dir;
void main() {
gl_Position = vec4(position, 1.0);
eye = vec3(modelViewMatrix[3]);
dir = vec3(modelViewMatrix * vec4(position.x , position.y , 1, 0));
}
(Насколько я понимаю, "глаз" - это, в основном, положение камеры, а "dir" отражает ориентацию камеры и то, как она используется во время марширования лучей, неявно приводит к перспективной проекции)
Соответствующие объекты сетки создаются следующим образом:
visualizeCameraPath: function(scene) {
// debug: visualize the camera path
var n= this.getNumberOfKeyFrames();
var material = new THREE.LineBasicMaterial({
color: 0xffffff
});
var geometry = new THREE.Geometry();
for (var i= 0; i<n; i++) {
var m= this.getKeyFrameMatrix(true, i);
var pos= new THREE.Vector3();
var q= new THREE.Quaternion();
var scale= new THREE.Vector3();
m.decompose(pos,q,scale);
geometry.vertices.push( new THREE.Vector3( pos.x, pos.y, pos.z ));
}
this.camPath = new THREE.Line( geometry, material );
this.camPath.frustumCulled = false; // Avoid getting clipped - does not seem to help one little bit
scene.add( this.camPath );
var radius= 0.04;
var g = new THREE.SphereGeometry(radius, 10, 10, 0, Math.PI * 2, 0, Math.PI * 2);
this.marker = new THREE.Mesh(g, new THREE.MeshNormalMaterial());
scene.add(this.marker);
}
чтобы воспроизвести анимацию, я обновляю камеру и положение маркера следующим образом (я думаю, это уже неправильно, когда я использую матрицу ввода "m" непосредственно на "shadowCamera" - хотя я думаю, что она содержит правильную позицию):
syncShadowCamera(m) {
var pos= new THREE.Vector3();
var q= new THREE.Quaternion();
var scale= new THREE.Vector3();
m.decompose(pos,q,scale);
this.applyMatrix(m, this.shadowCamera); // also sets camera position to "pos"
// highlight current camera-position on the camera-path-line
if (this.marker != null) this.marker.position.set(pos.x, pos.y, pos.z);
},
applyMatrix: function(m, targetObj3d) {
var pos= new THREE.Vector3();
var q= new THREE.Quaternion();
var scale= new THREE.Vector3();
m.decompose(pos,q,scale);
targetObj3d.position.set(pos.x, pos.y, pos.z);
targetObj3d.quaternion.set(q.x, q.y, q.z, q.w);
targetObj3d.scale= scale;
targetObj3d.updateMatrix(); // this.matrix.compose( this.position, this.quaternion, this.scale );
targetObj3d.updateMatrixWorld(true);
},
Я пробовал несколько вещей относительно камеры, и скриншот отражает вывод с отключенным this.projectionMatrix (см. Код ниже).
createShadowCamera: function() {
var speed = 0.00039507;
var z_near = Math.abs(speed);
var z_far = speed * 65535.0;
var fH = Math.tan( this.FOV_Y * Math.PI / 360.0 ) * z_near;
var fW = Math.tan( this.FOV_X * Math.PI / 360.0 ) * z_near;
// orig opengl used: glFrustum(-fW, fW, -fH, fH, z_near, z_far);
var camera= new THREE.PerspectiveCamera();
camera.updateProjectionMatrix = function() {
// this.projectionMatrix.makePerspective( -fW, fW, fH, -fH, z_near, z_far );
this.projectionMatrix= new THREE.Matrix4(); // hack: fallback to no projection
};
camera.updateProjectionMatrix();
return camera;
},
Моя первоначальная попытка состояла в том, чтобы использовать те же настройки, которые использовались шейдером opengl для фрактального фона (см. GlFrustum выше). К сожалению, кажется, что мне все же удалось правильно сопоставить входные данные "modelViewMatrix" (и проекцию, неявно выполняемую с помощью raymarching в шейдере) с эквивалентными настройками THREE.PerspectiveCamera (direction/projectionMatrix).
Есть ли здесь какой-нибудь эксперт по расчетам матриц, который знает, как получить правильные преобразования?
1 ответ
Наконец я нашел один взлом, который работает.
На самом деле проблема состояла из двух частей:
1) Порядок строк в главном столбце modelViewMatrix: порядок, ожидаемый вершинным шейдером, является противоположностью того, что ожидает оставшийся THREE.js.
2) Object3D-иерархия: то есть Сцена, Сетка, Геометрия, Вершины линий + Камера: куда поместить данные modelViewMatrix, чтобы они создавали желаемый результат (то есть тот же результат, который давал старое кровавое приложение opengl): я не доволен взлом, который я нашел здесь - но пока он единственный, который, кажется, работает:
- Я НЕ касаюсь камеры.. она остается на 0/0/0
- Я непосредственно перемещаю все вершины моей "линии"-Геометрии относительно реального положения камеры (см. "Положение" в modelViewMatrix)
- Затем я отключаю "matrixAutoUpdate" в сетке, содержащей мою геометрию "line", и копирую modelViewMatrix (в котором я сначала обнулел "position") в поле "matrix".
Бинго.. тогда это работает. (Все мои попытки достичь того же результата вращением / смещением камеры или смещением / вращением любого из задействованных Object3D с треском провалились...)
РЕДАКТИРОВАТЬ: я нашел лучший способ, чем обновление вершин и, по крайней мере, сохранить все манипуляции на уровне сетки (я все еще перемещаю мир вокруг - как это делало бы старое приложение OpenGL..). Чтобы получить правильную последовательность перемещения / вращения, можно также использовать ("m" по-прежнему является оригинальной моделью OpenGLViewMatrix - с информацией о положении 0/0/0):
var t= new THREE.Matrix4().makeTranslation(-cameraPos.x, -cameraPos.y, -cameraPos.z);
t.premultiply ( m );
obj3d.matrixAutoUpdate=false;
obj3d.matrix.copy(t);
Если кто-то знает лучший способ, который также работает (тот, где камера обновляется без необходимости напрямую манипулировать объектными матрицами), мне, безусловно, было бы интересно услышать это.