簡體   English   中英

OpenGL Google地圖樣式2D相機/縮放至鼠標光標

[英]OpenGL Google maps style 2D camera / zoom to mouse cursor

我正在嘗試在OpenGL中實現行為類似於Google Maps相機的2D相機。 特別是“縮放到鼠標點”功能。

到目前為止,我已經能夠實現“平移和縮放”確定-但僅在縮放鎖定在窗口/小部件的中心時才可以。 如果我嘗試縮放鼠標位置,則視圖似乎“跳轉”,並且在縮放級別增加后,我放大的項目不再位於鼠標光標下方。

我的相機課在下面-很多代碼,但是我不能讓它變得更小抱歉!

我在每個幀的開始處調用Apply() ,並在平移場景時調用SetX/YPos ,最后在滾動鼠標滾輪時使用鼠標位置的上一個比例+/- 0.1f調用SetScale


相機

class Camera
{
public:
    Camera();

    void Apply();

    void SetXPos(float xpos);
    void SetYPos(float ypos);
    void SetScale(float scaleFactor, float mx, float my);

    float XPos() const { return m_XPos; }
    float YPos() const { return m_YPos; }
    float Scale() const { return m_ScaleFactor; }

    void SetWindowSize(int w, int h);
    void DrawTestItems();

private:
    void init_matrix();

    float m_XPos;
    float m_YPos;

    float m_ScaleFactor;

    float m_Width;
    float m_Height;

    float m_ZoomX;
    float m_ZoomY;
};

camera.cpp

Camera::Camera()
    : m_XPos(0.0f),
      m_YPos(0.0f),
      m_ScaleFactor(1.0f),
      m_ZoomX(0.0f),
      m_ZoomY(0.0f),
      m_Width(0.0f),
      m_Height(0.0f)
{

}

// Called when window is created and when window is resized
void Camera::SetWindowSize(int w, int h)
{
    m_Width = (float)w;
    m_Height = (float)h;
}

