MVE ,libQGLViewer混合开发,Camera类冲突导致opengl显示问题

MVE ,libQGLViewer混合开发,Camera类冲突导致opengl显示问题

环境:QT5.12.7 + VS2017

一、问题描述

MVE 和 libQGLViewer均封装了Camera 类,两者初始参数并不同,导致三维显示时,鼠标缩放 和 拖动时 三维模型无法显示。

二、使用分析

1.MVE Camera类

梳理源码,查看Camera类定义如下:
代码如下(Camera):

/* --- Viewing matrix parameters --- */

    /** Position of the camera. */
    math::Vec3f pos;
    /** Viewing direction of the camera. */
    math::Vec3f viewing_dir;
    /** Up-vector of the camera. */
    math::Vec3f up_vec;

    /* --- Projection matrix parameters --- */

    /** Near clipping plane of the projection matrix. */
    float z_near;
    /** Far clipping plane of the projection matrix. */
    float z_far;
    /** Top and -Bottom clipping plane of the projection matrix. */
    float top;
    /** Right and -Left clipping plane of the projection matrix. */
    float right;

    /* --- Viewport parameters --- */

    /** The viewport width. */
    int width;
    /** The viewport height. */
    int height;

    /* --- Viewing and projection matrices --- */

    /** View matrix, use update_matrices() to calculate. */
    math::Matrix4f view;
    /** Inverse view matrix, use update_matrices() to calculate. */
    math::Matrix4f inv_view;
    /** Projection matrix, use update_matrices() to calculate. */
    math::Matrix4f proj;
    /** Inverse projection matrix, use update_matrices() to calculate. */
    math::Matrix4f inv_proj;

默认参数:

Camera::Camera (void)
    : pos(0.0f, 0.0f, 5.0f)
    , viewing_dir(0.0f, 0.0f, -1.0f)
    , up_vec(0.0f, 1.0f, 0.0f)
    , z_near(0.1f)
    , z_far(500.0f)
    , top(0.1f)
    , right(0.1f)
    , width(0)
    , height(0)
    , view(0.0f)
    , inv_view(0.0f)
    , proj(0.0f)
    , inv_proj(0.0f)
{
}

2.libQGLViewer Camera类

梳理源码,Camera 定义如下:
代码如下(Camera):

class QGLVIEWER_EXPORT Camera : public QObject {
#ifndef DOXYGEN
  friend class ::QGLViewer;
#endif

  Q_OBJECT

public:
  Camera();
  virtual ~Camera();

  Camera(const Camera &camera);
  Camera &operator=(const Camera &camera);

  /*! Enumerates the two possible types of Camera.

  See type() and setType(). This type mainly defines different Camera projection
  matrix (see loadProjectionMatrix()). Many other methods (pointUnderPixel(),
  convertClickToLine(), projectedCoordinatesOf(), pixelGLRatio()...) are
  affected by this Type. */
  enum Type { PERSPECTIVE, ORTHOGRAPHIC };

  /*! @name Position and orientation */
  //@{
public:
  Vec position() const;
  Vec upVector() const;
  Vec viewDirection() const;
  Vec rightVector() const;
  Quaternion orientation() const;

  void setFromModelViewMatrix(const GLdouble *const modelViewMatrix);
  void setFromProjectionMatrix(const qreal matrix[12]);

public Q_SLOTS:
  void setPosition(const Vec &pos);
  void setOrientation(const Quaternion &q);
  void setOrientation(qreal theta, qreal phi);
  void setUpVector(const Vec &up, bool noMove = true);
  void setViewDirection(const Vec &direction);
  //@}

  /*! @name Positioning tools */
  //@{
public Q_SLOTS:
  void lookAt(const Vec &target);
  void showEntireScene();
  void fitSphere(const Vec &center, qreal radius);
  void fitBoundingBox(const Vec &min, const Vec &max);
  void fitScreenRegion(const QRect &rectangle);
  void centerScene();
  void interpolateToZoomOnPixel(const QPoint &pixel);
  void interpolateToFitScene();
  void interpolateTo(const Frame &fr, qreal duration);
  //@}

