Babylon.js 第30章 网格变换

发布于:2022-12-22 ⋅ 阅读:(497) ⋅ 点赞:(0)

 

目录

一、定位网格

二、网格旋转

1、旋转方法

 2、自定义旋转

3、旋转四元数

4、旋转约定

YXZ:

ZXY:

ZXZ:

三、对其目标轴

 四、坐标变换

五、烘培转换

六、使用父子网格

1、使用父子网格关系 

2、添加父网格

 七、设置枢纽中心

三种沿轴旋转 


一、定位网格

网格其局部原点位于世界空间 (-1, 2, 1),即 mesh.position 位于 (-1, 2, 1);

设置网格位置方法:

mesh.position = new Vector3(2, 3, 4);//(2, 3, 4)
//(-1 + 2, 2 + 3, 1 + 4) = (1, 5, 5)
mesh.position.addInPlace(new Vector3(2, 3, 4)); 
mesh.translate(new BABYLON.Vector3(2, 3, 4), 1,
     BABYLON.Space.WORLD); //(-1 + 2, 2 + 3, 1 + 4) = (1, 5, 5)

mesh.position.x = 2; //(2, 2, 1)
mesh.position.y = 3; //(2, 3, 1)
mesh.position.z = 4; //(2, 3, 4)

mesh.position.x += 2; //(-1 + 2, 2, 1) = (1, 2, 1)
mesh.position.y += 3; //(1, 2 + 3, 1) = (1, 5, 1)
mesh.position.z += 4; //(1, 5, 1 + 4) = (1, 5, 5)

//生成的位置取决于网格的方向。在不知道网格旋转的情况下,不可能给出结果位置。
mesh.translate(new BABYLON.Vector3(2, 3, 4), 1, BABYLON.Space.LOCAL);
mesh.setPositionWithLocalVector(new BABYLON.Vector3(2, 3, 4));
mesh.locallyTranslate(new BABYLON.Vector3(2, 3. 4));

positionsetPositionWithLocalVector的向量是位置向量。translatelocalTranslateaddInPlace的那些是方向向量。

二、网格旋转

1、旋转方法

旋转沿着轴设定: 

mesh.rotation = new BABYLON.Vector3(alpha, beta, gamma);

 beta 围绕局部 y 轴旋转,然后 alpha 围绕局部 x 轴旋转,最后 gamma 围绕局部 z 轴旋转。

 2、自定义旋转

mesh.rotation.addRotation(Math.PI / 2, 0, 0)
    .addRotation(0, 0, Math.PI / 3).addRotation(0, Math.PI / 8);

将形成网格的当前旋转,进一步围绕 x 轴旋转 π/2,然后围绕 z 轴旋转 π/3,然后围绕 y 轴旋转 π/8。

指定轴的方向矢量和角度是产生旋转的另一种方法。这就是在世界空间或局部空间中使用旋转方法的方式。

mesh.rotate(new BABYLON.Vector3(1, 0 -1), Math.PI / 3, BABYLON.Space.WORLD);
mesh.rotate(new BABYLON.Vector3(1, 0 -1), Math.PI / 3, BABYLON.Space.LOCAL);

3、旋转四元数

四元数是一个四维向量 (x, y, z, w),要成为旋转四元数,它必须是单位向量,即 x 2 + y 2 + z 2 + w 2 = 1,我们已经使用了 rotate 来设置网格的旋转四元数。旋转属性相同,rotationQuaternion属性以局部原点为旋转中心设置网格的方向。除了旋转,您还可以通过使用直接获得旋转四元数

mesh.rotationQuaternion = new BABYLON.Quaternion
    .RotationAxis(new BABYLON.Vector3(1, 0, -1), Math.PI / 3);

 RotationAxis方法的参数是轴方向和角度。轴方向矢量应在世界空间中表示。任何旋转四元数都可以转换为欧拉角以与mesh.rotation一起使用,

您不能在同一网格上使用旋转四元数后跟旋转。一旦应用了rotationQuaternion,任何后续使用的旋转都会产生错误的方向,除非rotationQuaternion首先设置为null。请注意,这通常在导入模型时适用,因为其中许多模型已经具有旋转四元数集。

从 4.0 版开始,rotationQuaternion 设置为 null 时且仅当直接使用向量设置旋转时才会自动完成

mesh.rotation = new BABYLON.Vector3(0, 0, 0)

 每当您发现旋转错误时,值得在更新之前将 rotationQuaternion 设置null 。

4、旋转约定

 Mesh.rotation(alpha, beta, gamma) 使用三个欧拉角 alpha、beta 和 gamma,它们分别是围绕 X、Y 和 Z 轴的旋转。Babylon.js 使用的约定是基于 yaw、pitch 和 roll 约定,因此在局部空间中以 Y、X、Z 的顺序围绕 X、Y 和 Z 执行。

