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

Qt OpenGL–QOpenGLWidget绘制曲面图

前几篇博客中介绍了,使用原生Opengl接口,分别在VS2015环境中借助GLFW提供的窗口,展示了如何绘制曲面,以及在VS2015下,借助Qt5提供的窗口类实现同样的曲面绘制。本篇就使用Qt GUI模块中,封装的QOpenGLWidget,以及封装的方便类,如QOpenGLFunctions, QOpenGLShaderProgram等,实现同样的效果,可以发现Qt封装的类确实挺好用。

1. 环境

  • Windows 10, VS2019 community
  • Qt5.15

2. 主要代码

代码仍然集中在,那三个重写的函数:

  • initializeGL();
  • resizeGL();
  • repaintGL().
void Widget::initializeGL()
{
    initializeOpenGLFunctions();

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

    // generate default surface plot
    m_surfacePlotter.generateSurfacePlot(1.0f);

    // generate surface plot VAO, VBO, EBO
    mSurfaceVAO.create();
    mSurfaceVBO.create();
    mSurfaceEBO.create();

    // bind VAO
    mSurfaceVAO.bind();
    // SET VBO DATA
    mSurfaceVBO.bind();
    mSurfaceVBO.allocate(m_surfacePlotter.getVertices(), m_surfacePlotter.getNumElements() * sizeof(float));
    // set EBO data
    mSurfaceEBO.bind();
    mSurfaceEBO.allocate(m_surfacePlotter.getIndices(), m_surfacePlotter.getNumIndices()*sizeof(uint32_t));

    mSurfaceVBO.release();
    mSurfaceEBO.release();
    mSurfaceVAO.release();

    // generate cube VAO, VBO, EBO
    mCubeVAO.create();
    mCubeVBO.create();
    mCubeEBO.create();

    // bind cube VAO
    mCubeVAO.bind();
    // set VBO data
    mCubeVBO.bind();
    mCubeVBO.allocate(m_surfacePlotter.getCubeVertices(), 24*sizeof(float));
    // set EBO data
    mCubeEBO.bind();
    mCubeEBO.allocate(m_surfacePlotter.getCubeIndices(), 24*sizeof(uint32_t));

    mSurfaceVBO.release();
    mSurfaceEBO.release();
    mSurfaceVAO.release();

    //! init shaders

    // Compile vertex shader
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertexShader.vs");
    // Compile fragment shader
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragmentShader.fs");
    // Link shader pipeline
    shaderProgram.link();
    // Bind shader pipeline for use
    shaderProgram.bind();

    cubeShaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertexShader.vs");
    cubeShaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/whiteFragmentShader.fs");
    cubeShaderProgram.link();
    cubeShaderProgram.bind();// 将渲染程序绑定到当前上下文,任何之前的绑定将被释放

    mBasicTimer.start(12, this);
}

可以看到其中QOpenGLBuffer,QOpenGLVertexArrayObject,QOpenGLShaderProgram几个类的使用,方便了对资源的管理,总体感觉很清爽。

void Widget::resizeGL(int w, int h)
{
    // Calculate aspect ratio
    qreal aspect = qreal(w) / qreal(h ? h : 1);

    // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
    const qreal zNear = 0.1f, zFar = 99999.0f, fov = 45.0;

    // Reset projection
    m_proj.setToIdentity();

    // Set perspective projection
    m_proj.perspective(fov, aspect, zNear, zFar);

}

在resizeGL()中主要可以建立视口转换和投影变换等。

void Widget::paintGL()
{
    float currTime = QDateTime::currentSecsSinceEpoch();
    m_deltaTime = currTime - m_prevTime;
    m_prevTime = currTime;

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_surfacePlotter.generateSurfacePlot(1.0f);
    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, (float)(rect().width())/(rect().height()), 0.1f, 99999.0f);

    QMatrix4x4 modelMatrix;
    modelMatrix.setToIdentity();
    modelMatrix.translate(0.0, 0.0, -5.0);
    modelMatrix.rotate(45.0f, QVector3D(0.0f, 1.0f, 0.0f));