  /*! @name Frustum */
  //@{
public:
  /*! Returns the Camera::Type of the Camera.

  Set by setType(). Mainly used by loadProjectionMatrix().

  A Camera::PERSPECTIVE Camera uses a classical projection mainly defined by its
  fieldOfView().

  With a Camera::ORTHOGRAPHIC type(), the fieldOfView() is meaningless and the
  width and height of the Camera frustum are inferred from the distance to the
  pivotPoint() using getOrthoWidthHeight().

  Both types use zNear() and zFar() (to define their clipping planes) and
  aspectRatio() (for frustum shape). */
  Type type() const { return type_; }

  /*! Returns the vertical field of view of the Camera (in radians).

  Value is set using setFieldOfView(). Default value is pi/4 radians. This value
  is meaningless if the Camera type() is Camera::ORTHOGRAPHIC.

  The field of view corresponds the one used in \c gluPerspective (see manual).
  It sets the Y (vertical) aperture of the Camera. The X (horizontal) angle is
  inferred from the window aspect ratio (see aspectRatio() and
  horizontalFieldOfView()).

  Use setFOVToFitScene() to adapt the fieldOfView() to a given scene. */
  qreal fieldOfView() const { return fieldOfView_; }

Camera 类初始化:

Camera::Camera()
    : frame_(nullptr), fieldOfView_(M_PI / 4.0), modelViewMatrixIsUpToDate_(false),
      projectionMatrixIsUpToDate_(false) {
  // #CONNECTION# Camera copy constructor
  interpolationKfi_ = new KeyFrameInterpolator;
  // Requires the interpolationKfi_
  setFrame(new ManipulatedCameraFrame());

  // #CONNECTION# All these default values identical in initFromDOMElement.

  // Requires fieldOfView() to define focusDistance()
  setSceneRadius(1.0);

  // Initial value (only scaled after this)
  orthoCoef_ = tan(fieldOfView() / 2.0);

  // Also defines the pivotPoint(), which changes orthoCoef_. Requires a
  // frame().
  setSceneCenter(Vec(0.0, 0.0, 0.0));

  // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to
  // projectionMatrix_ below.
  setType(PERSPECTIVE);

  // #CONNECTION# initFromDOMElement default values
  setZNearCoefficient(0.005);
  setZClippingCoefficient(sqrt(3.0));

  // Dummy values
  setScreenWidthAndHeight(600, 400);

  // Stereo parameters
  setIODistance(0.062);
  setPhysicalScreenWidth(0.5);
  // focusDistance is set from setFieldOfView()

  // #CONNECTION# Camera copy constructor
  for (unsigned short j = 0; j < 16; ++j) {
    modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0);
    // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost
    // everywhere.
    projectionMatrix_[j] = 0.0;
  }
  computeProjectionMatrix();
}

问题解决过程

《1》每次绘制时用libQGLViewer Camera类参数去更新 MVE Camera类参数
在AddinManager 这种增加async_camera()函数,AddinManager 中state.gl_widget为QGLViewer子类

