OpenGL:屏幕到世界的转换和glm::unProject的良好使用

dm7nw8vv  于 2022-11-23  发布在  其他
关注(0)|答案(1)|浏览(259)

我有一个我认为是最基本的需求:从“鼠标在屏幕上的2D位置”,我需要得到“在3D世界中最近的3D点”。看起来像光线跟踪常见的问题(即使它不是我的)。
我在谷歌上搜索/阅读了很多:看起来这个主题很混乱,很多事情很快就变得很复杂。我最初的问题/需求涉及到很多我不知道的三维点(网格或点云从互联网上),所以,这是不可能的理解什么结果,你应该期待!因此,我决定创建简单的形状(三角形、四边形、立方体)与我 * 知道 * 的点(每个点的每个坐标在局部帧中为0.f或0.5f),并且,尝试查看当我在屏幕上移动鼠标光标时,我是否可以从鼠标光标“恢复”3D点位置。
注:所有形状的所有点的坐标均为已知值,如0.f或0.5f。例如,the triangle

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
};

我的工作
我有一个3D OpenGL渲染器,其中我添加了一个GUI,以便在渲染的场景上进行控制

  • 变形:txtytzrxryrz是用于更改model矩阵的控件。在代码中
// create transformations: model represents local to world transformation
model = glm::mat4(1.0f); // initialize matrix to identity matrix first
model = glm::translate(model, glm::vec3(tx, ty, tz));
model = glm::rotate(model, glm::radians(rx), glm::vec3(1.0f, 0.0f, 0.0f));
model = glm::rotate(model, glm::radians(ry), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, glm::radians(rz), glm::vec3(0.0f, 0.0f, 1.0f));
ourShader.setMat4("model", model);

model只改变形状在世界中的位置,与摄像机的位置无关(这是我从tutorials中理解的)。

  • Camera:从here开始,我最终创建了一个camera类,它包含viewproj矩阵。
// get view and projection from camera
view = cam.getViewMatrix();
ourShader.setMat4("view", view);
proj = cam.getProjMatrix((float)SCR_WIDTH, (float)SCR_HEIGHT, near, 100.f);
ourShader.setMat4("proj", proj);