//    modelMatrix.rotate(mRotation);

    shaderProgram.bind();
    shaderProgram.setUniformValue("zRange", (zRange == 0) ? 1.0f : zRange);
    shaderProgram.setUniformValue("zMin", m_surfacePlotter.getZMin());
    shaderProgram.setUniformValue("view", viewMatrix);
    shaderProgram.setUniformValue("projection", m_proj);
    shaderProgram.setUniformValue("model", modelMatrix);

    /// render surface
    m_surfacePlotter.generateSurfacePlot(static_cast<float>(elapsedSec/1000.0));

    mSurfaceVAO.bind();
    mSurfaceVBO.bind();
    mSurfaceEBO.bind();

    // populate new data, 再次allocate,会把buffer中之前的内容删除
    mSurfaceVBO.allocate(m_surfacePlotter.getVertices(), m_surfacePlotter.getNumElements() * sizeof (float));

    // Tell OpenGL programmable pipeline how to locate vertex position data
    int vertexLocation = shaderProgram.attributeLocation("pos");
    shaderProgram.enableAttributeArray(vertexLocation);
    shaderProgram.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 3*sizeof(float));
    glDrawElements(GL_LINES, m_surfacePlotter.getNumIndices(), GL_UNSIGNED_INT, 0);

    cubeShaderProgram.bind(); /// 再次bind()会释放之间的bind()的着色器程序
    cubeShaderProgram.setUniformValue("view", viewMatrix);
    cubeShaderProgram.setUniformValue("projection", m_proj);
    cubeShaderProgram.setUniformValue("model", modelMatrix);

    /// render cube
    mCubeVAO.bind();
    mCubeVBO.bind();
    mCubeEBO.bind();

    int vertexLoc = cubeShaderProgram.attributeLocation("pos");
    cubeShaderProgram.enableAttributeArray(vertexLoc);
    cubeShaderProgram.setAttributeBuffer(vertexLoc, GL_FLOAT, 0, 3, 3*sizeof(float));
    glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);

}

paintGL()仍然是绘制内容的主要战场.

3. 效果展示

在这里插入图片描述

4. 不足之处

这里的代码,我并没有实现好鼠标和键盘对图像的交互操作,如缩放,移动,旋转等,这其中涉及到对model-view-projection的准确理解,以及QMatrix4x4,QQuaternion的使用,还不能游刃有余。

5. 后续

继续加强对Qt中所封装的类理解和使用。从零开始造个轮子真不简单,下篇分享一个轮子QwtPlot3D.

绘制曲面柱体的代码: ```c // 绘制曲面柱体 void drawSurfaceCylinder(float height, float radius, int slices, int stacks) { float x = 0.0f, y = 0.0f, z = 0.0f; float theta = 0.0f, phi = 0.0f; float theta_step = 2 * M_PI / slices; float phi_step = M_PI / stacks; float texture_step_s = 1.0f / slices; float texture_step_t = 1.0f / stacks; float texture_s = 1.0f, texture_t = 1.0f; float texture_s_step = -2.0f / slices; float texture_t_step = -1.0f / stacks; float radius_step = radius / stacks; float radius_curr = radius; glBegin(GL_TRIANGLE_STRIP); for (int j = 0; j <= stacks; j++) { x = radius_curr; z = 0.0f; for (int i = 0; i <= slices; i++) { glTexCoord2f(texture_s, texture_t); glNormal3f(x / radius_curr, y / radius_curr, z / radius_curr); glVertex3f(x, y, z); x = radius_curr * cos(theta); z = radius_curr * sin(theta); glTexCoord2f(texture_s, texture_t - texture_step_t); glNormal3f(x / radius_curr, y / radius_curr, z / radius_curr); glVertex3f(x, y + height / stacks, z); theta += theta_step; texture_s += texture_step_s; } y += height / stacks; radius_curr -= radius_step; phi += phi_step; theta = 0.0f; texture_s = 1.0f; texture_t += texture_t_step; } glEnd(); } ``` 绘制圆柱体一半的代码: ```c // 绘制圆柱体一半 void drawHalfCylinder(float height, float radius, int slices) { float x = 0.0f, y = 0.0f, z = 0.0f; float theta = 0.0f; float theta_step = 2 * M_PI / slices; glBegin(GL_TRIANGLE_STRIP); for (int i = 0; i <= slices; i++) { x = radius * cos(theta); z = radius * sin(theta); glNormal3f(x / radius, y / radius, z / radius); glVertex3f(x, y, z); glNormal3f(x / radius, y / radius, z / radius); glVertex3f(x, y + height, z); theta += theta_step; } glEnd(); } ``` 这两个函数都需要在程序中包含math.h和GL/glut.h头文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值