QtOpenGL--使用OpenGL绘制曲面图(三)

使用QOpenGLWidget绘制3D曲面图

前面两篇文章分别演示了单独在Visual Studio 2015和Qt5.12中,编译运行仓库代码,因为个人经常在Qt环境下开发学习,恰好Qt GUI本身也有对原生opengl的适配和封装,可以使Qt窗口资源直接支持OpenGL的绘制操作,可以拜托对其他第三方库的依赖,因此用起来比较方便,下面将使用QtOpenGL对前面的代码进行“翻译”整理。

1. 环境

  • Windows10, VS2015, Qt5.12.12

2.主要内容

  • 继承QOpenGLWidget,QOpenGLFunctions_4_5_core,直接获得OpenGL接口支持;
  • 对鼠标按压,移动,滚轮事件等进行重写,实现缩放,旋转等交互效果;
  • 利用定时器事件,实现模拟动画效果;

3. 主要代码

  • 自定义类直接继承QOpenGLWidget,QOpenGLFunctions_4_5_core,重写initializeGL(),paintGL(),resizeGL();
void MyGLWidget::initializeGL()
{

    initializeOpenGLFunctions();

    glViewport(0, 0, rect().width(), rect().height());
    glEnable(GL_DEPTH_TEST);

    m_surfacePlotter.generateSurfacePlot(1.0f); // 产生初始数据,用于随后在显存上创建空间

    //! SURFACE PLOT

    // generate surface plot VAO and VBO and EBO
    glGenVertexArrays(1, &m_surfacePlotVAO);
    glGenBuffers(1, &m_surfacePlotVBO);
    glGenBuffers(1, &m_surfacePlotEBO);

    glBindVertexArray(m_surfacePlotVAO);

    // set VBO data
    glBindBuffer(GL_ARRAY_BUFFER, m_surfacePlotVBO);
    glBufferData(GL_ARRAY_BUFFER, m_surfacePlotter.getNumElements()*sizeof(float), m_surfacePlotter.getVertices(), GL_DYNAMIC_DRAW);

    // set EBO data
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfacePlotEBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_surfacePlotter.getNumIndices()*sizeof(uint32_t), m_surfacePlotter.getIndices(), GL_DYNAMIC_DRAW);

    // vertices attributes
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glEnableVertexAttribArray(0);

    //! CUBE

    // generate cube VAO and VBO and EBO
    glGenVertexArrays(1, &m_cubeVAO);
    glGenBuffers(1, &m_cubeVBO);
    glGenBuffers(1, &m_cubeEBO);

    glBindVertexArray(m_cubeVAO);

    // set VBO data
    glBindBuffer(GL_ARRAY_BUFFER, m_cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, 24*sizeof(float), m_surfacePlotter.getCubeVertices(), GL_STATIC_DRAW);

    // set EBO data
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_cubeEBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 24*sizeof(uint32_t), m_surfacePlotter.getCubeIndices(), GL_STATIC_DRAW);

    // vertices attributes
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //! init shaders
    const char* vertexShaderSource
            = "#version 450 core\n"
              "layout (location = 0) in vec3 pos;\n"
              "out vec3 fragPos;\n"
              "uniform mat4 model;\n"
              "uniform mat4 view;"
              "uniform mat4 projection;\n"

              "void main() {\n"
              " gl_Position = projection * view * model * vec4(pos, 1.0);\n"
              "fragPos = pos;\n"
              "}\n";

    const char *fragmentShaderSource
            = "#version 450 core\n"
              "in vec3 fragPos;\n"
              "out vec4 FragColor;\n"
              "uniform float zMin;\n"
              "uniform float zRange;\n"

              "vec4 getColor(float z) {\n"
                  "float startRed = 0.1;\n"
                  "float endRed = 1.0;\n"
                  "float startGreen = 0.3;\n"
                  "float endGreen = 0.2;\n"
                  "float startBlue = 0.7;\n"
                  "float endBlue = 0.0;\n"
                  "float percentFade = (z-zMin)/zRange;\n"
                  "float diffRed = endRed - startRed;\n"
                  "float diffGreen = endGreen - startGreen;\n"
                  "float diffBlue = endBlue - startBlue;\n"
                  "diffRed = (diffRed * percentFade) + startRed;\n"
                  "diffGreen = (diffGreen * percentFade) + startGreen;\n"
                  "diffBlue = (diffBlue * percentFade) + startBlue;\n"
                  "return vec4(diffRed, diffGreen, diffBlue, 1.0);\n"
              "}\n"

              "void main() {\n"
                  "float contrast = 0.6;\n"
                  " FragColor = getColor(fragPos.z);\n"
              "}\n";

    const char* cubeFragmentShaderSource
            = "#version 450 core\n"
              "in vec3 fragPos;\n"
              "out vec4 FragColor;\n"
              "void main() {\n"
              " FragColor = vec4(0.7, 0.7, 0.7, 1.0);\n"
              "}\n";

    m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(m_vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(m_vertexShader);
    checkCompileErrors(m_vertexShader, "VERTEX");

    m_surfaceShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(m_surfaceShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(m_surfaceShader);
    checkCompileErrors(m_surfaceShader,"FRAGMENT");

    m_whiteShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(m_whiteShader, 1, &cubeFragmentShaderSource, NULL);
    glCompileShader(m_whiteShader);
    checkCompileErrors(m_whiteShader, "FRAGMENT");

    m_vertexShaderProgram = glCreateProgram();
    glAttachShader(m_vertexShaderProgram, m_vertexShader);
    glAttachShader(m_vertexShaderProgram, m_surfaceShader);
    glLinkProgram(m_vertexShaderProgram);
    checkCompileErrors(m_vertexShaderProgram, "PROGRAM");

    m_cubeShaderProgram = glCreateProgram();
    glAttachShader(m_cubeShaderProgram, m_vertexShader);
    glAttachShader(m_cubeShaderProgram, m_whiteShader);
    glLinkProgram(m_cubeShaderProgram);
    checkCompileErrors(m_cubeShaderProgram, "PROGRAM");

    glDeleteShader(m_vertexShader);
    glDeleteShader(m_surfaceShader);
    glDeleteShader(m_whiteShader);

}

void MyGLWidget::paintGL()
{
    m_clearColor = {0.05f, 0.18f, 0.25f, 1.0f};
    glClearColor(m_clearColor.r, m_clearColor.g, m_clearColor.b, m_clearColor.alpha);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_surfacePlotter.generateSurfacePlot(1.0f); // 这里保证,每次绘图前,z轴最小值和范围都一致,保证视觉上统一
    int zRange = m_surfacePlotter.getZRange();

    QMatrix4x4 viewMatrix;
    viewMatrix.setToIdentity();
    viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 30.0f), QVector3D(0.0f, 0.0f, 30.0f) + QVector3D(0.0f, 0.0f, -0.5f), QVector3D(1.0f, 0.0f, 0.0f));

    QMatrix4x4 projectionMatrix;
    projectionMatrix.setToIdentity();
    projectionMatrix.perspective(/*45.0f*/mZoom, (float)(rect().width())/(rect().height()), 0.1f, 99999.0f);

    QMatrix4x4 defaultModelMatrix;
    defaultModelMatrix.setToIdentity();
    defaultModelMatrix.translate(0.0f, 0.0f, -0.5f);
    defaultModelMatrix.rotate(45.0f, QVector3D(0.0f, 1.0f, 0.0f));

    glUseProgram(m_vertexShaderProgram);
    glUniform1f(glGetUniformLocation(m_vertexShaderProgram, "zRange"), (zRange == 0) ? 1.0f : zRange);
    glUniform1f(glGetUniformLocation(m_vertexShaderProgram, "zMin"), m_surfacePlotter.getZMin());
    glUniformMatrix4fv(glGetUniformLocation(m_vertexShaderProgram, "view"), 1, GL_FALSE, viewMatrix.data());
    glUniformMatrix4fv(glGetUniformLocation(m_vertexShaderProgram, "projection"), 1, GL_FALSE, projectionMatrix.data());
    glUniformMatrix4fv(glGetUniformLocation(m_vertexShaderProgram, "model"), 1, GL_FALSE, (defaultModelMatrix*mModelMatrix).data());

    glUseProgram(m_cubeShaderProgram);
    glUniformMatrix4fv(glGetUniformLocation(m_cubeShaderProgram, "view"), 1, GL_FALSE, viewMatrix.data());
    glUniformMatrix4fv(glGetUniformLocation(m_cubeShaderProgram, "projection"), 1, GL_FALSE, projectionMatrix.data());
    glUniformMatrix4fv(glGetUniformLocation(m_cubeShaderProgram, "model"), 1, GL_FALSE, (defaultModelMatrix*mModelMatrix).data());

    m_surfacePlotter.generateSurfacePlot(static_cast<float>((elapsedSec%100000)/1000.0)); // 产生新的, 随时间变化的周期性数据,

    glUseProgram(m_vertexShaderProgram);
    glBindVertexArray(m_surfacePlotVAO);
    glBindBuffer(GL_ARRAY_BUFFER, m_surfacePlotVBO);
    glBufferData(GL_ARRAY_BUFFER, m_surfacePlotter.getNumElements()*sizeof(float), m_surfacePlotter.getVertices(), GL_DYNAMIC_DRAW);
    glDrawElements(GL_LINES, m_surfacePlotter.getNumIndices(),GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    glUseProgram(m_cubeShaderProgram);
    glBindVertexArray(m_cubeVAO);
    glBindBuffer(GL_ARRAY_BUFFER, m_cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, 24*sizeof(float), m_surfacePlotter.getCubeVertices(), GL_STATIC_DRAW);
    glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}
  • 重写鼠标事件
