簡體   English   中英

精度問題 - 遠離原點的觀點 - OpenGL C ++

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

我有一個用於控制相機的相機類,主要功能是:

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調用渲染地形網格。 網格本身以四邊形為中心(高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;

問題:

相機從原點開始,位於(0,0,0) 當我將它遠離該點移動時,它會導致沿X軸的旋轉不連續。 感覺就像鼠標光標與屏幕空間中的網格對齊,並且只有網格點處的位置被記錄為光標移動。

我還記錄了相機位置,當它變得非常明顯時,它在X或Z方向上距離原點大約為1,000,000。 我注意到這個'滯后'隨距離(從原點)線性增加。

此時還有一點Z戰斗 (或類似效果),即使我使用沒有位移的單個平面,也沒有平面可以重疊。 (我使用曲面細分着色器並渲染補丁。)補丁上出現黑點。 可能是霧造成的:

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.在着色器中,我使用常量對其進行縮放,然后使用曲面細分來獲得最佳性能和最大視圖距離。 在曲面細分評估着色器中計算頂點的最終位置。

mat4 MVP = projection_matrix*view_matrix*model_matrix;

正如您所見,我將我的矩陣發送到帶有glm庫的着色器。

+ Q:

浮點(或任何其他格式)的長度如何導致這種“精確丟失”,或者導致問題的原因。 view_matrix可能是導致這種情況的原因,但我仍然無法在運行時將其輸出到屏幕上。

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

編輯

比較相機位置和視圖矩陣:

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

如你所說,位置是一個很大的數字,如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);

在你的情況下,眼睛和中心是這些大(非常相似)的數字,然后glm減去它們來計算f。 這很糟糕,因為如果你減去兩個幾乎相等的浮點數,那么最高有效數字將被設置為零,這會讓你得到無關緊要(最錯誤的)數字。 並且您使用它進行進一步的計算,這只會強調錯誤。 查看此鏈接了解一些詳細信息。

z戰斗是類似的問題。 Z緩沖區不是線性的,由於透視分割,它在相機附近具有最佳分辨率。 z緩沖區范圍根據近和遠剪切平面值設置。 您總是希望在遠近值之間具有最小可能的比例(通常遠/近不應大於30000)。 openGL wiki上有一個很好的解釋,我建議你看看:)

回到相機問題 - 首先,我會考慮你是否真的需要這么大的場景。 我不這么認為,但是如果是的話,你可以嘗試不同地計算你的視圖矩陣,分別計算旋轉和翻譯,這可能有助於你的情況。 我通常處理相機的方式:

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(Smoke Particles)改編了這個代碼,我真的很喜歡:)

希望至少有一些幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM