如何实现由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:新增独立的全局立方体变换矩阵(推荐,易维护)
单独给整个立方体定义一个根节点的全局变换矩阵,六个面原来拼接用的平移、旋转代码不需要修改,渲染时把全局矩阵和每个面自己的局部矩阵相乘,作为最终的模型矩阵传给着色器即可。
- 定义全局立方体变换矩阵,初始为单位矩阵,不需要做任何初始变换:
QMatrix4x4 cubeGlobalMatrix; - 按键旋转逻辑只修改这个全局矩阵,用
preRotate方法把旋转加到变换链的最前端,这样旋转就是绕全局坐标系轴生效:if(ev->key() == Qt::Key_R) { cubeGlobalMatrix.preRotate(20.0f, QVector3D(1.0f, 0.0f, 0.0f)); } - 渲染每个平面时,最终使用的模型矩阵计算规则为
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,提问作者Андрей Петров
相关产品推荐
相关产品推荐

