从窗口点到3d坐标的opengl

时间:2016-12-11 21:10:52

标签: c++ opengl depth-buffer

对于我的一个大项目,我需要在某个时刻在屏幕上绘制一个立方体,我需要知道我是否点击了立方体的一个面。为此,我知道我必须使用深度缓冲区。但我尝试做的转换不起作用。这是我的代码:

#include "Includes.h"
#include "Shader.h"

#define WIDTH 800
#define HEIGHT 600


using namespace std;


sf::Event event;


GLfloat get_gl_depth(int x, int y);
glm::vec4 get3dPoint(glm::vec2 point2D, int width,int height, glm::mat4 modelMatrix);

int main()
{
    sf::ContextSettings settings;
    settings.depthBits = 32;
    settings.stencilBits = 8;
    settings.antialiasingLevel = 4;
    settings.majorVersion = 3;
    settings.minorVersion = 0;

    sf::Window window(sf::VideoMode(WIDTH, HEIGHT), "OpenGL", sf::Style::Default, settings);

    GLfloat vertices[] = {
        -2.0, -2.0, -2.0,  0.0f, 0.0f,
         2.0, -2.0, -2.0,  1.0f, 0.0f,
         2.0,  2.0, -2.0,  1.0f, 1.0f,
         2.0,  2.0, -2.0,  1.0f, 1.0f,
        -2.0,  2.0, -2.0,  0.0f, 1.0f,
        -2.0, -2.0, -2.0,  0.0f, 0.0f,

        -2.0, -2.0,  2.0,  0.0f, 0.0f,
         2.0, -2.0,  2.0,  1.0f, 0.0f,
         2.0,  2.0,  2.0,  1.0f, 1.0f,
         2.0,  2.0,  2.0,  1.0f, 1.0f,
        -2.0,  2.0,  2.0,  0.0f, 1.0f,
        -2.0, -2.0,  2.0,  0.0f, 0.0f,

        -2.0,  2.0,  2.0,  1.0f, 0.0f,
        -2.0,  2.0, -2.0,  1.0f, 1.0f,
        -2.0, -2.0, -2.0,  0.0f, 1.0f,
        -2.0, -2.0, -2.0,  0.0f, 1.0f,
        -2.0, -2.0,  2.0,  0.0f, 0.0f,
        -2.0,  2.0,  2.0,  1.0f, 0.0f,

         2.0,  2.0,  2.0,  1.0f, 0.0f,
         2.0,  2.0, -2.0,  1.0f, 1.0f,
         2.0, -2.0, -2.0,  0.0f, 1.0f,
         2.0, -2.0, -2.0,  0.0f, 1.0f,
         2.0, -2.0,  2.0,  0.0f, 0.0f,
         2.0,  2.0,  2.0,  1.0f, 0.0f,

        -2.0, -2.0, -2.0,  0.0f, 1.0f,
         2.0, -2.0, -2.0,  1.0f, 1.0f,
         2.0, -2.0,  2.0,  1.0f, 0.0f,
         2.0, -2.0,  2.0,  1.0f, 0.0f,
        -2.0, -2.0,  2.0,  0.0f, 0.0f,
        -2.0, -2.0, -2.0,  0.0f, 1.0f,

        -2.0,  2.0, -2.0,  0.0f, 1.0f,
         2.0,  2.0, -2.0,  1.0f, 1.0f,
         2.0,  2.0,  2.0,  1.0f, 0.0f,
         2.0,  2.0,  2.0,  1.0f, 0.0f,
        -2.0,  2.0,  2.0,  0.0f, 0.0f,
        -2.0,  2.0, -2.0,  0.0f, 1.0f
    };

    glewInit();
    glViewport(0, 0, WIDTH, HEIGHT);
    glEnable(GL_DEPTH_TEST);
    Shader ourShader("shaders/default.vs", "shaders/default.frag");


    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    bool running = true;

    glm::vec3 cubePosition;
    cubePosition.z=-3;
    window.setFramerateLimit(30);

    glm::mat4 model;
    glm::mat4 view;
    glm::mat4 projection;

    while (running)
    {
        model=glm::mat4();
        model=glm::translate(model,cubePosition);
        view=glm::mat4();
        projection=glm::mat4();
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);


        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        ourShader.Use();

        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");

        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        while (window.pollEvent(event))
        {
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                cubePosition.y-=0.2;
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                cubePosition.y+=0.2;
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
                cubePosition.x+=0.2;
            if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
                cubePosition.x-=0.2;

            if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
            {
                float x=sf::Mouse::getPosition(window).x;
                float y=sf::Mouse::getPosition(window).y;
                float z=1.0;

                glReadPixels(x,HEIGHT-y-1,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&z);

                x=2.0*(x/WIDTH)-1;
                y=-2.0*(y/HEIGHT)+1;

                glm::vec4 v=glm::vec4(x,y,z,1.0);
                v=v*glm::inverse(projection*view*model);

                v.x/=v.w;
                v.y/=v.w;
                v.z/=v.w;

                cout<<v.x<<' '<<v.y<<' '<<v.z<<' '<<v.w<<'\n';
            }

            if (event.type == sf::Event::Closed)
            {
                // end the program
                running = false;
            }
            else if (event.type == sf::Event::Resized)
            {
                // adjust the viewport when the window is resized
                glViewport(0, 0, event.size.width, event.size.height);
            }
        }

        window.display();
    }
    return 0;
}

因此,最初在屏幕中央绘制立方体,当我尝试点击左上角(第一个红色像素)时,我应该得到数字x = -2,y = 2但我得到x = 0.0150043,y = 0.0150043甚至不接近。我的错误在哪里?我按照本教程阅读了有关此主题的在线阅读: http://webglfactory.blogspot.ro/2011/05/how-to-convert-world-to-screen.html

1 个答案:

答案 0 :(得分:0)

投影意味着将世界坐标转换为标准化的非线性坐标。 NCC是一个2x2x2的立方体空间。从这个NCC空间到窗口屏幕坐标的转换由GPU“自动”完成,使用您在glViewport中传递的参数。

首先,您必须撤消屏幕-NCC翻译。使用https://www.opengl.org/sdk/docs/man2/xhtml/glViewport.xml公式。

因为窗口坐标是2D,所以无法返回深度坐标。从您的眼睛到2D窗口点以及其他地方都有一条直线。