基于QT5.12 OpenGL的3维地球开发

1 效果展示

2 项目示例完整代码下载

https://download.csdn.net/download/hellocode_7812/89025226

3 3D渲染部分代码

3.1 相机代码

Camera.h代码
#ifndef CAMERA_H
#define CAMERA_H

#include "Node.h"
#include "Macros.h"

#include <QMatrix4x4>
#include <QMouseEvent>
#include <QTimer>

namespace Earth
{
    class Camera : public Node
    {
        Q_OBJECT
    protected:
        Camera(QObject* parent = nullptr);

    public:

        virtual QMatrix4x4 GetViewProjectionMatrix();
        virtual QMatrix4x4 GetViewMatrix();
        virtual QMatrix4x4 GetRotationMatrix();
        virtual QVector3D GetViewDirection();

        virtual QMatrix4x4 GetProjectionMatrix() = 0;

        virtual void MouseDoubleClicked(QMouseEvent*);
        virtual void MousePressed(QMouseEvent*);
        virtual void MouseReleased(QMouseEvent*);
        virtual void MouseMoved(QMouseEvent*);
        virtual void WheelMoved(QWheelEvent*);
        virtual void KeyPressed(QKeyEvent*);
        virtual void KeyReleased(QKeyEvent*);
        virtual void Update(float);
        virtual void Reset();
        virtual void Resize(int width, int height);

        bool Active() const;
        void SetActive(bool newActive);

    signals:
        void ActiveChanged();

    protected:
        bool mActive;

        DEFINE_MEMBER(int, Width);
        DEFINE_MEMBER(int, Height);
        DEFINE_MEMBER(float, ZNear);
        DEFINE_MEMBER(float, ZFar);
    };
}

#endif // CAMERA_H
Camera.cpp
#include "Camera.h"

Earth::Camera::Camera(QObject* parent)
    : Node(parent)
    , mActive(false)
    , mWidth(1600)
    , mHeight(900)
    , mZNear(0.1f)
    , mZFar(10000.0f)
{}

QMatrix4x4 Earth::Camera::GetViewProjectionMatrix()
{
    return GetProjectionMatrix() * GetViewMatrix();
}

QMatrix4x4 Earth::Camera::GetViewMatrix()
{
    QMatrix4x4 viewMatrix;
    viewMatrix.rotate(Rotation().conjugated());
    viewMatrix.translate(-Position());

    return viewMatrix;
}

QMatrix4x4 Earth::Camera::GetRotationMatrix()
{
    auto rotation = GetViewMatrix();
    rotation.setColumn(3, QVector4D(0, 0, 0, 1));
    return rotation;
}

QVector3D Earth::Camera::GetViewDirection()
{
    return Rotation() * QVector3D(0, 0, -1);
}

void Earth::Camera::Resize(int width, int height)
{
    mWidth = width;
    mHeight = height;
}

bool Earth::Camera::Active() const
{
    return mActive;
}

void Earth::Camera::SetActive(bool newActive)
{
    if (mActive == newActive)
        return;

    mActive = newActive;

    emit ActiveChanged();
}

void Earth::Camera::MouseDoubleClicked(QMouseEvent*) {}

void Earth::Camera::MousePressed(QMouseEvent*) {}

void Earth::Camera::MouseReleased(QMouseEvent*) {}

void Earth::Camera::MouseMoved(QMouseEvent*) {}

void Earth::Camera::WheelMoved(QWheelEvent*) {}

void Earth::Camera::KeyPressed(QKeyEvent*) {}

void Earth::Camera::KeyReleased(QKeyEvent*) {}

void Earth::Camera::Update(float) {}

void Earth::Camera::Reset() {}

 3.2 控制器代码

Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H

#include "DummyCamera.h"
#include "Mouse.h"
#include "Model.h"
#include "Helper.h"
#include "ShaderManager.h"
#include "Sun.h"

#include <QObject>
#include <QOpenGLTexture>
#include <QOpenGLFramebufferObjectFormat>

