简体   繁体   English

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

[英]Precision issue - viewpoint far from origin - OpenGL C++

I have a camera class for controlling the camera, with the main function: 我有一个用于控制相机的相机类,主要功能是:

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);
};

It runs smooth, I can control the angle with my mouse in X and Y directions, but not around the Z axis (the Y is the "up" in world space). 它运行平稳,我可以用鼠标控制X和Y方向的角度,但不能绕Z轴控制(Y是世界空间中的“向上”)。

In my rendering method I render the terrain grid with one VAO call. 在我的渲染方法中,我使用一个VAO调用渲染地形网格。 The grid itself is a quad as the center (highes lod), and the others are L shaped grids scaled by powers of 2. It is always repositioned before the camera, scaled into world space, and displaced by a heightmap. 网格本身以四边形为中心(高lod),其他为L形网格,按2的幂进行缩放。它始终在摄像机前重新定位,缩放到世界空间,并由高度图移位。

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;

The problem: 问题:

The camera starts at the origin, at (0,0,0) . 相机从原点开始,位于(0,0,0) When I move it far away from that point, it causes the rotation along the X axis discontinuous. 当我将它远离该点移动时,它会导致沿X轴的旋转不连续。 It feels like the mouse cursor was aligned with a grid in screen space, and only the position at grid points were recorded as the cursor movement. 感觉就像鼠标光标与屏幕空间中的网格对齐,并且只有网格点处的位置被记录为光标移动。

I've also recorded the camera position when it gets pretty noticeable, it's about at 1,000,000 from the origin in X or Z directions. 我还记录了相机位置,当它变得非常明显时,它在X或Z方向上距离原点大约为1,000,000。 I've noticed that this 'lag' increases linearly with distance, (from the origin). 我注意到这个'滞后'随距离(从原点)线性增加。

There is also a little Z-fighting at this point (or similar effect), even if I use a single plane with no displacement, and no planes can overlap. 此时还有一点Z战斗 (或类似效果),即使我使用没有位移的单个平面,也没有平面可以重叠。 (I use tessellation shaders and render patches.) Black spots appear on the patches. (我使用曲面细分着色器并渲染补丁。)补丁上出现黑点。 May be caused by fog: 可能是雾造成的:

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));

Another strange behavior is the little rotation by the Z axis, this increases with distance too, but I don't use this kind of rotation. 另一个奇怪的行为是Z轴的小旋转,这也随距离增加,但我不使用这种旋转。

Variable formats: 变量格式:

The vertices are unsigned short format, the indexes are in unsigned int format. 顶点是unsigned short格式,索引采用unsigned int格式。 The cmc struct is the camera/cursor struct with double variables. cmc结构是具有double变量的相机/光标结构。

PI and C_SPEED are #define constants. PIC_SPEED#define常量。

Additional information: 附加信息:

The grid is created with the above mentioned ushort array, with the spacing of 1. In the shader I scale it with a constant, then use tessellation to achieve the best performance and the largest view distance. 使用上面提到的ushort数组创建网格,间距为1.在着色器中,我使用常量对其进行缩放,然后使用曲面细分来获得最佳性能和最大视图距离。 The final position of a vertex is calculated in the tessellation evaluation shader. 在曲面细分评估着色器中计算顶点的最终位置。

mat4 MVP = projection_matrix*view_matrix*model_matrix;

As you could see I send my matrices to the shader with the glm library. 正如您所见,我将我的矩阵发送到带有glm库的着色器。

+Q: + Q:

How could the length of a float (or any other format) cause this kind of 'precision loss', or whatever causes the problem. 浮点(或任何其他格式)的长度如何导致这种“精确丢失”,或者导致问题的原因。 The view_matrix could be a cause of this, but I still cannot output it on the screen at runtime. view_matrix可能是导致这种情况的原因,但我仍然无法在运行时将其输出到屏幕上。

PS : I don't know If this helps, but the view matrix at about the 'lag start location' is PS :我不知道如果这有帮助,但是关于'滞后开始位置'的视图矩阵是

-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

EDIT 编辑

Comparing the camera position and view matrix: 比较相机位置和视图矩阵:

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

It's a long post so I might not answer everything. 这是一个很长的帖子,所以我可能不会回答所有问题。 I think it is most likely precision issue. 我认为这很可能是精确问题。 Lets start with the camera rotation problem. 让我们从相机旋转问题开始。 I think the main problem is here 我认为主要问题在这里

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

As you said, position is quite a big number like 2.9657e+006 - and look what glm does in glm::lookAt: 如你所说,位置是一个很大的数字,如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);

In your case, eye and center are these big (very similar) numbers and then glm subtracts them to compute f. 在你的情况下,眼睛和中心是这些大(非常相似)的数字,然后glm减去它们来计算f。 This is bad, because if you subtract two almost equal floats, the most significant digits are set to zero, which leaves you with the insignificant (most erroneous) digits. 这很糟糕,因为如果你减去两个几乎相等的浮点数,那么最高有效数字将被设置为零,这会让你得到无关紧要(最错误的)数字。 And you use this for further computations, which only emphasizes the error. 并且您使用它进行进一步的计算,这只会强调错误。 Check this link for some details. 查看此链接了解一些详细信息。

The z-fighting is similar issue. z战斗是类似的问题。 Z-buffer is not linear, it has the best resolution near the camera because of the perspective divide. Z缓冲区不是线性的,由于透视分割,它在相机附近具有最佳分辨率。 The z-buffer range is set according to your near and far clipping plane values. z缓冲区范围根据近和远剪切平面值设置。 You always want to have the smallest possible ration between far and near values (generally far/near should not be greater than 30000). 您总是希望在远近值之间具有最小可能的比例(通常远/近不应大于30000)。 There is a very good explanation of this on the openGL wiki , I suggest you read it :) openGL wiki上有一个很好的解释,我建议你看看:)

Back to the camera issue - first, I would consider if you really need such a huge scene. 回到相机问题 - 首先,我会考虑你是否真的需要这么大的场景。 I don't think so, but if yes, you could try computing your view matrix differently, compute rotation and translation separately, which could help your case. 我不这么认为,但是如果是的话,你可以尝试不同地计算你的视图矩阵,分别计算旋转和翻译,这可能有助于你的情况。 The way I usually handle camera: 我通常处理相机的方式:

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;
        ...
    }
}

This way, the position would not affect your rotation. 这样,位置不会影响您的旋转。 I should add that I adapted this code from NVIDIA CUDA samples v5.0 (Smoke Particles), I really like it :) 我应该补充一点,我从NVIDIA CUDA样本v5.0(Smoke Particles)改编了这个代码,我真的很喜欢:)

Hope at least some of this helps. 希望至少有一些帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM