2017-08-25 40 views
1

我沒有鼠標的地形這些教訓採摘(但用C++)鼠標採摘小姐

https://www.youtube.com/watch?v=DLKN0jExRIM&index=29&listhLoLuZVfUksDP http://antongerdelan.net/opengl/raycasting.html

的問題是,鼠標的位置不對應的地方,光線與與地面:problem 垂直和水平有一個很大的錯誤。 不要看陰影,這不是一個正常的修正地圖。 什麼可能是錯的?我的代碼:

void MousePicker::update() { 

    view = cam->getViewMatrix(); 

    currentRay = calculateMouseRay(); 
    if (intersectionInRange(0, RAY_RANGE, currentRay)) { 
    currentTerrainPoint = binarySearch(0, 0, RAY_RANGE, currentRay); 
    } 
    else { 
    currentTerrainPoint = vec3(); 
    } 
} 
vec3 MousePicker::calculateMouseRay() {  
    glfwGetCursorPos(win, &mouseInfo.xPos, &mouseInfo.yPos); 
    vec2 normalizedCoords = getNormalizedCoords(mouseInfo.xPos, mouseInfo.yPos); 
    vec4 clipCoords = vec4(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f); 
    vec4 eyeCoords = toEyeCoords(clipCoords); 
    vec3 worldRay = toWorldCoords(eyeCoords); 

    return worldRay; 
} 

vec2 MousePicker::getNormalizedCoords(double xPos, double yPos) { 
    GLint width, height; 
    glfwGetWindowSize(win, &width, &height); 
    //GLfloat x = (2.0 * xPos)/width - 1.0f; 
    GLfloat x = -((width - xPos)/width - 0.5f) * 2.0f; 
    //GLfloat y = 1.0f - (2.0f * yPos)/height; 
    GLfloat y = ((height - yPos)/height - 0.5f) * 2.0f; 
    //float z = 1.0f; 
    mouseInfo.normalizedCoords = vec2(x, y); 

    return vec2(x,y); 
} 

vec4 MousePicker::toEyeCoords(vec4 clipCoords) { 
    vec4 invertedProjection = inverse(projection) * clipCoords; 
    //vec4 eyeCoords = translate(invertedProjection, clipCoords); 
    mouseInfo.eyeCoords = vec4(invertedProjection.x, invertedProjection.y, -1.0f, 0.0f); 
    return vec4(invertedProjection.x, invertedProjection.y, -1.0f, 0.0f); 
} 

vec3 MousePicker::toWorldCoords(vec4 eyeCoords) { 
    vec3 rayWorld = vec3(inverse(view) * eyeCoords); 
    vec3 mouseRay = vec3(rayWorld.x, rayWorld.y, rayWorld.z); 
    rayWorld = normalize(rayWorld); 
    mouseInfo.worldRay = rayWorld; 
    return rayWorld; 
} 

//********************************************************************************* 

vec3 MousePicker::getPointOnRay(vec3 ray, float distance) { 
    vec3 camPos = cam->getCameraPos(); 
    vec3 start = vec3(camPos.x, camPos.y, camPos.z); 
    vec3 scaledRay = vec3(ray.x * distance, ray.y * distance, ray.z * distance); 
    return vec3(start + scaledRay); 
} 

vec3 MousePicker::binarySearch(int count, float start, float finish, vec3 ray) { 
    float half = start + ((finish - start)/2.0f); 
    if (count >= RECURSION_COUNT) { 
    vec3 endPoint = getPointOnRay(ray, half); 
    //Terrain* ter = &getTerrain(endPoint.x, endPoint.z); 
    if (terrain != NULL) { 
     return endPoint; 
    } 
    else { 
     return vec3(); 
    } 
    } 

    if (intersectionInRange(start, half, ray)) { 
    return binarySearch(count + 1, start, half, ray); 
    } 
    else { 
    return binarySearch(count + 1, half, finish, ray); 
    } 
} 

bool MousePicker::intersectionInRange(float start, float finish, vec3 ray) { 
    vec3 startPoint = getPointOnRay(ray, start); 
    vec3 endPoint = getPointOnRay(ray, finish); 
    if (!isUnderGround(startPoint) && isUnderGround(endPoint)) { 
    return true; 
    } 
    else { 
    return false; 
    } 
} 