YXZ:

使用局部轴以 YXZ 顺序将独立旋转应用于新创建的网格(即具有零旋转的网格)

mesh.rotate(BABYLON.Axis.Y, yaw, BABYLON.Space.LOCAL);
mesh.rotate(BABYLON.Axis.X, pitch, BABYLON.Space.LOCAL);
mesh.rotate(BABYLON.Axis.Z, roll, BABYLON.Space.LOCAL);
mesh.rotation = new BABYLON.Vector3(pitch, yaw, roll);

ZXY:

mesh.rotate(BABYLON.Axis.Z, gamma, BABYLON.Space.WORLD);
mesh.rotate(BABYLON.Axis.X, alpha, BABYLON.Space.WORLD);
mesh.rotate(BABYLON.Axis.Y, beta, BABYLON.Space.WORLD);
mesh.rotation = new BABYLON.Vector3(alpha, beta, gamma);

使用四元数与使用RotationYawPitchRoll相同

const yprQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(yaw, pitch, roll);

ZXZ:

mesh.rotate(BABYLON.Axis.Z, alpha, BABYLON.Space.WORLD);
mesh.rotate(BABYLON.Axis.X, beta, BABYLON.Space.WORLD);
mesh.rotate(BABYLON.Axis.Z, gamma, BABYLON.Space.WORLD);
var abcQuaternion = BABYLON.Quaternion.RotationAlphaBetaGamma(alpha, beta, gamma);

三、对其目标轴

使用RotationFromAxis方法 

var orientation = BABYLON.Vector3.RotationFromAxis(axis1, axis2, axis3);
mesh.rotation = orientation;
  • axis1作为其局部空间中的 x 轴
  • axis2作为其局部空间中的 y 轴
  • axis3作为其局部空间中的 z 轴

将平面与切线对其:

    let path=[]
    for(let i=-100;i<100;i++){
        let x=i/10
        path.push(new BABYLON.Vector3(x,Math.sin(x),Math.cos(x)))
    }
    let Path3D=new BABYLON.Path3D(path)
    //获取曲线的坐标点
    let curve=Path3D.getCurve()
    //获取切线
    let tangents=Path3D.getTangents()
    //获取法线
    let normals=Path3D.getNormals()
    //获取副法线
    let binormals=Path3D.getBinormals()
    let lines=BABYLON.Mesh.CreateLines('line',path,scene)
    let plane=BABYLON.Mesh.CreatePlane('plane',2,scene)

    let p=0
    scene.registerBeforeRender(()=>{
        plane.rotation=new
        BABYLON.Vector3.RotationFromAxis(tangents[p],normals[p],binormals[p])
        plane.position=path[p]
        p++
        p%=curve.length
    })

new BABYLON.Vector3.RotationFromAxis(binormals[p], tangents[p], normals[p]);

使用以上代码会让目标Y轴沿切线方向前进。

给定两个球体,在它们之间绘制一个始终面向相机的平面。我们将设置红色和绿色球体、世界轴,显示为红色、绿色和蓝色线条,以及 xz 平面中的紫色平面。连接红色和绿色球体的是一个平面,上面有一个蓝色箭头,从绿色球体指向红色球体。

要求是箭头平面旋转使得

  • 它位于箭头上的正交轴之一沿着连接两个球体的线;
  • 位于箭头平面上的另一轴垂直于相机镜头;
  • 垂直于平面的轴沿着将箭头中间连接到相机的线。
  var axis1, axis2, axis3;

  axis1 = (sphere1.position).subtract(sphere2.position);
  mesh.scaling.x = axis1.length();

  scene.registerBeforeRender(function() {
    axis1 = (sphere1.position).subtract(sphere2.position);
    axis3 = BABYLON.Vector3.Cross(camera.position, axis1);
    axis2 = BABYLON.Vector3.Cross(axis3, axis1);
    
    mesh.rotation = BABYLON.Vector3.RotationFromAxis(axis1, axis2, axis3);
    pl.position = camera.position;
  });

 四、坐标变换

将 mesh_P 视为一个盒子,一个大小为 1 的立方体。在盒子的局部空间中,顶面的中心位于 (0, 0.5, 0)。将此框移动并旋转到新位置。我们希望 mesh_C,一个球体,位于该位置的盒子顶面的中心。

const matrix = mesh_P.computeWorldMatrix(true);//强制计算世界矩阵
const local_position = new BABYLON.Vector3(0, 0.5, ,0);//C在P中的相对位置
//获取C在空间中的位置
const global_position = BABYLON.Vector3.TransformCoordinates(
    local_position, matrix); 
mesh_C.position = global_position;

相较于设置的位置平移使用:

local_position.addInPlace(new BABYLON.Vector3(1, 1, 1))