#include <imgui.h>
#include <QtImGui.h>

namespace Earth
{
    class Window;

    class Controller : public QObject, protected QOpenGLFunctions
    {
        Q_OBJECT
    public:
        explicit Controller(QObject* parent = nullptr);

        void Init();
        void MouseDoubleClicked(QMouseEvent* event);
        void MousePressed(QMouseEvent* event);
        void MouseReleased(QMouseEvent* event);
        void MouseMoved(QMouseEvent* event);
        void WheelMoved(QWheelEvent* event);
        void KeyPressed(QKeyEvent* event);
        void KeyReleased(QKeyEvent* event);
        void Resize(int w, int h);
        void Render(float ifps);
        void SetWindow(Window* newWindow);

    private:
        void LoadModels();

    private:
        ShaderManager* mShaderManager;
        Window* mWindow;

        Sun* mSun;
        Model* mEarth;
        DummyCamera* mCamera;

        Mouse mMouse;
        int mZoomLevel;

        bool mUpdate;
        float mDistance;
        float mTilt;

        QMap<QString, ModelData*> mModelsData;
        QOpenGLTexture* mEarthTexture;

        int mWidth;
        int mHeight;

        // Mouse
        QOpenGLFramebufferObjectFormat mMousePositionFBOFormat;
        QOpenGLFramebufferObject* mMousePositionFBO;
        QVector4D mMouseWorldPosition;
        Qt::MouseButton mPressedButton;
    };
}


#endif // CONTROLLER_H
Controller.cpp
#include "Controller.h"
#include "Window.h"

#include <QApplication>
#include <QDebug>
#include <QDir>

Earth::Controller::Controller(QObject* parent)
    : QObject(parent)
    , mZoomLevel(100)
    , mUpdate(false)
    , mDistance(40.0f)
    , mTilt(0.0f)
    , mWidth(1600)
    , mHeight(900)
    , mMousePositionFBO(nullptr)
    , mPressedButton(Qt::NoButton)
{}

void Earth::Controller::Init()
{
    initializeOpenGLFunctions();
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_DEPTH_TEST);

    mShaderManager = new ShaderManager;

    mShaderManager->Init();

    mSun = new Sun;
    mEarth = new Model("Earth");

    mMousePositionFBOFormat.setSamples(0);
    mMousePositionFBOFormat.setAttachment(QOpenGLFramebufferObject::Attachment::Depth);
    mMousePositionFBOFormat.setInternalTextureFormat(GL_RGBA32F);

    mMousePositionFBO = new QOpenGLFramebufferObject(mWidth, mHeight, mMousePositionFBOFormat);

    QImage image(":/Textures/worldtopo.jpg");
    image = image.mirrored();
    mEarthTexture = new QOpenGLTexture(image);
    mEarthTexture->setWrapMode(QOpenGLTexture::WrapMode::Repeat);
    mEarthTexture->setMinMagFilters(QOpenGLTexture::Filter::LinearMipMapLinear, QOpenGLTexture::Filter::Linear);

    mCamera = new DummyCamera;
    mCamera->SetPosition(QVector3D(0, 0, mDistance));
    mCamera->SetVerticalFov(60.0f);
    mCamera->SetZNear(0.01f);
    mCamera->SetZFar(100000.0f);

    mEarth->SetPosition(QVector3D(0, 0, 0));

    LoadModels();
}

