glm::unProject 不起作用并且屏幕上的光线位置不正确

问题描述 投票:0回答:1

我尝试在 GLM 函数 glm::unProject 上实现光线拾取方法。我的问题是屏幕上的光线位置错误和视角错误。下面是一些简化的代码...

我的LookAt功能:

glm::mat4 ViewMatrix()
{
    Front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
    Front.y = -sin(glm::radians(Pitch));
    Front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
    Right = glm::normalize(glm::cross(Front, WorldUp));
    Up = glm::normalize(glm::cross(Right, Front));
    glm::vec3 PositionPan = RightPan + UpPan;
    Position += PositionPan;

    return glm::lookAt((Position + radius * (Front)), Position , Up); 
}

您会注意到我的函数包含错误的参数,但我想制作免费相机(例如 3ds max 等),以 0,0,0 为目标,并可能进行平移(PositionPan 表示位置)和缩放(半径)。我花了很多时间来正确地处理它,但我不知道有任何其他方法可以使它成为另一种方式。我怀疑这些函数是 Ray 函数与 GLM 错误工作的原因。如果您发现有问题,请告诉我。

我的变换功能:

glm::mat4 view = ViewMatrix();
glm::mat4 projection = glm::perspective(1.0f, 
(float)screenWidth / (float)screenHeight, 0.1f, 100.0f);

我的射线功能:

void Ray(posx, posy, glm::mat4 view, glm::mat4 projection )
{
     x = (posx / screenWidth - 0.5f) * 2.0f; 
     y = (posy / screenHeight - 0.5f) * -2.0f; 

     worldSpaceNear = glm::unProject(glm::vec3(x, y, 0.0), 
     view, projection,          
     glm::vec4(0.0, 0.0, screenWidth, screenHeight));

     worldSpaceFar = glm::unProject(glm::vec3(x, y, 1.0), 
     view, projection,   
     glm::vec4(0.0, 0.0, screenWidth, screenHeight));
}

接下来,我在 VAO 中传递 worldSpaceNear/Far 参数,并在几何着色器中生成一个从近平面到远平面的长平行六面体。从屏幕空间到剪辑空间的转换是正确的,但在调试时光线从左下角开始并立即消失。鼠标在屏幕上的位置对光线的影响并不显着。 但是! 如果我将 Ray 函数更改为经典方法:

void mRay(glm::mat4 view, glm::mat4 projection
{
    x = (posx / screenWidth - 0.5f) * 2.0f; 
    y = (posy / screenHeight - 0.5f) * -2.0f;

    glm::vec4 localNear{ x,  y, 0.0, 1.0 };
    glm::vec4 localFar{ x, y, 1.0, 1.0 };

    auto wsn = glm::inverse(projection * view) * localNear;
    worldSpaceNear = glm::vec3(wsn.x, wsn.y, wsn.z);

    auto wsf = glm::inverse(projection * view) * localFar;
    worldSpaceFar = glm::vec3(wsf.x, wsf.y, wsf.z);

}

一切都差不多了。射线跟踪鼠标,但它的视角不正确。光线必须根据透视而分散,但在我的例子中,它渴望到达远平面的中心。我该如何修复它,或者如何在 GLM 上解决它?

DERHASS 更新

在 mRay() 之后,我发送 worldSpaceNear/Far 到渲染循环。 我知道通常都是有方向的,但我就是这么做的:

 renderLine(glm::vec3 worldSpaceNear, glm::vec3 worldSpaceFar, 
 glm::mat4 view, glm::mat4 projection, glm::vec3 colorOverlay, 
 Shader shader)
 {
 ...
 //Create VAO, VBO
 ...
 GLfloat vertices[] ={
    worldSpaceNear.x - 0.01, worldSpaceNear.y + 0.01, worldSpaceNear.z,
    worldSpaceFar.x - 0.01,  worldSpaceFar.y + 0.01,  worldSpaceFar.z,
    worldSpaceNear.x - 0.01, worldSpaceNear.y - 0.01, worldSpaceNear.z...
 // Long box from near to far
...
glDrawArrays(GL_LINE_LOOP, 0, 8);
}

在顶点着色器中:

gl_Position = projection * view * model * vec4(position, 1.0f);

现在我想可能是我忘记从线的视图矩阵中删除旋转组件,反之亦然以从相机添加旋转。

opengl camera game-engine glm-math
1个回答
3
投票

glm::unProject
将点从窗口空间取消投影到眼睛空间(或您放入其中的任何矩阵)。但是,您未投影的点是

 x = (posx / screenWidth - 0.5f) * 2.0f; 
 y = (posy / screenHeight - 0.5f) * -2.0f; 

这不是窗户空间。您实际上在这里将其转换为标准化的设备坐标。 unproject 函数将在内部完成该转换(这就是您需要传递四个视口参数的原因)。这也解释了为什么光线移动不多,因为当这些值被解释为窗口空间时,在整个屏幕上移动鼠标只会将其改变两个像素。

您自己唯一要做的就是 y 中的镜像,因为 GL 的窗口空间原点位于左下角:

 x = posx;
 y = screenHeight - 1.0f - posy;
© www.soinside.com 2019 - 2024. All rights reserved.