首先都知道,在使用QOpenGlwidget窗口来渲染图片时要重写
void initializeGL() Q_DECL_OVERRIDE; void resizeGL(int w, int h) Q_DECL_OVERRIDE; void paintGL() Q_DECL_OVERRIDE;三个关键的函数, 其中initializeGL()在第一次paintGl()之前调用一次,窗口类刚创建的时候不调用。所以刚new 出一个类,在类没有执行一次painGl()之前opengl是没有初始化的,这个时候执行任何opengl的函数都会报错,切记!。
接下来直接放整个类的全部代码。
#ifndef GLSHOWPICTURE_H
#define GLSHOWPICTURE_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QWheelEvent>
class GlShowPicture : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
GlShowPicture(QWidget *parent = nullptr);
~GlShowPicture();
void setPicPath(QString path);
void setImage(const QImage &image);
void wheelEvent(QWheelEvent *event) override;
void mouseMoveEvent(QMouseEvent* e) override;
void mousePressEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;
public slots:
protected:
void initTextures();
void initShaders();
void initializeGL() Q_DECL_OVERRIDE;
void resizeGL(int w, int h) Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
private:
QVector<QVector3D> vertices;
QVector<QVector2D> texCoords;
QOpenGLShaderProgram program;
QOpenGLTexture *texture;
QMatrix4x4 projection;
QString m_picPath;
QImage m_img;
bool dragFlag_;
QPointF dragPos_;
float scaleVal_ =1; // 缩放倍率
bool picChange = true;
};
#endif // GLSHOWPICTURE_H
#include "glshowpicture.h"
GlShowPicture::GlShowPicture(QWidget *parent )
: QOpenGLWidget(parent)
{
qDebug()<<__FUNCTION__<<this;
texture = nullptr;
}
GlShowPicture::~GlShowPicture()
{
qDebug()<<this<<__FUNCTION__;
makeCurrent();
if (texture){
texture->destroy();
}
}
void GlShowPicture::setPicPath(QString path)
{
if(path.isEmpty()) return;
m_img.load(path);
vertices.clear();
// 计算横坐标长度占窗口的比例
float xRadio = m_img.width()*1.0/this->width();
float yRadio = m_img.height()*1.0/this->height();
if(xRadio < yRadio){
//顶点坐标
vertices.append(QVector3D(-1.0*(1.0/yRadio)*xRadio, -1, 1));//左下
vertices.append(QVector3D(1.0*(1.0/yRadio)*xRadio, -1, 1)); //右下
vertices.append(QVector3D(-1.0*(1.0/yRadio)*xRadio, 1, 1)); //左上
vertices.append(QVector3D(1.0*(1.0/yRadio)*xRadio, 1, 1)); //右上
}
else {
//顶点坐标
vertices.append(QVector3D(-1.0, -1*(1.0/xRadio)*yRadio, 1));//左下
vertices.append(QVector3D(1.0, -1*(1.0/xRadio)*yRadio, 1)); //右下
vertices.append(QVector3D(-1.0, 1*(1.0/xRadio)*yRadio, 1)); //左上
vertices.append(QVector3D(1.0, 1*(1.0/xRadio)*yRadio, 1)); //右上
}
// qDebug()<<__FUNCTION__<<xRadio<<yRadio<<vertices;
picChange = true;
update();
}
void GlShowPicture::initTextures()
{
// 加载 Avengers.jpg 图片
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
//重复使用纹理坐标
//纹理坐标(1.1, 1.2)与(0.1, 0.2)相同
texture->setWrapMode(QOpenGLTexture::Repeat);
// //设置纹理大小
// texture->setSize(this->width(), this->height());
//分配储存空间
texture->allocateStorage();
}
void GlShowPicture::initShaders()
{
//纹理坐标
texCoords.append(QVector2D(0, 1)); //左上
texCoords.append(QVector2D(1, 1)); //右上
texCoords.append(QVector2D(0, 0)); //左下
texCoords.append(QVector2D(1, 0)); //右下
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"attribute vec4 vertex;\n"
"attribute vec2 texCoord;\n"
"varying vec2 texc;\n"
"void main(void)\n"
"{\n"
" gl_Position = vertex;\n"
" texc = texCoord;\n"
"}\n";
vshader->compileSourceCode(vsrc);//编译顶点着色器代码
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"precision mediump float;\n" // 添加此行指定float类型的精度为mediump
"uniform sampler2D texture;\n"
"varying vec2 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc);\n"
"}\n";
fshader->compileSourceCode(fsrc); //编译纹理着色器代码
program.addShader(vshader);//添加顶点着色器
program.addShader(fshader);//添加纹理碎片着色器
program.bindAttributeLocation("vertex", 0);//绑定顶点属性位置
program.bindAttributeLocation("texCoord", 1);//绑定纹理属性位置
// 链接着色器管道
if (!program.link()){
close();
}
// 绑定着色器管道
if (!program.bind())
close();
}
void GlShowPicture::initializeGL()
{
qDebug()<<__FUNCTION__<<this;
initializeOpenGLFunctions(); //初始化OPenGL功能函数
glClearColor(0.9, 0.9, 0.9, 1); //设置背景颜色
glEnable(GL_TEXTURE_2D); //设置纹理2D功能可用
initTextures(); //初始化纹理设置
initShaders(); //初始化shaders
}
void GlShowPicture::resizeGL(int w, int h)
{
// 计算窗口横纵比
qreal aspect = qreal(w) / qreal(h ? h : 1);
// 设置近平面值 3.0, 远平面值 7.0, 视场45度
const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
// 重设投影
projection.setToIdentity();
// 设置透视投影
projection.perspective(fov, static_cast<float>(aspect), zNear, zFar);
}
void GlShowPicture::paintGL()
{
qDebug()<<this <<__FUNCTION__;
if(picChange){
// qDebug()<<__FUNCTION__<<this<<texture;
texture->destroy(); //消耗底层的纹理对象
// qDebug()<<__FUNCTION__<<this;
texture->create();
// qDebug()<<__FUNCTION__<<this<<texture;
texture->setData(m_img);
picChange = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕缓存和深度缓冲
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0); //矩阵变换
program.enableAttributeArray(0);
program.enableAttributeArray(1);
program.setAttributeArray(0, vertices.constData());
program.setAttributeArray(1, texCoords.constData());
program.setUniformValue("texture", 0); //将当前上下文中位置的统一变量设置为value
if(texture){
texture->bind(); //绑定纹理
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//绘制纹理
texture->release(); //释放绑定的纹理
}
else {
qDebug()<<__FUNCTION__<<"纹理没有加载";
}
// texture->destroy(); //消耗底层的纹理对象
// texture->create();
}
void GlShowPicture::setImage(const QImage &image)
{
// if(image.isNull()) return;
m_img = image;
vertices.clear();
// 计算横坐标长度占窗口的比例
qDebug()<<__FUNCTION__<<m_img.width()<<m_img.height();
float xRadio = m_img.width()*1.0/this->width();
float yRadio = m_img.height()*1.0/this->height();
if(xRadio < yRadio){
//顶点坐标
vertices.append(QVector3D(-1.0*(1.0/yRadio)*xRadio, -1, 1));//左下
vertices.append(QVector3D(1.0*(1.0/yRadio)*xRadio, -1, 1)); //右下
vertices.append(QVector3D(-1.0*(1.0/yRadio)*xRadio, 1, 1)); //左上
vertices.append(QVector3D(1.0*(1.0/yRadio)*xRadio, 1, 1)); //右上
}
else {
//顶点坐标
vertices.append(QVector3D(-1.0, -1*(1.0/xRadio)*yRadio, 1));//左下
vertices.append(QVector3D(1.0, -1*(1.0/xRadio)*yRadio, 1)); //右下
vertices.append(QVector3D(-1.0, 1*(1.0/xRadio)*yRadio, 1)); //左上
vertices.append(QVector3D(1.0, 1*(1.0/xRadio)*yRadio, 1)); //右上
}
qDebug()<<__FUNCTION__<<xRadio<<yRadio<<vertices;
picChange = true;
update();
}
void GlShowPicture::wheelEvent(QWheelEvent *event) // 滚轮事件
{
if(event->delta() > 0){ // 当滚轮远离使用者时 放大
scaleVal_ *=1.1;
for(int i=0;i<vertices.size();i++){
vertices[i][0] *= 1.1;
vertices[i][1] *= 1.1;
}
}else{ // 当滚轮向使用者方向旋转时 缩小
scaleVal_ *=0.9;
for(int i=0;i<vertices.size();i++){
vertices[i][0] *= 0.9;
vertices[i][1] *= 0.9;
}
}
update();
}
void GlShowPicture::mouseMoveEvent(QMouseEvent* e)
{
if (dragFlag_)
{
int scaledMoveX = e->x()-dragPos_.x();
int scaledMoveY = e->y()-dragPos_.y();
float moveXRadio = scaledMoveX*1.0/this->width();
float moveYRadio = scaledMoveY*1.0/this->height();
for(int i=0;i<vertices.size();i++){
vertices[i][0] += moveXRadio;
vertices[i][1] -= moveYRadio; // y轴是反的
}
dragPos_.setX(e->x());
dragPos_.setY(e->y());
update();
}
}
void GlShowPicture::mousePressEvent(QMouseEvent* e)
{
if(scaleVal_ > 0)
{
dragFlag_ = true;
dragPos_.setX(e->x());
dragPos_.setY(e->y());
}
}
void GlShowPicture::mouseReleaseEvent(QMouseEvent* e)
{
dragFlag_ = false;
}
- setImage和setPicpath是对外的接口,一个传图片路径来显示图片,一个传Qimage对象来显示Qimage
- 通过重写鼠标滚轮事件来进行放大缩小,通过重写鼠标点击事件来进行移动。实现的原理都是通过改变顶点坐标vertices里的值来进行放大缩小和移动。