WPF 3D - 围绕它自己的轴旋转模型

时间:2010-01-11 13:48:59

标签: wpf math 3d geometry

假设我有一个简单的WPF 3D场景设置,其中一个矩形围绕X轴旋转-45度,如下所示:

<Viewport3D>
    <Viewport3D.Camera>
        <PerspectiveCamera Position="0,0,4"/>
    </Viewport3D.Camera>
    <ModelVisual3D>
        <ModelVisual3D.Content>
            <DirectionalLight Color="White" Direction="-1,-1,-3" />
        </ModelVisual3D.Content>
    </ModelVisual3D>
    <ModelVisual3D>
        <ModelVisual3D.Content>
            <GeometryModel3D>
                <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions="-1,-1,0  1,-1,0  -1,1,0  1,1,0"
                                    TriangleIndices="0,1,2 1,3,2"/>
                </GeometryModel3D.Geometry>
                <GeometryModel3D.Material>
                    <DiffuseMaterial Brush="Red"/>
                </GeometryModel3D.Material>
            </GeometryModel3D>
        </ModelVisual3D.Content>
        <ModelVisual3D.Transform>
            <Transform3DGroup>
                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D Axis="1,0,0" Angle="-45"/>
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>
            </Transform3DGroup>
        </ModelVisual3D.Transform>
    </ModelVisual3D>
</Viewport3D>

这给了我以下内容:

alt text

现在我想围绕模型的Z轴将图像旋转45度。如果我只是像这样放入第二个RotateTransform3D:

                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D Axis="0,0,1" Angle="45"/>
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>

围绕场景的 Z轴旋转。对于这个特定的X旋转我已经找到了我需要的东西:

                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D Axis="0,1,1" Angle="45"/>
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>

但是我的数学失败了。任何人都可以告诉我如何使用任意X(和Y,如果你想)旋转吗?

3 个答案:

答案 0 :(得分:11)

好的,和一位数学家的朋友谈过,他给了我答案:

  

所以我想如果你需要做什么   你在矢量周围旋转   (1,0,0)以'a'的角度(即旋转   围绕x轴,所以改变你的   在y-z平面上的物体。)

     

进一步轮换

     

x' - (1,0,0)保持不变!

     

y' - (0,cosa,sina)

     

z' - (0,-sina,cosa)

     

类似的原则将适用   x-z平面中的旋转(0,1,0)

     

x' - (-sina,0,cosa)

     

y' - (0,1,0) - 相同的

     

z' - (sina,o,cosa)

     

并且在(0,0,1)

周围的x-y平面中      

x' - (-sina,cosa,0)

     

y' - (cosa,sina,0)

     

z' - (0,0,1)保持不变

     

TADA!

更新:我创建了一个函数来计算一个矩阵,该矩阵将旋转所有3个轴中的对象。这可以与MatrixTransform3D一起使用。

    Matrix3D CalculateRotationMatrix(double x, double y, double z)
    {
        Matrix3D matrix = new Matrix3D();

        matrix.Rotate(new Quaternion(new Vector3D(1, 0, 0), x));
        matrix.Rotate(new Quaternion(new Vector3D(0, 1, 0) * matrix, y));
        matrix.Rotate(new Quaternion(new Vector3D(0, 0, 1) * matrix, z));

        return matrix;
    }

答案 1 :(得分:1)

您想出的答案由三个不同的旋转组成,提供了一个可以应用于对象的变换矩阵。这有效,但考虑到问题的措辞,这是不必要的。

在所描述的问题中,您希望平面绕其 Z 轴旋转,但是该旋转的结果绕 场景的 X 轴旋转,如下所示:

after two rotations

这是将您想出的变换添加到原始变换的结果,即使用这样的分组变换:

<Transform3DGroup>
  <RotateTransform3D>
    <RotateTransform3D.Rotation>
      <AxisAngleRotation3D Axis="1,0,0" Angle="-45"/>
    </RotateTransform3D.Rotation>
  </RotateTransform3D>
  <RotateTransform3D>
    <RotateTransform3D.Rotation>
      <AxisAngleRotation3D Axis="0,1,1" Angle="45"/>
    </RotateTransform3D.Rotation>
  </RotateTransform3D>
</Transform3DGroup>

问题是,您实际上首先遇到的唯一问题,至少考虑到上述问题,是您以错误的顺序组合原始转换。您只需在 X 轴旋转之前插入 Z 轴旋转 而不是之后即可实现完全相同的结果:

<Transform3DGroup>
  <RotateTransform3D>
    <RotateTransform3D.Rotation>
      <AxisAngleRotation3D Axis="0,0,1" Angle="45"/>
    </RotateTransform3D.Rotation>
  </RotateTransform3D>
  <RotateTransform3D>
    <RotateTransform3D.Rotation>
      <AxisAngleRotation3D Axis="1,0,0" Angle="-45"/>
    </RotateTransform3D.Rotation>
  </RotateTransform3D>
</Transform3DGroup>

确实,您在发布的答案中发布的方法实际上只是完成相同组合的不同方式。如果您比较调用方法返回的 Matrix3D 值,传递 -4545,即 CalculateRotationMatrix(-45, 0, 45),您会得到与 {{1} 返回的值基本相同}} 组的属性,如上声明(或在代码隐藏中初始化)。

似乎采用不同方法的唯一原因是,与使用 Transform3DGroup.Value 对象进行组合相比,直接处理矩阵数学时的组合顺序(如您的方法所做的那样)是不同的(因为该对象反转组合,使其在例如 XAML 声明中更自然地出现)。

(我说“本质上”,因为由于计算四元数的额外步骤引入的舍入误差,某些矩阵分量之间存在非常微小的差异。例如,在对旋转进行分组时,Transform3DGroup组件的值为 M12,而使用您的方法的相同组件的值为 0.5。矩阵不会被比较为相同,但当应用于场景中的对象时,它们具有完全相同的视觉效果。 )

现在,在代码隐藏中计算矩阵没有任何问题,如果这是您的代码需要的架构方式。并且使用四元数是一种久经考验的真实方法,特别是为了在两个不同的旋转之间进行插值(即您想要为旋转设置动画并为此目的轻松计算中间旋转)。

但我认为理解“围绕其自身的轴旋转对象”之类的事情并不真正重要,这一点很重要。对象的变换总是相对于它所在的坐标空间发生,而不是它自己的局部坐标空间。后者总是被移动以保持与对象本身对齐,任何时候该对象相对于其父对象旋转或以其他方式变换(无论是整个场景,对于顶级对象,还是场景中的某个其他对象)第一个对象是的子对象)。

WPF 中有许多不同的方法来组成 3D 变换矩阵,但它们实际上都归结为同一件事:将两个或多个矩阵相乘以获得最终一个。当通过场景对象图中的父/子关系将变换应用于不同的对象时,这甚至是正确的。事实上,恕我直言,在 3D 渲染 API 中使用对象的层次关系如果您打算让给定的对象仅绕其自身的单个轴旋转,则更为惯用。 IE。在您的示例中,您可以为对象提供一个父对象,将 X 轴旋转应用于该父对象,然后仅将 Z 轴旋转应用于子对象。

例如像这样:

0.49999999999999989

同样,同样的精确结果,只是表达矩阵组成的不同方式。在这种情况下,这将是在渲染平面仅应围绕“其自己的”Z 轴旋转,但仍相对于也移动的某个父级移动和/或旋转的场景中表达变换的更自然的方式和/或在场景中旋转(例如,关节机械臂上的手、汽车上的轮子或某些战车上的炮塔,仅举几个经常出现在 3D 渲染场景中的例子)。

答案 2 :(得分:0)