超详细讲解。QT+OpenGL画出不同纹理面立方体(部分面可反色)

是自己来CSDN的第一篇文章。据说CSDN下载代码文件还要收积分,能看这个问题的多半是像我一样学生刚开始学,坚决不直接放文件收费!都是学生不容易!最近刚开始学openGL,想着怎么能画出旋转立方体,每个面可以有不同纹理,这里只用了三个照片,其中两张混合在一起(mix)作为一面(LearnopenGL课程中纹理单元那一节,忘记可以看看),此面在立方体中占了四个面,可以反色。另外一张是单独的作为一面,占有立方体剩下两面。如果想每个面都不一样,都是相同的道理,后面会讲。因为初学者,有一些可能理解有错,欢迎大家指出。学习是跟着Bilibili学的,所以代码很多部分是直接用了bilibili AXB老师的,在此感谢!(B站搜一个qtopengl就能搜出来)

大致效果图就是这样子(笑脸占了两面,剩下的反色的图片占了四面),

        

大致思路:

1.先自己写36*5个点,36是因为画6个面,每个面用6个点(两个三角形,这里没有用索引),然后5个点是前三个代表三角形的点坐标,后两个是纹理坐标。然后建立VAO,VBO,再分别绑定,刚开始想着用两个VAO和VBO,然后再用两个vertice数组,把笑脸的那两个放到一个vertice里,然后把那个混合照片的放另外一个数组里,但后来一想,不需要那样子做,用一个vertice完全可以,直接到时候VAO里的指针全部识别,在最后draw函数之前调用一下shederprogram程序就可以(这块刚开始学没懂的话后面也会详细讲)

2.创建两个着色器程序,一个就叫做shaderprogram,一个叫做shaderprogram1,后面简写SS1。然后这俩加入源文件,再链接一下,就算激活着色器程序了。然后使用的时候,切记切记,一定要bind一下!!!我就是因为没有bind S1,然后莫名改了老半天,甚至怀疑之前学的是不是全部理解错了QAQ。这时候我们设一个QOpenGLtexture指针的值,把图片加载进去,并且用下mirror函数(镜像),不然Y坐标是负的,再bind一下,然后开始给着色器设置,使用setuniformvalue就行,默认的是0,然后比如混合图片多个纹理单元的话就再1 2 3.这里相当于给片段着色器里的采样器Sampler赋值,让他们区别开,跟你texture指针目前还没啥关系,后面绘画前才会开始真正的绑定。并且发现比较狗的一点是,如果你赋值时给textwall赋值,不小心写成了textwllll之类的,那边不会报错,而会将错就错的显示一张图片。然后开始用S1着色器程序,也是同理,S1因为是新的着色器程序,执行了新的着色器函数,所以setuniformvalue也是从0开始。

3.这一切完成后开始用3D空间知识了,这里不详细讲了,B站AXB老师和learnopenGL课程上讲的很明白,就是三个矩阵,model,view,projection。projection可以直接固定写法,view是观察矩阵,就是看你摄像机放在哪里了,然后记住view矩阵最后一个设置成负数,因为相当于从摄像机开始往前看,你的图像是Z轴负半轴,相当于正半轴从电脑屏幕指向你,而你看电脑屏幕时,以你为原点,电脑屏幕就是负半轴了。model矩阵的话,也很容易,进行translate和rotate操作,这里我们让他随时间改变,设个时间槽函数。

4.接下来万事俱备,只欠东风。开始调用,记住一定有三个bind,一个是VAO的bind,一个是着色器程序S的绑定,一个是着色器程序S1的绑定。相当于你刚开始用了VAO把数组vertice里传进去,不同的着色器程序绑定决定你去执行不同的代码。我们画的时候说清楚起始和结束点就可以了。比如bind vao后,我S绑定一下,然后QOpenGLtexture绑定某个值,让指针对象和之前赋值好的Sampler真正对应。这时我们开始绘画,你想画哪一部分的面就用drawarray函数,因为shader texture和vao不bind在一起的时候没啥关联,draw之前bind一下,就可以画你想要的效果了。

整体结构图

 

下面就是代码部分了

首先是头文件部分,第一个是我们新建的class,第二个是mianwindow.h

#ifndef AXBOPENGLWIDGET_H
#define AXBOPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTimer>
class AXBOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    enum Shape{None,Rect,Circle,Triangle};
    explicit AXBOpenGLWidget(QWidget *parent = nullptr);
    ~AXBOpenGLWidget();

    void drawShape(Shape shape);
    void setWirefame(bool wireframe);
protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
    void keyPressEvent(QKeyEvent *event);
signals:

public slots:
    void on_timeout();
private:
    Shape m_shape;
    QOpenGLShaderProgram shaderProgram;
    QOpenGLShaderProgram shaderProgram1;
    QOpenGLTexture * textureWall;
    QOpenGLTexture * textureSmile;
    QOpenGLTexture * textureSmall;
    QTimer timer;
};



#endif // AXBOPENGLWIDGET_H
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_actDrawRect_triggered();

    void on_actClear_triggered();

    void on_actWireframe_triggered();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

然后就是CPP了

#include "axbopenglwidget.h"
#include <QTime>
#include<math.h>
unsigned int VBO, VAO,EBO;
unsigned int VBO1, VAO1,EBO1;
float vertices[] = {
-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, 1.0f, 0.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, 1.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, 0.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, 1.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, 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, 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,


};

QVector<QVector3D> cubePositions= {
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)*/
};
float vertices1[]={

    -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
};