void Earth::Controller::Render(float ifps)
{
    if (mUpdate)
    {
        // Earth
        {
            auto rotation = mEarth->Rotation();
            rotation = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), -mMouse.mDz * ifps * qMax(2.0f, mDistance - 10)) * rotation;
            rotation = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), -mMouse.mDx * ifps * (mDistance - 10)) * rotation;
            rotation = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), -mMouse.mDy * ifps * (mDistance - 10)) * rotation;
            mEarth->SetRotation(rotation);
        }

        // Camera
        {
            mTilt -= mMouse.mDw * ifps * 30;
            mTilt = qBound(0.0f, mTilt, 89.0f);

            auto rotation = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), mTilt);
            auto position = QVector3D(0, 0, 10) + rotation * QVector3D(0, 0, mDistance - 10);

            mCamera->SetRotation(rotation);
            mCamera->SetPosition(position);
        }

        mMouse.mDx = 0.0f;
        mMouse.mDy = 0.0f;
        mMouse.mDz = 0.0f;
        mMouse.mDw = 0.0f;
        mUpdate = false;
    }

    mSun->SetDirection(mCamera->GetViewDirection());

    // Render
    {
        mMousePositionFBO->bind();
        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        mShaderManager->Bind(ShaderManager::ShaderType::EarthMousePositionShader);
        mShaderManager->SetUniformValue("MVP", mCamera->GetViewProjectionMatrix() * mEarth->Transformation());
        if (mModelsData.contains("Earth"))
            mModelsData.value("Earth")->Render();
        mShaderManager->Release();

        QOpenGLFramebufferObject::bindDefault();
        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        mShaderManager->Bind(ShaderManager::ShaderType::EarthShader);
        mShaderManager->SetUniformValue("M", mEarth->Transformation());
        mShaderManager->SetUniformValue("N", mEarth->Transformation().normalMatrix());
        mShaderManager->SetUniformValue("VP", mCamera->GetViewProjectionMatrix());
        mShaderManager->SetUniformValue("earthAmbient", mEarth->GetAmbient());
        mShaderManager->SetUniformValue("earthDiffuse", mEarth->GetDiffuse());
        mShaderManager->SetUniformValue("earthSpecular", mEarth->GetSpecular());
        mShaderManager->SetUniformValue("earthShininess", mEarth->GetShininess());
        mShaderManager->SetUniformValue("cameraPosition", mCamera->Position());
        mShaderManager->SetUniformValue("sunDirection", mSun->GetDirection());
        mShaderManager->SetUniformValue("sunColor", mSun->GetColor());
        mShaderManager->SetUniformValue("sunAmbient", mSun->GetAmbient());
        mShaderManager->SetUniformValue("sunDiffuse", mSun->GetDiffuse());
        mShaderManager->SetUniformValue("sunSpecular", mSun->GetSpecular());
        mEarthTexture->bind(0);
        mShaderManager->SetSampler("earthTexture", 0, mEarthTexture->textureId());
        if (mModelsData.contains("Earth"))
            mModelsData.value("Earth")->Render();
        mShaderManager->Release();
    }

    QtImGui::newFrame();
    glViewport(0, 0, mWindow->width(), mWindow->height());
    ImGui::SetNextWindowSize(ImVec2(420, 820), ImGuiCond_FirstUseEver);
    ImGui::Begin("Debug");
    ImGui::TextColored(ImVec4(1, 1, 0, 1), "Latitude: %.6f, Longitude: %.6f)", mMouseWorldPosition[0], mMouseWorldPosition[1]);
    ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    ImGui::End();

    ImGui::Render();
    QtImGui::render();
}

void Earth::Controller::LoadModels()
{
    qInfo() << "Loading and creating all models...";

    QDir dir(":/Models");
    dir.setNameFilters(QStringList("*.obj"));
    auto files = dir.entryList(QDir::Filter::AllEntries);

    for (const auto& file : qAsConst(files))
    {
        QString modelName = file.left(file.lastIndexOf("."));
        ModelData* modelData = Earth::Helper::LoadModelData(dir.path() + "/" + file);
        mModelsData.insert(modelName, modelData);

        if (modelData)
            modelData->Create();
    }

    qInfo() << "All models are loaded.";
}

void Earth::Controller::WheelMoved(QWheelEvent* event)
{
    if (event->angleDelta().y() < 0)
        mDistance = mDistance / 0.95;

    if (event->angleDelta().y() > 0)
        mDistance = mDistance * 0.95;

    mDistance = qBound(10.0f + 2 * mCamera->GetZNear(), mDistance, 1000.0f);

    mUpdate = true;
}