摄像头是一个类似苍蝇的摄像头,可以在移动鼠标或使用键盘箭头时移动,它 * 不 * 作用于model,而 * 只 * 作用于viewproj(这是我从tutorials中理解的)。
然后,着色器以如下方式使用modelviewproj

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main()
{
   // note that we read the multiplication from right to left
   gl_Position = proj * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0);
  • 屏幕到世界:由于使用glm::unProject并不总是返回我所期望的结果,我添加了一个不使用它的控件(back-projecting by-hand)。
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouseCursorCallback(GLFWwindow* window, double xposIn, double yposIn)
{
    // screen to world transformation

    xposScreen = xposIn;
    yposScreen = yposIn;

    int windowWidth = 0, windowHeight = 0; // size in screen coordinates.
    glfwGetWindowSize(window, &windowWidth, &windowHeight);
    int frameWidth = 0, frameHeight = 0; // size in pixel.
    glfwGetFramebufferSize(window, &frameWidth, &frameHeight);
    glm::vec2 frameWinRatio = glm::vec2(frameWidth, frameHeight) /
                              glm::vec2(windowWidth, windowHeight);
    glm::vec2 screen2DPos = glm::vec2(xposScreen, yposScreen);
    glm::vec2 frame2DPos = screen2DPos * frameWinRatio; // window / frame sizes may be different.
    frame2DPos = frame2DPos + glm::vec2(0.5f, 0.5f); // shift to GL's center convention.
    glm::vec3 frame3DPos = glm::vec3(0.0f, 0.0f, 0.0f);
    frame3DPos.x = frame2DPos.x;
    frame3DPos.y = frameHeight - 1.0f - frame2DPos.y; // GL's window origin is at the bottom left
    frame3DPos.z = 0.f;
    glReadPixels((GLint) frame3DPos.x, (GLint) frame3DPos.y, // CAUTION: cast to GLint.
                 1, 1, GL_DEPTH_COMPONENT,
                 GL_FLOAT, &zbufScreen); // CAUTION: GL_DOUBLE is NOT supported.
    frame3DPos.z = zbufScreen; // z-buffer.

然后我可以根据GUI中的控件调用或不调用glm::unProjectback-projecting by-hand

glm::vec3 world3DPos = glm::vec3(0.0f, 0.0f, 0.0f);
if (screen2WorldUsingGLM) {
    glm::vec4 viewport(0.0f, 0.0f, (float) frameWidth, (float) frameHeight);
    world3DPos = glm::unProject(frame3DPos, view * model, proj, viewport);
} else {
    glm::mat4 trans = proj * view * model;
    glm::vec4 frame4DPos(frame3DPos, 1.f);
    frame4DPos = glm::inverse(trans) * frame4DPos;
    world3DPos.x = frame4DPos.x / frame4DPos.w;
    world3DPos.y = frame4DPos.y / frame4DPos.w;
    world3DPos.z = frame4DPos.z / frame4DPos.w;
}

问题glm::unProject文档中说Map the specified window coordinates (win.x, win.y, win.z) into object coordinates,但是,我不确定什么是对象坐标。对象坐标是指here描述的局部、世界、视图还是剪辑空间?

  • 无论形状是2D(三角形、四边形)还是3D(立方体),都 * 始终 * 允许Z缓冲。
glEnable(GL_DEPTH_TEST); // Enable z-buffer.
while (!glfwWindowShouldClose(window)) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // also clear the z-buffer

在图片中我得到

摄像头位于(0.,0.,0.)并“向前看”(front = -z,因为z轴从屏幕到我是正的)。(使用txtytzrxryrz)“在摄影机前面”与tz = -5(5个单位跟随摄影机的front矢量)
我得到的

三角形处于初始设置

我在世界框架中有正确的xposypos,但zpos = 0.不正确(允许z缓冲)。我预期的是zpos = -5(作为tz = -5)。

问题:为什么zpos不正确?

如果不使用glm::unProject,则会得到外太空结果

问题:为什么手工“反投影”与glm::unProject相比没有返回一致的结果?这合乎逻辑吗?它们是不同的操作吗?(我认为它们应该是等效的,但显然不是)

三角形随平移移动

在平移了大约tx = 0.5之后,我仍然得到了相同的坐标(局部框架),我希望之前的坐标沿着x轴平移。不使用glm::unProject也会在这里返回外空间结果...

问题:为什么忽略转换(由model应用,而不是viewproj)?

立方体处于初始设置

x1c4d 1x指令集
我得到了正确的xposyposzpos?!...那么为什么这与“2D”三角形的工作方式不一样(对我来说是“3D”三角形,所以,它们应该表现相同)?

立方体随平移移动

沿着ty平移这次似乎没有效果(仍然获得相同的坐标-局部帧)。

问题:就像三角形一样,为什么忽略平移?

∮我想得到的
主要问题是为什么忽略model变换?如果这是意料之中的,我想知道为什么。如果有一种方法可以从鼠标光标的位置恢复形状在世界中的“真实”位置(包括model变换),我想知道如何恢复。

6qftjkof

6qftjkof1#

问题:glm::unProject doc说将指定的窗口坐标(win.x,win.y,win.z)Map到对象坐标,但是,我不确定什么是对象坐标。对象坐标是指这里描述的局部、世界、视图还是剪辑空间?
由于我是OpenGL新手,我没有从glm::unProject文档中得到对象坐标是引用局部空间的另一种方式。将view*model传递给glm::unProject并再次应用model,或者将view传递给glm::unProject,如下所述:Screen Coordinates to World Coordinates
这修复了我观察到的所有怪异行为。

相关问题