unsigned int indices[] = { // note that we start from 0!
                           0, 1, 3, // first triangle
                           1, 2, 3 // second triangle
                         };
float ratio=0.5;
AXBOpenGLWidget::AXBOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    connect(&timer,SIGNAL(timeout()),this,SLOT(on_timeout()));
    timer.start(100);
}

AXBOpenGLWidget::~AXBOpenGLWidget()
{
    makeCurrent();
    glDeleteBuffers(1,&VBO);
    glDeleteBuffers(1,&EBO);
    glDeleteVertexArrays(1,&VAO);
    //glDeleteBuffers(1,&VBO1);
    //glDeleteBuffers(1,&EBO1);
    //glDeleteVertexArrays(1,&VAO1);
    doneCurrent();
}

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

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

void AXBOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    bool success;
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    success=shaderProgram.link();
    if(!success)
        qDebug()<<"ERR:"<<shaderProgram.log();

    shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes_1.vert");
    shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes_1.frag");
    success=shaderProgram1.link();
    if(!success)
        qDebug()<<"ERR:"<<shaderProgram1.log();



    textureWall=new QOpenGLTexture(QImage(":/images/images/wall.jpg").mirrored());
    textureSmile=new QOpenGLTexture(QImage(":/images/images/smile.jpeg").mirrored());
//bind以后然后赋值
    shaderProgram.bind();
    shaderProgram.setUniformValue("textureSmile",1);
    shaderProgram.setUniformValue("textureWall",0);

    QMatrix4x4 projection;
    projection.perspective(45,(float)width()/height(),0.1,100);
    shaderProgram.setUniformValue("projection", projection);
    glBindVertexArray(0);
//mirror是为了镜像,前面有讲
    textureSmall=new 
    QOpenGLTexture(QImage(":/images/images/awesomeface.png").mirrored());
    shaderProgram1.bind();
    shaderProgram1.setUniformValue("textureSmall",0);
    shaderProgram1.setUniformValue("projection", projection);
    glBindVertexArray(0);

}

void AXBOpenGLWidget::resizeGL(int w, int h)
{
    //Q_UNUSED(w);Q_UNUSED(h);
    //glViewport(0, 0, w, h);
}

void AXBOpenGLWidget::paintGL()
{
    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 model1;
    QMatrix4x4 view1;
    unsigned int time=QTime::currentTime().msec();

    view.translate(0.0,0.0,-3);
    view1.translate(0.0,0.0,-3);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shaderProgram.bind();
    shaderProgram.setUniformValue("ratio",ratio);
    shaderProgram.setUniformValue("view", view);
    model.setToIdentity();
    model.translate(cubePositions[0]);
    model.rotate(time, 1.0f, 1.0f, 1.0f);
    shaderProgram.setUniformValue("model", model);
    shaderProgram1.bind();
    shaderProgram1.setUniformValue("ratio",ratio);
    shaderProgram1.setUniformValue("view", view);
    shaderProgram1.setUniformValue("model", model);

    switch (m_shape) {
    case Rect:
        glBindVertexArray(VAO);
       shaderProgram1.bind();
       textureSmall->bind(0);
       glDrawArrays(GL_TRIANGLES,0,24);
//bind后可以知道执行哪一段着色器程序了,然后画出相应的点就可以了
        shaderProgram.bind();
        textureWall->bind(0);
        textureSmile->bind(1);
        glDrawArrays(GL_TRIANGLES,24,36);


        break;
    }
}
//这个是特效,按键盘上下键,两张混合图效果不同
#include <QKeyEvent>
void AXBOpenGLWidget::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;

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

void AXBOpenGLWidget::on_timeout()
{
    update();
}

下面是mainwindow.cpp,main。cpp没变化,所以就不写了

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setCentralWidget(ui->openGLWidget);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_actDrawRect_triggered()
{
    ui->openGLWidget->drawShape(AXBOpenGLWidget::Rect);
}

void MainWindow::on_actClear_triggered()
{
//一些效果函数
    ui->openGLWidget->drawShape(AXBOpenGLWidget::None);
}

void MainWindow::on_actWireframe_triggered()
{
    ui->openGLWidget->setWirefame(ui->actWireframe->isChecked());
}

 这是ui图,注意三个地方,一个是窗口是openGLwidget,一个这个窗口要提升为头文件.h,让这个窗口和头文件联系起来(B站教程有),再就是底下的设置函数,这些B站AXB老师都有讲解,自己看视频很容易懂。

 后面就是那四个着色器代码了

shapes.vert

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCord;
out vec2 TexCord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
    gl_Position = projection*view*model*vec4(aPos.x, aPos.y, aPos.z, 1.0f);
    TexCord=aTexCord;
}

shapes.frag

#version 330 core
out vec4 FragColor;
out vec4 Color;
out vec4 textureColor;
in vec2 TexCord;
uniform sampler2D textureWall;
uniform sampler2D textureSmile;
uniform sampler2D textureSmall;
uniform float ratio;
void main(){
   textureColor = mix(texture(textureWall,TexCord),texture(textureSmile,TexCord),ratio);
//反色算法,就是1-rgb,最后加个alpha就行
FragColor=vec4(1.0-textureColor.r,1.0-textureColor.g,1.0-textureColor.b,1.0f);
  
}

剩下的那俩,一个vert和上面的vert一样,剩下那个就正常显示,很easy,不反色

 

全部结束了,希望对你有帮助!

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值