void Earth::Controller::MousePressed(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        mMouse.mX = event->pos().x();
        mMouse.mY = event->pos().y();
        mMouse.mPressedButton = Qt::LeftButton;
    }

    if (event->button() == Qt::MiddleButton)
    {
        mMouse.mZ = event->pos().y();
        mMouse.mPressedButton = Qt::MiddleButton;
    }

    if (event->button() == Qt::RightButton)
    {
        mMouse.mW = event->pos().y();
        mMouse.mPressedButton = Qt::RightButton;
    }
}

void Earth::Controller::MouseReleased(QMouseEvent* event)
{
    mMouse.mPressedButton = Qt::NoButton;
}

void Earth::Controller::MouseMoved(QMouseEvent* event)
{
    if (ImGui::GetIO().WantCaptureMouse)
        return;

    if (mMouse.mPressedButton == Qt::LeftButton)
    {
        mMouse.mDx += mMouse.mX - event->pos().x();
        mMouse.mDy += mMouse.mY - event->pos().y();

        mMouse.mX = event->pos().x();
        mMouse.mY = event->pos().y();

        mUpdate = true;
    }

    if (mMouse.mPressedButton == Qt::MiddleButton)
    {
        mMouse.mDz += mMouse.mZ - event->pos().y();
        mMouse.mZ = event->pos().y();

        mUpdate = true;
    }

    if (mMouse.mPressedButton == Qt::RightButton)
    {
        mMouse.mDw += mMouse.mW - event->pos().y();
        mMouse.mW = event->pos().y();
        mUpdate = true;
    }

    mMousePositionFBO->bind();
    glReadPixels(event->pos().x(), mMousePositionFBO->height() - event->pos().y(), 1, 1, GL_RGBA, GL_FLOAT, &mMouseWorldPosition);
    mMousePositionFBO->release();
}

void Earth::Controller::KeyPressed(QKeyEvent* event)
{
}

void Earth::Controller::KeyReleased(QKeyEvent* event)
{
}

void Earth::Controller::Resize(int w, int h)
{
    mWindow->makeCurrent();
    mCamera->Resize(w, h);
    mWidth = w;
    mHeight = h;

    if (mMousePositionFBO)
    {
        delete mMousePositionFBO;
        mMousePositionFBO = new QOpenGLFramebufferObject(mWidth, mHeight, mMousePositionFBOFormat);
    }

    mWindow->doneCurrent();
}

void Earth::Controller::MouseDoubleClicked(QMouseEvent*)
{
}

void Earth::Controller::SetWindow(Window* newWindow)
{
    mWindow = newWindow;
}

3.3 模型加载代码

Model.h
#ifndef MODEL_H
#define MODEL_H

#include "Node.h"

namespace Earth
{
    class Model : public Node
    {
    public:
        explicit Model(const QString& modelName, QObject* parent = nullptr);
        virtual ~Model();

    protected:
        DEFINE_MEMBER(QVector4D, Color);
        DEFINE_MEMBER(float, Ambient);
        DEFINE_MEMBER(float, Diffuse);
        DEFINE_MEMBER(float, Specular);
        DEFINE_MEMBER(float, Shininess);
        DEFINE_MEMBER(QString, Name);
    };
}


#endif // MODEL_H

 

Model.cpp
#include "Model.h"

Earth::Model::Model(const QString& modelName, QObject* parent)
    : Node(parent)
    , mColor(1, 1, 1, 1)
    , mAmbient(0.5f)
    , mDiffuse(1.0f)
    , mSpecular(0.25f)
    , mShininess(4.0f)
{
    mName = modelName;
}

Earth::Model::~Model() {}

 

ModelData.h
#ifndef MODELDATA_H
#define MODELDATA_H

#include <QVector3D>
#include <QVector4D>
#include <QVector2D>

#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLVertexArrayObject>
#include <QVector>

namespace Earth
{

    class ModelData : protected QOpenGLFunctions
    {
    public:
        ModelData();

        struct Vertex {
            QVector4D position;
            QVector3D normal;
            QVector2D textureCoords;
        };