qreal left, right, bottom, top;
	qreal screenHalfWidth, halfWidth, side, shift, delta;

	this->camera.pos = math::Vec3f{ (float)this->state.gl_widget->camera()->position().x,(float)this->state.gl_widget->camera()->position().y,(float)this->state.gl_widget->camera()->position().z };
	this->camera.up_vec = math::Vec3f{ (float)this->state.gl_widget->camera()->upVector().x,(float)this->state.gl_widget->camera()->upVector().y,(float)this->state.gl_widget->camera()->upVector().z };
	this->camera.viewing_dir = math::Vec3f{ (float)this->state.gl_widget->camera()->viewDirection().x,(float)this->state.gl_widget->camera()->viewDirection().y,(float)this->state.gl_widget->camera()->viewDirection().z };
	this->camera.z_far = this->state.gl_widget->camera()->zFar();
	this->camera.z_near = this->state.gl_widget->camera()->zNear();
	this->camera.height = this->state.gl_widget->camera()->screenHeight();
	this->camera.width = this->state.gl_widget->camera()->screenWidth();
	
	bool leftBuffer = false;
	screenHalfWidth = this->state.gl_widget->camera()->focusDistance() * tan(this->state.gl_widget->camera()->horizontalFieldOfView() / 2.0);
	shift = screenHalfWidth * this->state.gl_widget->camera()->IODistance() / this->state.gl_widget->camera()->physicalScreenWidth();
	// should be * current y  / y total
	// to take into account that the window doesn't cover the entire screen

	// compute half width of "view" at znear and the delta corresponding to
	// the shifted camera to deduce what to set for asymmetric frustums
	halfWidth = this->state.gl_widget->camera()->zNear() * tan(this->state.gl_widget->camera()->horizontalFieldOfView() / 2.0);
	delta = shift * this->state.gl_widget->camera()->zNear() / this->state.gl_widget->camera()->focusDistance();
	side = leftBuffer ? -1.0 : 1.0;

	left = -halfWidth + side * delta;
	right = halfWidth + side * delta;
	top = halfWidth / this->state.gl_widget->camera()->aspectRatio();
	bottom = -top;
	this->camera.right = right;
	this->camera.top = top;
	this->camera.update_matrices();
	this->update_camera();

《2》 按上步修改后 三维模型能够显示,但是鼠标缩放和拖动时 模型和 libQGLViewer 中显示坐标轴明显不对应,两者对鼠标操作处理差别很大,例如WheelEven事件:
MVE Camera 处理方式:

this->makeCurrent();
	ogl::MouseEvent e;
	if (event->delta() < 0)
		e.type = ogl::MOUSE_EVENT_WHEEL_DOWN;
	else
		e.type = ogl::MOUSE_EVENT_WHEEL_UP;
	e.button = ogl::MOUSE_BUTTON_NONE;
	e.button_mask = event->buttons();
	e.x = event->x() * this->device_pixel_ratio;
	e.y = event->y() * this->device_pixel_ratio;
	//this->context->mouse_event(e);

	this->repaint_async();

libQGLViewer Camera 处理方式:

	static const qreal WHEEL_SENSITIVITY_COEF = 8E-4;
	return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF;

考虑到两个camera类始终涉及参数定义和计算方法的不同维护起来问题和无法做到统一,故决定剔除MVE 中Camera类,(PS 用libQGLViewer 中camera类去替换 MVECamera 太复杂了,涉及到很多接口适配,还是不这么干了,选个简单的方法,哈哈)
1>AddinManager 不在继承CameraTrackballContext,

class AddinManager : public QWidget/*, public ogl::CameraTrackballContext*/

CameraTrackballContext 类源码:

template <typename CTRL> class CameraContext;
typedef CameraContext<CamTrackball> CameraTrackballContext;
/**
 * A trackball camera control that consumes mouse events
 * and delivers viewing parameters for the camera.
 */
class CamTrackball
{
public:
    CamTrackball (void);

    void set_camera (Camera* camera);
    bool consume_event (MouseEvent const& event);
    bool consume_event (KeyboardEvent const& event);

    void set_camera_params (math::Vec3f const& center,
        math::Vec3f const& lookat, math::Vec3f const& upvec);

    math::Vec3f get_campos (void) const;
    math::Vec3f get_viewdir (void) const;
    math::Vec3f const& get_upvec (void) const;

private:
    math::Vec3f get_center (int x, int y);
    void handle_tb_rotation (int x, int y);
    math::Vec3f get_ball_normal (int x, int y);

private:
    /* Camera information. */
    Camera* cam;

    /* Current trackball configuration. */
    float tb_radius;
    math::Vec3f tb_center;
    math::Vec3f tb_tocam;
    math::Vec3f tb_upvec;

    /* Variables to calculate rotation and zoom. */
    int rot_mouse_x;
    int rot_mouse_y;
    math::Vec3f rot_tb_tocam;
    math::Vec3f rot_tb_upvec;

    float zoom_tb_radius;
    int zoom_mouse_y;
};