示例1:

scene.registerAfterRender(function () {
        box.rotate(BABYLON.Axis.Y, Math.PI / 150, BABYLON.Space.LOCAL);
        box.rotate(BABYLON.Axis.X, Math.PI / 200, BABYLON.Space.LOCAL);
        box.translate(new BABYLON.Vector3(-1, -1, -1)
        .normalize(), 0.001, BABYLON.Space.WORLD)
        small.rotationQuaternion = box.rotationQuaternion;
        matrix = box.getWorldMatrix();
        y += 0.001;
        local_pos = new BABYLON.Vector3(0, y, 0);
        small.position = BABYLON.Vector3.TransformCoordinates(local_pos, matrix);

    })

示例二:

var phi = 0;
    scene.registerAfterRender(function () {
        matrix = disc.getWorldMatrix();
        disc.rotate(BABYLON.Axis.Y, Math.PI / 150, BABYLON.Space.LOCAL);
        disc.rotate(BABYLON.Axis.Z, Math.PI / 200, BABYLON.Space.LOCAL);
        disc.position = new BABYLON.Vector3(15 * Math.cos(phi),
        16 * Math.sin(phi), 5)
        boxes.rotationQuaternion = disc.rotationQuaternion;
        boxes.position = BABYLON.Vector3.TransformCoordinates(
        boxes_position, matrix);
        phi +=0.01;

    });

五、烘培转换

最直接的方法是对网格应用变换。例如,取一个边为 1 的盒子并将其放置在 (0, 3, 0) 处。它的顶点存储为 (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, 0.5, -0.5), (-0.5, - 0.5, 0.5), (0.5, -0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, 0.5) 其局部原点 (0, 3, 0) 存储在世界矩阵中。当这个当前变换被烘焙到它的顶点时,顶点现在存储为 (-0.5, 2.5, -0.5), (0.5, 2.5, -0.5), (0.5, 3.5, -0.5), (-0.5, 3.5, -0.5), (-0.5, 2.5, 0.5), (0.5, 2.5, 0.5), (0.5, 3.5, 0.5), (-0.5, 3.5, 0.5) 存储本地原点 (0, 0, 0)在世界矩阵中。现在,任何旋转都发生在旋转中心 3 位于框的中间下方。

box.bakeCurrentTransformIntoVertices();

六、使用父子网格

1、使用父子网格关系 

 TransformNode 是一个未渲染但可用作变换中心的对象。与使用空网格作为父级相比,这可以减少内存使用并提高渲染速度,并且比使用枢轴矩阵更简单。将 TransformNode 设置为父节点,然后旋转 TransformNode。TransformNode 可以用作网格和一些灯光和相机的变换中心 (CoT),通过充当它们的父级来增加多功能性。

var CoT = new BABYLON.TransformNode("root");
box.parent = CoT; 

2、添加父网格

有三种方式

meshC.parent = meshP; //1
meshC.setParent(meshP); //2
meshP.addChild(meshC); //3

移除子网格:

meshC.parent = null;
meshC.setParent(null);
meshP.removeChild(meshC);

 七、设置枢纽中心

设置或获取枢纽中心点: 

mesh.setPivotPoint(Vector3);
mesh.getPivotPoint();
mesh.getAbsolutePivotPoint();

 设置枢纽中心点:

mesh.setPivotPoint(new BABYLON.Vector3(x, y, z));

使用setPivotPointthen后,getPivotPoint将为您提供枢轴点与本地原点的相对位置及其getAbsolutePivotPoint在世界空间中的位置。

三种沿轴旋转 

 1、通过设置一个node根节点,然后旋转根节点。

    let XS=new BABYLON.TransformNode('root')
    XS.position=new BABYLON.Vector3(1,1,1)
    sphere.parent=XS,
    sphere.position=new BABYLON.Vector3(10,10,10)
    scene.registerBeforeRender(()=>{
        XS.rotate(new BABYLON.Vector3(10,10,0),0.02,BABYLON.Space.WORLD)
    })

2、使用父子

和上面的方法一样

    sphere.parent=sphere1
    sphere.position=new BABYLON.Vector3(10,0,0)

    scene.registerBeforeRender(()=>{
        sphere1.rotate(new BABYLON.Vector3(10,5,10),0.2,BABYLON.Space.LOCAL)
    })

3、使用setPivotMatrix

    sphere.setPivotMatrix(BABYLON.Matrix.Translation(0,10,0))
    sphere.position=new BABYLON.Vector3(10,10,0)

    scene.registerBeforeRender(()=>{
        sphere.rotation.x+=0.02
        //sphere.rotate(new BABYLON.Vector3(10,5,10),0.02,BABYLON.Space.LOCAL)
    })

 

本文含有隐藏内容,请 开通VIP 后查看