        void AddVertex(const Vertex& vertex);
        void Create();
        void Render();

    private:
        QVector<Vertex> mVertices;
        QOpenGLVertexArrayObject mVAO;
        QOpenGLBuffer mVBO;
    };
}



#endif // MODELDATA_H

 

ModelData.cpp
#include "ModelData.h"

Earth::ModelData::ModelData() {}

void Earth::ModelData::AddVertex(const Vertex& vertex)
{
    mVertices << vertex;
}

void Earth::ModelData::Create()
{
    initializeOpenGLFunctions();
    mVAO.create();
    mVAO.bind();

    mVBO.create();
    mVBO.bind();
    mVBO.setUsagePattern(QOpenGLBuffer::UsagePattern::StaticDraw);
    mVBO.allocate(mVertices.constData(), sizeof(Vertex) * mVertices.size());

    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, textureCoords));
    glEnableVertexAttribArray(2);

    mVBO.release();
    mVAO.release();
}

void Earth::ModelData::Render()
{
    if (mVAO.isCreated())
    {
        mVAO.bind();
        glDrawArrays(GL_TRIANGLES, 0, mVertices.size());
        mVAO.release();
    }
}

Node.h

#ifndef NODE_H
#define NODE_H
#include "Macros.h"

#include <QMatrix4x4>
#include <QObject>
#include <QQuaternion>
#include <QVector3D>

#include <imgui.h>
#include <QtImGui.h>

namespace Earth
{
    class Node : public QObject
    {
    protected:
        friend class OpenGLWidget;

        explicit Node(QObject* parent = nullptr);
        virtual ~Node();

    public:
        const QMatrix4x4& Transformation() const;
        void SetTransformation(const QMatrix4x4& newTransformation);

        const QQuaternion& Rotation() const;
        void SetRotation(const QQuaternion& newRotation);

        const QVector3D& Position() const;
        void SetPosition(const QVector3D& newPosition);

        const QVector3D& Scale() const;
        void SetScale(const QVector3D& newScale);

        void SetPosition(float x, float y, float z) { SetPosition(QVector3D(x, y, z)); }
        void SetScale(float x, float y, float z) { SetScale(QVector3D(x, y, z)); }

    private:
        void UpdateTransformation();

    private:
        QMatrix4x4 mTransformation;
        QQuaternion mRotation;
        QVector3D mPosition;
        QVector3D mScale;

        DEFINE_MEMBER(bool, Visible)
    };
}

#endif // NODE_H

Node.cpp

#include "Node.h"
#include "Helper.h"

#include <QtMath>

Earth::Node::Node(QObject* parent)
    : QObject(parent)
    , mPosition(0, 0, 0)
    , mScale(1, 1, 1)
    , mVisible(true)
{}

Earth::Node::~Node() {}

const QQuaternion& Earth::Node::Rotation() const
{
    return mRotation;
}

void Earth::Node::SetRotation(const QQuaternion& newRotation)
{
    mRotation = newRotation;

    UpdateTransformation();
}

const QVector3D& Earth::Node::Position() const
{
    return mPosition;
}

void Earth::Node::SetPosition(const QVector3D& newPosition)
{
    mPosition = newPosition;

    UpdateTransformation();
}

const QVector3D& Earth::Node::Scale() const
{
    return mScale;
}

void Earth::Node::SetScale(const QVector3D& newScale)
{
    mScale = newScale;

    UpdateTransformation();
}

const QMatrix4x4& Earth::Node::Transformation() const
{
    return mTransformation;
}

void Earth::Node::SetTransformation(const QMatrix4x4& newTransformation)
{
    mTransformation = newTransformation;

    mPosition = mTransformation.column(3).toVector3D();
    mRotation = QQuaternion::fromRotationMatrix(mTransformation.normalMatrix());
}

void Earth::Node::UpdateTransformation()
{
    mTransformation.setToIdentity();
    mTransformation.scale(mScale);
    mTransformation.rotate(mRotation);
    mTransformation.setColumn(3, QVector4D(mPosition, 1.0f));
}

