c++ - 精度问题 - 远离原点的观点 - OpenGL C++

标签 c++ opengl camera glsl precision

我有一个控制相机的相机类,主要功能:

void PNDCAMERA::renderMatrix()
{
    float dttime=getElapsedSeconds();
    GetCursorPos(&cmc.p_cursorPos);
    ScreenToClient(hWnd, &cmc.p_cursorPos);

    double d_horangle=((double)cmc.p_cursorPos.x-(double)cmc.p_origin.x)/(double)screenWidth*PI;
    double d_verangle=((double)cmc.p_cursorPos.y-(double)cmc.p_origin.y)/(double)screenHeight*PI;

    cmc.horizontalAngle=d_horangle+cmc.d_horangle_prev;
    cmc.verticalAngle=d_verangle+cmc.d_verangle_prev;

    if(cmc.verticalAngle>PI/2) cmc.verticalAngle=PI/2;
    if(cmc.verticalAngle<-PI/2) cmc.verticalAngle=-PI/2;

    changevAngle(cmc.verticalAngle);
    changehAngle(cmc.horizontalAngle);

    rightVector=glm::vec3(sin(horizontalAngle - PI/2.0f),0,cos(horizontalAngle - PI/2.0f));
    directionVector=glm::vec3(cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle));

    upVector=glm::vec3(glm::cross(rightVector,directionVector));

    glm::normalize(upVector);
    glm::normalize(directionVector);
    glm::normalize(rightVector);


    if(moveForw==true)
    {
        cameraPosition=cameraPosition+directionVector*(float)C_SPEED*dttime;
    }
    if(moveBack==true)
    {
        cameraPosition=cameraPosition-directionVector*(float)C_SPEED*dttime;
    }
    if(moveRight==true)
    {
        cameraPosition=cameraPosition+rightVector*(float)C_SPEED*dttime;
    }
    if(moveLeft==true)
    {
        cameraPosition=cameraPosition-rightVector*(float)C_SPEED*dttime;
    }

    glViewport(0,0,screenWidth,screenHeight);
    glScissor(0,0,screenWidth,screenHeight);
    projection_matrix=glm::perspective(60.0f, float(screenWidth) / float(screenHeight), 1.0f, 40000.0f);

    view_matrix = glm::lookAt(
        cameraPosition,
        cameraPosition+directionVector,
        upVector);

    gShader->bindShader();
    gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));
    gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
    gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));
    gShader->sendUniform("camera_position",cameraPosition.x,cameraPosition.y,cameraPosition.z);
    gShader->sendUniform("screen_size",(GLfloat)screenWidth,(GLfloat)screenHeight);
};

它运行流畅,我可以用鼠标控制 X 和 Y 方向的角度,但不能围绕 Z 轴(Y 是世界空间中的“向上”)。

在我的渲染方法中,我使用一个 VAO 调用来渲染地形网格。网格本身是以四边形为中心(highes lod),其他都是按 2 的幂缩放的 L 形网格。它总是在相机前重新定位,缩放到世界空间,并由高度图置换。

rcampos.x = round((camera_position.x)/(pow(2,6)*gridscale))*(pow(2,6)*gridscale);
rcampos.y = 0;
rcampos.z = round((camera_position.z)/(pow(2,6)*gridscale))*(pow(2,6)*gridscale);

vPos =  vec3(uv.x,0,uv.y)*pow(2,LOD)*gridscale + rcampos;
vPos.y = texture(hmap,vPos.xz/horizontal_scale).r*vertical_scale;

问题:

相机从原点开始,位于 (0,0,0)。当我将它移离该点时,它会导致沿 X 轴的旋转不连续。感觉鼠标光标与屏幕空间中的网格对齐,并且仅将网格点处的位置记录为光标移动。

当它变得非常明显时,我还记录了相机位置,它在 X 或 Z 方向上距离原点大约 1,000,000。我注意到这种“滞后”随距离(从原点)线性增加。

此时还有一点Z-fighting(或类似效果),即使我使用没有位移的单个平面,也没有平面可以重叠。 (我使用曲面 segmentation 着色器和渲染补丁。)补丁上出现黑点。可能是雾造成的:

float fc = (view_matrix*vec4(Pos,1)).z/(view_matrix*vec4(Pos,1)).w;
float fResult = exp(-pow(0.00005f*fc, 2.0)); 
fResult = clamp(fResult, 0.0, 1.0);

gl_FragColor = vec4(mix(vec4(0.0,0.0,0.0,0),vec4(n,1),fResult));

另一个奇怪的行为是 Z 轴的小旋转,这也随着距离的增加而增加,但我不使用这种旋转。

可变格式:

顶点是unsigned short格式,索引是unsigned int格式。 cmc 结构是带有 double 变量的相机/光标结构。

PIC_SPEED#define 常量。

附加信息:

网格是用上面提到的 ushort 数组创建的,间距为 1。在着色器中我用一个常数缩放它,然后使用曲面 segmentation 来实现最佳性能和最大视距. 顶点的最终位置在 segmentation 评估着色器中计算。

mat4 MVP = projection_matrix*view_matrix*model_matrix;

如您所见,我使用 glm 库将我的矩阵发送到着色器。

+问:

