我的 3d OpenGL 对象围绕世界原点旋转,而不是本地 space 原点。我做错了什么或误解了什么?
My 3d OpenGL object rotates around the world origin, not local-space origin. What am I doing wrong or misunderstanding?
我已经被困在这个问题上两天了,我不确定还能去哪里找。我正在使用 OpenGL 渲染两个 3d 立方体,并尝试对这些场景中的每个立方体应用局部旋转以响应我按下按钮。
我已经到了我的立方体在 3d 中旋转的地步 space,但它们都围绕世界 space 原点旋转,而不是它们自己的本地原点。
(情侣秒视频)
https://www.youtube.com/watch?v=3mrK4_cCvUw
网上一搜,合适的MVP计算公式如下:
auto const model = TranslationMatrix * RotationMatrix * ScaleMatrix;
auto const modelview = projection * view * model;
我的每个立方体都有自己的 "model",定义如下:
struct model
{
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale = glm::vec3{1.0f};
};
当我按下键盘上的一个按钮时,我创建了一个代表新角度的四元数,并将它与之前的旋转四元数相乘,就地更新它。
函数如下所示:
template<typename TData>
void rotate_entity(TData &data, ecst::entity_id const eid, float const angle,
glm::vec3 const& axis) const
{
auto &m = data.get(ct::model, eid);
auto const q = glm::angleAxis(glm::degrees(angle), axis);
m.rotation = q * m.rotation;
// I'm a bit unsure on this last line above, I've also tried the following without fully understanding the difference
// m.rotation = m.rotation * q;
}
轴由用户提供,如下所示:
// inside user-input handling function
float constexpr ANGLE = 0.2f;
...
// y-rotation
case SDLK_u: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, 1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
case SDLK_i: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, -1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
根据我在示例代码中找到的内容,我的 GLSL 顶点着色器非常简单:
// attributes input to the vertex shader
in vec4 a_position; // position value
// output of the vertex shader - input to fragment
// shader
out vec3 v_uv;
uniform mat4 u_mvmatrix;
void main()
{
gl_Position = u_mvmatrix * a_position;
v_uv = vec3(a_position.x, a_position.y, a_position.z);
}
在我的绘制代码中,我用来计算每个立方体的 MVP 的确切代码是:
...
auto const& model = shape.model();
auto const tmatrix = glm::translate(glm::mat4{}, model.translation);
auto const rmatrix = glm::toMat4(model.rotation);
auto const smatrix = glm::scale(glm::mat4{}, model.scale);
auto const mmatrix = tmatrix * rmatrix * smatrix;
auto const mvmatrix = projection * view * mmatrix;
// simple wrapper that does logging and forwards to glUniformMatrix4fv()
p.set_uniform_matrix_4fv(logger, "u_mvmatrix", mvmatrix);
在我的程序的早期,我这样计算 view/projection 矩阵:
auto const windowheight = static_cast<GLfloat>(hw.h);
auto const windowwidth = static_cast<GLfloat>(hw.w);
auto projection = glm::perspective(60.0f, (windowwidth / windowheight), 0.1f, 100.0f);
auto view = glm::lookAt(
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
glm::vec3(0.0f, 0.0f, -1.0f), // look at origin
glm::vec3(0.0f, 1.0f, 0.0f)); // "up" vector
我的立方体在 world-space 中的位置在 Z 轴上,因此它们应该是可见的:
cube0.set_world_position(0.0f, 0.0f, 0.0f, 1.0f);
cube1.set_world_position(-0.7f, 0.7f, 0.0f, 1.0f);
// I call set_world_position() exactly once before my game enter's it's main loop.
// I never call this again, it just modifies the vertex used as the center of the shape.
// It doesn't modify the model matrix at all.
// I call it once before my game enter's it's game loop, and I never modify it after that.
所以,我的问题是,更新对象的旋转是否合适?
我应该直接在对象的 "model" 中存储 一个四元数吗?
我应该将平移和缩放存储为单独的 vec3 吗?
有更简单的方法吗?我一直在阅读和重新阅读我能找到的任何东西,但我没有看到任何人以同样的方式这样做。
本教程的细节有点短,特别是如何将旋转应用于现有旋转(我相信这只是将四元数相乘,这就是我在 rotate_entity(. ..) 以上)。
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
https://github.com/opengl-tutorials/ogl/blob/master/tutorial17_rotations/tutorial17.cpp#L306-L311
将自己生成的 "MVP" 矩阵存储为我的 "model" 并应用 glm::transform/glm:: 是否更有意义? scale/glm::直接对MVP矩阵进行旋转操作? (我之前尝试过最后一个选项,但我也不知道如何让它发挥作用)。
谢谢!
编辑:更好 [=76=]
通常,您不想修改模型的各个顶点在 CPU 上的位置。这就是顶点程序的全部目的。模型矩阵的作用是在顶点程序中定位模型在世界中的位置。
要绕中心旋转模型,您需要先将中心移动到原点,然后旋转它,然后将中心移动到它的最终位置。假设您有一个从 (0,0,0) 延伸到 (1,1,1) 的立方体。您需要:
- 将立方体平移 (-0.5, -0.5, -0.5)
- 旋转角度
- 将立方体平移 (0.5, 0.5, 0.5)
- 将立方体平移到场景中它所属的任何地方
您可以将最后 2 个转换合并为一个,当然,您可以将所有这些转换合并为一个矩阵,即您的模型矩阵。
我已经被困在这个问题上两天了,我不确定还能去哪里找。我正在使用 OpenGL 渲染两个 3d 立方体,并尝试对这些场景中的每个立方体应用局部旋转以响应我按下按钮。
我已经到了我的立方体在 3d 中旋转的地步 space,但它们都围绕世界 space 原点旋转,而不是它们自己的本地原点。
(情侣秒视频) https://www.youtube.com/watch?v=3mrK4_cCvUw
网上一搜,合适的MVP计算公式如下:
auto const model = TranslationMatrix * RotationMatrix * ScaleMatrix;
auto const modelview = projection * view * model;
我的每个立方体都有自己的 "model",定义如下:
struct model
{
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale = glm::vec3{1.0f};
};
当我按下键盘上的一个按钮时,我创建了一个代表新角度的四元数,并将它与之前的旋转四元数相乘,就地更新它。
函数如下所示:
template<typename TData>
void rotate_entity(TData &data, ecst::entity_id const eid, float const angle,
glm::vec3 const& axis) const
{
auto &m = data.get(ct::model, eid);
auto const q = glm::angleAxis(glm::degrees(angle), axis);
m.rotation = q * m.rotation;
// I'm a bit unsure on this last line above, I've also tried the following without fully understanding the difference
// m.rotation = m.rotation * q;
}
轴由用户提供,如下所示:
// inside user-input handling function
float constexpr ANGLE = 0.2f;
...
// y-rotation
case SDLK_u: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, 1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
case SDLK_i: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, -1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
根据我在示例代码中找到的内容,我的 GLSL 顶点着色器非常简单:
// attributes input to the vertex shader
in vec4 a_position; // position value
// output of the vertex shader - input to fragment
// shader
out vec3 v_uv;
uniform mat4 u_mvmatrix;
void main()
{
gl_Position = u_mvmatrix * a_position;
v_uv = vec3(a_position.x, a_position.y, a_position.z);
}
在我的绘制代码中,我用来计算每个立方体的 MVP 的确切代码是:
...
auto const& model = shape.model();
auto const tmatrix = glm::translate(glm::mat4{}, model.translation);
auto const rmatrix = glm::toMat4(model.rotation);
auto const smatrix = glm::scale(glm::mat4{}, model.scale);
auto const mmatrix = tmatrix * rmatrix * smatrix;
auto const mvmatrix = projection * view * mmatrix;
// simple wrapper that does logging and forwards to glUniformMatrix4fv()
p.set_uniform_matrix_4fv(logger, "u_mvmatrix", mvmatrix);
在我的程序的早期,我这样计算 view/projection 矩阵:
auto const windowheight = static_cast<GLfloat>(hw.h);
auto const windowwidth = static_cast<GLfloat>(hw.w);
auto projection = glm::perspective(60.0f, (windowwidth / windowheight), 0.1f, 100.0f);
auto view = glm::lookAt(
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
glm::vec3(0.0f, 0.0f, -1.0f), // look at origin
glm::vec3(0.0f, 1.0f, 0.0f)); // "up" vector
我的立方体在 world-space 中的位置在 Z 轴上,因此它们应该是可见的:
cube0.set_world_position(0.0f, 0.0f, 0.0f, 1.0f);
cube1.set_world_position(-0.7f, 0.7f, 0.0f, 1.0f);
// I call set_world_position() exactly once before my game enter's it's main loop.
// I never call this again, it just modifies the vertex used as the center of the shape.
// It doesn't modify the model matrix at all.
// I call it once before my game enter's it's game loop, and I never modify it after that.
所以,我的问题是,更新对象的旋转是否合适?
我应该直接在对象的 "model" 中存储 一个四元数吗?
我应该将平移和缩放存储为单独的 vec3 吗?
有更简单的方法吗?我一直在阅读和重新阅读我能找到的任何东西,但我没有看到任何人以同样的方式这样做。
本教程的细节有点短,特别是如何将旋转应用于现有旋转(我相信这只是将四元数相乘,这就是我在 rotate_entity(. ..) 以上)。 http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/ https://github.com/opengl-tutorials/ogl/blob/master/tutorial17_rotations/tutorial17.cpp#L306-L311
将自己生成的 "MVP" 矩阵存储为我的 "model" 并应用 glm::transform/glm:: 是否更有意义? scale/glm::直接对MVP矩阵进行旋转操作? (我之前尝试过最后一个选项,但我也不知道如何让它发挥作用)。
谢谢!
编辑:更好 [=76=]
通常,您不想修改模型的各个顶点在 CPU 上的位置。这就是顶点程序的全部目的。模型矩阵的作用是在顶点程序中定位模型在世界中的位置。
要绕中心旋转模型,您需要先将中心移动到原点,然后旋转它,然后将中心移动到它的最终位置。假设您有一个从 (0,0,0) 延伸到 (1,1,1) 的立方体。您需要:
- 将立方体平移 (-0.5, -0.5, -0.5)
- 旋转角度
- 将立方体平移 (0.5, 0.5, 0.5)
- 将立方体平移到场景中它所属的任何地方
您可以将最后 2 个转换合并为一个,当然,您可以将所有这些转换合并为一个矩阵,即您的模型矩阵。