QT中的OpenGL学习-----3D图形

一、3D坐标系

记住V_clip = M_projection * M_view * M_model * V_local就行,可以在顶点着色器里面添加位置信息:

#version 330 core
layout (location = 2) in vec3 aPos;//location属性位置有16个
layout (location = 3) in vec3 aColor;
layout (location = 1) in vec2 aTextureCoordinates;
out vec3 ourColor;
out vec2 TextureCoordinates;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ourColor = aColor;
    TextureCoordinates = aTextureCoordinates;
}

片段着色器不用改变:

#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TextureCoordinates;
uniform sampler2D textureWall;
uniform sampler2D textureLe;
uniform sampler2D textureSmall;
uniform sampler2D texturePJ;
uniform float ratio;
void main()
{
    FragColor = mix(texture(textureWall, TextureCoordinates), texture(textureLe, TextureCoordinates), ratio);
}

二、透视投影

这方面可以恶补下线性代数知识。

三、代码实现

3.1 构建一个正方体的点

        在OpenGL里面,三角形是基础图形,正方体6个面,每一个面都是两个三角形拼接而成,所以点需要2×3×6=36个顶点坐标,在坐标的数组里面可以放入颜色、纹理等信息,后面调用解析的时候注意步长和位置即可。

        在编写顶点的时候,只要能连起来是个正方体即可,不同的点做原点都可以,但是一定要连起来,纹理是2个数来表示X和Y,和顶点坐标没有关系。下面是构建的一个例子:

float vertices[] =
{
    //positions //texture coordinates
    //正面
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    -0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    //背面
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    //左面
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    -0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    -0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //右面
    0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //上面
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //下面
    -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 1.0f
};

3.2 画出正方体

注意要给M_projection * M_view * M_model这三个矩阵,写在外面也可以,但是多个3D图形的话model需要初始化,不然除了第一次的图形是单位矩阵,for循环后面的都不是。

void OpenGLWidget::paintGL()
{
    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    unsigned int time = QTime::currentTime().msec();
    //先旋转再位移,图片在固定位置旋转
    //先位移再旋转,图片围着一个地方转
    //具体参考线性代数矩阵知识
//    model.rotate(time, 1.0f, 5.0f, 0.5f);//旋转
    view.translate(0.0, 0.0, -3);
    projection.perspective(45, (float)width()/height(), 0.1, 100);
    m_shaderProgram.setUniformValue("projection", projection);


    //red:0.0f无色,1.0f全红
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);//加了立方体可以封面
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_shaderProgram.bind();//绑定可以理解为使用它
    m_shaderProgram.setUniformValue("ratio", ratio);
    glBindVertexArray(VAO);

    switch (m_shape)
    {
    case Rect:
        //纹理绑定
        textureLe->bind(1);
        textureWall->bind(0);

        //图形绘画
        //画之前设置3D图形
        //V_clip = M_projection * M_view * M_model * V_local
        m_shaderProgram.setUniformValue("view", view);
        foreach(auto item, cubePositions)
        {
            model.setToIdentity();//初始化
            model.translate(item);//位移
            model.rotate(time, 1.0f, 5.0f, 0.5f);//旋转
            m_shaderProgram.setUniformValue("model", model);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

        break;
    default:
        break;
    }
}

3.3 完整代码

#include "openglwidget.h"

unsigned int VBO, VAO, EBO;
float ratio = 0.5;
float vertices[] =
{
    //positions //texture coordinates
    //正面
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    -0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    //背面
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    //左面
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    -0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    -0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //右面
    0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
    0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //上面
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
    -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
    //下面
    -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
    0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f, 0.0f, 1.0f
};

unsigned int indices[] = //note that we start from 0!
{
    0, 1, 3,//第一个三角形
    1, 2, 3 // 第二个三角形
};

//多个3D图形,建立位移数组
QVector<QVector3D> cubePositions =
{
    //X,Y,Z
    QVector3D(0.0f, 0.0f, 0.0f),
    QVector3D(2.0f, 5.0f, -15.0f),
    QVector3D(-1.5f, -2.2f, -2.5f),
    QVector3D(-3.8f, -2.0f, -12.3f),
    QVector3D(2.4f, -0.4f, -3.5f),
    QVector3D(-1.7f, 3.0f, -7.5f),
    QVector3D(1.3f, -2.0f, -2.5f),
    QVector3D(1.5f, 2.0f, -2.5f),
    QVector3D(1.5f, 0.2f, -1.5f),
    QVector3D(-1.3f, 1.0f, -1.5f)
};

OpenGLWidget::OpenGLWidget(QWidget* parent) : QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    m_timer = new QTimer;
    m_timer->start(100);
    connect(m_timer, &QTimer::timeout, this, [=](){
        update();
    });
}

