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.