【问题标题】:Setting matrixWorld property of a three.js object设置 three.js 对象的 matrixWorld 属性
【发布时间】:2016-09-23 14:24:51
【问题描述】:

我有一个数组,其中包含一个编码旋转和位置数据的 4x4 变换矩阵(我已经确认该矩阵表示 Octave 中的有效变换)。我想使用这个数组的内容设置一个three.js对象(Object3D)的matrixWorld属性,即根据变换矩阵设置对象的位置和旋转。

根据three.js 文档中的this page 和此处提出的各种问题(例如this onethis one),似乎关键是将nameOfObject.matrixAutoUpdate 设置为false。然而,考虑到这一点,我尝试了多种方法来设置nameOfObject.matrixWorld,但它们都没有改变对象的渲染位置:它保持在原点,没有旋转,就像matrixWorld 的情况一样是 4x4 单位矩阵。

这是我尝试过的(此代码在调用 render() 之前的更新方法中):

// Setting up a three.js matrix
var tempMatrix = new THREE.Matrix4();
tempMatrix.fromArray(arrayContainingTransformationMatrix);

// Values are set as expected
console.log(tempMatrix.elements);

// Note that the approaches below were tried one at a time

// First approach (doesn't work)
nameOfObject.matrixAutoUpdate = false;
nameOfObject.matrixWorld.copy(tempMatrix);

// Second approach (also doesn't work)
// Based on the second SO question linked above
nameOfObject.matrixAutoUpdate = false;
nameOfObject.matrix.copy(tempMatrix);
nameOfObject.updateMatrixWorld(true);

// Third approach (this doesn't work either, unfortunately)
nameOfObject.matrixAutoUpdate = false;
nameOfObject.matrixWorld.fromArray(arrayContainingTransformationMatrix);

// Appears to be set correctly regardless of which approach is used
console.log(sphere1.matrixWorld.elements);

几点说明:

  • 我认为实际上没有必要在每次迭代中将 matrixAutoUpdate 设置为 false,但现在我这样做只是为了安全起见。
  • 如果 nameOfObject.position 基于变换矩阵第四列中的值而不是更改 matrixWorld 进行修改,则对象的位置会按预期更改,因此这似乎不是渲染问题。
  • 我在多个位置读到过,在手动设置矩阵时不应调用 updateMatrix(),因为它会根据 positionrotation 属性覆盖它。我假设updateMatrixWorld() 也有类似的考虑,但我没有找到关于它的作用的太多讨论。

任何建议将不胜感激!最坏的情况,我会查看源代码,但到目前为止,three.js 一直很容易使用,我怀疑我只是遗漏了一些东西。

【问题讨论】:

  • 只能设置对象的矩阵。您不能设置对象的世界矩阵。这是你的问题吗? “如何设置 object.matrix 以使其产生特定的 object.matrixWorld - 考虑到对象父母的变换?”
  • 如果对象的世界矩阵是只读的,那就回答了我的问题。我认为可以设置它,因为Object3D docs 中的matrixWorld 属性未标记为“只读”(其他只读属性的描述,例如id,以“只读”开头)。
  • 它不是只读的。渲染器正在重置它。
  • 是否可以阻止渲染器这样做? (鉴于SceneUtils 中的attachdetach 方法,我意识到这不是必需的。)matrixAutoUpdate 的描述似乎暗示将其设置为false 应该防止matrixmatrixWorld 更新,但这似乎只会阻止matrix 被更新。
  • 我正在尝试了解您想要实现的目标。如果您在我的第一条评论中回答问题,那将很有帮助。

标签: javascript matrix 3d three.js transform


【解决方案1】:

一般来说,我们会使用以下方法来更新一个对象的matrixWorld。

    const mesh = new THREE.Mesh();
    const matrixWorld = new THREE.Matrix4();
    // this will update mesh position, quaternion and scale with new matrix.
    matrixWorld.decompose(mesh.position, mesh.quaternion, mesh.scale);

