1. 程式人生 > >Qt Opengl-widget-practice

Qt Opengl-widget-practice

這次我們將實踐,實現一個線條。在z=0的平面繪製兩條相交直線。

效果如下:

首先,我們做一個Ui,

class OpenglShow : public QOpenGLWidget,protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenglShow(QWidget *parent = nullptr);
    ~OpenglShow(){}

    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);

private:
    // 投影矩陣 pMat
    QMatrix4x4 m_projectMat;
    // viewMat
    QMatrix4x4 m_viewMat;

    // renderItem
    Line *m_lineItem;
};
void OpenglShow::initializeGL()
{
    // opengl方法
    initializeOpenGLFunctions();
    // 深度測試開啟
    glEnable(GL_DEPTH_TEST);

    // 檢視矩陣單位一
    m_viewMat.setToIdentity();
    // 從z軸正1的位置朝向原點,頭的朝向為y軸正方向
    m_viewMat.lookAt(QVector3D(0,0,2), QVector3D(0,0,0), QVector3D(0,1,0));

    // renderItem
    m_lineItem  = new Line(this);
}

void OpenglShow::paintGL()
{
    glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QVector<QVector3D> data;
    data.push_back(QVector3D(-1,0,0));
    data.push_back(QVector3D(1,0,0));
    m_lineItem->updataBuffer(data);
    m_lineItem->render(m_projectMat * m_viewMat);

    data[0].setY(0.5);
    data[1].setY(-0.5);
    m_lineItem->updataBuffer(data);
    m_lineItem->render(m_projectMat * m_viewMat);
}

void OpenglShow::resizeGL(int w, int h)
{
    // 視口即全屏
    glViewport(0, 0, w, h);

    // 投影矩陣單位一
    m_projectMat.setToIdentity();
    // 透視投影  眼角 寬高比 近平面 遠平面 確定視錐體
    m_projectMat.perspective(90.0f, (GLfloat)w/(GLfloat)h, 0.1f, 1000.0f);
}

線條實現:

class Line: public BaseRender
{
    //..... 
private:
    QVector3D m_startVec;
    QVector3D m_endVec;  
}

Line::Line(QObject *parent)
    :BaseRender (parent)
{    
    m_startVec = QVector3D(0, -1, 1);
    m_endVec = QVector3D(0, 1, -1);
    vertexVec.push_back(m_startVec);
    vertexVec.push_back(m_endVec);
    initBuffer();
    initShader();
}

Line::~Line()
{}

void Line::render(QMatrix4x4 vpmat)
{
    // render前資料繫結
    m_buffer.bind();
    m_shaderPro->bind();

    // 啟用屬性ID為0的,即頂點座標
    m_shaderPro->enableAttributeArray(0);
    // 頂點座標傳值
    m_shaderPro->setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
    // 自身modelMat
    QMatrix4x4 modelMat;
    modelMat.setToIdentity();
    // 告知著色器mvp矩陣和顏色
    m_shaderPro->setUniformValue("qt_ModelViewProjectionMatrix",vpmat * modelMat);
    m_shaderPro->setUniformValue("qt_color",QVector4D(0, 0, 0.7, 0.6));

    // 開始draw
    glDrawArrays(GL_LINES, 0, vertexVec.length());

    // 解綁
    m_shaderPro->release();
    m_buffer.release();
}

void Line::updataBuffer(QVector<QVector3D> lineVecs)
{
    vertexVec[0] = lineVecs.at(0);
    vertexVec[1] = lineVecs.at(1);
    m_buffer.bind();
    // glBufferSubData(off ,data, count) 等效於write
    m_buffer.write(0,vertexVec.constData(), sizeof(QVector3D)*2);
    m_buffer.release();
}

void Line::initVertex()
{

}
void Line::initBuffer()
{
    // 頂點資料初始化
    initVertex();
    // buffer設定為DynamicDraw  buffer can be modified more times
    m_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);  
    // buffer的建立,繫結,申請記憶體,釋放 
    m_buffer.create();
    m_buffer.bind();
    m_buffer.allocate(vertexVec.constData(), sizeof(QVector3D)*vertexVec.length());
    m_buffer.release();
}

void Line::initShader()
{
    // 著色器程式 建立,新增頂點和片元著色器,連結,編譯,釋放,和屬性ID
    m_shaderPro = new QOpenGLShaderProgram(this);
    m_shaderPro->addShaderFromSourceFile(QOpenGLShader::Vertex,":/planes.vert");
    m_shaderPro->addShaderFromSourceFile(QOpenGLShader::Fragment,":/planes.frag");
    m_shaderPro->bindAttributeLocation("qt_Vertex",0);
    m_shaderPro->link();
    m_shaderPro->bind();
    m_shaderPro->release();
}

 

頂點著色器,只使用了頂點座標和mvp矩陣

// 著色器程式屬性繫結 ID = 0
attribute vec4 qt_Vertex;
uniform mat4 qt_ModelViewProjectionMatrix;

void main(void)
{
    gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;
}

片元著色器,僅指定顏色,常用來確認頂點座標是否ok

uniform vec4 qt_color;

void main(void)
{
    gl_FragColor = qt_color;
}