gpt4 book ai didi

c++ - OpenGL-鼠标坐标到空间坐标

转载 作者:太空宇宙 更新时间:2023-11-03 10:24:05 26 4
gpt4 key购买 nike

我的目标是在鼠标指向的位置放置一个球体(Z坐标为0)。

我看到了这个question,但是我还不了解MVP矩阵的概念,因此我做了一些研究,现在有两个问题:

如何从相机设置(例如查找,眼睛和向上 vector )创建 View 矩阵?

我也阅读了this tutorial about several camera types和这个webgl

我仍然可以将它们放在一起,我也不知道如何获得投影矩阵...

我应该采取什么步骤来实现所有这些?

最佳答案

在渲染中,通常通过模型矩阵, View 矩阵和投影矩阵来变换场景的每个网格。

  • 投影矩阵:
    投影矩阵描述了从场景的3D点到视口(viewport)的2D点的映射。投影矩阵从 View 空间转换为剪辑空间,并且剪辑空间中的坐标转换为范围为(-1,-1,-1)至(1、1、1)的归一化设备坐标(NDC)通过除以剪辑坐标的w分量。
  • View 矩阵:
    View 矩阵描述了从中查看场景的方向和位置。 View 矩阵从世界空间转换为 View (眼睛)空间。在视口(viewport)的坐标系中,X轴指向左侧,Y轴指向上方,Z轴指向 View 之外(请注意,在右侧系统中,Z轴是X-轴的叉积轴和Y轴)。
  • 模型矩阵:
    模型矩阵定义场景中网格的位置,方向和相对大小。模型矩阵将顶点位置从网格转换为世界空间。

  • 模型矩阵如下所示:
    ( X-axis.x, X-axis.y, X-axis.z, 0 )
    ( Y-axis.x, Y-axis.y, Y-axis.z, 0 )
    ( Z-axis.x, Z-axis.y, Z-axis.z, 0 )
    ( trans.x, trans.y, trans.z, 1 )

    View

    在视口(viewport)上,X轴指向左侧,Y轴指向上方,Z轴指向 View 之外(请注意,在右手系统中,Z轴是X轴与Y-轴的叉积轴)。

    view coordinates

    下面的代码定义了一个矩阵,该矩阵准确地封装了计算场景外观所需的步骤:
  • 将模型坐标转换为视口(viewport)坐标。
  • 旋转,以 View 的方向看。
  • 眼睛位置的运动

  • 以下代码与 gluLookAtglm::lookAt相同:
    using TVec3  = std::array< float, 3 >;
    using TVec4 = std::array< float, 4 >;
    using TMat44 = std::array< TVec4, 4 >;

    TVec3 Cross( TVec3 a, TVec3 b ) { return { a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] }; }
    float Dot( TVec3 a, TVec3 b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
    void Normalize( TVec3 & v )
    {
    float len = sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
    v[0] /= len; v[1] /= len; v[2] /= len;
    }

    TMat44 Camera::LookAt( const TVec3 &pos, const TVec3 &target, const TVec3 &up )
    {
    TVec3 mz = { pos[0] - target[0], pos[1] - target[1], pos[2] - target[2] };
    Normalize( mz );
    TVec3 my = { up[0], up[1], up[2] };
    TVec3 mx = Cross( my, mz );
    Normalize( mx );
    my = Cross( mz, mx );

    TMat44 v{
    TVec4{ mx[0], my[0], mz[0], 0.0f },
    TVec4{ mx[1], my[1], mz[1], 0.0f },
    TVec4{ mx[2], my[2], mz[2], 0.0f },
    TVec4{ Dot(mx, pos), Dot(my, pos), -Dot(mz, pos), 1.0f }
    };

    return v;
    }

    投影

    投影矩阵描述了从场景的3D点到视口(viewport)的2D点的映射。它从眼睛空间转换为剪辑空间,并且通过除以剪辑坐标的 w分量,将剪辑空间中的坐标转换为归一化设备坐标(NDC)。 NDC的范围是(-1,-1,-1)至(1,1,1)。 NDC之外的每个几何都将被裁剪。

    摄像机视锥的近平面和远平面之间的对象映射到NDC的范围(-1,1)。

    正交投影

    在正交投影中,眼睛空间中的坐标被线性映射到归一化的设备坐标。

    Orthographic Projection

    正射投影矩阵:
    r = right, l = left, b = bottom, t = top, n = near, f = far 

    2/(r-l) 0 0 0
    0 2/(t-b) 0 0
    0 0 -2/(f-n) 0
    -(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1

    透视投影

    在“透视投影”中,投影矩阵描述了从针孔相机看到的世界3D点到视口(viewport)的2D点的映射。摄像机视锥中的眼睛空间坐标(截断的金字塔)映射到立方体(规范化的设备坐标)。

    Perspective Projection

    透视投影矩阵:
    r = right, l = left, b = bottom, t = top, n = near, f = far

    2*n/(r-l) 0 0 0
    0 2*n/(t-b) 0 0
    (r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
    0 0 -2*f*n/(f-n) 0

    在哪里:
    a = w / h
    ta = tan( fov_y / 2 );

    2 * n / (r-l) = 1 / (ta * a)
    2 * n / (t-b) = 1 / ta

    如果投影是对称的,视线在视口(viewport)的中心,并且视场没有移位,则可以简化矩阵:
    1/(ta*a)  0     0              0
    0 1/ta 0 0
    0 0 -(f+n)/(f-n) -1
    0 0 -2*f*n/(f-n) 0

    以下函数将计算与 gluPerspective相同的投影矩阵:
    #include <array>

    const float cPI = 3.14159265f;
    float ToRad( float deg ) { return deg * cPI / 180.0f; }

    using TVec4 = std::array< float, 4 >;
    using TMat44 = std::array< TVec4, 4 >;

    TMat44 Perspective( float fov_y, float aspect )
    {
    float fn = far + near
    float f_n = far - near;
    float r = aspect;
    float t = 1.0f / tan( ToRad( fov_y ) / 2.0f );

    return TMat44{
    TVec4{ t / r, 0.0f, 0.0f, 0.0f },
    TVec4{ 0.0f, t, 0.0f, 0.0f },
    TVec4{ 0.0f, 0.0f, -fn / f_n, -1.0f },
    TVec4{ 0.0f, 0.0f, -2.0f*far*near / f_n, 0.0f }
    };
    }

    3在透视投影中恢复 View 空间位置的解决方案
  • 带有视野和纵横比

  • 由于投影矩阵由视场和宽高比定义,因此可以利用视场和宽高比恢复视口(viewport)位置。如果它是对称的透视投影,并且已标准化的设备坐标,则深度和近,远平面都是已知的。

    恢复 View 空间中的Z距离:
    z_ndc = 2.0 * depth - 1.0;
    z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));

    通过XY归一化的设备坐标恢复 View 空间位置:
    ndc_x, ndc_y = xy normalized device coordinates in range from (-1, -1) to (1, 1):

    viewPos.x = z_eye * ndc_x * aspect * tanFov;
    viewPos.y = z_eye * ndc_y * tanFov;
    viewPos.z = -z_eye;

    2. 和投影矩阵

    由视场和纵横比定义的投影参数存储在投影矩阵中。因此,可以通过对称矩阵投影中的投影矩阵中的值恢复视口(viewport)位置。

    注意投影矩阵,视场和宽高比之间的关系:
    prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect);
    prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov;

    prjMat[2][2] = -(f+n)/(f-n)
    prjMat[2][2] = -2*f*n/(f-n)

    恢复 View 空间中的Z距离:
    A     = prj_mat[2][2];
    B = prj_mat[3][2];
    z_ndc = 2.0 * depth - 1.0;
    z_eye = B / (A + z_ndc);

    通过XY归一化的设备坐标恢复 View 空间位置:
    viewPos.x = z_eye * ndc_x / prjMat[0][0];
    viewPos.y = z_eye * ndc_y / prjMat[1][1];
    viewPos.z = -z_eye;

    3. 与逆投影矩阵

    当然,可以通过反投影矩阵来恢复视口(viewport)位置。
    mat4 inversePrjMat = inverse( prjMat );
    vec4 viewPosH = inversePrjMat * vec4(ndc_x, ndc_y, 2.0*depth - 1.0, 1.0)
    vec3 viewPos = viewPos.xyz / viewPos.w;

    进一步了解:
  • How to render depth linearly in modern OpenGL with gl_FragCoord.z in fragment shader?
  • Transform the modelMatrix
  • Perspective projection and view matrix: Both depth buffer and triangle face orientation are reversed in OpenGL
  • How to compute the size of the rectangle that is visible to the camera at a given coordinate?
  • How to recover view space position given view space depth value and ndc xy
  • Is it possble get which surface of cube will be click in OpenGL?
  • 关于c++ - OpenGL-鼠标坐标到空间坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46749675/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com