这是三个文档中的官方建议。

https://threejs.org/docs/#manual/en/introduction/Matrix-transformations

【讨论】:

    【解决方案2】:

    查看源代码。

    updateMatrix: function () {
            this.matrix.compose( this.position, this.quaternion, this.scale );
            this.matrixWorldNeedsUpdate = true;
        },
    

    我们看到 Object3D 的矩阵是根据它的位置、四元数和比例参数构建的。

    因此,与其设置矩阵,不如设置构成矩阵的各个部分。

    例如,此代码转换场景,使其以范围的中间为中心并缩放以适应。

        let xrange = xmax-xmin, yrange = ymax-ymin, zrange = zmax-zmin;
        let range = (xrange+yrange+zrange)/3;
        scene.scale.set(2/range,2/range,2/range);
        scene.position.set( -(xmax+xmin)/2, -(ymax+ymin)/2, -(zmax+zmin)/2); 
        seene.updateMatrix();
    

    【讨论】:

    • 如问题中所述,“我[拥有]一个包含编码旋转和位置数据的 4x4 变换矩阵的数组”,而不是位置和方向的单独表示。 (变换的来源是一个旧的 NDI NIR 光学跟踪器,它只能产生变换矩阵。)分解变换矩阵是可能的,但这样做会增加不必要的计算负荷,因为无论如何threejs 都必须自己生成变换矩阵;因此,给它提供已经可用的转换矩阵会更有效。
    【解决方案3】:

    目前,我可以通过设置对象的matrix 属性(相对于对象的父对象的局部变换)来实现所需的结果。因为对象的父对象是scene,所以这样的代码可以工作:

    scene.add(nameOfObject);
    
    // Using this transformation matrix (translation of two units along the x axis):
    // 1 0 0 2
    // 0 1 0 0
    // 0 0 1 0
    // 0 0 0 1
    
    // Column-major version of the transformation
    var tempArrayCM = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1];
    // Per the docs, Matrix4.fromArray() uses column-major format
    
    // Row-major version of the transformation
    var matrixT = new THREE.Matrix4();
    matrixT.set(1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    // Per the docs, Matrix4.set() uses row-major format
    
    nameOfObject.matrixAutoUpdate = false;
    
    nameOfObject.matrix.fromArray(tempArrayCM); // This works
    
    //nameOfObject.matrix.copy(matrixT); // This also works
    
    //nameOfObject.matrixWorld.fromArray(tempArrayCM); // No effect
    
    //nameOfObject.matrixWorld.copy(matrixT); // No effect
    

    似乎设置matrixWorld 不起作用。我想当有问题的对象是scene 的孩子时这很好,但我可以想象如果对象是scene 的孩子的孩子,这会导致问题,并且有必要将其世界位置设置为独立于其父母(编辑:似乎SceneUtilsattachdetach 方法允许这样做,尽管是间接的)。

    我不会将此标记为答案,因为它实际上只是一种解决方法,仅当对象的父对象为 scene 时才适用。

    顺便说一句:我觉得 Matrix4.set() 使用行优先顺序但 Matrix4.fromArray()Matrix4.elements 使用列优先顺序有点奇怪。

    【讨论】:

    • 你的循环中必须有 .updateMatrix() ,对吧?
    • 否,但它的省略与what I've read about updateMatrix() 一致(即在手动设置matrix 时不应调用它,因为它将覆盖使用@987654337 计算的转换@ 和 rotation 属性)。
    • Matrix4.set() 使用行优先顺序,因此代码在打印为 4 行的块时是人类可读的。 Matrix4.elements 使用以列为主的顺序来与 webGL 兼容。 Matrix4.fromArray() 使用列优先顺序与Matrix4.elements 兼容。
    • 直接设置matrixWorld是没有意义的,因为Scene扩展了Object3D,即Scene有自己的矩阵,Scene中的所有对象也是Object3D的子对象,Object3D的变换应该影响它的子对象。
    猜你喜欢
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多