bool MousePicker::isUnderGround(vec3 testPoint) { 
    //Terrain* ter = &getTerrain(testPoint.x, testPoint.z); 
    float height = 0; 
    if (terrain != NULL) { 
    height = terrain->getHeightPoint(testPoint.x, testPoint.z); 
    mouseInfo.height = height; 
    } 
    if (testPoint.y < height) { 
    return true; 
    } 
    else { 
    return false; 
    } 

} 

Terrain MousePicker::getTerrain(float worldX, float worldZ) { 
    return *terrain; 
} 

回答

2

在透視投影中,從眼睛位置到屏幕上一點的光線可以由2個點定義。第一點是眼睛(相機)的位置,它是(0,0,0)在視圖空間。第二點必須通過屏幕上的位置來計算。
必須將屏幕位置轉換爲標準化設備座標,範圍從(-1,-1)到(1,1)。

w = with of the viewport 
h = height of the viewport 
x = X position of the mouse 
y = Y position ot the mouse 

GLfloat ndc_x = 2.0 * x/w - 1.0; 
GLfloat ndc_y = 1.0 - 2.0 * y/h; // invert Y axis 

爲了計算上的射線,其穿過攝像機的位置和通過在屏幕上的點的一個點,視場和透視投影的縱橫比必須是已知的:

fov_y = vertical field of view angle in radians 
aspect = w/h 

GLfloat tanFov = tan(fov_y * 0.5); 
glm::vec3 ray_P = vec3(ndc_x * aspect * tanFov, ndc_y * tanFov, -1.0)); 

從攝像機位置通過在屏幕上的一個點的光線可以通過以下位置(P0)和歸一化的方向(dir)來定義,在世界空間:

view = view matrix 

glm::mat4 invView = glm::inverse(view); 

glm::vec3 P0 = invView * glm::vec3(0.0f, 0.0f, 0.0f); 
      // = glm::vec3(view[3][0], view[3][1], view[3][2]); 

glm::vec3 dir = glm::normalize(invView * ray_P - P0); 

在這種情況下,回答下列問題將是有趣太:

應用在你的代碼的結果以下變化:

透視投影矩陣是這樣的:

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 

它如下:從屏幕(小鼠)

aspect = w/h 
tanFov = tan(fov_y * 0.5); 

p[0][0] = 2*n/(r-l) = 1.0/(tanFov * aspect) 
p[1][1] = 2*n/(t-b) = 1.0/tanFov 

座標轉換到歸一化設備座標:

vec2 MousePicker::getNormalizedCoords(double x, double y) { 

    GLint w, h; 
    glfwGetWindowSize(win, &width, &height); 

    GLfloat ndc_x = 2.0 * x/w - 1.0; 
    GLfloat ndc_y = 1.0 - 2.0 * y/h; // invert Y axis 
    mouseInfo.normalizedCoords = vec2(ndc_x, ndc_x); 
    return vec2(ndc_x, ndc_x); 
} 

計算從相機位置通過屏幕上的一個點(鼠標位置)在世界空間拍攝的光線:

vec3 MousePicker::calculateMouseRay(void) {  
    glfwGetCursorPos(win, &mouseInfo.xPos, &mouseInfo.yPos); 
    vec2 normalizedCoords = getNormalizedCoords(mouseInfo.xPos, mouseInfo.yPos); 

    ray_Px = normalizedCoords.x/projection[0][0]; // projection[0][0] == 1.0/(tanFov * aspect) 
    ray_Py = normalizedCoords.y/projection[1][1]; // projection[1][1] == 1.0/tanFov 
    glm::vec3 ray_P = vec3(ray_Px, ray_Py, -1.0f)); 

    vec3  camPos = cam->getCameraPos(); // == glm::vec3(view[3][0], view[3][1], view[3][2]);  
    glm::mat4 invView = glm::inverse(view); 

    glm::vec3 P0 = camPos; 
    glm::vec3 dir = glm::normalize(invView * ray_P - P0); 
    return dir; 
}