void MyGLWidget::mousePressEvent(QMouseEvent *event)
{
    mPreMousePos = event->pos();
    m_bIsMousePressed = true;
}

void MyGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    m_bIsMousePressed = false;
}

void MyGLWidget::mouseMoveEvent(QMouseEvent *event)
{


    if (m_bIsMousePressed) {

        qDebug() << __FUNCTION__ << __LINE__ << event->localPos();
        // get current cursor coordinates
        QPointF currMousePos = event->pos();

        // get points on arcball
        QVector3D va = getArcballVector(mPreMousePos.x(), mPreMousePos.y());
        QVector3D vb = getArcballVector(currMousePos.x(), currMousePos.y());

        float speedFactor = 0.1f;
        float angleOfRotation = speedFactor * acos(std::fmin(1.0f, QVector3D::dotProduct(va, vb)));

        // to get the axis of rotation, need to convert from camera coordinates to world coordinates
        QVector4D axisCamera = QVector3D::crossProduct(va, vb).toVector4D();

        QMatrix4x4 viewMatrix;
        viewMatrix.setToIdentity();
        viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 30.0f), QVector3D(0.0f, 0.0f, 30.0f) + QVector3D(0.0f, 0.0f, -0.5f), QVector3D(1.0f, 0.0f, 0.0f));

        /// FIXED ME: 下面翻译的可能和源代码不么对应!!!
        QMatrix4x4 cameraToModel = (viewMatrix*mModelMatrix).inverted();
        QVector4D axisModel = cameraToModel * axisCamera;

        // update model rotation matrix
        float tolerance = 1e-4;
        if (angleOfRotation > tolerance) {
            mModelMatrix.rotate(angleOfRotation*1000, axisModel.toVector3D());
        }

        // update cursor position
        mPreMousePos = currMousePos;
    }
}

void MyGLWidget::wheelEvent(QWheelEvent *event)
{
    float delta = event->angleDelta().y() * 0.05f;
    mZoom -= delta;

    if(mZoom < 1.0f) {
        mZoom = 1.0f;
    }
    if (mZoom > 100.f) {
        mZoom = 100.0f;
    }
}

4 运行效果演示

在这里插入图片描述

5. 后续

后面将会仔细记录以下,QtOpenGL使用方法,并进一步使用Qt对OpenGL的封装类,如QOpenGLContext ,QOpenGLBuffer,QOpenGLVertexArrayObject,QOpenGLShaderProgram等对原生OpenGL资源封装方便类,来使程序的移植性更好,尤其在涉及嵌入式QT的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值