float (或任何其他格式)的长度如何导致这种“精度损失”,或任何导致问题的原因。 view_matrix 可能是造成这种情况的原因,但我仍然无法在运行时将其输出到屏幕上。

PS:我不知道这是否有帮助,但是“滞后开始位置”附近的 View 矩阵是

-0.49662       -0.49662      0.863129   0
 0.00514956     0.994097     0.108373   0
-0.867953       0.0582648   -0.493217   0
 1.62681e+006   16383.3     -290126     1

编辑

比较相机位置和 View 矩阵:

view matrix = 0.967928      0.967928    0.248814   0
             -0.00387854    0.988207    0.153079   0
             -0.251198      -0.149134   0.956378   0
             -2.88212e+006  89517.1     -694945    1

position =    2.9657e+006,   6741.52,   -46002

最佳答案

这是一篇很长的帖子,所以我可能无法回答所有问题。 我认为这很可能是精度问题。让我们从相机旋转问题开始。我认为主要问题在这里

view_matrix = glm::lookAt(
        cameraPosition,
        cameraPosition+directionVector,
        upVector);

正如您所说,position 是一个很大的数字,例如 2.9657e+006 - 看看 glm 在 glm::lookAt 中做了什么:

GLM_FUNC_QUALIFIER detail::tmat4x4<T> lookAt
    (
        detail::tvec3<T> const & eye,
        detail::tvec3<T> const & center,
        detail::tvec3<T> const & up
    )
    {
        detail::tvec3<T> f = normalize(center - eye);
        detail::tvec3<T> u = normalize(up);
        detail::tvec3<T> s = normalize(cross(f, u));
        u = cross(s, f);

在您的例子中,eye 和 center 是这些大(非常相似)的数字,然后 glm 将它们相减以计算 f。这很糟糕,因为如果你减去两个几乎相等的 float ,最重要的数字将被设置为零,这会留下微不足道的(最错误的)数字。并且您将其用于进一步的计算,这只会强调错误。检查这个link了解一些细节。

z-fighting 是类似的问题。 Z 缓冲区不是线性的,由于透视划分,它在相机附近具有最佳分辨率。 z 缓冲区范围是根据您的近距和远距裁剪平面值设置的。您总是希望 far 和 near 值之间的比率尽可能小(通常 far/near 不应大于 30000)。 openGL wiki 上对此有很好的解释。 , 我建议你读一下:)

回到相机问题——首先,我会考虑你是否真的需要这么大的场景。我不这么认为,但如果是的话,你可以尝试以不同的方式计算你的 View 矩阵,分别计算旋转和平移,这可能对你的情况有所帮助。我通常处理相机的方式:

glm::vec3 cameraPos;
glm::vec3 cameraRot;
glm::vec3 cameraPosLag;
glm::vec3 cameraRotLag;

int ox, oy;
const float inertia = 0.08f; //mouse inertia
const float rotateSpeed = 0.2f; //mouse rotate speed (sensitivity)
const float walkSpeed = 0.25f; //walking speed (wasd)

void updateCameraViewMatrix() {
    //camera inertia
    cameraPosLag += (cameraPos - cameraPosLag) * inertia;
    cameraRotLag += (cameraRot - cameraRotLag) * inertia;
    // view transform
    g_CameraViewMatrix = glm::rotate(glm::mat4(1.0f), cameraRotLag[0], glm::vec3(1.0, 0.0, 0.0));
    g_CameraViewMatrix = glm::rotate(g_CameraViewMatrix, cameraRotLag[1], glm::vec3(0.0, 1.0, 0.0));
    g_CameraViewMatrix = glm::translate(g_CameraViewMatrix, cameraPosLag);
}
void mousePositionChanged(int x, int y) {
    float dx, dy;
    dx = (float) (x - ox);
    dy = (float) (y - oy);

    ox = x;
    oy = y;

    if (mouseRotationEnabled) {
        cameraRot[0] += dy * rotateSpeed;
        cameraRot[1] += dx * rotateSpeed;
    }
}
void keyboardAction(int key, int action) {
    switch (key) {
        case 'S':// backwards
            cameraPos[0] -= g_CameraViewMatrix[0][2] * walkSpeed;
            cameraPos[1] -= g_CameraViewMatrix[1][2] * walkSpeed;
            cameraPos[2] -= g_CameraViewMatrix[2][2] * walkSpeed;
            break;
        ...
    }
}

这样,位置就不会影响你的旋转。我应该补充一点,我从 NVIDIA CUDA 示例 v5.0(烟雾粒子)改编了这段代码,我真的很喜欢它:)

希望至少有一些帮助。

关于c++ - 精度问题 - 远离原点的观点 - OpenGL C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19987726/

相关文章:

c++ - VirtualAlloc 在某些硬盘配置上失败

c++ - 任何人解释 C++ 代码行

c++ - 变换反馈的完整设置(openGL)

c++ - 在 OpenGL 中移动 3d 形状

c++ - 强制Qt摄像机视频格式

c++ - 如何在 64 位 Solaris 上安装 AWS C++ SDK 库

c++ - OpenGL/GLUT 在 mainEventLoop() 之后创建窗口

c++ - ROS:谷歌制图师/制图

java - 未检测到闪光灯

android - 使用 Android 6 的 Nexus 5 上的相机预览损坏