#include <QObject>
#include <QOpenGLWidget>
#include <GL/gl.h>
#include <GL/glu.h>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QGLWidget>
#include <QImage>

QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);

class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit MyGLWidget(QWidget *parent = 0);

signals:

public slots:
    void initializeGL() Q_DECL_OVERRIDE;
    void resizeGL(int w, int h) Q_DECL_OVERRIDE;
    void paintGL() Q_DECL_OVERRIDE;
    void setBackground(QImage image);
    void initTextures();
    void initShaders();
private:
    QVector<QVector3D> vertices;
    QVector<QVector2D> texCoords;
    QOpenGLShaderProgram program;
    QOpenGLTexture *texture;
    QImage m_image;
    QMatrix4x4 projection;

};

#include "myglwidget.h"
#include <QDebug>
#include <QGraphicsOpacityEffect>
MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}

void MyGLWidget::initTextures()
{

    if(m_image.isNull())
    {
        qDebug("Cannot open the image...");
        QImage dummy(128, 128, QImage::Format_RGB32);//当没找到所需打开的图片时,创建一副128*128大小,深度为32位的位图
        dummy.fill(Qt::green);
        m_image = dummy;
    }
    // 加载 Avengers.jpg 图片
    texture = new QOpenGLTexture(m_image);

    // 设置最近的过滤模式,以缩小纹理
    texture->setMinificationFilter(QOpenGLTexture::Nearest); //滤波

    // 设置双线性过滤模式,以放大纹理
    texture->setMagnificationFilter(QOpenGLTexture::Linear);

    //重复使用纹理坐标
    //纹理坐标(1.1, 1.2)与(0.1, 0.2)相同
    texture->setWrapMode(QOpenGLTexture::Repeat);
}
void MyGLWidget::initShaders()
{
    //纹理坐标
    texCoords.append(QVector2D(0, 1)); //左上
    texCoords.append(QVector2D(1, 1)); //右上
    texCoords.append(QVector2D(0, 0)); //左下
    texCoords.append(QVector2D(1, 0)); //右下
    //顶点坐标
    vertices.append(QVector3D(-1, -1, 1));//左下
    vertices.append(QVector3D(1, -1, 1)); //右下
    vertices.append(QVector3D(-1, 1, 1)); //左上
    vertices.append(QVector3D(1, 1, 1));  //右上
    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 =
            "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 MyGLWidget::initializeGL()
{
    initializeOpenGLFunctions(); //初始化OPenGL功能函数
    glClearColor(0, 0, 0, 0);    //设置背景为黑色
    glEnable(GL_CULL_FACE);
    glEnable(GL_TEXTURE_2D);
    initTextures();
    initShaders();
}

void MyGLWidget::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 MyGLWidget::paintGL()
{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕缓存和深度缓冲
    texture->bind();                                    //绑定纹理
    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);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void MyGLWidget::setBackground(QImage image)
{
    m_image = image;
    update();
}