2>AddinManager paint_impl函数同步libQGLViewer Cannera 参数,模型每次repaint,依靠该函数实现,同步参数如下:(吐槽下qreal left, right, bottom, top;这几个参数居然是函数局部变量计算出来的,找了好久,一直以为和WVE一样是成员变量赋值就好了,。。。。)

qreal left, right, bottom, top;
	qreal screenHalfWidth, halfWidth, side, shift, delta;

	cam.pos = math::Vec3f{ (float)this->state.gl_widget->camera()->position().x,(float)this->state.gl_widget->camera()->position().y,(float)this->state.gl_widget->camera()->position().z };
	cam.up_vec = math::Vec3f{ (float)this->state.gl_widget->camera()->upVector().x,(float)this->state.gl_widget->camera()->upVector().y,(float)this->state.gl_widget->camera()->upVector().z };
	cam.viewing_dir = math::Vec3f{ (float)this->state.gl_widget->camera()->viewDirection().x,(float)this->state.gl_widget->camera()->viewDirection().y,(float)this->state.gl_widget->camera()->viewDirection().z };
	cam.z_far = this->state.gl_widget->camera()->zFar();
	cam.z_near = this->state.gl_widget->camera()->zNear();
	
	
	bool leftBuffer = false;
	screenHalfWidth = this->state.gl_widget->camera()->focusDistance() * tan(this->state.gl_widget->camera()->horizontalFieldOfView() / 2.0);
	shift = screenHalfWidth * this->state.gl_widget->camera()->IODistance() / this->state.gl_widget->camera()->physicalScreenWidth();
	// should be * current y  / y total
	// to take into account that the window doesn't cover the entire screen

	// compute half width of "view" at znear and the delta corresponding to
	// the shifted camera to deduce what to set for asymmetric frustums
	halfWidth = this->state.gl_widget->camera()->zNear() * tan(this->state.gl_widget->camera()->horizontalFieldOfView() / 2.0);
	delta = shift * this->state.gl_widget->camera()->zNear() / this->state.gl_widget->camera()->focusDistance();
	side = leftBuffer ? -1.0 : 1.0;

	left = -halfWidth + side * delta;
	right = halfWidth + side * delta;
	top = halfWidth / this->state.gl_widget->camera()->aspectRatio();
	bottom = -top;
	cam.right = right;
	cam.top = top;
	cam.update_matrices();
	
		this->state.send_uniform(cam);

3>在paint_impl repalint前设置state.ui_needs_redraw = true;,之前该变量更新在CameraTrackballContext中
4> class Viewer : public QGLViewer 中void set_context(ogl::Context* context);改为
void set_addinMan(AddinManager *mger);用于调用opengl 初始化函数init_impl ,
init_impl 函数如下:

#ifdef _WIN32
    /* Initialize GLEW. */
    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if (err != GLEW_OK)
    {
        std::cout << "Error initializing GLEW: " << glewGetErrorString(err)
            << std::endl;
        std::exit(EXIT_FAILURE);
    }
#endif

    /* Load shaders. */
    this->state.load_shaders();
    this->state.init_ui();

    for (std::size_t i = 0; i < this->addins.size(); ++i)
    {
        this->addins[i]->set_state(&this->state);
        this->addins[i]->init();
        this->connect(this->addins[i], SIGNAL(mesh_generated(
            std::string const&, mve::TriangleMesh::Ptr)), this,
            SLOT(on_mesh_generated(std::string const&,
            mve::TriangleMesh::Ptr)));
    }

class Viewer : public QGLViewer 鼠标事处理是去除ogl::Context* context函数,如mousePressEvent,

QGLViewer::mousePressEvent(event);
	emit sig_replain();
	this->makeCurrent();
	ogl::MouseEvent e;
	e.type = ogl::MOUSE_EVENT_PRESS;
	e.button = (ogl::MouseButton)event->button();
	e.button_mask = event->buttons();
	e.x = event->x() * this->device_pixel_ratio;
	e.y = event->y() * this->device_pixel_ratio;

	//this->context->mouse_event(e);去除
	this->repaint_async();

你可能感兴趣的:(Troubleshoot,Opengl,c++,opengl)