OpenGL with QtWidgets:练习之扑克翻转

徘徊边缘 提交于 2020-02-04 12:05:57

(本文是LearnOpenGL的学习笔记,教程中文翻译地址https://learnopengl-cn.github.io/

1.实现思路

(本来想拿扑克的正反面来做练习,奈何没找到免费的资源图片,白嫖不易,于是拿两张明星图片来代替了)

代码主要参考教程 "坐标系统" 一节的源码,把顶点坐标和纹理坐标传递给着色器,然后旋转model矩阵就能实现了翻转效果了。

两个纹理的位置是一样的,那么如何区分正反面呢?在OpenGL中默认逆时针的顶点顺序是正面,所以我们把两个面的顶点顺序反一下,一个顺时针一个逆时针就可以了。有了正反面,还需要把背面隐藏起来,OpenGL中有面剔除的功能,我们把背面剔除即可。(参考:https://www.jianshu.com/p/4e165df3ae26

glEnable(GL_CULL_FACE); //开启面剔除
glCullFace(GL_BACK); //剔除背面
//glFrontFace(GL_CW); //逆时针顶点为正面GL_CCW(默认),顺时针GL_CW

解决了正反面问题,还有个问题是如何把两张图分别贴到正反面 。开始我想的是用两个着色器程序分别绘制,但是感觉太麻烦了,索性把两张图合并到一起(这样就只有一个纹理贴图了),用纹理坐标来分割两张图贴到正反面。(如果其中一个图左右翻转了,把纹理坐标x翻转下就可以了)

    //VAO,VBO(一个面两个三角)360/460(应该用qimage获取宽高,这里设定死了)
    const float texture_width=360.0f/460.0f;
    float vertices[] = {
        -texture_width/2, -0.5f, 0.0f,  0.0f, 0.5f, //左下角
        texture_width/2, -0.5f, 0.0f,  1.0f, 0.5f, //右下角
        texture_width/2,  0.5f, 0.0f,  1.0f, 1.0f, //右上角

        texture_width/2,  0.5f, 0.0f,  1.0f, 1.0f, //右上角
        -texture_width/2,  0.5f, 0.0f,  0.0f, 1.0f, //左上角
        -texture_width/2, -0.5f, 0.0f,  0.0f, 0.5f, //左下角

        -texture_width/2, -0.5f, 0.0f,  1.0f, 0.0f, //左下角
        -texture_width/2,  0.5f, 0.0f,  1.0f, 0.5f, //左上角
        texture_width/2,  0.5f, 0.0f,  0.0f, 0.5f, //右上角

        texture_width/2,  0.5f, 0.0f,  0.0f, 0.5f, //右上角
        texture_width/2, -0.5f, 0.0f,  0.0f, 0.0f, //右下角
        -texture_width/2, -0.5f, 0.0f,  1.0f, 0.0f, //左下角
    };

坐标变换的代码用的教程里的方式,只旋转了model矩阵。

    QMatrix4x4 view; //观察矩阵,后退一点
    view.translate(QVector3D(0.0f, 0.0f, -1.5f));
    _shaderProgram.setUniformValue("view", view);
    QMatrix4x4 projection; //透视投影
    projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
    _shaderProgram.setUniformValue("projection", projection);
    QMatrix4x4 model;//模型矩阵
    model.rotate(_rotate, QVector3D(0.0f, 1.0f, 0.0f));
    _shaderProgram.setUniformValue("model", model);

(代码里没有堆叠的问题,所以可以不用深度缓冲,我复制粘贴没去掉)

2.实现代码

(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git

我的GLTestPoker类实现效果(左边图片右边GIF):

GLTestPoker类代码:

#ifndef GLTESTPOKER_H
#define GLTESTPOKER_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QTimer>

class GLTestPoker : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    explicit GLTestPoker(QWidget *parent = nullptr);
    ~GLTestPoker();

protected:
    //设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
    void initializeGL() override;
    //渲染OpenGL场景,每当需要更新小部件时使用
    void paintGL() override;
    //设置OpenGL视口、投影等,每当尺寸大小改变时调用
    void resizeGL(int width, int height) override;

private:
    //着色器程序
    QOpenGLShaderProgram _shaderProgram;
    //顶点数组对象
    QOpenGLVertexArrayObject _vao;
    //顶点缓冲
    QOpenGLBuffer _vbo;
    //纹理(因为不能赋值,所以只能声明为指针)
    QOpenGLTexture *_texture = nullptr;
    //定时器,做动画效果
    QTimer *_timer=nullptr;
    //旋转角度
    int _rotate=0;
};

#endif // GLTESTPOKER_H
#include "GLTestPoker.h"

GLTestPoker::GLTestPoker(QWidget *parent)
    : QOpenGLWidget(parent)
{
    _timer=new QTimer(this);
    connect(_timer,&QTimer::timeout,this,[this](){
        _rotate+=2;
        update();
    });
    _timer->setInterval(50);
}

GLTestPoker::~GLTestPoker()
{
    makeCurrent();
    _vbo.destroy();
    _vao.destroy();
    delete _texture;
    doneCurrent();
}

void GLTestPoker::initializeGL()
{
    //为当前上下文初始化OpenGL函数解析
    initializeOpenGLFunctions();

    //着色器代码
    //in输入,out输出,uniform从cpu向gpu发送
    //因为OpenGL纹理颠倒过来的,所以取反vec2(aTexCoord.x, 1-aTexCoord.y);
    const char *vertex_str=R"(#version 330 core
                           layout (location = 0) in vec3 aPos;
                           layout (location = 1) in vec2 aTexCoord;
                           out vec2 TexCoord;
                           uniform mat4 model;
                           uniform mat4 view;
                           uniform mat4 projection;

                           void main()
                           {
                           gl_Position = projection * view * model * vec4(aPos, 1.0f);
                           TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
                           })";
    const char *fragment_str=R"(#version 330 core
                             in vec2 TexCoord;
                             uniform sampler2D texture1;

                             void main()
                             {
                             gl_FragColor = texture(texture1, TexCoord);
                             })";

    //将source编译为指定类型的着色器,并添加到此着色器程序
    if(!_shaderProgram.addCacheableShaderFromSourceCode(
                QOpenGLShader::Vertex,vertex_str)){
        qDebug()<<"compiler vertex error"<<_shaderProgram.log();
    }
    if(!_shaderProgram.addCacheableShaderFromSourceCode(
                QOpenGLShader::Fragment,fragment_str)){
        qDebug()<<"compiler fragment error"<<_shaderProgram.log();
    }
    //使用addShader()将添加到该程序的着色器链接在一起。
    if(!_shaderProgram.link()){
        qDebug()<<"link shaderprogram error"<<_shaderProgram.log();
    }

    //VAO,VBO(一个面两个三角)宽高比360/460
    const QImage img_temp(":/liuyifei.png");
    //上下两张图,所以高度除以二
    const float texture_width=1.0f*img_temp.width()/(img_temp.height()/2);
    float vertices[] = {
        -texture_width/2, -0.5f, 0.0f,  0.0f, 0.5f, //左下角
        texture_width/2, -0.5f, 0.0f,  1.0f, 0.5f, //右下角
        texture_width/2,  0.5f, 0.0f,  1.0f, 1.0f, //右上角

        texture_width/2,  0.5f, 0.0f,  1.0f, 1.0f, //右上角
        -texture_width/2,  0.5f, 0.0f,  0.0f, 1.0f, //左上角
        -texture_width/2, -0.5f, 0.0f,  0.0f, 0.5f, //左下角

        -texture_width/2, -0.5f, 0.0f,  1.0f, 0.0f, //左下角
        -texture_width/2,  0.5f, 0.0f,  1.0f, 0.5f, //左上角
        texture_width/2,  0.5f, 0.0f,  0.0f, 0.5f, //右上角

        texture_width/2,  0.5f, 0.0f,  0.0f, 0.5f, //右上角
        texture_width/2, -0.5f, 0.0f,  0.0f, 0.0f, //右下角
        -texture_width/2, -0.5f, 0.0f,  1.0f, 0.0f, //左下角
    };
    _vao.create();
    _vao.bind();
    _vbo=QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    _vbo.create();
    _vbo.bind();
    _vbo.allocate(vertices,sizeof(vertices));

    // position attribute
    int attr = -1;
    attr = _shaderProgram.attributeLocation("aPos");
    //setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
    _shaderProgram.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);
    _shaderProgram.enableAttributeArray(attr);
    // texture coord attribute
    attr = _shaderProgram.attributeLocation("aTexCoord");
    _shaderProgram.setAttributeBuffer(attr, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5);
    _shaderProgram.enableAttributeArray(attr);

    // texture 1
    //直接生成绑定一个2d纹理, 并生成多级纹理MipMaps
    _texture = new QOpenGLTexture(img_temp, QOpenGLTexture::GenerateMipMaps);
    if(!_texture->isCreated()){
        qDebug() << "Failed to load texture";
    }
    // set the texture wrapping parameters
    // 等于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    _texture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
    _texture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
    // set texture filtering parameters
    //等价于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    _texture->setMinificationFilter(QOpenGLTexture::Linear);
    _texture->setMagnificationFilter(QOpenGLTexture::Linear);

    _shaderProgram.bind();
    _shaderProgram.setUniformValue("texture1", 0);
    _shaderProgram.release();

    _timer->start();
}

void GLTestPoker::paintGL()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    //清除深度缓冲
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Z缓冲(Z-buffer),深度缓冲(Depth Buffer)。
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_CULL_FACE); //剔除
    glCullFace(GL_BACK); //剔除背面
    //glFrontFace(GL_CW); //默认逆时针顶点为正面GL_CCW

    glActiveTexture(GL_TEXTURE0);
    _texture->bind();

    _shaderProgram.bind();
    _vao.bind();

    QMatrix4x4 view; //观察矩阵,后退一点
    view.translate(QVector3D(0.0f, 0.0f, -1.5f));
    _shaderProgram.setUniformValue("view", view);
    QMatrix4x4 projection; //透视投影
    projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
    _shaderProgram.setUniformValue("projection", projection);
    QMatrix4x4 model;//模型矩阵
    model.rotate(_rotate, QVector3D(0.0f, 1.0f, 0.0f));
    _shaderProgram.setUniformValue("model", model);

    glDrawArrays(GL_TRIANGLES, 0, 12);

    _vao.release();
    _texture->release();
    _shaderProgram.release();
}

void GLTestPoker::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);
}

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!