You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现由6个平面拼接的立方体网格绕全局X轴正确旋转?

六平面拼接立方体的全局整体旋转实现问题
  • 实现逻辑:通过6次调用draw_plane接口绘制立方体,先生成XY平面面片,再对平面执行旋转变换拼接生成立方体结构,目标是实现立方体的正确整体旋转。

初始效果与配置代码

旋转前的正常立方体效果:
旋转前立方体效果

旋转前的初始网格配置代码:

planeXY.setupMesh();
    
planeXY1.setupMesh();
planeXY1.modelMatrix.translate(QVector3D(0.0, 0.0, 2.0));

planeZY.setupMesh();
planeZY.modelMatrix.rotate(-90.0f, QVector3D(0.0f, 1.0f, 0.0f));

planeZY1.setupMesh();
planeZY1.modelMatrix.translate(QVector3D(2.0, 0.0, 0.0));
planeZY1.modelMatrix.rotate(-90.0f, QVector3D(0.0f, 1.0f, 0.0f));

planeXZ.setupMesh();
planeXZ.modelMatrix.rotate(90.0f, QVector3D(1.0f, 0.0f, 0.0f));

planeXZ1.setupMesh();
planeXZ1.modelMatrix.translate(QVector3D(0.0, 2.0, 0.0));
planeXZ1.modelMatrix.rotate(90.0f, QVector3D(1.0f, 0.0f, 0.0f));

平面生成逻辑,对应CreateMeshPlane函数实现:

Mesh CreateMeshPlane(Vector3D bottomleft, size_t numvertices_x, 
                     size_t numvertices_y, size_t worldsize_x, 
                     size_t worldsize_y)
{
    Mesh mesh;
    int numVerts = numvertices_x * numvertices_y;
    int numFaces = (numvertices_x - 1) * (numvertices_y - 1);
    int numIndices = numFaces * 6;

    float xStep = (float)worldsize_x / (numvertices_x - 1);
    float yStep = (float)worldsize_y / (numvertices_y - 1);

    mesh.vertices.resize(numVerts);
    mesh.indices.resize(numIndices);
   
    
    
    for (size_t y = 0; y < numvertices_y; y++)
    {
        for (size_t x = 0; x < numvertices_x; x++)
        {
                mesh.vertices[x + (y * numvertices_x)] = QVector3D(bottomleft.x() + (xStep * x), bottomleft.y() + (yStep * y), bottomleft.z());
        }
    }

    int offset = 0;

    for (int i = 0; i < numIndices; i+=6)
    {
        unsigned int cornerIndex = i/6 + offset;

        if ((cornerIndex + 1)%numvertices_x == 0)
        {
            offset++;
            cornerIndex++;
        }

        // First triangle
        int idx = 0;
        Face face;
        face.FaceIndices.resize(6);

        face.Edges.push_back(Edge((unsigned int)cornerIndex, (unsigned int)cornerIndex + numvertices_x));

        face.FaceIndices[idx] = (unsigned int)cornerIndex;/*0*/
        idx++;
        face.FaceIndices[idx] = (unsigned int)cornerIndex + numvertices_x; /*3*/
        idx++;

        face.Edges.push_back(Edge((unsigned int)cornerIndex + numvertices_x, (unsigned int)cornerIndex + numvertices_x + 1));
        face.FaceIndices[idx] = (unsigned int)cornerIndex + numvertices_x + 1; /*4*/
        idx++;

        // Second triangle
        face.FaceIndices[idx] = (unsigned int)cornerIndex;
        face.Edges.push_back(Edge((unsigned int)cornerIndex, (unsigned int)cornerIndex + 1));
        idx++;
        face.FaceIndices[idx] = (unsigned int)cornerIndex + numvertices_x + 1;
        idx++;
        face.Edges.push_back(Edge((unsigned int)cornerIndex + numvertices_x + 1, (unsigned int)cornerIndex + 1));
        face.FaceIndices[idx] = (unsigned int)cornerIndex + 1;
        mesh.faces.push_back(face);
    }

    return mesh;
}

各平面的初始化创建代码:

planeXZ = CreateMeshPlane({0.0, 0.0, 0.0}, ny, nx, 2, 2);
planeXZ1 = CreateMeshPlane({0.0, 0.0, 0.0}, ny, nx, 2, 2);

planeXY = CreateMeshPlane({0.0, 0.0, 0.0}, ny, nz, 2, 2);
planeXY1 = CreateMeshPlane({0.0, 0.0, 0.0}, ny, nz, 2, 2);           

planeZY = CreateMeshPlane({0.0,0.0,0.0}, nx, nz, 2, 2);
planeZY1 = CreateMeshPlane({0.0, 0.0,0.0}, nx, nz, 2, 2);

问题现象

尝试实现按下R键时所有平面绕X轴旋转20度的交互,初始错误实现代码如下:

if(ev->key() == Qt::Key_R)
{
    planeXY.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXY1.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeZY.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeZY1.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXZ.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXZ1.modelMatrix.rotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
}

运行后得到错误旋转效果:
错误旋转效果
现象为每个平面都绕自身的局部X轴旋转,立方体结构被打散,需要实现所有平面统一绕全局X轴完成整体旋转。

解决方案

出问题的核心原因是矩阵变换的应用顺序错了:Qt自带的QMatrix4x4变换采用右乘规则,直接调用rotate()追加的变换,都是在已有变换的局部坐标系下生效的。现在每个平面的modelMatrix已经存了拼接立方体用的平移、旋转值,再直接追加rotate,自然是每个面绕自己的局部X轴转动,立方体必然散架。
要实现绕全局轴整体旋转,选下面任意一种方法即可:

方案1:新增独立的全局立方体变换矩阵(推荐,易维护)

单独给整个立方体定义一个根节点的全局变换矩阵,六个面原来拼接用的平移、旋转代码不需要修改,渲染时把全局矩阵和每个面自己的局部矩阵相乘,作为最终的模型矩阵传给着色器即可。

  1. 定义全局立方体变换矩阵,初始为单位矩阵,不需要做任何初始变换:
    QMatrix4x4 cubeGlobalMatrix;
    
  2. 按键旋转逻辑只修改这个全局矩阵,用preRotate方法把旋转加到变换链的最前端,这样旋转就是绕全局坐标系轴生效:
    if(ev->key() == Qt::Key_R)
    {
        cubeGlobalMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    }
    
  3. 渲染每个平面时,最终使用的模型矩阵计算规则为 cubeGlobalMatrix * 平面对象的modelMatrix,将这个结果传入着色器计算顶点位置即可得到正确的整体旋转效果。

方案2:调整单个平面的变换调用方法

如果不想额外新增全局矩阵,可以把按键逻辑里所有的rotate调用全部替换为preRotate,让每次的旋转都插入到每个面自身变换的最前端,也能实现全局旋转效果:

if(ev->key() == Qt::Key_R)
{
    planeXY.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXY1.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeZY.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeZY1.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXZ.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
    planeXZ1.modelMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f));
}

注意:该方法实现简单,但后续如果要添加整体平移、缩放等逻辑,六个面的矩阵都需要同步修改,容易遗漏,长期开发优先选择第一种层级变换的实现思路。


内容的提问来源于stack exchange,提问作者Андрей Петров

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:27:30