OpenGLWidget::~OpenGLWidget()
{
    makeCurrent();
    glDeleteBuffers(1,&VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteVertexArrays(1,&VAO);
    doneCurrent();
}

void OpenGLWidget::drawShape(Shape shape)
{
    m_shape = shape;
    update();
}

void OpenGLWidget::setWireframe(bool wireframe)
{
    makeCurrent();
    if (wireframe)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }
    else
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    update();
    doneCurrent();
}

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    //使用QT封装的着色器
    bool success;
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/shapes.vert");//顶点着色器
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/shapes.frag");//片段着色器
    success = m_shaderProgram.link();//连接到着色器
    if(!success)
        qDebug() << "ERR:" << m_shaderProgram.log();

    //创建VBO和VAO对象,并赋予ID
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    //绑定VBO和VAO对象
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    //为当前绑定到target的缓冲区对象创建一个新的数据存储
    //如果data不是NULL,则使用来自此指针的数据初始化数据存储
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //访问着色器里面属性的位置
    m_shaderProgram.bind();
    GLint posLocation = m_shaderProgram.attributeLocation("aPos");//询问着色器里面变量aPos顶点信息的属性位置
    GLint textureLocation = m_shaderProgram.attributeLocation("aTextureCoordinates");

    //告知显卡如何解析缓冲里的顶点属性值
    glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

    //告知显卡如何解析缓冲里的纹理属性值
    glVertexAttribPointer(textureLocation, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

    //开启VAO管理的顶点属性值
    glEnableVertexAttribArray(posLocation);

    //开启VAO管理的纹理属性值
    glEnableVertexAttribArray(textureLocation);

    //EBO创建与绑定
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //纹理
    textureWall = new QOpenGLTexture(QImage(":/imags/imag/1.jpg").mirrored());
    textureLe = new QOpenGLTexture(QImage(":/imags/imag/2.jpg").mirrored());
    textureSmall = new QOpenGLTexture(QImage(":/imags/imag/3.jpg").mirrored());
    texturePJ = new QOpenGLTexture(QImage(":/imags/imag/4.jpg").mirrored());
    //设置纹理单元,纹理单元有16个
    m_shaderProgram.bind();
    m_shaderProgram.setUniformValue("textureWall", 0);
    m_shaderProgram.setUniformValue("textureLe", 1);
    m_shaderProgram.setUniformValue("textureSmall", 2);
    m_shaderProgram.setUniformValue("texturePJ", 3);

    //设置渐远纹理
    texturePJ->generateMipMaps();

    //纹理绑定
    textureLe->bind(1);
    textureWall->bind(0);

    //VAO和VBO绑定为0,相当于释放休息
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    QMatrix4x4 projection;
    projection.perspective(45, (float)width()/height(), 0.1, 100);
    m_shaderProgram.setUniformValue("projection", projection);
}

void OpenGLWidget::resizeGL(int w, int h)
{
    Q_UNUSED(h)
    Q_UNUSED(w)
}

void OpenGLWidget::paintGL()
{
    QMatrix4x4 model;
    QMatrix4x4 view;

    unsigned int time = QTime::currentTime().msec();
    //先旋转再位移,图片在固定位置旋转
    //先位移再旋转,图片围着一个地方转
    //具体参考线性代数矩阵知识
//    model.rotate(time, 1.0f, 5.0f, 0.5f);//旋转
    view.translate(0.0, 0.0, -3);


    //red:0.0f无色,1.0f全红
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);//加了立方体可以封面
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_shaderProgram.bind();//绑定可以理解为使用它
    m_shaderProgram.setUniformValue("ratio", ratio);
    glBindVertexArray(VAO);

    switch (m_shape)
    {
    case Rect:
        //纹理绑定
        textureLe->bind(1);
        textureWall->bind(0);

        //图形绘画
        //画之前设置3D图形
        //V_clip = M_projection * M_view * M_model * V_local
        m_shaderProgram.setUniformValue("view", view);
        foreach(auto item, cubePositions)
        {
            model.setToIdentity();//初始化
            model.translate(item);//位移
            model.rotate(time, 1.0f, 5.0f, 0.5f);//旋转
            m_shaderProgram.setUniformValue("model", model);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

        break;
    default:
        break;
    }
}

void OpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    case Qt::Key_Up:
        ratio += 0.1;
        break;
    case Qt::Key_Down:
        ratio -= 0.1;
        break;
    default:
        break;
    }
    if(ratio>1) ratio = 1;
    if(ratio<0) ratio = 0;

    m_shaderProgram.bind();
    m_shaderProgram.setUniformValue("ratio", ratio);
    update();
}



详细代码看压缩文件。

3.4 学习地址

5-3代码实现3D效果_哔哩哔哩_bilibili

四、效果展示

3D旋转跳跃画圈圈

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值