void Camera::init_matrix()
{
    glViewport(0, 0, m_Width, m_Height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    float new_W = m_Width * m_ScaleFactor;
    float new_H = m_Height * m_ScaleFactor;

    // Point to zoom on
    float new_x = m_ZoomX;
    float new_y = m_ZoomY;

    glOrtho( -new_W/2+new_x,
              new_W/2+new_x,
              new_H/2+new_y,
              -new_H/2+new_y,
             -1,1);


    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void Camera::Apply()
{
    // Zoom
    init_matrix();

    // Pan
    glTranslatef( m_XPos, m_YPos, 1.0f );

    DrawTestItems();
}

void Camera::SetXPos(float xpos)
{
    m_XPos = xpos;
}

void Camera::SetYPos(float ypos)
{
    m_YPos = ypos;
}

// mx,my = window coords of mouse pos when wheel was scrolled
// scale factor goes up or down by 0.1f
void Camera::SetScale(float scaleFactor, float mx, float my)
{

    m_ZoomX = (float)mx;
    m_ZoomY = (float)my;

    m_ScaleFactor = scaleFactor;

}

void Camera::DrawTestItems()
{

}

更新:我似乎已經注意到2個問題:

  1. SetScale中的鼠標位置不正確-我不知道為什么。
  2. 無論我嘗試glOrtho如何使屏幕的中心成為縮放點,我都確認手動設置了該縮放點/對其進行了硬編碼。 在Google地圖中,屏幕不會像這樣“粘”在中心。

再次更新:

如果這有任何區別,我也使用Qt,我只有一個基本的QGLWidget,並且正在使用鼠標滾輪事件執行縮放。 我獲取了轉輪事件的增量,然后對從轉輪事件傳遞到鼠標位置的比例添加或減去了0.1f。

  1. 使用當前縮放系數和模型/項目/視圖矩陣獲取鼠標光標的世界空間坐標。
  2. 調整縮放系數
  3. 使用新的縮放系數再次獲取世界空間鼠標坐標
  4. 通過世界空間鼠標坐標中的差異移動相機位置
  5. 使用新的相機位置和縮放系數重繪場景

像這樣(在wheel()回調中):

#include <GL/freeglut.h>

#include <iostream>
using namespace std;

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>


glm::dvec3 Unproject( const glm::dvec3& win )
{
    glm::ivec4 view;
    glm::dmat4 proj, model;
    glGetDoublev( GL_MODELVIEW_MATRIX, &model[0][0] );
    glGetDoublev( GL_PROJECTION_MATRIX, &proj[0][0] );
    glGetIntegerv( GL_VIEWPORT, &view[0] );

    glm::dvec3 world = glm::unProject( win, model, proj, view );
    return world;
}

// unprojects the given window point
// and finds the ray intersection with the Z=0 plane
glm::dvec2 PlaneUnproject( const glm::dvec2& win )
{
    glm::dvec3 world1 = Unproject( glm::dvec3( win, 0.01 ) );
    glm::dvec3 world2 = Unproject( glm::dvec3( win, 0.99 ) );

    // u is a value such that:
    // 0 = world1.z + u * ( world2.z - world1.z )
    double u = -world1.z / ( world2.z - world1.z );
    // clamp u to reasonable values
    if( u < 0 ) u = 0;
    if( u > 1 ) u = 1;

    return glm::dvec2( world1 + u * ( world2 - world1 ) );
}

// pixels per unit
const double ppu = 1.0;

glm::dvec2 center( 0 );
double scale = 1.0;
void ApplyCamera()
{
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    const double w = glutGet( GLUT_WINDOW_WIDTH ) / ppu;
    const double h = glutGet( GLUT_WINDOW_HEIGHT ) / ppu;
    glOrtho( -w/2, w/2, -h/2, h/2, -1, 1 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glScaled( scale, scale, 1.0 );
    glTranslated( -center[0], -center[1], 0 );
}

glm::dvec2 mPos;

glm::dvec2 centerStart( 0 );
int btn = -1;

void mouse( int button, int state, int x, int y )
{
    ApplyCamera();

    y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
    mPos = glm::ivec2( x, y );

    btn = button;
    if( GLUT_LEFT_BUTTON == btn && GLUT_DOWN == state )
    {
        centerStart = PlaneUnproject( glm::dvec2( x, y ) );
    }
    if( GLUT_LEFT_BUTTON == btn && GLUT_UP == state )
    {
        btn = -1;
    }

    glutPostRedisplay();
}

void motion( int x, int y )
{
    y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
    mPos = glm::ivec2( x, y );

    if( GLUT_LEFT_BUTTON == btn )
    {
        ApplyCamera();
        glm::dvec2 cur = PlaneUnproject( glm::dvec2( x, y ) );
        center += ( centerStart - cur );
    }

    glutPostRedisplay();
}

void passiveMotion( int x, int y )
{
    y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
    mPos = glm::ivec2( x, y );
    glutPostRedisplay();
}

void wheel( int wheel, int direction, int x, int y )
{
    y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
    mPos = glm::ivec2( x, y );

    ApplyCamera();
    glm::dvec2 beforeZoom = PlaneUnproject( glm::dvec2( x, y ) );

    const double scaleFactor = 0.90;
    if( direction == -1 )   scale *= scaleFactor;
    if( direction ==  1 )   scale /= scaleFactor;

    ApplyCamera();
    glm::dvec2 afterZoom = PlaneUnproject( glm::dvec2( x, y ) );

    center += ( beforeZoom - afterZoom );

    glutPostRedisplay();
}

void display()
{
    glClearColor( 0, 0, 0, 1 );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    ApplyCamera();

    glm::dvec2 cur = PlaneUnproject( mPos );
    cout << cur.x << " " << cur.y << " " << scale << endl;

    glPushMatrix();
    glScalef( 50, 50, 1 );
    glBegin( GL_QUADS );
    glColor3ub( 255, 255, 255 );
    glVertex2i( -1, -1 );
    glVertex2i(  1, -1 );
    glVertex2i(  1,  1 );
    glVertex2i( -1,  1 );
    glEnd();
    glPopMatrix();

    glutSwapBuffers();
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutInitWindowSize( 600, 600 );
    glutCreateWindow( "GLUT" );

    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutMouseWheelFunc( wheel );
    glutDisplayFunc( display );
    glutPassiveMotionFunc( passiveMotion );

    glutMainLoop();
    return 0;
}

暫無
暫無

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

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