如何移动 3D 盒子以保持在旋转平面上?

Posted

技术标签:

【中文标题】如何移动 3D 盒子以保持在旋转平面上?【英文标题】:How can I move a 3D box in order to stay on a rotating plane? 【发布时间】:2019-01-13 10:33:08 【问题描述】:

我有一个旋转平面和一个盒子。当飞机旋转并倾斜一个角度时,我希望盒子保持在飞机上的相同位置。

Here平面没有倾斜,

当 here 飞机倾斜时,

但盒子不跟随平面向下。

在每次更新时,我都会渲染该框,并通过glm::translate 中的给定vec3 对其进行翻译:


    glm::mat4 modelMatrix = glm::mat4(1);
    modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));
    modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));
    modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));
    modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));
    modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
    RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));

飞机通过按WASD键移动:

    if (window->KeyHold(GLFW_KEY_W) && (anglePlaneOX > -90.0f)) 
        anglePlaneOX -= deltaTime * DELTA_SLOPE;
    
    if (window->KeyHold(GLFW_KEY_S) && (anglePlaneOX < 90.0f)) 
        anglePlaneOX += deltaTime * DELTA_SLOPE;
    
    if (window->KeyHold(GLFW_KEY_D) && (anglePlaneOZ > -90.0f)) 
        anglePlaneOZ -= deltaTime * DELTA_SLOPE;
    
    if (window->KeyHold(GLFW_KEY_A) && (anglePlaneOZ < 90.0f)) 
        anglePlaneOZ += deltaTime * DELTA_SLOPE;
    

我尝试了以下

按A键时:

boxOY += deltaTime * (1 - (float)cos((double)anglePlaneOZ * PI / 180));

当按下 W 键时:

boxOX -= deltaTime * sinf(anglePlaneOX * PI / 180);

但这些似乎都不起作用。

为了使盒子相应地移动到平面,数学关系是什么?

【问题讨论】:

【参考方案1】:

您必须在旋转“之前”进行翻译:

glm::mat4 modelMatrix = glm::mat4(1);  

modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));

modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));  
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));  
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));

modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ)); //  translate here

modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));  

解释:

翻译矩阵如下所示:

glm::mat4 translate;
translate[0] : ( 1,  0,  0,  0 )
translate[1] : ( 0,  1,  0,  0 )
translate[2] : ( 0,  0,  1,  0 )
translate[3] : ( tx, ty, tz, 1 )

围绕 Y 轴的旋转矩阵如下所示:

mat4  rotate;
float angle;
rotate[0] : ( cos(angle),  0, sin(angle), 0 )
rotate[1] : ( 0,           1, 0,          0 )
rotate[2] : ( -sin(angle), 0, cos(angle), 0 )
rotate[3] : ( 0,           0, 0,          1 ) 

translate * rotate 的结果是这样的:

model[0] : ( cos(angle),  0,  sin(angle), 0 )
model[1] : ( 0,           1,  0,          0 )
model[2] : ( -sin(angle), 0,  cos(angle), 0 )
model[3] : ( tx,          ty, tz,         1 )

注意,rotate * translate 的结果是:

model[0] : ( cos(angle),                     0,   sin(angle),                     0 )
model[1] : ( 0,                              1,   0,                              0 )
model[2] : ( -sin(angle),                    0,   cos(angle),                     0 )
model[3] : ( cos(angle)*tx - sin(angle)*tx,  ty,  sin(angle)*tz + cos(angle)*tz,  1 )

【讨论】:

这真的很有帮助,谢谢。但是,当飞机向下倾斜超过 45 度时(imgur.com/a/uydhjIy),盒子似乎落在了飞机上,并且在向上倾斜超过 45 度时保持在“上方”。 (imgur.com/a/3921x0U)。按A键时使用的公式为:boxOX -= deltaTime * (float)sinf(anglePlaneOZ * PI / 180);boxOY += deltaTime * (1 - (float)cos((double)anglePlaneOZ * PI / 180)); 立方体网格的中心必须在 (0, 0, 0) 并且立方体的位置必须完全由 (boxOX, boxOY, boxOZ) 定义 @DanBrezeanu 最后一个参数是RenderSimpleMesh的盒子位置吗?将其更改为 0,0,0,并使用矩阵平移来调整位置。 @immibis 不,最后一个参数是颜色。语法如下void RenderSimpleMesh(Mesh *mesh, Shader *shader, const glm::mat4 &amp; modelMatrix, const glm::vec3 &amp; color)

以上是关于如何移动 3D 盒子以保持在旋转平面上?的主要内容,如果未能解决你的问题,请参考以下文章

三 JS - 如何用 Y 平面切割 3D 对象?

无法以所需的方式旋转立方体 - 平面和鼠标移动方法

如何在SVG中旋转期间保持文本方向不变

Plotly React 在重新渲染之间保持缩放和旋转

在 Unity 3D 中使用触摸输入在地形上移动相机

实现一个复杂的基于旋转的相机