3.4 鼠标事件监控

Mouse.h
#ifndef MOUSE_H
#define MOUSE_H

#include <QObject>

namespace Earth
{
    class Mouse
    {
    public:
        Mouse();

        Qt::MouseButton mPressedButton;
        float mX;
        float mY;
        float mZ;
        float mW;
        float mDx;
        float mDy;
        float mDz;
        float mDw;
    };
}


#endif // MOUSE_H

 Mouse.cpp

#include "Mouse.h"

Earth::Mouse::Mouse()
    : mPressedButton(Qt::NoButton)
    , mX(0)
    , mY(0)
    , mZ(0)
    , mW(0)
    , mDx(0)
    , mDy(0)
    , mDz(0)
    , mDw(0)
{}

4 界面窗口代码

Window.h
#ifndef WINDOW_H
#define WINDOW_H

#include <QOpenGLExtraFunctions>
#include <QOpenGLFunctionsPrivate>
#include <QOpenGLWindow>

#include <imgui.h>
#include <QtImGui.h>

namespace Earth
{
    class Controller;

    class Window : public QOpenGLWindow, protected QOpenGLExtraFunctions
    {
        Q_OBJECT
    public:
        Window(QWindow* parent = nullptr);

    private:
        void initializeGL() override;
        void resizeGL(int w, int h) override;
        void paintGL() override;
        void keyPressEvent(QKeyEvent*) override;
        void keyReleaseEvent(QKeyEvent*) override;
        void mousePressEvent(QMouseEvent*) override;
        void mouseReleaseEvent(QMouseEvent*) override;
        void mouseMoveEvent(QMouseEvent*) override;
        void wheelEvent(QWheelEvent*) override;

    private:
        long long mPreviousTime;
        long long mCurrentTime;
        Controller* mController;
    };
}

#endif // WINDOW_H
Window.cpp
#include "Window.h"
#include "Controller.h"

#include <QDateTime>
#include <QKeyEvent>

#include <QDebug>

Earth::Window::Window(QWindow *parent)
    : QOpenGLWindow(QOpenGLWindow::UpdateBehavior::NoPartialUpdate, parent)

{
    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
    format.setMajorVersion(4);
    format.setMinorVersion(3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setSamples(4);
    format.setSwapInterval(1);
    setFormat(format);

    connect(this, &QOpenGLWindow::frameSwapped, this, [=]() { update(); });
}

void Earth::Window::initializeGL()
{
    initializeOpenGLFunctions();

    mCurrentTime = QDateTime::currentMSecsSinceEpoch();
    mPreviousTime = mCurrentTime;

    QtImGui::initialize(this);

    mController = new Controller;
    mController->SetWindow(this);
    mController->Init();
}

void Earth::Window::resizeGL(int w, int h)
{
    glViewport(0, 0, width(), height());

    mController->Resize(w, h);
}

void Earth::Window::paintGL()
{
    mCurrentTime = QDateTime::currentMSecsSinceEpoch();
    float ifps = (mCurrentTime - mPreviousTime) * 0.001f;
    mPreviousTime = mCurrentTime;

    mController->Render(ifps);
}

void Earth::Window::keyPressEvent(QKeyEvent *event)
{
    mController->KeyPressed(event);
}

void Earth::Window::keyReleaseEvent(QKeyEvent *event)
{
    mController->KeyReleased(event);
}

void Earth::Window::mousePressEvent(QMouseEvent *event)
{
    mController->MousePressed(event);
}

void Earth::Window::mouseReleaseEvent(QMouseEvent *event)
{
    mController->MouseReleased(event);
}

void Earth::Window::mouseMoveEvent(QMouseEvent *event)
{
    mController->MouseMoved(event);
}

void Earth::Window::wheelEvent(QWheelEvent *event)
{
    mController->WheelMoved(event);
}

 main.cpp

#include "Window.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Earth::Window w;
    w.resize(1600, 900);
    w.show();

    return app.exec();
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值